Re: png++: diffs in test bench libpng12 vs. libpng16

John Bowler <[email protected]>
Newsgroups gmane.comp.graphics.png.devel
Message-ID <CAP7U399QkOXz4tF2h+Mtk+-Ar22vdx4UkFgeuHOWYF4FQy0kKg@mail.gmail.com>
On Sat, Nov 26, 2016 at 11:38 PM, Alex Shulgin <[email protected]> wrote:
> The way that test suite is designed is that it runs a number of conversions
> using png++ over about a 100 of pre-selected test files from the official
> pngsuite and compares the resulting files with the expected "standard"
> output files using cmp(1).

You mean direct comparison of numerical pixel values?

That doesn't work between major releases and sometimes fails within a
single major release.  It also doesn't work between different libpng
configurations; changing from floating to fixed point changes the
results as does changing the speed/accuracy configuration parameters.

Major releases do three major things in this area:

1) Fix bugs; 1.6 still has a long-standing really bad math error
(doing the gamma correction twice sometimes) and there are other,
lesser, errors.

2) Improve accuracy: this lead to small changes.  I think you saw the
effects of one change to how the RGB->gray coefficients are stored
that increased accuracy a very small amount.

3) Improve speed: sometimes this lessens accuracy and sometimes it
doesn't alter it but it does change the result; e.g. if a calculation
is accurate to +/-10 in the final number then a different calculation
can make two libpng versions different by up to 21!

> There seems to be a diff in the latest byte of the gAMA chunk (why?)

I couldn't tell from your email what changed (reformated diff -u of
two od streams is kind of difficult to decode).   The question is,
what is the *right* answer.  I'm assuming you set the gamma value in
the output and can look at the value as it passes through your hands.

Regardless of whether you use the FP or the fixed APIs PNG gamma
values should round-trip perfectly, but if you use an FP value like
1.000005, which is the PNG gamma value 100000.5, then I can imagine
that 1.2 would end up with 100,000 (round to 0), 1.6 should end up
with 100,001 (round to nearest) and another version could go back to
100,000 (round to even; the traditional high accuracy math choice).

I think it is quite likely that 1.2 didn't round in some cases; gamma
values are really only accurate to 1dp, so having 5dp accuracy is
definitely overkill.

> that is probably the reason why the complete IDAT chunk is completely
> different as well.

IDAT chunks depend on PNG line filter settings, zlib settings, IDAT
chunk size settings and potentially zlib version.  Those things can
potentially change between identical builds on different machines.
Expect IDAT chunks to change even between minor releases.

Still from sng, both files produce identical output:
...
> gAMA {1.0000}

You need five DP - the accuracy of a gAMA chunk - to know if it changed.

> 3. More interestingly, there is one failure where the differences are only
> in the IDAT chunk (but only in first 2 and latest 4 bytes before the final
> zero quadruplet):

The last 4 are the Adler32 checksum of the uncompressed data (used to
detect otherwise undetectable errors in the zlib stream if the PNG CRC
is ignored, which it often is on the last chunk because of the way
libpng works.)

The first 2 have been changed because the zlib window size has been
optimized.  I think that was done in 1.4.  The optimization is another
thing that can change between major releases.

> - Is it worth trying to find the root causes for these differences or was it
> never supposed to be 100% stable?

The PNGs will change with every major release and at least some of the
above can change in minor releases.  (Particularly as security fixes
are done in minor releases).

Other than as a way of discovering what might have changed in a
release I can't see any point:

> - Should I change my approach to testing or am I missing something?

Well, for sure test the *uncompressed* output.

When you do math using libpng expect it to change; even if 1.6 met the
requirements of human vision (+/-1%) there are major speed/time
tradeoffs.  Anyway, 8-bit encoded values cannot meet the requirements
for all images; 256 levels just aren't enough, so big errors creep in
to the darker colors regardless of the accuracy.

The only way to check the math stuff is to check it for the accuracy
you need, bearing in mind that libpng doesn't always do that well ;-)
People needing accuracy need to use 16-bit encodings and do the math
themselves if the accuracy doesn't correspond to the underlying libpng
assumption that the data is for human consumption.

> - Is there a way to adapt the png++ code to produce the same output both
> with libpng12 and 16?

Don't ask libpng to do math.  Most important of all, handle any gamma
yourself; it's easy to get an effectively exact answer if you have
floating point (ANSI-C float is sufficient), but bear in mind that the
actual accuracy of the data is severely limited with an 8-bit
encoding.

Any transform which changes the encoding is doing math, for example
converting a 16-bit value to an 8-bit value does math.  In most cases
(RGB to gray is an exception) the math is documented in the spec but
even so that doesn't help because:

1) libpng doesn't always do the right math; that's a bug.
2) The math produces exact encoded values, but doing it is slow, so
libpng currently uses approximations; that's a deliberate compromise.

John Bowler

------------------------------------------------------------------------------
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.