Add support for Animated PNG

Desktop / Chromium - scroggo [chromium.org] - 14 March 2017 17:36 EDT

Update the browser accept header to state that APNG is supported. Split the decoding of PNG images into two stages: parsing and decoding. During parsing, chunks are handled one of three ways:- acTL and fcTL chunks, which specify properties of an APNG are read and the properties are stored. If they contain an error before IDAT, the image is treated as a static PNG, as recommended by the spec [1]. If they contain an error after IDAT, this is a failure, since some frames may have been decoded. CRCs are calculated and compared to their expected values. Mismatches are considered errors.- fdAT and IDAT chunks have their locations stored for decoding later. Any ordering violations result in either a static image or failed image, depending on whether IDAT has been seen yet.
- Other chunks before IDAT are passed to libpng for processing.

Each frame is decoded as if it were a complete PNG file. fdATs are converted to IDATs (and their CRCs are ignored**) and the IHDR is modified for subset frames. The rowAvailable callback positions subset frames properly within the full image buffer. For a static PNG or the first frame decoded (assuming its size matches IHDR) the png struct used during parsing is reused. Otherwise, a new png struct is created for the duration of the decode.

Follow the APNG spec as closely as possible and use the APNG test page [2] as a guide for intended behavior. All of the valid APNG on the test page work as expected. For the invalid APNG:- Errors that occur before IDAT show the default image, as intended- Errors afterwards are typically treated as failures (see Future work, below)- The final three images draw incorrectly. They have incorrectly sized IDATs/ fdATs. These could be respected by checking the warning that libpng sends (in the case of extra data) or keeping track of how many rows have been seen (in the case of too little data), although we currently ignore this for static PNGs anyway. (crbug.com/698808)

The first frame can be decoded progressively. Other frames are not reported until the following fcTL chunk has been reached during parse.

Add a reference test, modified from WebKit's fast/images/animated-png.html with the following changes:- use window.internals.advanceImageAnimation instead of waiting for a timeout, for more reliable testing- disable two of the images, which look the same to my eyes, but are not identical (I suspect due to blending differences as compared to how the reference tests were created).

Add gtests. Update progressive tests to reuse a SharedBuffer, rather than recreating it, to reduce test run-time.

Fix a bug in ImageFrameGenerator where it called setMemoryAllocator before setting the data, and add related tests.

Stop calling setFailed inside ImageDecoder::initFrameBuffer, return false instead. Clients are now expected to call setFailed (update the WEBP and GIF clients). This is safer, because setFailed may delete an object that called initFrameBuffer.

In WEBPImageDecoder: rename frameIsLoadedAtIndex to frameIsReceivedAtIndex, for consistency with PNGImageDecoder.

Always call ImageFrame::setStatus last (e.g. after calling onInitFrameBuffer, correctAlphaWhenFrameBufferSawNoAlpha).

Future work:- Revert to showing the default image for failures past IDAT. This is tricky, since the client may be holding on to previous frames, and we need to make sure they switch to using the IDAT frame, even if it is not part of the animation. For now, we mark the decoder as having failed. (crbug.com/699675)

** We cannot allow libpng to check the CRC since we modified the chunk. We could check the CRC directly as a separate step.

[1] https://wiki.mozilla.org/APNG_Specification [2] https://philip.html5.org/tests/apng/tests.html

BUG=1171 BUG=437662

