Re: [PNG] Meeting minutes - Jul 7th, 2025

On Mon, Jul 7, 2025 at 9:57 AM Fares Alhassen <falhassen@google.com> wrote:

> Dear all,
>
> Just to follow up on an action item in the minutes:
>
> Android has documentation about its use of PNG gainmaps for the screenshot
> use case:
> https://source.android.com/docs/core/graphics/hdr-screenshots#android16-hdr-shot
>
> While it provides a general gainmap framework that we might want to
> consider for PNG 4e, the spec does not document how ISO metadata is encoded.
>

The problem with that approach is that it really is just a packaged PNG -
IHDR all the way, I assume, to IEND whereas the established way of
including ancillary images in PNG is APNG.  In APNG the encoding is a
separate 'IHDR' in fcTL with the 'IDAT' data split across multiple chunks.

I do think APNG would have been better implemented the way gdAT is
implemented; it's much easier to write a nested parser, however it wasn't
done that way.  So I'd like to propose a _general_ solution which is
consistent with existing APNG parsers but applies not just to gainmaps but
any of the possible ancillary image requirements that have been raised and,
indeed, most that I am aware of.

The extension is simply two new chunks called 'iHDR' and 'iDAT'.  These
chunks are, indeed, IHDR and IDAT chunks but with a header which identifies
the ancillary image purpose and allows for the 'iDAT' chunks to be
interpreted in the correct order.

The proposal for the two chunks is:

*iHDR *[text keyword][instance id][IHDR data]
    *text keyword*: an 8 byte registered keyword containing 8 "keyword"
characters as defined for tEXt etc.  The registration space is separate
from that of text chunks (and iCCP and so on) and the length is exactly 8
characters.
*    instance id*: a 31-bit positive (greater than 0) unsigned integral
value.
    *IHDR data*: exactly as IHDR

*iDAT *[text keyword][instance id][sequence number][IDAT data]
    The first two fields must match an iHDR.
    *sequence number*: a PER INSTANCE 31-bit unsigned sequence number for
ordering starting at 0
    *IDAT data*: exactly as IDAT; deflate compressed data for the image

I use a fixed size header because it makes writing code to handle the
checking far easier.  This format can be understood independently of the
specific "keyword", so a library implementation does not need new code for
each new ancillary image type.

The format also allows for parallel decode and this is important for gain
maps and other ancillary images that might provide a per-pixel *transform *of
the data.  A streaming implementation of PNG decodes rows on-the-fly and,
in many cases, transforms row-by-row as well to obtain a convenient format
for processing.  Thus it is desirable to be able to parallel stream the
gain map row-by-row as the image it modifies is read row-by-row.  An
implementation can buffer the gainmap iDAT chunks (reordering them as
required) then run two separate decoders in parallel.  I'm working on this
for APNG support; it's not a requirement for APNG but it is a good
engineering solution and cleans up a lot of libpng code.

The format also allows for additional chunks containing ancillary
information for the ancillary image.  The twelve byte header should be
enough for a library implementation to match other, possibly unknown,
chunks to the correct ancillary image but my basic idea is an "lABL"
(label) chunk:

*lABL *[text keyword][instance id][sub-chunk][data]
    The twelve byte header identifies the corresponding ancillary image or,
when [instance id] is 0, all of the corresponding ancillary images with the
given keyword
    *sub-chunk* is a four-byte PNG chunk name with the "reserved" bit set,
for example *gAmP*, which is either publicly approved (as in gAmP) or a
privately defined (preferably with the definition itself published) format,
*gamP*.

Once again this is extensible without requiring additional code in any
library implementation.  The intention here is that a library should not
have to implement any more than the minimum support for reading and/or
writing PNG files.  In this case the library can extract ancillary images
(the difficult bit) without needing to know what is *in* them, or what they
mean!

The approach also allows for extensions which have been discussed but
previously rejected because they break existing encoders.  IHDR cannot have
new values for bit depth, colour type, filter method or compression method
because adding to those fields causes the canonical PNG image, the one in
IDAT, to be undecodable without explicit change to all the decoders.  This
is not true of iHDR because it *is* ancillary; a library can still store
the chunks even though it doesn't know how to decode them and, more
important, a decoder can simply discard them and decode what it knows how
to decode.

John Bowler <jbowler@acm.org>

>

Received on Monday, 7 July 2025 21:14:23 UTC