fix: add MapLibre retina sprite builder
This commit is contained in:
75
tools/cm95_maplibre_retina_sprite.py
Executable file
75
tools/cm95_maplibre_retina_sprite.py
Executable file
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import Any
|
||||
|
||||
|
||||
def _scale_pair_list(value: Any, factor: int) -> Any:
|
||||
if not isinstance(value, list):
|
||||
return value
|
||||
out = []
|
||||
for item in value:
|
||||
if isinstance(item, list) and len(item) == 2:
|
||||
out.append([item[0] * factor, item[1] * factor])
|
||||
else:
|
||||
out.append(item)
|
||||
return out
|
||||
|
||||
|
||||
def _retina_entry(entry: dict[str, Any], factor: int) -> dict[str, Any]:
|
||||
out = dict(entry)
|
||||
for key in ("x", "y", "width", "height"):
|
||||
if key in out:
|
||||
out[key] = int(out[key]) * factor
|
||||
if isinstance(out.get("content"), list) and len(out["content"]) == 4:
|
||||
out["content"] = [v * factor for v in out["content"]]
|
||||
for key in ("stretchX", "stretchY"):
|
||||
if key in out:
|
||||
out[key] = _scale_pair_list(out[key], factor)
|
||||
out["pixelRatio"] = factor
|
||||
return out
|
||||
|
||||
|
||||
def build_retina(prefix: Path, *, factor: int = 2) -> dict[str, Any]:
|
||||
src_png = prefix.with_suffix(".png")
|
||||
src_json = prefix.with_suffix(".json")
|
||||
if not src_png.is_file() or not src_json.is_file():
|
||||
raise FileNotFoundError(f"missing sprite pair for prefix {prefix}")
|
||||
if shutil.which("convert") is None:
|
||||
raise RuntimeError("ImageMagick convert is required")
|
||||
|
||||
dst_png = prefix.with_name(prefix.name + f"@{factor}x").with_suffix(".png")
|
||||
dst_json = prefix.with_name(prefix.name + f"@{factor}x").with_suffix(".json")
|
||||
data = json.loads(src_json.read_text(encoding="utf-8"))
|
||||
retina = {name: _retina_entry(dict(entry), factor) for name, entry in data.items()}
|
||||
|
||||
subprocess.run(
|
||||
["convert", str(src_png), "-filter", "point", "-resize", f"{factor * 100}%", str(dst_png)],
|
||||
check=True,
|
||||
)
|
||||
dst_json.write_text(json.dumps(retina, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
return {
|
||||
"prefix": str(prefix),
|
||||
"entry_count": len(retina),
|
||||
"png": str(dst_png),
|
||||
"json": str(dst_json),
|
||||
"pixel_ratio": factor,
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(description="Build MapLibre @2x sprite assets from a 1x sprite pair.")
|
||||
ap.add_argument("prefix", nargs="+", type=Path)
|
||||
args = ap.parse_args()
|
||||
result = [build_retina(prefix) for prefix in args.prefix]
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user