Initial patch is a re-upload of issue 2386453003 at patchset 39 (http://crrev.com/2386453003#ps1260001) by joostouwerling@google.com, which also used https://codereview.chromium.org/1567053002/ by maxstepin@gmail.com as a reference.

Review-Url: https://codereview.chromium.org/2618633004 Cr-Commit-Position: refs/heads/master@{#456840}

7d2b8c4 Add support for Animated PNG
.../loader/mime_sniffing_resource_handler.cc | 4 +-
.../mime_sniffing_resource_handler_unittest.cc | 8 +-
.../philip/tests/2d.drawImage.animated.poster.html | 20 +-
.../LayoutTests/http/tests/misc/xhtml-expected.txt | 2 +-
.../LayoutTests/images/animated-png-expected.html | 16 +
.../WebKit/LayoutTests/images/animated-png.html | 63 ++
.../LayoutTests/images/resources/apng00-ref.png | Bin 0 -> 428 bytes
.../WebKit/LayoutTests/images/resources/apng00.png | Bin 0 -> 786 bytes
.../LayoutTests/images/resources/apng01-ref.png | Bin 0 -> 215 bytes
.../WebKit/LayoutTests/images/resources/apng01.png | Bin 0 -> 859 bytes
.../LayoutTests/images/resources/apng02-ref.png | Bin 0 -> 235 bytes
.../WebKit/LayoutTests/images/resources/apng02.png | Bin 0 -> 952 bytes
.../LayoutTests/images/resources/apng04-ref.png | Bin 0 -> 824 bytes
.../WebKit/LayoutTests/images/resources/apng04.png | Bin 0 -> 1488 bytes
.../LayoutTests/images/resources/apng08-ref.png | Bin 0 -> 1449 bytes
.../WebKit/LayoutTests/images/resources/apng08.png | Bin 0 -> 2158 bytes
.../LayoutTests/images/resources/apng10-ref.png | Bin 0 -> 445 bytes
.../WebKit/LayoutTests/images/resources/apng10.png | Bin 0 -> 925 bytes
.../LayoutTests/images/resources/apng11-ref.png | Bin 0 -> 240 bytes
.../WebKit/LayoutTests/images/resources/apng11.png | Bin 0 -> 893 bytes
.../LayoutTests/images/resources/apng12-ref.png | Bin 0 -> 277 bytes
.../WebKit/LayoutTests/images/resources/apng12.png | Bin 0 -> 992 bytes
.../LayoutTests/images/resources/apng14-ref.png | Bin 0 -> 962 bytes
.../WebKit/LayoutTests/images/resources/apng14.png | Bin 0 -> 1616 bytes
.../LayoutTests/images/resources/apng18-ref.png | Bin 0 -> 2245 bytes
.../WebKit/LayoutTests/images/resources/apng18.png | Bin 0 -> 3049 bytes
.../LayoutTests/images/resources/apng24-ref.png | Bin 0 -> 1130 bytes
.../WebKit/LayoutTests/images/resources/apng24.png | Bin 0 -> 347 bytes
.../LayoutTests/images/resources/apng26-ref.png | Bin 0 -> 4208 bytes
.../WebKit/LayoutTests/images/resources/apng26.png | Bin 0 -> 453 bytes
.../LayoutTests/images/resources/empty-frame.png | Bin 0 -> 288 bytes
.../png-animated-idat-not-part-of-animation.png | Bin 0 -> 4174 bytes
.../png-animated-idat-part-of-animation.png | Bin 0 -> 434 bytes
.../png-animated-three-independent-frames.png | Bin 0 -> 404 bytes
.../platform/graphics/DeferredImageDecoderTest.cpp | 59 +-
.../platform/graphics/ImageFrameGenerator.cpp | 27 +-
.../platform/image-decoders/ImageDecoder.cpp | 7 +-
.../Source/platform/image-decoders/ImageDecoder.h | 18 +-
.../image-decoders/ImageDecoderTestHelpers.cpp | 34 +-
.../image-decoders/ImageDecoderTestHelpers.h | 4 +
.../image-decoders/gif/GIFImageDecoder.cpp | 5 +-
.../image-decoders/gif/GIFImageDecoderTest.cpp | 30 +
.../image-decoders/png/PNGImageDecoder.cpp | 303 +++++--
.../platform/image-decoders/png/PNGImageDecoder.h | 33 +-
.../image-decoders/png/PNGImageDecoderTest.cpp | 926 ++++++++++++++++++++-
.../platform/image-decoders/png/PNGImageReader.cpp | 613 +++++++++++++-
.../platform/image-decoders/png/PNGImageReader.h | 107 ++-
.../image-decoders/webp/WEBPImageDecoder.cpp | 9 +-
48 files changed, 2053 insertions(+), 235 deletions(-)

Upstream: git.chromium.org


  • Share