| Index: third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.cpp
|
| diff --git a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.cpp b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.cpp
|
| deleted file mode 100644
|
| index 3a3268c73a202c8b843a9633cea6435f451ae2de..0000000000000000000000000000000000000000
|
| --- a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.cpp
|
| +++ /dev/null
|
| @@ -1,690 +0,0 @@
|
| -/*
|
| - * Copyright (C) 2006 Apple Computer, Inc.
|
| - * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
|
| - *
|
| - * Portions are Copyright (C) 2001 mozilla.org
|
| - *
|
| - * Other contributors:
|
| - * Stuart Parmenter <stuart@mozilla.com>
|
| - *
|
| - * This library is free software; you can redistribute it and/or
|
| - * modify it under the terms of the GNU Lesser General Public
|
| - * License as published by the Free Software Foundation; either
|
| - * version 2.1 of the License, or (at your option) any later version.
|
| - *
|
| - * This library is distributed in the hope that it will be useful,
|
| - * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
| - * Lesser General Public License for more details.
|
| - *
|
| - * You should have received a copy of the GNU Lesser General Public
|
| - * License along with this library; if not, write to the Free Software
|
| - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
| - *
|
| - * Alternatively, the contents of this file may be used under the terms
|
| - * of either the Mozilla Public License Version 1.1, found at
|
| - * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
|
| - * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
|
| - * (the "GPL"), in which case the provisions of the MPL or the GPL are
|
| - * applicable instead of those above. If you wish to allow use of your
|
| - * version of this file only under the terms of one of those two
|
| - * licenses (the MPL or the GPL) and not to allow others to use your
|
| - * version of this file under the LGPL, indicate your decision by
|
| - * deletingthe provisions above and replace them with the notice and
|
| - * other provisions required by the MPL or the GPL, as the case may be.
|
| - * If you do not delete the provisions above, a recipient may use your
|
| - * version of this file under any of the LGPL, the MPL or the GPL.
|
| - */
|
| -
|
| -#include "platform/image-decoders/png/PNGImageReader.h"
|
| -
|
| -#include <memory>
|
| -#include "platform/image-decoders/FastSharedBufferReader.h"
|
| -#include "platform/image-decoders/SegmentReader.h"
|
| -#include "platform/image-decoders/png/PNGImageDecoder.h"
|
| -#include "platform/wtf/PtrUtil.h"
|
| -#include "zlib.h"
|
| -
|
| -namespace {
|
| -
|
| -inline blink::PNGImageDecoder* imageDecoder(png_structp png) {
|
| - return static_cast<blink::PNGImageDecoder*>(png_get_progressive_ptr(png));
|
| -}
|
| -
|
| -void PNGAPI pngHeaderAvailable(png_structp png, png_infop) {
|
| - imageDecoder(png)->HeaderAvailable();
|
| -}
|
| -
|
| -void PNGAPI pngRowAvailable(png_structp png,
|
| - png_bytep row,
|
| - png_uint_32 rowIndex,
|
| - int state) {
|
| - imageDecoder(png)->RowAvailable(row, rowIndex, state);
|
| -}
|
| -
|
| -void PNGAPI pngFrameComplete(png_structp png, png_infop) {
|
| - imageDecoder(png)->FrameComplete();
|
| -}
|
| -
|
| -void PNGAPI pngFailed(png_structp png, png_const_charp) {
|
| - longjmp(JMPBUF(png), 1);
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -namespace blink {
|
| -
|
| -PNGImageReader::PNGImageReader(PNGImageDecoder* decoder, size_t initial_offset)
|
| - : width_(0),
|
| - height_(0),
|
| - decoder_(decoder),
|
| - initial_offset_(initial_offset),
|
| - read_offset_(initial_offset),
|
| - progressive_decode_offset_(0),
|
| - idat_offset_(0),
|
| - idat_is_part_of_animation_(false),
|
| - expect_idats_(true),
|
| - is_animated_(false),
|
| - parsed_signature_(false),
|
| - parsed_ihdr_(false),
|
| - parse_completed_(false),
|
| - reported_frame_count_(0),
|
| - next_sequence_number_(0),
|
| - fctl_needs_dat_chunk_(false),
|
| - ignore_animation_(false) {
|
| - png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0);
|
| - info_ = png_create_info_struct(png_);
|
| - png_set_progressive_read_fn(png_, decoder_, nullptr, pngRowAvailable,
|
| - pngFrameComplete);
|
| -}
|
| -
|
| -PNGImageReader::~PNGImageReader() {
|
| - png_destroy_read_struct(png_ ? &png_ : 0, info_ ? &info_ : 0, 0);
|
| - DCHECK(!png_ && !info_);
|
| -}
|
| -
|
| -// This method reads from the FastSharedBufferReader, starting at offset,
|
| -// and returns |length| bytes in the form of a pointer to a const png_byte*.
|
| -// This function is used to make it easy to access data from the reader in a
|
| -// png friendly way, and pass it to libpng for decoding.
|
| -//
|
| -// Pre-conditions before using this:
|
| -// - |reader|.size() >= |read_offset| + |length|
|
| -// - |buffer|.size() >= |length|
|
| -// - |length| <= |kBufferSize|
|
| -//
|
| -// The reason for the last two precondition is that currently the png signature
|
| -// plus IHDR chunk (8B + 25B = 33B) is the largest chunk that is read using this
|
| -// method. If the data is not consecutive, it is stored in |buffer|, which must
|
| -// have the size of (at least) |length|, but there's no need for it to be larger
|
| -// than |kBufferSize|.
|
| -static constexpr size_t kBufferSize = 33;
|
| -const png_byte* ReadAsConstPngBytep(const FastSharedBufferReader& reader,
|
| - size_t read_offset,
|
| - size_t length,
|
| - char* buffer) {
|
| - DCHECK(length <= kBufferSize);
|
| - return reinterpret_cast<const png_byte*>(
|
| - reader.GetConsecutiveData(read_offset, length, buffer));
|
| -}
|
| -
|
| -bool PNGImageReader::ShouldDecodeWithNewPNG(size_t index) const {
|
| - if (!png_)
|
| - return true;
|
| - const bool first_frame_decode_in_progress = progressive_decode_offset_;
|
| - const bool frame_size_matches_ihdr =
|
| - frame_info_[index].frame_rect == IntRect(0, 0, width_, height_);
|
| - if (index)
|
| - return first_frame_decode_in_progress || !frame_size_matches_ihdr;
|
| - return !first_frame_decode_in_progress && !frame_size_matches_ihdr;
|
| -}
|
| -
|
| -// Return false on a fatal error.
|
| -bool PNGImageReader::Decode(SegmentReader& data, size_t index) {
|
| - if (index >= frame_info_.size())
|
| - return true;
|
| -
|
| - const FastSharedBufferReader reader(&data);
|
| -
|
| - if (!is_animated_) {
|
| - if (setjmp(JMPBUF(png_)))
|
| - return false;
|
| - DCHECK_EQ(0u, index);
|
| - progressive_decode_offset_ += ProcessData(
|
| - reader, frame_info_[0].start_offset + progressive_decode_offset_, 0);
|
| - return true;
|
| - }
|
| -
|
| - DCHECK(is_animated_);
|
| -
|
| - const bool decode_with_new_png = ShouldDecodeWithNewPNG(index);
|
| - if (decode_with_new_png) {
|
| - ClearDecodeState(0);
|
| - png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0);
|
| - info_ = png_create_info_struct(png_);
|
| - png_set_progressive_read_fn(png_, decoder_, pngHeaderAvailable,
|
| - pngRowAvailable, pngFrameComplete);
|
| - }
|
| -
|
| - if (setjmp(JMPBUF(png_)))
|
| - return false;
|
| -
|
| - if (decode_with_new_png)
|
| - StartFrameDecoding(reader, index);
|
| -
|
| - if (!index && (!FirstFrameFullyReceived() || progressive_decode_offset_)) {
|
| - const bool decoded_entire_frame = ProgressivelyDecodeFirstFrame(reader);
|
| - if (!decoded_entire_frame)
|
| - return true;
|
| - progressive_decode_offset_ = 0;
|
| - } else {
|
| - DecodeFrame(reader, index);
|
| - }
|
| -
|
| - static png_byte iend[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 174, 66, 96, 130};
|
| - png_process_data(png_, info_, iend, 12);
|
| - png_destroy_read_struct(&png_, &info_, 0);
|
| - DCHECK(!png_ && !info_);
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void PNGImageReader::StartFrameDecoding(const FastSharedBufferReader& reader,
|
| - size_t index) {
|
| - // If the frame is the size of the whole image, just re-process all header
|
| - // data up to the first frame.
|
| - const IntRect& frame_rect = frame_info_[index].frame_rect;
|
| - if (frame_rect == IntRect(0, 0, width_, height_)) {
|
| - ProcessData(reader, initial_offset_, idat_offset_);
|
| - return;
|
| - }
|
| -
|
| - // Process the IHDR chunk, but change the width and height so it reflects
|
| - // the frame's width and height. ImageDecoder will apply the x,y offset.
|
| - constexpr size_t kHeaderSize = kBufferSize;
|
| - char read_buffer[kHeaderSize];
|
| - const png_byte* chunk =
|
| - ReadAsConstPngBytep(reader, initial_offset_, kHeaderSize, read_buffer);
|
| - png_byte* header = reinterpret_cast<png_byte*>(read_buffer);
|
| - if (chunk != header)
|
| - memcpy(header, chunk, kHeaderSize);
|
| - png_save_uint_32(header + 16, frame_rect.Width());
|
| - png_save_uint_32(header + 20, frame_rect.Height());
|
| - // IHDR has been modified, so tell libpng to ignore CRC errors.
|
| - png_set_crc_action(png_, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
|
| - png_process_data(png_, info_, header, kHeaderSize);
|
| -
|
| - // Process the rest of the header chunks.
|
| - ProcessData(reader, initial_offset_ + kHeaderSize,
|
| - idat_offset_ - kHeaderSize);
|
| -}
|
| -
|
| -// Determine if the bytes 4 to 7 of |chunk| indicate that it is a |tag| chunk.
|
| -// - The length of |chunk| must be >= 8
|
| -// - The length of |tag| must be = 4
|
| -static inline bool IsChunk(const png_byte* chunk, const char* tag) {
|
| - return memcmp(chunk + 4, tag, 4) == 0;
|
| -}
|
| -
|
| -bool PNGImageReader::ProgressivelyDecodeFirstFrame(
|
| - const FastSharedBufferReader& reader) {
|
| - size_t offset = frame_info_[0].start_offset;
|
| -
|
| - // Loop while there is enough data to do progressive decoding.
|
| - while (reader.size() >= offset + 8) {
|
| - char read_buffer[8];
|
| - // At the beginning of each loop, the offset is at the start of a chunk.
|
| - const png_byte* chunk = ReadAsConstPngBytep(reader, offset, 8, read_buffer);
|
| - const png_uint_32 length = png_get_uint_32(chunk);
|
| - DCHECK(length <= PNG_UINT_31_MAX);
|
| -
|
| - // When an fcTL or IEND chunk is encountered, the frame data has ended.
|
| - // Return true, since all frame data is decoded.
|
| - if (IsChunk(chunk, "fcTL") || IsChunk(chunk, "IEND"))
|
| - return true;
|
| -
|
| - // If this chunk was already decoded, move on to the next.
|
| - if (progressive_decode_offset_ >= offset + length + 12) {
|
| - offset += length + 12;
|
| - continue;
|
| - }
|
| -
|
| - // Three scenarios are possible here:
|
| - // 1) Some bytes of this chunk were already decoded in a previous call.
|
| - // Continue from there.
|
| - // 2) This is an fdAT chunk. Convert it to an IDAT chunk to decode.
|
| - // 3) This is any other chunk. Pass it to libpng for processing.
|
| - size_t end_offset_chunk = offset + length + 12;
|
| -
|
| - if (progressive_decode_offset_ >= offset + 8) {
|
| - offset = progressive_decode_offset_;
|
| - } else if (IsChunk(chunk, "fdAT")) {
|
| - ProcessFdatChunkAsIdat(length);
|
| - // Skip the sequence number.
|
| - offset += 12;
|
| - } else {
|
| - png_process_data(png_, info_, const_cast<png_byte*>(chunk), 8);
|
| - offset += 8;
|
| - }
|
| -
|
| - size_t bytes_left_in_chunk = end_offset_chunk - offset;
|
| - size_t bytes_decoded = ProcessData(reader, offset, bytes_left_in_chunk);
|
| - progressive_decode_offset_ = offset + bytes_decoded;
|
| - if (bytes_decoded < bytes_left_in_chunk)
|
| - return false;
|
| - offset += bytes_decoded;
|
| - }
|
| -
|
| - return false;
|
| -}
|
| -
|
| -void PNGImageReader::ProcessFdatChunkAsIdat(png_uint_32 fdat_length) {
|
| - // An fdAT chunk is build up as follows:
|
| - // - |length| (4B)
|
| - // - fdAT tag (4B)
|
| - // - sequence number (4B)
|
| - // - frame data (|length| - 4B)
|
| - // - CRC (4B)
|
| - // Thus, to reformat this into an IDAT chunk, do the following:
|
| - // - write |length| - 4 as the new length, since the sequence number
|
| - // must be removed.
|
| - // - change the tag to IDAT.
|
| - // - omit the sequence number from the data part of the chunk.
|
| - png_byte chunk_idat[] = {0, 0, 0, 0, 'I', 'D', 'A', 'T'};
|
| - png_save_uint_32(chunk_idat, fdat_length - 4);
|
| - // The CRC is incorrect when applied to the modified fdAT.
|
| - png_set_crc_action(png_, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
|
| - png_process_data(png_, info_, chunk_idat, 8);
|
| -}
|
| -
|
| -void PNGImageReader::DecodeFrame(const FastSharedBufferReader& reader,
|
| - size_t index) {
|
| - size_t offset = frame_info_[index].start_offset;
|
| - size_t end_offset = offset + frame_info_[index].byte_length;
|
| - char read_buffer[8];
|
| -
|
| - while (offset < end_offset) {
|
| - const png_byte* chunk = ReadAsConstPngBytep(reader, offset, 8, read_buffer);
|
| - const png_uint_32 length = png_get_uint_32(chunk);
|
| - DCHECK(length <= PNG_UINT_31_MAX);
|
| -
|
| - if (IsChunk(chunk, "fdAT")) {
|
| - ProcessFdatChunkAsIdat(length);
|
| - // The frame data and the CRC span |length| bytes, so skip the
|
| - // sequence number and process |length| bytes to decode the frame.
|
| - ProcessData(reader, offset + 12, length);
|
| - } else {
|
| - png_process_data(png_, info_, const_cast<png_byte*>(chunk), 8);
|
| - ProcessData(reader, offset + 8, length + 4);
|
| - }
|
| -
|
| - offset += 12 + length;
|
| - }
|
| -}
|
| -
|
| -// Compute the CRC and compare to the stored value.
|
| -static bool CheckCrc(const FastSharedBufferReader& reader,
|
| - size_t chunk_start,
|
| - size_t chunk_length) {
|
| - constexpr size_t kSizeNeededForfcTL = 26 + 4;
|
| - char read_buffer[kSizeNeededForfcTL];
|
| - DCHECK(chunk_length + 4 <= kSizeNeededForfcTL);
|
| - const png_byte* chunk = ReadAsConstPngBytep(reader, chunk_start + 4,
|
| - chunk_length + 4, read_buffer);
|
| -
|
| - char crc_buffer[4];
|
| - const png_byte* crc_position = ReadAsConstPngBytep(
|
| - reader, chunk_start + 8 + chunk_length, 4, crc_buffer);
|
| - png_uint_32 crc = png_get_uint_32(crc_position);
|
| - return crc == crc32(crc32(0, Z_NULL, 0), chunk, chunk_length + 4);
|
| -}
|
| -
|
| -bool PNGImageReader::CheckSequenceNumber(const png_byte* position) {
|
| - png_uint_32 sequence = png_get_uint_32(position);
|
| - if (sequence != next_sequence_number_ || sequence > PNG_UINT_31_MAX)
|
| - return false;
|
| -
|
| - ++next_sequence_number_;
|
| - return true;
|
| -}
|
| -
|
| -// Return false if there was a fatal error; true otherwise.
|
| -bool PNGImageReader::Parse(SegmentReader& data, ParseQuery query) {
|
| - if (parse_completed_)
|
| - return true;
|
| -
|
| - const FastSharedBufferReader reader(&data);
|
| -
|
| - if (!ParseSize(reader))
|
| - return false;
|
| -
|
| - if (!decoder_->IsDecodedSizeAvailable())
|
| - return true;
|
| -
|
| - // For non animated images (identified by no acTL chunk before the IDAT),
|
| - // there is no need to continue parsing.
|
| - if (!is_animated_) {
|
| - FrameInfo frame;
|
| - frame.start_offset = read_offset_;
|
| - // This should never be read in this case, but initialize just in case.
|
| - frame.byte_length = kFirstFrameIndicator;
|
| - frame.duration = 0;
|
| - frame.frame_rect = IntRect(0, 0, width_, height_);
|
| - frame.disposal_method = ImageFrame::DisposalMethod::kDisposeKeep;
|
| - frame.alpha_blend = ImageFrame::AlphaBlendSource::kBlendAtopBgcolor;
|
| - DCHECK(frame_info_.IsEmpty());
|
| - frame_info_.push_back(frame);
|
| - parse_completed_ = true;
|
| - return true;
|
| - }
|
| -
|
| - if (query == ParseQuery::kSize)
|
| - return true;
|
| -
|
| - DCHECK_EQ(ParseQuery::kMetaData, query);
|
| - DCHECK(is_animated_);
|
| -
|
| - // Loop over the data and manually register all frames. Nothing is passed to
|
| - // libpng for processing. A frame is registered on the next fcTL chunk or
|
| - // when the IEND chunk is found. This ensures that only complete frames are
|
| - // reported, unless there is an error in the stream.
|
| - char read_buffer[kBufferSize];
|
| - while (reader.size() >= read_offset_ + 8) {
|
| - const png_byte* chunk =
|
| - ReadAsConstPngBytep(reader, read_offset_, 8, read_buffer);
|
| - const size_t length = png_get_uint_32(chunk);
|
| - if (length > PNG_UINT_31_MAX)
|
| - return false;
|
| -
|
| - const bool idat = IsChunk(chunk, "IDAT");
|
| - if (idat && !expect_idats_)
|
| - return false;
|
| -
|
| - const bool fd_at = IsChunk(chunk, "fdAT");
|
| - if (fd_at && expect_idats_)
|
| - return false;
|
| -
|
| - if (fd_at || (idat && idat_is_part_of_animation_)) {
|
| - fctl_needs_dat_chunk_ = false;
|
| - if (!new_frame_.start_offset) {
|
| - // Beginning of a new frame's data.
|
| - new_frame_.start_offset = read_offset_;
|
| -
|
| - if (frame_info_.IsEmpty()) {
|
| - // This is the first frame. Report it immediately so it can be
|
| - // decoded progressively.
|
| - new_frame_.byte_length = kFirstFrameIndicator;
|
| - frame_info_.push_back(new_frame_);
|
| - }
|
| - }
|
| -
|
| - if (fd_at) {
|
| - if (reader.size() < read_offset_ + 8 + 4)
|
| - return true;
|
| - const png_byte* sequence_position =
|
| - ReadAsConstPngBytep(reader, read_offset_ + 8, 4, read_buffer);
|
| - if (!CheckSequenceNumber(sequence_position))
|
| - return false;
|
| - }
|
| -
|
| - } else if (IsChunk(chunk, "fcTL") || IsChunk(chunk, "IEND")) {
|
| - // This marks the end of the previous frame.
|
| - if (new_frame_.start_offset) {
|
| - new_frame_.byte_length = read_offset_ - new_frame_.start_offset;
|
| - if (frame_info_[0].byte_length == kFirstFrameIndicator) {
|
| - frame_info_[0].byte_length = new_frame_.byte_length;
|
| - } else {
|
| - frame_info_.push_back(new_frame_);
|
| - if (IsChunk(chunk, "fcTL")) {
|
| - if (frame_info_.size() >= reported_frame_count_)
|
| - return false;
|
| - } else { // IEND
|
| - if (frame_info_.size() != reported_frame_count_)
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - new_frame_.start_offset = 0;
|
| - }
|
| -
|
| - if (reader.size() < read_offset_ + 12 + length)
|
| - return true;
|
| -
|
| - if (IsChunk(chunk, "IEND")) {
|
| - parse_completed_ = true;
|
| - return true;
|
| - }
|
| -
|
| - if (length != 26 || !CheckCrc(reader, read_offset_, length))
|
| - return false;
|
| -
|
| - chunk =
|
| - ReadAsConstPngBytep(reader, read_offset_ + 8, length, read_buffer);
|
| - if (!ParseFrameInfo(chunk))
|
| - return false;
|
| -
|
| - expect_idats_ = false;
|
| - } else if (IsChunk(chunk, "acTL")) {
|
| - // There should only be one acTL chunk, and it should be before the
|
| - // IDAT chunk.
|
| - return false;
|
| - }
|
| -
|
| - read_offset_ += 12 + length;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -// If |length| == 0, read until the stream ends. Return number of bytes
|
| -// processed.
|
| -size_t PNGImageReader::ProcessData(const FastSharedBufferReader& reader,
|
| - size_t offset,
|
| - size_t length) {
|
| - const char* segment;
|
| - size_t total_processed_bytes = 0;
|
| - while (reader.size() > offset) {
|
| - size_t segment_length = reader.GetSomeData(segment, offset);
|
| - if (length > 0 && segment_length + total_processed_bytes > length)
|
| - segment_length = length - total_processed_bytes;
|
| -
|
| - png_process_data(png_, info_,
|
| - reinterpret_cast<png_byte*>(const_cast<char*>(segment)),
|
| - segment_length);
|
| - offset += segment_length;
|
| - total_processed_bytes += segment_length;
|
| - if (total_processed_bytes == length)
|
| - return length;
|
| - }
|
| - return total_processed_bytes;
|
| -}
|
| -
|
| -// Process up to the start of the IDAT with libpng.
|
| -// Return false for a fatal error. True otherwise.
|
| -bool PNGImageReader::ParseSize(const FastSharedBufferReader& reader) {
|
| - if (decoder_->IsDecodedSizeAvailable())
|
| - return true;
|
| -
|
| - char read_buffer[kBufferSize];
|
| -
|
| - if (setjmp(JMPBUF(png_)))
|
| - return false;
|
| -
|
| - if (!parsed_signature_) {
|
| - if (reader.size() < read_offset_ + 8)
|
| - return true;
|
| -
|
| - const png_byte* chunk =
|
| - ReadAsConstPngBytep(reader, read_offset_, 8, read_buffer);
|
| - png_process_data(png_, info_, const_cast<png_byte*>(chunk), 8);
|
| - read_offset_ += 8;
|
| - parsed_signature_ = true;
|
| - new_frame_.start_offset = 0;
|
| - }
|
| -
|
| - // Process APNG chunks manually, pass other chunks to libpng.
|
| - for (png_uint_32 length = 0; reader.size() >= read_offset_ + 8;
|
| - read_offset_ += length + 12) {
|
| - const png_byte* chunk =
|
| - ReadAsConstPngBytep(reader, read_offset_, 8, read_buffer);
|
| - length = png_get_uint_32(chunk);
|
| -
|
| - if (IsChunk(chunk, "IDAT")) {
|
| - // Done with header chunks.
|
| - idat_offset_ = read_offset_;
|
| - fctl_needs_dat_chunk_ = false;
|
| - if (ignore_animation_)
|
| - is_animated_ = false;
|
| - if (!is_animated_ || 1 == reported_frame_count_)
|
| - decoder_->SetRepetitionCount(kAnimationNone);
|
| - if (!decoder_->SetSize(width_, height_))
|
| - return false;
|
| - decoder_->SetColorSpace();
|
| - decoder_->HeaderAvailable();
|
| - return true;
|
| - }
|
| -
|
| - // Wait until the entire chunk is available for parsing simplicity.
|
| - if (reader.size() < read_offset_ + length + 12)
|
| - break;
|
| -
|
| - if (IsChunk(chunk, "acTL")) {
|
| - if (ignore_animation_)
|
| - continue;
|
| - if (is_animated_ || length != 8 || !parsed_ihdr_ ||
|
| - !CheckCrc(reader, read_offset_, 8)) {
|
| - ignore_animation_ = true;
|
| - continue;
|
| - }
|
| - chunk =
|
| - ReadAsConstPngBytep(reader, read_offset_ + 8, length, read_buffer);
|
| - reported_frame_count_ = png_get_uint_32(chunk);
|
| - if (!reported_frame_count_ || reported_frame_count_ > PNG_UINT_31_MAX) {
|
| - ignore_animation_ = true;
|
| - continue;
|
| - }
|
| - png_uint_32 repetition_count = png_get_uint_32(chunk + 4);
|
| - if (repetition_count > PNG_UINT_31_MAX) {
|
| - ignore_animation_ = true;
|
| - continue;
|
| - }
|
| - is_animated_ = true;
|
| - decoder_->SetRepetitionCount(static_cast<int>(repetition_count) - 1);
|
| - } else if (IsChunk(chunk, "fcTL")) {
|
| - if (ignore_animation_)
|
| - continue;
|
| - if (length != 26 || !parsed_ihdr_ ||
|
| - !CheckCrc(reader, read_offset_, 26)) {
|
| - ignore_animation_ = true;
|
| - continue;
|
| - }
|
| - chunk =
|
| - ReadAsConstPngBytep(reader, read_offset_ + 8, length, read_buffer);
|
| - if (!ParseFrameInfo(chunk) ||
|
| - new_frame_.frame_rect != IntRect(0, 0, width_, height_)) {
|
| - ignore_animation_ = true;
|
| - continue;
|
| - }
|
| - idat_is_part_of_animation_ = true;
|
| - } else if (IsChunk(chunk, "fdAT")) {
|
| - ignore_animation_ = true;
|
| - } else {
|
| - png_process_data(png_, info_, const_cast<png_byte*>(chunk), 8);
|
| - ProcessData(reader, read_offset_ + 8, length + 4);
|
| - if (IsChunk(chunk, "IHDR")) {
|
| - parsed_ihdr_ = true;
|
| - width_ = png_get_image_width(png_, info_);
|
| - height_ = png_get_image_height(png_, info_);
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Not enough data to call HeaderAvailable.
|
| - return true;
|
| -}
|
| -
|
| -void PNGImageReader::ClearDecodeState(size_t index) {
|
| - if (index)
|
| - return;
|
| - png_destroy_read_struct(png_ ? &png_ : nullptr, info_ ? &info_ : nullptr, 0);
|
| - DCHECK(!png_ && !info_);
|
| - progressive_decode_offset_ = 0;
|
| -}
|
| -
|
| -const PNGImageReader::FrameInfo& PNGImageReader::GetFrameInfo(
|
| - size_t index) const {
|
| - DCHECK(index < frame_info_.size());
|
| - return frame_info_[index];
|
| -}
|
| -
|
| -// Extract the fcTL frame control info and store it in new_frame_. The length
|
| -// check on the fcTL data has been done by the calling code.
|
| -bool PNGImageReader::ParseFrameInfo(const png_byte* data) {
|
| - if (fctl_needs_dat_chunk_)
|
| - return false;
|
| -
|
| - png_uint_32 frame_width = png_get_uint_32(data + 4);
|
| - png_uint_32 frame_height = png_get_uint_32(data + 8);
|
| - png_uint_32 x_offset = png_get_uint_32(data + 12);
|
| - png_uint_32 y_offset = png_get_uint_32(data + 16);
|
| - png_uint_16 delay_numerator = png_get_uint_16(data + 20);
|
| - png_uint_16 delay_denominator = png_get_uint_16(data + 22);
|
| -
|
| - if (!CheckSequenceNumber(data))
|
| - return false;
|
| - if (!frame_width || !frame_height)
|
| - return false;
|
| - if (x_offset + frame_width > width_ || y_offset + frame_height > height_)
|
| - return false;
|
| -
|
| - new_frame_.frame_rect =
|
| - IntRect(x_offset, y_offset, frame_width, frame_height);
|
| -
|
| - if (delay_denominator)
|
| - new_frame_.duration = delay_numerator * 1000 / delay_denominator;
|
| - else
|
| - new_frame_.duration = delay_numerator * 10;
|
| -
|
| - enum DisposeOperations : png_byte {
|
| - kAPNG_DISPOSE_OP_NONE = 0,
|
| - kAPNG_DISPOSE_OP_BACKGROUND = 1,
|
| - kAPNG_DISPOSE_OP_PREVIOUS = 2,
|
| - };
|
| - const png_byte& dispose_op = data[24];
|
| - switch (dispose_op) {
|
| - case kAPNG_DISPOSE_OP_NONE:
|
| - new_frame_.disposal_method = ImageFrame::DisposalMethod::kDisposeKeep;
|
| - break;
|
| - case kAPNG_DISPOSE_OP_BACKGROUND:
|
| - new_frame_.disposal_method =
|
| - ImageFrame::DisposalMethod::kDisposeOverwriteBgcolor;
|
| - break;
|
| - case kAPNG_DISPOSE_OP_PREVIOUS:
|
| - new_frame_.disposal_method =
|
| - ImageFrame::DisposalMethod::kDisposeOverwritePrevious;
|
| - break;
|
| - default:
|
| - return false;
|
| - }
|
| -
|
| - enum BlendOperations : png_byte {
|
| - kAPNG_BLEND_OP_SOURCE = 0,
|
| - kAPNG_BLEND_OP_OVER = 1,
|
| - };
|
| - const png_byte& blend_op = data[25];
|
| - switch (blend_op) {
|
| - case kAPNG_BLEND_OP_SOURCE:
|
| - new_frame_.alpha_blend = ImageFrame::AlphaBlendSource::kBlendAtopBgcolor;
|
| - break;
|
| - case kAPNG_BLEND_OP_OVER:
|
| - new_frame_.alpha_blend =
|
| - ImageFrame::AlphaBlendSource::kBlendAtopPreviousFrame;
|
| - break;
|
| - default:
|
| - return false;
|
| - }
|
| -
|
| - fctl_needs_dat_chunk_ = true;
|
| - return true;
|
| -}
|
| -
|
| -} // namespace blink
|
|
|