diff --git a/docs/src/sources-sprites.md b/docs/src/sources-sprites.md index 5b24cb8e..ac865b12 100644 --- a/docs/src/sources-sprites.md +++ b/docs/src/sources-sprites.md @@ -1,6 +1,7 @@ ## Sprite Sources -Given a directory with SVG images, Martin will generate a sprite -- a JSON index and a PNG image, for both low and highresolution displays. The SVG filenames without extension will be used as the sprites' image IDs (remember that one sprite and thus `sprite_id` contains multiple images). +Given a directory with SVG images, Martin will generate a sprite -- a JSON index and a PNG image, for both low and highresolution displays. +The SVG filenames without extension will be used as the sprites' image IDs (remember that one sprite and thus `sprite_id` contains multiple images). The images are searched recursively in the given directory, so subdirectory names will be used as prefixes for the image IDs. For example `icons/bicycle.svg` will be available as `icons/bicycle` sprite image. @@ -40,6 +41,19 @@ the PNG, there is a high DPI version available at `/sprite/@2x.json`. } ``` +##### Coloring at runtime via Signed Distance Fields (SDFs) + +If you want to set the color of a sprite at runtime, you will need use the [Signed Distance Fields (SDFs)](https://steamcdn-a.akamaihd.net/apps/valve/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf)-endpoints. +For example, maplibre does support the image being modified via the [`icon-color`](https://maplibre.org/maplibre-style-spec/layers/#icon-color) and [`icon-halo-color`](https://maplibre.org/maplibre-style-spec/layers/#icon-halo-color) properties if using SDFs. + +SDFs have the significant **downside of only allowing one color**. +If you want multiple colors, you will need to layer icons on top of each other. + +The following APIs are available: + +- `/sdf_sprite/.json` for getting a sprite index as SDF and +- `/sdf_sprite/.png` for getting sprite PNGs as SDF + #### Combining Multiple Sprites Multiple `sprite_id` values can be combined into one sprite with the same pattern as for tile diff --git a/docs/src/using.md b/docs/src/using.md index 82c11ee4..3e5c4056 100644 --- a/docs/src/using.md +++ b/docs/src/using.md @@ -2,18 +2,19 @@ Martin data is available via the HTTP `GET` endpoints: -| URL | Description | -|-----------------------------------------|------------------------------------------------| -| `/` | Web UI | -| `/catalog` | [List of all sources](#catalog) | -| `/{sourceID}` | [Source TileJSON](#source-tilejson) | -| `/{sourceID}/{z}/{x}/{y}` | Map Tiles | -| `/{source1},…,{sourceN}` | [Composite Source TileJSON](#source-tilejson) | -| `/{source1},…,{sourceN}/{z}/{x}/{y}` | [Composite Source Tiles](sources-composite.md) | -| `/sprite/{spriteID}[@2x].{json,png}` | [Sprite sources](sources-sprites.md) | -| `/font/{font}/{start}-{end}` | [Font source](sources-fonts.md) | -| `/font/{font1},…,{fontN}/{start}-{end}` | [Composite Font source](sources-fonts.md) | -| `/health` | Martin server health check: returns 200 `OK` | +| URL | Description | +|------------------------------------------|------------------------------------------------| +| `/` | Web UI | +| `/catalog` | [List of all sources](#catalog) | +| `/{sourceID}` | [Source TileJSON](#source-tilejson) | +| `/{sourceID}/{z}/{x}/{y}` | Map Tiles | +| `/{source1},…,{sourceN}` | [Composite Source TileJSON](#source-tilejson) | +| `/{source1},…,{sourceN}/{z}/{x}/{y}` | [Composite Source Tiles](sources-composite.md) | +| `/sprite/{spriteID}[@2x].{json,png}` | [Sprite sources](sources-sprites.md) | +| `/sdf_sprite/{spriteID}[@2x].{json,png}` | [SDF Sprite sources](sources-sprites.md) | +| `/font/{font}/{start}-{end}` | [Font source](sources-fonts.md) | +| `/font/{font1},…,{fontN}/{start}-{end}` | [Composite Font source](sources-fonts.md) | +| `/health` | Martin server health check: returns 200 `OK` | ### Duplicate Source ID diff --git a/martin/src/sprites/mod.rs b/martin/src/sprites/mod.rs index 07e1c769..033f9631 100644 --- a/martin/src/sprites/mod.rs +++ b/martin/src/sprites/mod.rs @@ -146,7 +146,7 @@ impl SpriteSources { /// Given a list of IDs in a format "id1,id2,id3", return a spritesheet with them all. /// `ids` may optionally end with "@2x" to request a high-DPI spritesheet. - pub async fn get_sprites(&self, ids: &str) -> SpriteResult { + pub async fn get_sprites(&self, ids: &str, as_sdf: bool) -> SpriteResult { let (ids, dpi) = if let Some(ids) = ids.strip_suffix("@2x") { (ids, 2) } else { @@ -162,7 +162,7 @@ impl SpriteSources { }) .collect::>>()?; - get_spritesheet(sprite_ids.into_iter(), dpi).await + get_spritesheet(sprite_ids.into_iter(), dpi, as_sdf).await } } @@ -175,6 +175,7 @@ async fn parse_sprite( name: String, path: PathBuf, pixel_ratio: u8, + as_sdf: bool, ) -> SpriteResult<(String, Sprite)> { let on_err = |e| SpriteError::IoError(e, path.clone()); @@ -186,7 +187,12 @@ async fn parse_sprite( let tree = Tree::from_data(&buffer, &Options::default()) .map_err(|e| SpriteParsingError(e, path.clone()))?; - let sprite = Sprite::new(tree, pixel_ratio).ok_or_else(|| SpriteInstError(path.clone()))?; + let sprite = if as_sdf { + Sprite::new_sdf(tree, pixel_ratio) + } else { + Sprite::new(tree, pixel_ratio) + }; + let sprite = sprite.ok_or_else(|| SpriteInstError(path.clone()))?; Ok((name, sprite)) } @@ -194,6 +200,7 @@ async fn parse_sprite( pub async fn get_spritesheet( sources: impl Iterator, pixel_ratio: u8, + as_sdf: bool, ) -> SpriteResult { // Asynchronously load all SVG files from the given sources let mut futures = Vec::new(); @@ -203,11 +210,14 @@ pub async fn get_spritesheet( for path in paths { let name = sprite_name(&path, &source.path) .map_err(|e| SpriteProcessingError(e, source.path.clone()))?; - futures.push(parse_sprite(name, path, pixel_ratio)); + futures.push(parse_sprite(name, path, pixel_ratio, as_sdf)); } } let sprites = try_join_all(futures).await?; let mut builder = SpritesheetBuilder::new(); + if as_sdf { + builder.make_sdf(); + } builder.sprites(sprites.into_iter().collect()); // TODO: decide if this is needed and/or configurable @@ -234,24 +244,32 @@ mod tests { let sprites = SpriteSources::resolve(&mut cfg).unwrap().0; assert_eq!(sprites.len(), 2); - test_src(sprites.values(), 1, "all_1").await; - test_src(sprites.values(), 2, "all_2").await; + //.sdf => generate sdf from png, add sdf == true + //- => does not generate sdf, omits sdf == true + for extension in ["_sdf", ""] { + test_src(sprites.values(), 1, "all_1", extension).await; + test_src(sprites.values(), 2, "all_2", extension).await; - test_src(sprites.get("src1").into_iter(), 1, "src1_1").await; - test_src(sprites.get("src1").into_iter(), 2, "src1_2").await; + test_src(sprites.get("src1").into_iter(), 1, "src1_1", extension).await; + test_src(sprites.get("src1").into_iter(), 2, "src1_2", extension).await; - test_src(sprites.get("src2").into_iter(), 1, "src2_1").await; - test_src(sprites.get("src2").into_iter(), 2, "src2_2").await; + test_src(sprites.get("src2").into_iter(), 1, "src2_1", extension).await; + test_src(sprites.get("src2").into_iter(), 2, "src2_2", extension).await; + } } async fn test_src( sources: impl Iterator, pixel_ratio: u8, filename: &str, + extension: &str, ) { - let path = PathBuf::from(format!("../tests/fixtures/sprites/expected/{filename}")); - - let sprites = get_spritesheet(sources, pixel_ratio).await.unwrap(); + let path = PathBuf::from(format!( + "../tests/fixtures/sprites/expected/{filename}{extension}" + )); + let sprites = get_spritesheet(sources, pixel_ratio, extension == "_sdf") + .await + .unwrap(); let mut json = serde_json::to_string_pretty(sprites.get_index()).unwrap(); json.push('\n'); let png = sprites.encode_png().unwrap(); diff --git a/martin/src/srv/server.rs b/martin/src/srv/server.rs index c4f3e0f3..75af0e6b 100755 --- a/martin/src/srv/server.rs +++ b/martin/src/srv/server.rs @@ -114,7 +114,9 @@ pub fn router(cfg: &mut web::ServiceConfig, #[allow(unused_variables)] usr_cfg: .service(get_tile); #[cfg(feature = "sprites")] - cfg.service(crate::srv::sprites::get_sprite_json) + cfg.service(crate::srv::sprites::get_sprite_sdf_json) + .service(crate::srv::sprites::get_sprite_json) + .service(crate::srv::sprites::get_sprite_sdf_png) .service(crate::srv::sprites::get_sprite_png); #[cfg(feature = "fonts")] diff --git a/martin/src/srv/sprites.rs b/martin/src/srv/sprites.rs index 55a61a81..d9d4c284 100644 --- a/martin/src/srv/sprites.rs +++ b/martin/src/srv/sprites.rs @@ -15,7 +15,18 @@ async fn get_sprite_png( path: Path, sprites: Data, ) -> ActixResult { - let sheet = get_sprite(&path, &sprites).await?; + let sheet = get_sprite(&path, &sprites, false).await?; + Ok(HttpResponse::Ok() + .content_type(ContentType::png()) + .body(sheet.encode_png().map_err(map_internal_error)?)) +} + +#[route("/sdf_sprite/{source_ids}.png", method = "GET", method = "HEAD")] +async fn get_sprite_sdf_png( + path: Path, + sprites: Data, +) -> ActixResult { + let sheet = get_sprite(&path, &sprites, true).await?; Ok(HttpResponse::Ok() .content_type(ContentType::png()) .body(sheet.encode_png().map_err(map_internal_error)?)) @@ -31,13 +42,31 @@ async fn get_sprite_json( path: Path, sprites: Data, ) -> ActixResult { - let sheet = get_sprite(&path, &sprites).await?; + let sheet = get_sprite(&path, &sprites, false).await?; Ok(HttpResponse::Ok().json(sheet.get_index())) } -async fn get_sprite(path: &SourceIDsRequest, sprites: &SpriteSources) -> ActixResult { +#[route( + "/sdf_sprite/{source_ids}.json", + method = "GET", + method = "HEAD", + wrap = "middleware::Compress::default()" +)] +async fn get_sprite_sdf_json( + path: Path, + sprites: Data, +) -> ActixResult { + let sheet = get_sprite(&path, &sprites, true).await?; + Ok(HttpResponse::Ok().json(sheet.get_index())) +} + +async fn get_sprite( + path: &SourceIDsRequest, + sprites: &SpriteSources, + as_sdf: bool, +) -> ActixResult { sprites - .get_sprites(&path.source_ids) + .get_sprites(&path.source_ids, as_sdf) .await .map_err(|e| match e { SpriteError::SpriteNotFound(_) => ErrorNotFound(e.to_string()), diff --git a/tests/expected/configured/sdf_spr_cmp.json b/tests/expected/configured/sdf_spr_cmp.json new file mode 100644 index 00000000..7a179b17 --- /dev/null +++ b/tests/expected/configured/sdf_spr_cmp.json @@ -0,0 +1,34 @@ +{ + "another_bicycle": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 26, + "y": 22, + "sdf": true + }, + "bear": { + "height": 22, + "pixelRatio": 1, + "width": 22, + "x": 26, + "y": 0, + "sdf": true + }, + "bicycle": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 0, + "y": 26, + "sdf": true + }, + "sub/circle": { + "height": 26, + "pixelRatio": 1, + "width": 26, + "x": 0, + "y": 0, + "sdf": true + } +} diff --git a/tests/expected/configured/sdf_spr_cmp.png b/tests/expected/configured/sdf_spr_cmp.png new file mode 100644 index 00000000..0575b9f7 Binary files /dev/null and b/tests/expected/configured/sdf_spr_cmp.png differ diff --git a/tests/expected/configured/sdf_spr_cmp.png.txt b/tests/expected/configured/sdf_spr_cmp.png.txt new file mode 100644 index 00000000..8f385201 --- /dev/null +++ b/tests/expected/configured/sdf_spr_cmp.png.txt @@ -0,0 +1 @@ +tests/output/configured/sdf_spr_cmp.png: PNG image data, 48 x 47, 8-bit gray+alpha, non-interlaced diff --git a/tests/expected/configured/sdf_spr_cmp_2.json b/tests/expected/configured/sdf_spr_cmp_2.json new file mode 100644 index 00000000..7da6e323 --- /dev/null +++ b/tests/expected/configured/sdf_spr_cmp_2.json @@ -0,0 +1,34 @@ +{ + "another_bicycle": { + "height": 36, + "pixelRatio": 2, + "width": 36, + "x": 84, + "y": 0, + "sdf": true + }, + "bear": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 46, + "y": 0, + "sdf": true + }, + "bicycle": { + "height": 36, + "pixelRatio": 2, + "width": 36, + "x": 84, + "y": 36, + "sdf": true + }, + "sub/circle": { + "height": 46, + "pixelRatio": 2, + "width": 46, + "x": 0, + "y": 0, + "sdf": true + } +} diff --git a/tests/expected/configured/sdf_spr_cmp_2.png b/tests/expected/configured/sdf_spr_cmp_2.png new file mode 100644 index 00000000..8f370ed6 Binary files /dev/null and b/tests/expected/configured/sdf_spr_cmp_2.png differ diff --git a/tests/expected/configured/sdf_spr_cmp_2.png.txt b/tests/expected/configured/sdf_spr_cmp_2.png.txt new file mode 100644 index 00000000..803547c0 --- /dev/null +++ b/tests/expected/configured/sdf_spr_cmp_2.png.txt @@ -0,0 +1 @@ +tests/output/configured/sdf_spr_cmp_2.png: PNG image data, 120 x 72, 8-bit gray+alpha, non-interlaced diff --git a/tests/expected/configured/sdf_spr_mysrc.json b/tests/expected/configured/sdf_spr_mysrc.json new file mode 100644 index 00000000..03636083 --- /dev/null +++ b/tests/expected/configured/sdf_spr_mysrc.json @@ -0,0 +1,10 @@ +{ + "bicycle": { + "height": 36, + "pixelRatio": 2, + "width": 36, + "x": 0, + "y": 0, + "sdf": true + } +} diff --git a/tests/expected/configured/sdf_spr_mysrc.png b/tests/expected/configured/sdf_spr_mysrc.png new file mode 100644 index 00000000..78f86130 Binary files /dev/null and b/tests/expected/configured/sdf_spr_mysrc.png differ diff --git a/tests/expected/configured/sdf_spr_mysrc.png.txt b/tests/expected/configured/sdf_spr_mysrc.png.txt new file mode 100644 index 00000000..7c5ce711 --- /dev/null +++ b/tests/expected/configured/sdf_spr_mysrc.png.txt @@ -0,0 +1 @@ +tests/output/configured/sdf_spr_mysrc.png: PNG image data, 36 x 36, 8-bit gray+alpha, non-interlaced diff --git a/tests/expected/configured/sdf_spr_src1.json b/tests/expected/configured/sdf_spr_src1.json new file mode 100644 index 00000000..d90ee2d8 --- /dev/null +++ b/tests/expected/configured/sdf_spr_src1.json @@ -0,0 +1,26 @@ +{ + "another_bicycle": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 26, + "y": 22, + "sdf": true + }, + "bear": { + "height": 22, + "pixelRatio": 1, + "width": 22, + "x": 26, + "y": 0, + "sdf": true + }, + "sub/circle": { + "height": 26, + "pixelRatio": 1, + "width": 26, + "x": 0, + "y": 0, + "sdf": true + } +} diff --git a/tests/expected/configured/sdf_spr_src1.png b/tests/expected/configured/sdf_spr_src1.png new file mode 100644 index 00000000..77717779 Binary files /dev/null and b/tests/expected/configured/sdf_spr_src1.png differ diff --git a/tests/expected/configured/sdf_spr_src1.png.txt b/tests/expected/configured/sdf_spr_src1.png.txt new file mode 100644 index 00000000..8d4e37eb --- /dev/null +++ b/tests/expected/configured/sdf_spr_src1.png.txt @@ -0,0 +1 @@ +tests/output/configured/sdf_spr_src1.png: PNG image data, 48 x 43, 8-bit gray+alpha, non-interlaced diff --git a/tests/expected/configured/sdf_spr_src1_.json b/tests/expected/configured/sdf_spr_src1_.json new file mode 100644 index 00000000..9b7683ec --- /dev/null +++ b/tests/expected/configured/sdf_spr_src1_.json @@ -0,0 +1,26 @@ +{ + "another_bicycle": { + "height": 36, + "pixelRatio": 2, + "width": 36, + "x": 84, + "y": 0, + "sdf": true + }, + "bear": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 46, + "y": 0, + "sdf": true + }, + "sub/circle": { + "height": 46, + "pixelRatio": 2, + "width": 46, + "x": 0, + "y": 0, + "sdf": true + } +} diff --git a/tests/expected/configured/sdf_spr_src1_.png b/tests/expected/configured/sdf_spr_src1_.png new file mode 100644 index 00000000..d61be9e0 Binary files /dev/null and b/tests/expected/configured/sdf_spr_src1_.png differ diff --git a/tests/expected/configured/sdf_spr_src1_.png.txt b/tests/expected/configured/sdf_spr_src1_.png.txt new file mode 100644 index 00000000..b1c908d2 --- /dev/null +++ b/tests/expected/configured/sdf_spr_src1_.png.txt @@ -0,0 +1 @@ +tests/output/configured/sdf_spr_src1_.png: PNG image data, 120 x 46, 8-bit gray+alpha, non-interlaced diff --git a/tests/fixtures/sprites/expected/all_1_sdf.json b/tests/fixtures/sprites/expected/all_1_sdf.json new file mode 100644 index 00000000..7a179b17 --- /dev/null +++ b/tests/fixtures/sprites/expected/all_1_sdf.json @@ -0,0 +1,34 @@ +{ + "another_bicycle": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 26, + "y": 22, + "sdf": true + }, + "bear": { + "height": 22, + "pixelRatio": 1, + "width": 22, + "x": 26, + "y": 0, + "sdf": true + }, + "bicycle": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 0, + "y": 26, + "sdf": true + }, + "sub/circle": { + "height": 26, + "pixelRatio": 1, + "width": 26, + "x": 0, + "y": 0, + "sdf": true + } +} diff --git a/tests/fixtures/sprites/expected/all_1_sdf.png b/tests/fixtures/sprites/expected/all_1_sdf.png new file mode 100644 index 00000000..0575b9f7 Binary files /dev/null and b/tests/fixtures/sprites/expected/all_1_sdf.png differ diff --git a/tests/fixtures/sprites/expected/all_2_sdf.json b/tests/fixtures/sprites/expected/all_2_sdf.json new file mode 100644 index 00000000..7da6e323 --- /dev/null +++ b/tests/fixtures/sprites/expected/all_2_sdf.json @@ -0,0 +1,34 @@ +{ + "another_bicycle": { + "height": 36, + "pixelRatio": 2, + "width": 36, + "x": 84, + "y": 0, + "sdf": true + }, + "bear": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 46, + "y": 0, + "sdf": true + }, + "bicycle": { + "height": 36, + "pixelRatio": 2, + "width": 36, + "x": 84, + "y": 36, + "sdf": true + }, + "sub/circle": { + "height": 46, + "pixelRatio": 2, + "width": 46, + "x": 0, + "y": 0, + "sdf": true + } +} diff --git a/tests/fixtures/sprites/expected/all_2_sdf.png b/tests/fixtures/sprites/expected/all_2_sdf.png new file mode 100644 index 00000000..8f370ed6 Binary files /dev/null and b/tests/fixtures/sprites/expected/all_2_sdf.png differ diff --git a/tests/fixtures/sprites/expected/src1_1_sdf.json b/tests/fixtures/sprites/expected/src1_1_sdf.json new file mode 100644 index 00000000..d90ee2d8 --- /dev/null +++ b/tests/fixtures/sprites/expected/src1_1_sdf.json @@ -0,0 +1,26 @@ +{ + "another_bicycle": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 26, + "y": 22, + "sdf": true + }, + "bear": { + "height": 22, + "pixelRatio": 1, + "width": 22, + "x": 26, + "y": 0, + "sdf": true + }, + "sub/circle": { + "height": 26, + "pixelRatio": 1, + "width": 26, + "x": 0, + "y": 0, + "sdf": true + } +} diff --git a/tests/fixtures/sprites/expected/src1_1_sdf.png b/tests/fixtures/sprites/expected/src1_1_sdf.png new file mode 100644 index 00000000..77717779 Binary files /dev/null and b/tests/fixtures/sprites/expected/src1_1_sdf.png differ diff --git a/tests/fixtures/sprites/expected/src1_2_sdf.json b/tests/fixtures/sprites/expected/src1_2_sdf.json new file mode 100644 index 00000000..9b7683ec --- /dev/null +++ b/tests/fixtures/sprites/expected/src1_2_sdf.json @@ -0,0 +1,26 @@ +{ + "another_bicycle": { + "height": 36, + "pixelRatio": 2, + "width": 36, + "x": 84, + "y": 0, + "sdf": true + }, + "bear": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 46, + "y": 0, + "sdf": true + }, + "sub/circle": { + "height": 46, + "pixelRatio": 2, + "width": 46, + "x": 0, + "y": 0, + "sdf": true + } +} diff --git a/tests/fixtures/sprites/expected/src1_2_sdf.png b/tests/fixtures/sprites/expected/src1_2_sdf.png new file mode 100644 index 00000000..d61be9e0 Binary files /dev/null and b/tests/fixtures/sprites/expected/src1_2_sdf.png differ diff --git a/tests/fixtures/sprites/expected/src2_1_sdf.json b/tests/fixtures/sprites/expected/src2_1_sdf.json new file mode 100644 index 00000000..bf3f7792 --- /dev/null +++ b/tests/fixtures/sprites/expected/src2_1_sdf.json @@ -0,0 +1,10 @@ +{ + "bicycle": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 0, + "y": 0, + "sdf": true + } +} diff --git a/tests/fixtures/sprites/expected/src2_1_sdf.png b/tests/fixtures/sprites/expected/src2_1_sdf.png new file mode 100644 index 00000000..1fd1735f Binary files /dev/null and b/tests/fixtures/sprites/expected/src2_1_sdf.png differ diff --git a/tests/fixtures/sprites/expected/src2_2_sdf.json b/tests/fixtures/sprites/expected/src2_2_sdf.json new file mode 100644 index 00000000..03636083 --- /dev/null +++ b/tests/fixtures/sprites/expected/src2_2_sdf.json @@ -0,0 +1,10 @@ +{ + "bicycle": { + "height": 36, + "pixelRatio": 2, + "width": 36, + "x": 0, + "y": 0, + "sdf": true + } +} diff --git a/tests/fixtures/sprites/expected/src2_2_sdf.png b/tests/fixtures/sprites/expected/src2_2_sdf.png new file mode 100644 index 00000000..78f86130 Binary files /dev/null and b/tests/fixtures/sprites/expected/src2_2_sdf.png differ diff --git a/tests/test.sh b/tests/test.sh index 2b8ea6a1..e82074f6 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -351,18 +351,30 @@ test_png pmt_0_0_0 pmt/0/0/0 test_png pmt2_0_0_0 pmt2/0/0/0 # HTTP pmtiles # Test sprites -test_jsn spr_src1 sprite/src1.json -test_png spr_src1 sprite/src1.png -test_jsn spr_src1_2x sprite/src1@2x.json -test_png spr_src1_2x sprite/src1@2x.png -test_jsn spr_mysrc sprite/mysrc.json -test_png spr_mysrc sprite/mysrc.png -test_jsn spr_mysrc_2x sprite/mysrc@2x.json -test_png spr_mysrc_2x sprite/mysrc@2x.png -test_jsn spr_cmp sprite/src1,mysrc.json -test_png spr_cmp sprite/src1,mysrc.png -test_jsn spr_cmp_2x sprite/src1,mysrc@2x.json -test_png spr_cmp_2x sprite/src1,mysrc@2x.png +test_jsn spr_src1 sprite/src1.json +test_jsn sdf_spr_src1 sdf_sprite/src1.json +test_png spr_src1 sprite/src1.png +test_png sdf_spr_src1 sdf_sprite/src1.png +test_jsn spr_src1_2x sprite/src1@2x.json +test_jsn sdf_spr_src1_ sdf_sprite/src1@2x.json +test_png spr_src1_2x sprite/src1@2x.png +test_png sdf_spr_src1_ sdf_sprite/src1@2x.png +test_jsn spr_mysrc sprite/mysrc.json +test_jsn sdf_spr_mysrc sdf_sprite/mysrc.json +test_png spr_mysrc sprite/mysrc.png +test_png sdf_spr_mysrc sdf_sprite/mysrc.png +test_jsn spr_mysrc_2x sprite/mysrc@2x.json +test_jsn sdf_spr_mysrc sdf_sprite/mysrc@2x.json +test_png spr_mysrc_2x sprite/mysrc@2x.png +test_png sdf_spr_mysrc sdf_sprite/mysrc@2x.png +test_jsn spr_cmp sprite/src1,mysrc.json +test_jsn sdf_spr_cmp sdf_sprite/src1,mysrc.json +test_png spr_cmp sprite/src1,mysrc.png +test_png sdf_spr_cmp sdf_sprite/src1,mysrc.png +test_jsn spr_cmp_2x sprite/src1,mysrc@2x.json +test_jsn sdf_spr_cmp_2 sdf_sprite/src1,mysrc@2x.json +test_png spr_cmp_2x sprite/src1,mysrc@2x.png +test_png sdf_spr_cmp_2 sdf_sprite/src1,mysrc@2x.png # Test fonts test_font font_1 font/Overpass%20Mono%20Light/0-255