Skip to content

Commit dca1dbe

Browse files
authored
feat: skip PNG renders whose source has not changed (#147)
* feat: skip PNG renders whose source has not changed * chore: re-render every PNG once to stamp its source hash
1 parent 0323c4b commit dca1dbe

116 files changed

Lines changed: 84 additions & 6 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎brand/apparel/back-lockup-2400.png‎

90 Bytes

‎brand/apparel/chest-mark-1050.png‎

90 Bytes

‎brand/build/raster.py‎

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,41 @@
1+
import hashlib
12
import shutil
23
import subprocess
34
from pathlib import Path
45

6+
try:
7+
from PIL import Image, PngImagePlugin
8+
except ModuleNotFoundError:
9+
Image = PngImagePlugin = None
10+
511
_PNG_COLORS = 32 # palette size; flat art needs few (8 already looked clean; 32 = headroom)
12+
_SOURCE_KEY = "source-sha256"
13+
14+
15+
def _source_hash(svg_path: Path, width: int | None, height: int | None) -> str:
16+
h = hashlib.sha256(svg_path.read_bytes())
17+
h.update(f"|{width}|{height}|{_PNG_COLORS}".encode())
18+
return h.hexdigest()
19+
20+
21+
def _stored_hash(png_path: Path) -> str | None:
22+
if Image is None or not png_path.is_file():
23+
return None
24+
with Image.open(png_path) as im:
25+
return im.info.get(_SOURCE_KEY)
626

727

8-
def _quantize_png(path: Path, colors: int = _PNG_COLORS) -> None:
28+
def _quantize_png(path: Path, source_hash: str, colors: int = _PNG_COLORS) -> None:
929
"""Re-save a PNG as an indexed-palette image — visually lossless for flat
1030
art. FASTOCTREE preserves alpha, so it is correct for both the opaque
1131
social cards and the transparent marks. No-op if Pillow is unavailable."""
12-
try:
13-
from PIL import Image
14-
except ModuleNotFoundError:
32+
if Image is None:
1533
return
1634
im = Image.open(path).convert("RGBA")
1735
q = im.quantize(colors=colors, method=Image.Quantize.FASTOCTREE)
18-
q.save(path, format="PNG", optimize=True)
36+
info = PngImagePlugin.PngInfo()
37+
info.add_text(_SOURCE_KEY, source_hash)
38+
q.save(path, format="PNG", optimize=True, pnginfo=info)
1939

2040

2141
def export_png(
@@ -28,12 +48,15 @@ def export_png(
2848
exe = shutil.which("rsvg-convert")
2949
if exe is None:
3050
return False
51+
source_hash = _source_hash(svg_path, width, height)
52+
if _stored_hash(png_path) == source_hash:
53+
return True
3154
args = [exe]
3255
if width is not None:
3356
args += ["-w", str(width)]
3457
if height is not None:
3558
args += ["-h", str(height)]
3659
args += [str(svg_path), "-o", str(png_path)]
3760
subprocess.run(args, check=True)
38-
_quantize_png(png_path)
61+
_quantize_png(png_path, source_hash)
3962
return True

‎brand/org/apple-touch-icon-180.png‎

90 Bytes

‎brand/org/avatar-1024.png‎

90 Bytes

‎brand/org/avatar-circle-1024.png‎

90 Bytes

‎brand/org/boosty-cover.png‎

90 Bytes

‎brand/org/favicon-16.png‎

90 Bytes

‎brand/org/favicon-32.png‎

90 Bytes

‎brand/org/favicon-48.png‎

90 Bytes

0 commit comments

Comments
 (0)