You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I would also like to see some discussion of the issue raised by the now
well established ACES AP0 profile. It is described here: https://acescentral.com/knowledge-base-2/aces-working-spaces. Because one
of the AP0 end-points has a negative y the colour space can't be
represented in cHRM. There is also no support in cICP (fixing this raises
a boatload of issues.)
The specification of the cHRM chunk is, currently, that it only allows
unsigned values for the 4 chromaticities however the encoding supports
signed values as well (the encoding is a 32 bit encoding, unsigned values
only use 31 bits). As I've said simply extending the definition to allow
signed values; changing the word "unsigned" to "signed" at the relevant
place in the cHRM specification, does not invalidate any existing PNG cHRM
chunks and, while initially only applicable to ACES and other, similar,
workflows is easy to accommodate in all PNG implementations that I've seen
(perhaps some already do through lack of checking!)
The maximum PNG unsigned value is 0x7FFFFFFF. All the PNG
32-bit signed values are encoded with bit sequences outside this range
and are therefore rejected as invalid. All valid PNG unsigned values
are alsovalid signed values positive values and all the the
unsigned values have exactly the same 32-bit (4-byte) representation as the
identical PNG signed values.
All the 32-bit sequences which correspond to negative PNG signed values are
invalid PNG 32-bit unsigned encodings.
I'm simply pointing out that the "dead" space in the PNG 32-bit encoding of
PNG unsigned values (which currently results in the cHRM chunk being
rejected if used) can simply be used to fix this serious defect in the PNG
specification.
Given the existing wording in the PNG spec, I would consider the highest bit in the cHRM to be considered to be a reserved bit. Its value shall be zero (which is enforced by current implementations) and parsers shall reject files where it's not zero.
But it's perfectly acceptable for future versions of the spec to assign a new meaning to the reserved bit being one. Existing implementations will reject these files as not conforming to the current version of the spec, but that is fine, this is a new feature after all.
Writers that want to make use of it need to take care that older implementations may reject their files, but this is true with all new features.
OH! You're right!
I forgot that the PNG spec saves the high bit of a 4-byte unsigned int.
Well then, that is SUPER convenient. And sorry I missed that when you
originally said it above. (Side note: This isn't true for 2-byte unsigned
ints.)
You can't, of course, because of the negative y component for blue. AP0
is even wider than ProPhoto (which /can /be encoded in PNG). From SMPTE
ST2065-1.2021: https://doi.org/10.5594/SMPTE.ST2065-1.2021
Table 1 — ACES RGB primaries chromaticity values
Red x 0.7347 y 0.2653
Green x 0.0 y 1.0
Blue x 0.0001 y –0.077
Note that, while this fixes the four-byte values in cHRM it does not fix the two-byte chromaticity values in mDCV. However, there we are constrained by SMPTE-ST 2086 which forbids them:
5.3 xy Chromaticity Coordinates
xy chromaticity coordinates shall be calculated as specified in ISO 11664-3:2012 Section 7, and shall be specified with four decimal places. The x coordinate shall be in the range [0.0001, 0.7400] and the y coordinate shall be in the range [0.0001, 0.8400]
We are also constrained by physics, since a real mastering display cannot have imaginary primaries!
We need two test files. One with AP0 primaries, which explicitly encode a negative y component for blue. The other with AP1 primaries; the x and y values are all positive but the (computed) z value is 1 - 0.713 - 0.293 = -0.006
chris@SuperNomad:/mnt/c/Users/chris/Documents/PNG/cHRM$ pngcheck -c -v cHRM-AP0.png
File: cHRM-AP0.png (1569 bytes)
chunk IHDR at offset 0x0000c, length 13
32 x 32 image, 24-bit RGB, non-interlaced
chunk gAMA at offset 0x00025, length 4: 1.0000
chunk cHRM at offset 0x00035, length 32
White x = 0.32168 y = 0.33767, Red x = 0.73470 y = 0.26530
Green x = 0.00000 y = 1.00000, Blue x = 0.00010 y = -0.07699
chunk IDAT at offset 0x00061, length 1397
zlib: deflated, 32K window, default compression
chunk tEXt at offset 0x005e2, length 43, keyword: Comment
chunk IEND at offset 0x00619, length 0
No errors detected in cHRM-AP0.png (6 chunks, 48.9% compression).
chris@SuperNomad:/mnt/c/Users/chris/Documents/PNG/cHRM$ pngcheck -c -v cHRM-AP1.png
File: cHRM-AP1.png (1563 bytes)
chunk IHDR at offset 0x0000c, length 13
32 x 32 image, 24-bit RGB, non-interlaced
chunk gAMA at offset 0x00025, length 4: 1.0000
chunk cHRM at offset 0x00035, length 32
White x = 0.32168 y = 0.33767, Red x = 0.71300 y = 0.29300
Green x = 0.16500 y = 0.83000, Blue x = 0.12800 y = 0.04400
chunk IDAT at offset 0x00061, length 1397
zlib: deflated, 32K window, default compression
chunk tEXt at offset 0x005e2, length 37, keyword: Comment
chunk IEND at offset 0x00613, length 0
No errors detected in cHRM-AP1.png (6 chunks, 49.1% compression).
Activity
svgeesus commentedon Jul 7, 2025
John also wrote
All the 32-bit sequences which correspond to negative PNG signed values are
invalid PNG 32-bit unsigned encodings.
@leo-barnes wrote
And @ProgramMax agreed
As did I
Table 1 — ACES RGB primaries chromaticity values
svgeesus commentedon Jul 7, 2025
Agreed at 7 July 2025 meeting to add this correction to PNG 4th edition
Update `cHRM` to use four byte signed integer. Add ACES AP0 example a…
Update changes #527
svgeesus commentedon Jul 7, 2025
Note that, while this fixes the four-byte values in
cHRMit does not fix the two-byte chromaticity values inmDCV. However, there we are constrained by SMPTE-ST 2086 which forbids them:We are also constrained by physics, since a real mastering display cannot have imaginary primaries!
svgeesus commentedon Jul 7, 2025
We need two test files. One with AP0 primaries, which explicitly encode a negative
ycomponent for blue. The other with AP1 primaries; thexandyvalues are all positive but the (computed)zvalue is 1 - 0.713 - 0.293 = -0.006svgeesus commentedon Jul 7, 2025
Hmm, problem. TweakPNG allows me to input the AP0 primaries, but when I try to edit them again it displays the negative value as unsigned huge number.
It does let me save though and stock pngcheck 4.0.0 then complains - about green, not blue!
chris@SuperNomad:/mnt/c/Users/chris/Documents/PNG/cHRM$ pngcheck -c -v cHRM-AP0.png File: cHRM-AP0.png (1569 bytes) chunk IHDR at offset 0x0000c, length 13 32 x 32 image, 24-bit RGB, non-interlaced chunk gAMA at offset 0x00025, length 4: 1.0000 chunk cHRM at offset 0x00035, length 32: invalid green point 0 1 ERRORS DETECTED in cHRM-AP0.pngsvgeesus commentedon Jul 7, 2025
Oh, I see pngcheck gives an error (incorrectly) for any non-real primaries (outside the spectral locus):
Update `cHRM` to use four byte signed integer. Add ACES AP0 example a…
Update changes #527
svgeesus commentedon Jul 7, 2025
Test files (not yet in WPT, what are the pass criteria?) posted
cHRM-AP0.pngcHRM-AP1.pngsvgeesus commentedon Jul 7, 2025
My patched pngcheck shows the correct values.
chris@SuperNomad:/mnt/c/Users/chris/Documents/PNG/cHRM$ pngcheck -c -v cHRM-AP0.png File: cHRM-AP0.png (1569 bytes) chunk IHDR at offset 0x0000c, length 13 32 x 32 image, 24-bit RGB, non-interlaced chunk gAMA at offset 0x00025, length 4: 1.0000 chunk cHRM at offset 0x00035, length 32 White x = 0.32168 y = 0.33767, Red x = 0.73470 y = 0.26530 Green x = 0.00000 y = 1.00000, Blue x = 0.00010 y = -0.07699 chunk IDAT at offset 0x00061, length 1397 zlib: deflated, 32K window, default compression chunk tEXt at offset 0x005e2, length 43, keyword: Comment chunk IEND at offset 0x00619, length 0 No errors detected in cHRM-AP0.png (6 chunks, 48.9% compression). chris@SuperNomad:/mnt/c/Users/chris/Documents/PNG/cHRM$ pngcheck -c -v cHRM-AP1.png File: cHRM-AP1.png (1563 bytes) chunk IHDR at offset 0x0000c, length 13 32 x 32 image, 24-bit RGB, non-interlaced chunk gAMA at offset 0x00025, length 4: 1.0000 chunk cHRM at offset 0x00035, length 32 White x = 0.32168 y = 0.33767, Red x = 0.71300 y = 0.29300 Green x = 0.16500 y = 0.83000, Blue x = 0.12800 y = 0.04400 chunk IDAT at offset 0x00061, length 1397 zlib: deflated, 32K window, default compression chunk tEXt at offset 0x005e2, length 37, keyword: Comment chunk IEND at offset 0x00613, length 0 No errors detected in cHRM-AP1.png (6 chunks, 49.1% compression).Explain why negative values are backwards compatible #527
svgeesus commentedon Jul 8, 2025
I guess the pass criterion is simply that the image is not rejected as invalid.
Negative cHRM (#529)