Skip to content

feat(png)!: migrate PNG encode to gamut-png - #47

Merged
justin13888 merged 1 commit into
masterfrom
23-migrate-png-encode-gamut
Jul 19, 2026
Merged

feat(png)!: migrate PNG encode to gamut-png#47
justin13888 merged 1 commit into
masterfrom
23-migrate-png-encode-gamut

Conversation

@justin13888

Copy link
Copy Markdown
Collaborator

Closes #23.

Stacked on #46; merge order #42#44#45#46 → this.

What is Delivered

  • Encode-only swap to gamut_png::PngEncoder (decode stays zune-png — that is Migrate PNG decode to gamut-png #32). 8-bit keeps the existing 16→8 conversion into ImageRef<Rgb8>; 16-bit feeds samples straight to ImageRef<Rgb16>, deleting the manual big-endian packing. No dual encode backends: png-encode-zune is gone, png-encode = dep:gamut-png, zune-png remains only behind png-decode-zune (grep-verified).
  • Metadata via encoder setterswith_exif (bare TIFF from the Migrate metadata stack to gamut-metadata #19 gamut-exif builder), with_icc_profile, with_xmp — replacing img-parts PNG chunk muxing on this path. The PNG path drops container-embed/zune-runtime entirely.
  • PngEncodeConfig replaces ZunePngEncodeConfig, exposing exactly gamut's knobs (compression Store/Fast/Default/Best; filter None/Fixed/MinSumAbs/BruteForce; auto_reduce) — nothing invented, per the intentional-API rule. EncodeOptions::PngZunePngGamut; codec id png/zunepng/gamut.
  • New test: PNG metadata round-trip asserting eXIf + iCCP + iTXt/XMP chunk presence and exact 16-bit pixel fidelity through the independent zune decoder.

Bench vs the zune baseline (issue requirement; ref gamut#224)

1024×1024 synthetic 16-bit gradient, release, avg of 5, defaults:

size encode time
zune 16-bit 6,302,493 B 20.5 ms
gamut 16-bit 26,245 B (−99.6%) 96.3 ms
zune 8-bit 3,151,917 B 2.6 ms
gamut 8-bit 13,041 B (−99.6%) 47.3 ms

zune 0.5 was emitting essentially uncompressed output; gamut's filtering + DEFLATE costs real time for a massive size win. Gradient content exaggerates the ratio; Store/Fast are exposed for speed-first callers.

Verification Done

Run independently after implementation: cargo test --workspace 384/0; --features full 533/0 (fixtures ran); --features serde 366/0; clippy workspace + full-features -D warnings both 0 issues; --no-default-features --features png standalone builds clean; feature-graph grep confirms zune-png is decode-only. lefthook green.

PNG encoding moves from zune-png to gamut_png::PngEncoder. The 8-bit path
keeps the existing 16->8 high-byte conversion into ImageRef<Rgb8>; the 16-bit
path hands the RgbImage samples to ImageRef<Rgb16> directly, dropping the
manual big-endian packing (gamut serialises 16-bit PNG itself). Decode stays
on zune-png until the gamut decoder migration (#32) — encode-only swap, no
dual encode backends.

Metadata now embeds through the encoder itself — with_exif (bare TIFF from
the gamut-exif builder), with_icc_profile, with_xmp — replacing the img-parts
PNG chunk muxing on this path; the PNG path no longer pulls container-embed
or zune-runtime. A new metadata round-trip test asserts eXIf/iCCP/iTXt chunk
presence plus exact 16-bit pixel round-trip through the zune decoder.

Config surface: ZunePngEncodeConfig -> PngEncodeConfig, exposing exactly
gamut's encoder knobs (compression level Store/Fast/Default/Best, filter
strategy None/Fixed/MinSumAbs/BruteForce, auto_reduce) — no invented options.
EncodeOptions::PngZune -> PngGamut; encode codec id png/zune -> png/gamut.
Feature graph: tier-4 collapses, png-encode = dep:gamut-png; png-encode-zune
deleted; zune-png remains only behind png-decode-zune.

Baseline vs zune (1024x1024 synthetic 16-bit gradient, release, avg of 5,
default options; ref gamut#224): output size 6,302,493 -> 26,245 bytes
(-99.6%; zune 0.5 emitted essentially uncompressed output), encode time
20.5ms -> 96.3ms (16-bit) and 2.6ms -> 47.3ms (8-bit) — the size win costs
real filtering+DEFLATE time; Store/Fast levels are exposed for speed-first
callers. Gradient content exaggerates the size ratio.

BREAKING CHANGE: ZunePngEncodeConfig/EncodeOptions::PngZune renamed; PNG
encode output bytes change (properly compressed).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate PNG encode to gamut-png

1 participant