The one lawful PNG representation gamut-png cannot write. The crate says so itself, at
src/decoder.rs:1327:
the encoder cannot write interlaced files or greyscale/truecolour tRNS colour keys
Every tRNS write in the crate is inside the indexed path (src/encoder.rs:319, :511).
Ancillary (src/ancillary.rs:84-105) has no trns field, and Reduced
(src/reduce.rs:17-49) has no colour-key variant. The decoder already reads one
(src/decoded.rs:135), so only the encoder half is missing.
Consequence
An RGBA image with binary alpha and more than 256 colours must keep a full fourth channel, where
RGB + a 6-byte tRNS chunk is lawful and drops 25% of the payload before DEFLATE runs. Same for
GrayAlpha8 with binary alpha.
Scope
- Scan for: alpha ∈ {0, 255} only, and a colour no opaque pixel uses.
- Finding an unused key without the full colour set: test a handful of candidate keys for
collision in one pass, rather than materialising every colour.
- New
Reduced variants, and a tRNS slot in the non-indexed pre_idat closure
(src/encoder.rs:339, :357).
with_transparent_cleanup (landed in #224) is a natural precondition: it collapses every
invisible pixel to one colour, which is exactly what a colour key needs.
Verification
The libpng oracle decodes the result, and gamut's own decoder already resolves colour keys, so
both directions are checkable immediately. tests/size_contract.rs's sprite_rgba8 budget is
deliberately loose at 1.00 today because this axis is missing — tightening it is the acceptance
test.
Refs #224
The one lawful PNG representation
gamut-pngcannot write. The crate says so itself, atsrc/decoder.rs:1327:Every
tRNSwrite in the crate is inside the indexed path (src/encoder.rs:319,:511).Ancillary(src/ancillary.rs:84-105) has notrnsfield, andReduced(
src/reduce.rs:17-49) has no colour-key variant. The decoder already reads one(
src/decoded.rs:135), so only the encoder half is missing.Consequence
An RGBA image with binary alpha and more than 256 colours must keep a full fourth channel, where
RGB+ a 6-bytetRNSchunk is lawful and drops 25% of the payload before DEFLATE runs. Same forGrayAlpha8with binary alpha.Scope
collision in one pass, rather than materialising every colour.
Reducedvariants, and atRNSslot in the non-indexedpre_idatclosure(
src/encoder.rs:339,:357).with_transparent_cleanup(landed in #224) is a natural precondition: it collapses everyinvisible pixel to one colour, which is exactly what a colour key needs.
Verification
The libpng oracle decodes the result, and gamut's own decoder already resolves colour keys, so
both directions are checkable immediately.
tests/size_contract.rs'ssprite_rgba8budget isdeliberately loose at 1.00 today because this axis is missing — tightening it is the acceptance
test.
Refs #224