Skip to content

Note the sRGB color space in PNG and APNG output - #2389

Open
robto09 wants to merge 2 commits into
cashapp:masterfrom
robto09:fix/issue-40-note-srgb
Open

Note the sRGB color space in PNG and APNG output#2389
robto09 wants to merge 2 commits into
cashapp:masterfrom
robto09:fix/issue-40-note-srgb

Conversation

@robto09

@robto09 robto09 commented Aug 10, 2026

Copy link
Copy Markdown

Summary

Paparazzi writes snapshot images as PNG and APNG files whose pixels are in the sRGB color space, but the files never declared it. Decoders have to guess the color space of every snapshot. I made the color space explicit by writing the PNG sRGB chunk with the Perceptual rendering intent, as the first step toward wide color screenshots (see #40).

What I changed

  • PngConstants: added the SRGB chunk header and a constant for the Perceptual rendering intent (value 0).
  • ApngWriter: every PNG now carries an sRGB chunk with a one byte payload. I placed the chunk after the header chunks (IHDR, and acTL/fcTL for animated output) and before IDAT, which is where the PNG specification requires it.
  • ApngReader: I taught the reader to accept the sRGB chunk and skip its payload, so files written by the updated writer round trip cleanly. Without this branch the code does not compile, because the chunk dispatcher requires exhaustive handling of every known chunk.
  • CHANGELOG.md: added an entry under Unreleased.

Why a real chunk and not a workaround

I used the sRGB chunk because it is the standard PNG mechanism for declaring the color space of image data, defined in the PNG 1.2 specification. Any conforming decoder, including ImageIO and Android's BitmapFactory, understands it. This keeps every snapshot self-describing and gives us a solid baseline when we add wide color gamut support later.

How I tested it

  • Added ApngWriterTest.writesSRGBColorSpaceChunkAfterIHDR, which asserts the chunk id, its position between IHDR and IDAT, the Perceptual intent byte, and a valid CRC.
  • Added ApngReaderTest.decodesPNGWithSRGBColorSpaceChunk, which writes a PNG with the updated writer and decodes it with the reader.
  • Updated the four existing chunk sequence tests to include the new chunk.
  • APNG suite: 20 tests, 0 failures.
  • Full :paparazzi:test suite: 126 tests, 0 failures.
  • I also validated the output independently: a Python PNG chunk parser reports IHDR, sRGB, IDAT, IEND with intent byte 0 and valid CRCs, and macOS sips decodes the file.

Follow ups

  • The delta-*.png diagnostics written via ImageIO.write in ImageUtils are a separate output path and I left them unchanged; they can get the same treatment later.
  • Third party PNGs with unknown ancillary chunks (gAMA, PLTE, tEXt) still fail in ApngReader because Header.valueOf throws for chunk types outside the enum; a graceful skip for unknown chunks would be a good follow up.

Fixes #40

robto09 added 2 commits August 9, 2026 22:12
Paparazzi writes PNG and APNG snapshots whose pixels are in the sRGB color space, but the files never declared it. As the first step toward wide color screenshots, write the PNG sRGB chunk with the perceptual rendering intent so every decoder knows the image data is sRGB.

The sRGB chunk goes after the header chunks and before the image data, which is where the PNG specification requires it. The APNG reader now accepts the chunk and skips its single payload byte, so files written by the updated writer round trip cleanly.

Refs: cashapp#40
Assert that a static PNG written by ApngWriter carries an sRGB chunk with the perceptual rendering intent between IHDR and IDAT, and that animated output places the chunk before IDAT as well. Also verify the APNG reader decodes a file written by the updated writer.
@geoff-powell geoff-powell added this to the 2.0.0-alpha06 milestone Aug 10, 2026
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.

Wide color screenshots

2 participants