feat: tiered PNG compression for artifact saving

This commit is contained in:
yyc12345 committed 2026-10-16 22:09:49 +08:00
1 parent d0a3d48c17
commit 02dc22364a
2 files changed
+67 -4

No files matched your search

@@ -24,9 +24,16 @@ class TempArtSink(Sink):
return Address(AddressDomain.Temporary, *self.__comps)
def push(self, env: Environment, art: Artifact) -> None:
"""
Save given artifact to temporary directory.
The artifact is explicitly saved with the intermediary (faster)
encoder configuration, because temporary artifacts are transient
and usually get re-loaded and re-encoded later.
"""
p = self.push_hint(env)
LOGGER.info("Saving temporary artifact: %s", p)
artio.save_artifact(art, p)
artio.save_artifact(art, p, for_intermediary=True)
def repull(self, env: Environment) -> Artifact:
"""
+59 -3
View File
@@ -42,9 +42,65 @@ def load_artifact(p: Path) -> Artifact:
return PIL.Image.open(p, "r", ["PNG"])
def save_artifact(art: Artifact, p: Path) -> None:
def save_artifact(art: Artifact, p: Path, for_intermediary: bool = False) -> None:
"""
Save given artifact as PNG file.
The PNG encoder settings are selected in tiers by artifact size:
smaller artifacts are saved with lighter compression while larger
ones are saved with stronger compression. This tiered configuration
exists to strike a balance between final artifact file size and
assembly speed, since strong compression is slow and only pays off
on large images.
:param art: The artifact to be saved.
:param p: The path to saved PNG file.
:param for_intermediary: Whether this artifact is saved as an
intermediary. Intermediaries are transient and will be loaded
and re-encoded later, so they are always saved with the
balanced, faster configuration regardless of their size.
"""
LOGGER.debug("Saving artifact: %s", p)
art.save(p, format="PNG")
# compute optimize flag and compress level based on artifact size.
optimize: bool
compress_level: int
if for_intermediary:
# If this image is saved for intermediary,
# we use default balanced config.
optimize = False
compress_level = 6
else:
pixels = art.width * art.height
if pixels >= 1024**2:
# >= 1024x1024, super large icon,
# use best compression config.
optimize = True
compress_level = 9
elif pixels >= 256**2:
# >= 256x256 and < 1024x1024, large icon,
# use max compress level.
optimize = False
compress_level = 9
elif pixels >= 64**2:
# >= 64x64 and < 256x256, medium icon,
# use higher compress level.
optimize = False
compress_level = 8
elif pixels >= 16**2:
# >= 16x16 and < 64x64, small icon,
# use higher compress level.
optimize = False
compress_level = 7
else:
# < 16x16, very small icon,
# use default balanced config.
optimize = False
compress_level = 6
# save as PNG with computed optimize flag and compress level
art.save(p, format="PNG", optimize=optimize, compress_level=compress_level)
def dup_artifact(src: Path, dst: Path) -> None:
@@ -124,7 +180,7 @@ def link_artifact(src: Path, dst: Path) -> None:
f'No common directory between "{src}" and "{dst}", '
f"can not create relative symlink."
)
# SAFETY: the symlink target must always be stored with POSIX
# separators ("/"). On Windows, "relative_to" yields a
# backslash-separated path, and "symlink_to" stores the target string