| 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
|
| index 177b58af86dbbebe6f631714da4501214ac11b31..4f9d48a70837fc2e82494d9aeb984307415b8a38 100644
|
| --- a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.cpp
|
| +++ b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.cpp
|
| @@ -38,11 +38,18 @@
|
|
|
| #include "platform/image-decoders/png/PNGImageReader.h"
|
|
|
| +#include "platform/RuntimeEnabledFeatures.h"
|
| +#include "platform/image-decoders/FastSharedBufferReader.h"
|
| +
|
| #include "platform/image-decoders/SegmentReader.h"
|
| #include "png.h"
|
| #include "wtf/PtrUtil.h"
|
| +#include "zlib.h"
|
| #include <memory>
|
|
|
| +#define get16(p) ((p)[0] << 8 | (p)[1])
|
| +#define get32(p) ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])
|
| +
|
| namespace {
|
|
|
| inline blink::PNGImageDecoder* imageDecoder(png_structp png) {
|
| @@ -75,13 +82,22 @@ namespace blink {
|
| PNGImageReader::PNGImageReader(PNGImageDecoder* decoder, size_t readOffset)
|
| : m_decoder(decoder),
|
| m_readOffset(readOffset),
|
| - m_currentBufferSize(0),
|
| - m_decodingSizeOnly(false),
|
| + m_parseOffset(readOffset),
|
| + m_decodeOffset(readOffset),
|
| + m_infoSize(0),
|
| + m_isAnimated(false),
|
| + m_isParsed(false),
|
| + m_width(0),
|
| + m_height(0),
|
| + m_repetitionCount(cAnimationNone),
|
| + m_posterFrame(0),
|
| + m_visibleFrames(1),
|
| m_hasAlpha(false) {
|
| m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0);
|
| m_info = png_create_info_struct(m_png);
|
| png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable,
|
| pngRowAvailable, pngComplete);
|
| + memset(&m_currentFrame, 0, sizeof(m_currentFrame));
|
| }
|
|
|
| PNGImageReader::~PNGImageReader() {
|
| @@ -91,26 +107,208 @@ PNGImageReader::~PNGImageReader() {
|
| m_readOffset = 0;
|
| }
|
|
|
| -bool PNGImageReader::decode(const SegmentReader& data, bool sizeOnly) {
|
| - m_decodingSizeOnly = sizeOnly;
|
| +// PNG signature and IHDR size
|
| +static const size_t headerSize = 33;
|
| +// fcTL size
|
| +static const size_t frameControlSize = 34;
|
|
|
| - // We need to do the setjmp here. Otherwise bad things will happen.
|
| - if (setjmp(JMPBUF(m_png)))
|
| - return m_decoder->setFailed();
|
| -
|
| - const char* segment;
|
| - while (size_t segmentLength = data.getSomeData(segment, m_readOffset)) {
|
| - m_readOffset += segmentLength;
|
| - m_currentBufferSize = m_readOffset;
|
| - png_process_data(m_png, m_info,
|
| - reinterpret_cast<png_bytep>(const_cast<char*>(segment)),
|
| - segmentLength);
|
| - if (sizeOnly ? m_decoder->isDecodedSizeAvailable()
|
| - : m_decoder->frameIsCompleteAtIndex(0))
|
| +bool PNGImageReader::parse(SegmentReader& data) {
|
| + const unsigned long maxPNGSize = 1000000UL;
|
| + size_t inputSize = data.size();
|
| + const png_byte* chunk;
|
| + FastSharedBufferReader reader(&data);
|
| + char readBuffer[frameControlSize];
|
| +
|
| + if (m_parseOffset == m_readOffset) {
|
| + if (m_readOffset + headerSize > inputSize)
|
| + return false;
|
| +
|
| + chunk = reinterpret_cast<const png_byte*>(reader.getConsecutiveData(
|
| + m_readOffset + 8, headerSize - 8, readBuffer));
|
| + const png_byte* chunkId = chunk + 4;
|
| + png_uint_32 chunkSize = get32(chunk);
|
| + if (memcmp(chunkId, "IHDR", 4) || chunkSize != 13)
|
| + return false;
|
| + m_currentFrame.width = m_width = get32(chunk + 8);
|
| + m_currentFrame.height = m_height = get32(chunk + 12);
|
| + if (m_width > maxPNGSize || m_height > maxPNGSize)
|
| + return false;
|
| + png_byte bitDepth = chunk[16];
|
| + if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8 &&
|
| + bitDepth != 16)
|
| + return false;
|
| + png_byte colorType = chunk[17];
|
| + if (colorType == 1 || colorType == 5 || colorType > 6)
|
| + return false;
|
| + png_uint_32 chunkCrc = get32(chunk + 21);
|
| + if (crc32(crc32(0, Z_NULL, 0), chunkId, 17) != chunkCrc)
|
| + return false;
|
| + m_infoSize = m_parseOffset = m_readOffset + headerSize;
|
| + }
|
| +
|
| + while (!m_isParsed) {
|
| + if (m_parseOffset + 8 > inputSize)
|
| + return false;
|
| +
|
| + chunk = reinterpret_cast<const png_byte*>(
|
| + reader.getConsecutiveData(m_parseOffset, 8, readBuffer));
|
| + const png_byte* chunkId = chunk + 4;
|
| + png_uint_32 chunkSize = get32(chunk);
|
| +
|
| + if (!memcmp(chunkId, "IDAT", 4) && !m_currentFrame.start) {
|
| + m_currentFrame.start = m_parseOffset;
|
| + if (!m_decoder->setSize(m_width, m_height))
|
| + return false;
|
| + png_destroy_read_struct(&m_png, &m_info, 0);
|
| + m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0);
|
| + m_info = png_create_info_struct(m_png);
|
| + png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable,
|
| + pngRowAvailable, pngComplete);
|
| +
|
| + if (setjmp(JMPBUF(m_png)))
|
| + return false;
|
| +
|
| + processData(data, 0, m_readOffset, m_parseOffset + 8);
|
| + }
|
| +
|
| + size_t chunkEnd = m_parseOffset + chunkSize + 12;
|
| + if (chunkEnd > inputSize)
|
| + return false;
|
| +
|
| + if (!memcmp(chunkId, "IEND", 4) && !chunkSize) {
|
| + if (!m_currentFrame.start)
|
| + return false;
|
| + m_isParsed = true;
|
| + m_currentFrame.finish = chunkEnd;
|
| + m_frames.append(m_currentFrame);
|
| return true;
|
| + }
|
| +
|
| + if (RuntimeEnabledFeatures::animatedPNGEnabled()) {
|
| + if (!memcmp(chunkId, "fdAT", 4) && m_isAnimated &&
|
| + !m_currentFrame.start) {
|
| + m_currentFrame.start = m_parseOffset;
|
| + } else if (!memcmp(chunkId, "acTL", 4) && chunkSize == 8 &&
|
| + !m_isAnimated && !m_currentFrame.start) {
|
| + chunk = reinterpret_cast<const png_byte*>(
|
| + reader.getConsecutiveData(m_parseOffset, 16, readBuffer));
|
| + m_isAnimated = true;
|
| + m_posterFrame = 1;
|
| + m_visibleFrames = 0;
|
| + m_repetitionCount = static_cast<int>(get32(chunk + 12)) - 1;
|
| + } else if (!memcmp(chunkId, "fcTL", 4) && chunkSize == 26 &&
|
| + (m_isAnimated || !m_currentFrame.start)) {
|
| + if (m_currentFrame.start) {
|
| + m_currentFrame.finish = chunkEnd;
|
| + m_frames.append(m_currentFrame);
|
| + m_currentFrame.start = 0;
|
| + } else if (m_frames.isEmpty()) {
|
| + m_posterFrame = 0;
|
| + }
|
| +
|
| + chunk = reinterpret_cast<const png_byte*>(reader.getConsecutiveData(
|
| + m_parseOffset, frameControlSize, readBuffer));
|
| + m_currentFrame.width = get32(chunk + 12);
|
| + m_currentFrame.height = get32(chunk + 16);
|
| + m_currentFrame.xOffset = get32(chunk + 20);
|
| + m_currentFrame.yOffset = get32(chunk + 24);
|
| + unsigned numerator = get16(chunk + 28);
|
| + unsigned denominator = get16(chunk + 30);
|
| + m_currentFrame.duration = !denominator
|
| + ? numerator * 10
|
| + : (int)(numerator * 1000.0 / denominator);
|
| + m_currentFrame.dispose = chunk[32];
|
| + m_currentFrame.blend = chunk[33];
|
| +
|
| + if (m_currentFrame.width > maxPNGSize ||
|
| + m_currentFrame.height > maxPNGSize ||
|
| + m_currentFrame.xOffset > maxPNGSize ||
|
| + m_currentFrame.yOffset > maxPNGSize ||
|
| + m_currentFrame.xOffset + m_currentFrame.width > m_width ||
|
| + m_currentFrame.yOffset + m_currentFrame.height > m_height ||
|
| + m_currentFrame.dispose > 2 || m_currentFrame.blend > 1)
|
| + return false;
|
| +
|
| + if (!m_visibleFrames) {
|
| + m_currentFrame.blend = 0;
|
| + if (m_currentFrame.dispose == 2)
|
| + m_currentFrame.dispose = 1;
|
| + }
|
| + m_visibleFrames++;
|
| + } else if (!m_currentFrame.start && m_frames.isEmpty()) {
|
| + m_infoSize = chunkEnd;
|
| + }
|
| + }
|
| +
|
| + m_parseOffset = chunkEnd;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool PNGImageReader::decode(SegmentReader& data, size_t index) {
|
| + static png_byte dataIEND[12] = {0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130};
|
| +
|
| + index += m_posterFrame;
|
| + if (index && index >= m_frames.size())
|
| + return true;
|
| +
|
| + if (index || m_decodeOffset == m_readOffset) {
|
| + png_destroy_read_struct(&m_png, &m_info, 0);
|
| + m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0);
|
| + m_info = png_create_info_struct(m_png);
|
| + png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable,
|
| + pngRowAvailable, pngComplete);
|
| }
|
|
|
| - return false;
|
| + if (setjmp(JMPBUF(m_png)))
|
| + return false;
|
| +
|
| + if (!index) {
|
| + size_t endOffset = !m_frames.isEmpty() ? m_frames[0].finish : 0;
|
| + processData(data, index, m_decodeOffset, endOffset);
|
| + if (m_decodeOffset == endOffset)
|
| + png_process_data(m_png, m_info, dataIEND, 12);
|
| + } else {
|
| + const png_byte* chunk;
|
| + FastSharedBufferReader reader(&data);
|
| + char readBuffer[headerSize];
|
| +
|
| + m_decodeOffset = m_readOffset;
|
| + size_t offset = m_frames[index].start;
|
| + size_t endOffset = m_frames[index].finish;
|
| + if (data.size() < endOffset)
|
| + return true;
|
| +
|
| + png_set_crc_action(m_png, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
|
| + if (m_frames[index].width != m_width ||
|
| + m_frames[index].height != m_height) {
|
| + unsigned char header[headerSize];
|
| + chunk = reinterpret_cast<const png_byte*>(
|
| + reader.getConsecutiveData(m_readOffset, headerSize, readBuffer));
|
| + memcpy(&header[0], chunk, headerSize);
|
| + png_save_uint_32(&header[16], m_frames[index].width);
|
| + png_save_uint_32(&header[20], m_frames[index].height);
|
| + png_process_data(m_png, m_info, header, headerSize);
|
| + processData(data, index, m_readOffset + headerSize, m_infoSize);
|
| + } else {
|
| + processData(data, index, m_readOffset, m_infoSize);
|
| + }
|
| +
|
| + png_byte dataIDAT[8] = {0, 0, 0, 0, 73, 68, 65, 84};
|
| + while (offset < endOffset) {
|
| + chunk = reinterpret_cast<const png_byte*>(
|
| + reader.getConsecutiveData(offset, 8, readBuffer));
|
| + png_uint_32 chunkSize = get32(chunk);
|
| + if (!memcmp(chunk + 4, "fdAT", 4)) {
|
| + png_save_uint_32(&dataIDAT[0], chunkSize - 4);
|
| + png_process_data(m_png, m_info, dataIDAT, 8);
|
| + processData(data, index, offset + 12, offset + 12 + chunkSize);
|
| + }
|
| + offset += chunkSize + 12;
|
| + }
|
| + png_process_data(m_png, m_info, dataIEND, 12);
|
| + }
|
| + return true;
|
| }
|
|
|
| } // namespace blink
|
|
|