| Index: Source/platform/image-decoders/png/PNGImageDecoder.cpp
|
| diff --git a/Source/platform/image-decoders/png/PNGImageDecoder.cpp b/Source/platform/image-decoders/png/PNGImageDecoder.cpp
|
| index 99fde2f278671211987446b4b73657c8e4f37586..5e830db615f733128d37c385f542ef30b4fc6339 100644
|
| --- a/Source/platform/image-decoders/png/PNGImageDecoder.cpp
|
| +++ b/Source/platform/image-decoders/png/PNGImageDecoder.cpp
|
| @@ -39,6 +39,7 @@
|
| #include "config.h"
|
| #include "platform/image-decoders/png/PNGImageDecoder.h"
|
|
|
| +#include "platform/RuntimeEnabledFeatures.h"
|
| #include "wtf/PassOwnPtr.h"
|
|
|
| #include "png.h"
|
| @@ -92,8 +93,15 @@ public:
|
| PNGImageReader(PNGImageDecoder* decoder)
|
| : m_decoder(decoder)
|
| , m_readOffset(0)
|
| - , m_currentBufferSize(0)
|
| - , m_decodingSizeOnly(false)
|
| + , m_parseOffset(0)
|
| + , 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)
|
| #if USE(QCMSLIB)
|
| , m_transform(0)
|
| @@ -103,6 +111,7 @@ public:
|
| 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()
|
| @@ -110,6 +119,20 @@ public:
|
| close();
|
| }
|
|
|
| + struct FrameInfo {
|
| + unsigned start;
|
| + unsigned finish;
|
| + unsigned width;
|
| + unsigned height;
|
| + unsigned xOffset;
|
| + unsigned yOffset;
|
| + unsigned duration;
|
| + unsigned dispose;
|
| + unsigned blend;
|
| + };
|
| +
|
| + static const unsigned headerSize = 33;
|
| +
|
| void close()
|
| {
|
| if (m_png && m_info)
|
| @@ -119,34 +142,188 @@ public:
|
| clearColorTransform();
|
| #endif
|
| m_readOffset = 0;
|
| + m_parseOffset = 0;
|
| }
|
|
|
| - bool decode(const SharedBuffer& data, bool sizeOnly)
|
| + bool parse(const SharedBuffer& data)
|
| {
|
| - m_decodingSizeOnly = sizeOnly;
|
| + if (data.size() < headerSize)
|
| + return false;
|
| +
|
| + const unsigned long maxPNGSize = 1000000UL;
|
| + png_bytep inputData = reinterpret_cast<png_bytep>(const_cast<char*>(data.data()));
|
| + unsigned inputSize = data.size();
|
| +
|
| + if (!m_parseOffset) {
|
| + if (memcmp(data.data() + 12, "IHDR", 4))
|
| + return false;
|
| + m_currentFrame.width = m_width = png_get_uint_32(inputData + 16);
|
| + m_currentFrame.height = m_height = png_get_uint_32(inputData + 20);
|
| + if (m_width > maxPNGSize || m_height > maxPNGSize)
|
| + return false;
|
| + png_byte bitDepth = inputData[24];
|
| + if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8 && bitDepth != 16)
|
| + return false;
|
| + png_byte colorType = inputData[25];
|
| + if (colorType == 1 || colorType == 5 || colorType > 6)
|
| + return false;
|
| + if (crc32(crc32(0, Z_NULL, 0), inputData + 12, 17) != png_get_uint_32(inputData + 29))
|
| + return false;
|
| +
|
| + m_infoSize = m_parseOffset = headerSize;
|
| + }
|
| +
|
| + while (!m_isParsed) {
|
| + if (inputSize < m_parseOffset + 12)
|
| + return false;
|
| +
|
| + png_bytep chunk = inputData + m_parseOffset;
|
| + png_bytep chunkId = chunk + 4;
|
| + unsigned chunkSize = png_get_uint_32(chunk);
|
| + unsigned chunkEnd = m_parseOffset + chunkSize + 12;
|
| +
|
| + 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 (!memcmp(chunkId, "IDAT", 4) && !m_currentFrame.start) {
|
| + m_currentFrame.start = m_parseOffset;
|
| + if (!m_decoder->setSize(m_width, m_height))
|
| + return false;
|
| + }
|
| +
|
| + if (inputSize < chunkEnd)
|
| + return false;
|
| +
|
| + 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) {
|
| + m_isAnimated = true;
|
| + m_posterFrame = 1;
|
| + m_visibleFrames = 0;
|
| + m_repetitionCount = static_cast<int>(png_get_uint_32(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;
|
| + }
|
| +
|
| + m_currentFrame.width = png_get_uint_32(chunk + 12);
|
| + m_currentFrame.height = png_get_uint_32(chunk + 16);
|
| + m_currentFrame.xOffset = png_get_uint_32(chunk + 20);
|
| + m_currentFrame.yOffset = png_get_uint_32(chunk + 24);
|
| + unsigned numerator = png_get_uint_16(chunk + 28);
|
| + unsigned denominator = png_get_uint_16(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 decode(const SharedBuffer& 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_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);
|
| + }
|
|
|
| - // 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 (unsigned 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))
|
| + return false;
|
| +
|
| + if (!index) {
|
| + const char* segment;
|
| + unsigned endOffset = (!m_frames.isEmpty()) ? m_frames[0].finish : 0;
|
| + while (unsigned segmentLength = data.getSomeData(segment, m_readOffset)) {
|
| + if (endOffset && m_readOffset + segmentLength > endOffset)
|
| + segmentLength = endOffset - m_readOffset;
|
| +
|
| + m_readOffset += segmentLength;
|
| + png_process_data(m_png, m_info, reinterpret_cast<png_bytep>(const_cast<char*>(segment)), segmentLength);
|
| +
|
| + if (m_readOffset == endOffset) {
|
| + png_process_data(m_png, m_info, dataIEND, 12);
|
| + break;
|
| + }
|
| + }
|
| + } else {
|
| + m_readOffset = 0;
|
| + png_bytep inputData = reinterpret_cast<png_bytep>(const_cast<char*>(data.data()));
|
| + png_bytep chunk = inputData + m_frames[index].start;
|
| + png_bytep finish = inputData + m_frames[index].finish;
|
| + if (inputData + data.size() < finish)
|
| 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];
|
| + memcpy(&header[0], inputData, 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);
|
| + png_process_data(m_png, m_info, inputData + headerSize, m_infoSize - headerSize);
|
| + } else {
|
| + png_process_data(m_png, m_info, inputData, m_infoSize);
|
| + }
|
| +
|
| + png_byte dataIDAT[8] = {0, 0, 0, 0, 73, 68, 65, 84};
|
| + while (chunk < finish) {
|
| + unsigned size = png_get_uint_32(chunk);
|
| + if (!memcmp(chunk + 4, "fdAT", 4)) {
|
| + png_save_uint_32(&dataIDAT[0], size - 4);
|
| + png_process_data(m_png, m_info, dataIDAT, 8);
|
| + png_process_data(m_png, m_info, chunk + 12, size);
|
| + }
|
| + chunk += size + 12;
|
| + }
|
| + png_process_data(m_png, m_info, dataIEND, 12);
|
| }
|
|
|
| - return false;
|
| + return true;
|
| }
|
|
|
| png_structp pngPtr() const { return m_png; }
|
| png_infop infoPtr() const { return m_info; }
|
|
|
| - void setReadOffset(unsigned offset) { m_readOffset = offset; }
|
| - unsigned currentBufferSize() const { return m_currentBufferSize; }
|
| - bool decodingSizeOnly() const { return m_decodingSizeOnly; }
|
| + int repetitionCount() const { return m_repetitionCount; }
|
| + size_t imagesCount() const { return m_frames.isEmpty() ? 1 : m_frames.size() - m_posterFrame; }
|
| + const FrameInfo * frameAtIndex(size_t index) const { return m_frames.isEmpty() ? &m_currentFrame : &m_frames[index + m_posterFrame]; }
|
| void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; }
|
| bool hasAlpha() const { return m_hasAlpha; }
|
|
|
| @@ -194,8 +371,17 @@ private:
|
| png_infop m_info;
|
| PNGImageDecoder* m_decoder;
|
| unsigned m_readOffset;
|
| - unsigned m_currentBufferSize;
|
| - bool m_decodingSizeOnly;
|
| + unsigned m_parseOffset;
|
| + unsigned m_infoSize;
|
| + bool m_isAnimated;
|
| + bool m_isParsed;
|
| + unsigned m_width;
|
| + unsigned m_height;
|
| + int m_repetitionCount;
|
| + size_t m_posterFrame;
|
| + size_t m_visibleFrames;
|
| + FrameInfo m_currentFrame;
|
| + Vector<FrameInfo> m_frames;
|
| bool m_hasAlpha;
|
| OwnPtr<png_byte[]> m_interlaceBuffer;
|
| #if USE(QCMSLIB)
|
| @@ -207,6 +393,7 @@ private:
|
| PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOption colorOptions, size_t maxDecodedBytes)
|
| : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes)
|
| , m_hasColorProfile(false)
|
| + , m_currentFrame(0)
|
| {
|
| }
|
|
|
| @@ -214,6 +401,48 @@ PNGImageDecoder::~PNGImageDecoder()
|
| {
|
| }
|
|
|
| +bool PNGImageDecoder::frameIsCompleteAtIndex(size_t index) const
|
| +{
|
| + if (!m_reader || failed())
|
| + return false;
|
| + if (m_frameBufferCache.size() <= 1)
|
| + return ImageDecoder::frameIsCompleteAtIndex(index);
|
| + bool frameIsLoadedAtIndex = index < m_frameBufferCache.size();
|
| + return frameIsLoadedAtIndex;
|
| +}
|
| +
|
| +float PNGImageDecoder::frameDurationAtIndex(size_t index) const
|
| +{
|
| + return (index < m_frameBufferCache.size()) ? m_frameBufferCache[index].duration() : 0;
|
| +}
|
| +
|
| +int PNGImageDecoder::repetitionCount() const
|
| +{
|
| + return (!m_reader || failed()) ? cAnimationNone : m_reader->repetitionCount();
|
| +}
|
| +
|
| +size_t PNGImageDecoder::clearCacheExceptFrame(size_t clearExceptFrame)
|
| +{
|
| + if (clearExceptFrame >= m_frameBufferCache.size() || m_frameBufferCache.size() <= 1)
|
| + return 0;
|
| +
|
| + size_t prevFrame = clearExceptFrame;
|
| + if (m_frameBufferCache[clearExceptFrame].disposalMethod() == ImageFrame::DisposeOverwritePrevious)
|
| + prevFrame = m_frameBufferCache[clearExceptFrame].requiredPreviousFrameIndex();
|
| +
|
| + while (clearExceptFrame != kNotFound && m_frameBufferCache[clearExceptFrame].status() != ImageFrame::FrameComplete)
|
| + clearExceptFrame = m_frameBufferCache[clearExceptFrame].requiredPreviousFrameIndex();
|
| +
|
| + size_t frameBytesCleared = 0;
|
| + for (size_t i = 0; i < m_frameBufferCache.size(); ++i) {
|
| + if (i != prevFrame && i != clearExceptFrame) {
|
| + frameBytesCleared += frameBytesAtIndex(i);
|
| + clearFrameBuffer(i);
|
| + }
|
| + }
|
| + return frameBytesCleared;
|
| +}
|
| +
|
| #if USE(QCMSLIB)
|
| static void getColorProfile(png_structp png, png_infop info, ColorProfile& colorProfile, bool& sRGB)
|
| {
|
| @@ -255,22 +484,7 @@ void PNGImageDecoder::headerAvailable()
|
| {
|
| png_structp png = m_reader->pngPtr();
|
| png_infop info = m_reader->infoPtr();
|
| - png_uint_32 width = png_get_image_width(png, info);
|
| - png_uint_32 height = png_get_image_height(png, info);
|
| -
|
| - // Protect against large PNGs. See http://bugzil.la/251381 for more details.
|
| - const unsigned long maxPNGSize = 1000000UL;
|
| - if (width > maxPNGSize || height > maxPNGSize) {
|
| - longjmp(JMPBUF(png), 1);
|
| - return;
|
| - }
|
| -
|
| - // Set the image size now that the image header is available.
|
| - if (!setSize(width, height)) {
|
| - longjmp(JMPBUF(png), 1);
|
| - return;
|
| - }
|
| -
|
| + png_uint_32 width, height;
|
| int bitDepth, colorType, interlaceType, compressionType, filterType, channels;
|
| png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceType, &compressionType, &filterType);
|
|
|
| @@ -337,17 +551,6 @@ void PNGImageDecoder::headerAvailable()
|
| ASSERT(channels == 3 || channels == 4);
|
|
|
| m_reader->setHasAlpha(channels == 4);
|
| -
|
| - if (m_reader->decodingSizeOnly()) {
|
| - // If we only needed the size, halt the reader.
|
| -#if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)
|
| - // '0' argument to png_process_data_pause means: Do not cache unprocessed data.
|
| - m_reader->setReadOffset(m_reader->currentBufferSize() - png_process_data_pause(png, 0));
|
| -#else
|
| - m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size);
|
| - png->buffer_size = 0;
|
| -#endif
|
| - }
|
| }
|
|
|
| void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int)
|
| @@ -356,7 +559,7 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex,
|
| return;
|
|
|
| // Initialize the framebuffer if needed.
|
| - ImageFrame& buffer = m_frameBufferCache[0];
|
| + ImageFrame& buffer = m_frameBufferCache[m_currentFrame];
|
| if (buffer.status() == ImageFrame::FrameEmpty) {
|
| png_structp png = m_reader->pngPtr();
|
| if (!buffer.setSize(size().width(), size().height())) {
|
| @@ -365,8 +568,10 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex,
|
| }
|
|
|
| unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3;
|
| - if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(png, m_reader->infoPtr())) {
|
| - m_reader->createInterlaceBuffer(colorChannels * size().width() * size().height());
|
| + if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(png, m_reader->infoPtr())
|
| + || m_currentFrame) {
|
| + if (!m_reader->interlaceBuffer())
|
| + m_reader->createInterlaceBuffer(colorChannels * size().width() * size().height());
|
| if (!m_reader->interlaceBuffer()) {
|
| longjmp(JMPBUF(png), 1);
|
| return;
|
| @@ -375,7 +580,8 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex,
|
|
|
| #if USE(QCMSLIB)
|
| if (m_reader->colorTransform()) {
|
| - m_reader->createRowBuffer(colorChannels * size().width());
|
| + if (!m_reader->rowBuffer())
|
| + m_reader->createRowBuffer(colorChannels * size().width());
|
| if (!m_reader->rowBuffer()) {
|
| longjmp(JMPBUF(png), 1);
|
| return;
|
| @@ -385,8 +591,10 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex,
|
| buffer.setStatus(ImageFrame::FramePartial);
|
| buffer.setHasAlpha(false);
|
|
|
| - // For PNGs, the frame always fills the entire image.
|
| - buffer.setOriginalFrameRect(IntRect(IntPoint(), size()));
|
| + if (!initFrameBuffer()) {
|
| + longjmp(JMPBUF(png), 1);
|
| + return;
|
| + }
|
| }
|
|
|
| /* libpng comments (here to explain what follows).
|
| @@ -438,6 +646,10 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex,
|
| png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer);
|
| }
|
|
|
| + // Only do incremental image display for the first frame.
|
| + if (m_currentFrame)
|
| + return;
|
| +
|
| #if USE(QCMSLIB)
|
| if (qcms_transform* transform = m_reader->colorTransform()) {
|
| qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width());
|
| @@ -481,30 +693,171 @@ void PNGImageDecoder::complete()
|
| if (m_frameBufferCache.isEmpty())
|
| return;
|
|
|
| - m_frameBufferCache[0].setStatus(ImageFrame::FrameComplete);
|
| + ImageFrame& buffer = m_frameBufferCache[m_currentFrame];
|
| + buffer.setStatus(ImageFrame::FrameComplete);
|
| +
|
| + if (!m_currentFrame)
|
| + return;
|
| +
|
| + const IntRect& rect = buffer.originalFrameRect();
|
| + bool hasAlpha = m_reader->hasAlpha();
|
| + unsigned colorChannels = hasAlpha ? 4 : 3;
|
| + unsigned alphaMask = 255;
|
| + ImageFrame::AlphaBlendSource blendMethod = buffer.alphaBlendSource();
|
| +
|
| + if (blendMethod == ImageFrame::BlendAtopPreviousFrame && !hasAlpha)
|
| + blendMethod = ImageFrame::BlendAtopBgcolor;
|
| +
|
| + png_bytep row = m_reader->interlaceBuffer();
|
| + for (int y = rect.y(); y < rect.maxY(); ++y, row += colorChannels * size().width()) {
|
| + png_bytep pixel = row;
|
| +#if USE(QCMSLIB)
|
| + if (qcms_transform* transform = m_reader->colorTransform()) {
|
| + qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width());
|
| + pixel = m_reader->rowBuffer();
|
| + }
|
| +#endif
|
| + ImageFrame::PixelData* address = buffer.getAddr(rect.x(), y);
|
| + if (hasAlpha) {
|
| + if (blendMethod == ImageFrame::BlendAtopBgcolor) {
|
| + if (buffer.premultiplyAlpha()) {
|
| + for (int x = rect.x(); x < rect.maxX(); ++x, pixel += 4) {
|
| + buffer.setRGBAPremultiply(address++, pixel[0], pixel[1], pixel[2], pixel[3]);
|
| + alphaMask &= pixel[3];
|
| + }
|
| + } else {
|
| + for (int x = rect.x(); x < rect.maxX(); ++x, pixel += 4) {
|
| + buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], pixel[3]);
|
| + alphaMask &= pixel[3];
|
| + }
|
| + }
|
| + } else {
|
| + if (buffer.premultiplyAlpha()) {
|
| + for (int x = rect.x(); x < rect.maxX(); ++x, pixel += 4) {
|
| + buffer.overRGBAPremultiply(address++, pixel[0], pixel[1], pixel[2], pixel[3]);
|
| + alphaMask &= pixel[3];
|
| + }
|
| + } else {
|
| + for (int x = rect.x(); x < rect.maxX(); ++x, pixel += 4) {
|
| + buffer.overRGBARaw(address++, pixel[0], pixel[1], pixel[2], pixel[3]);
|
| + alphaMask &= pixel[3];
|
| + }
|
| + }
|
| + }
|
| + } else {
|
| + for (int x = rect.x(); x < rect.maxX(); ++x, pixel += 3) {
|
| + buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], 255);
|
| + }
|
| + }
|
| + }
|
| +
|
| + if (alphaMask == 255) {
|
| + if (buffer.originalFrameRect().contains(IntRect(IntPoint(), size()))) {
|
| + buffer.setHasAlpha(false);
|
| + } else {
|
| + size_t frameIndex = m_currentFrame;
|
| + const ImageFrame* prevBuffer = &m_frameBufferCache[--frameIndex];
|
| + while (frameIndex && (prevBuffer->disposalMethod() == ImageFrame::DisposeOverwritePrevious))
|
| + prevBuffer = &m_frameBufferCache[--frameIndex];
|
| + if ((prevBuffer->disposalMethod() == ImageFrame::DisposeOverwriteBgcolor)
|
| + && !prevBuffer->hasAlpha() && buffer.originalFrameRect().contains(prevBuffer->originalFrameRect()))
|
| + buffer.setHasAlpha(false);
|
| + }
|
| + } else if (blendMethod == ImageFrame::BlendAtopBgcolor && !buffer.hasAlpha()) {
|
| + buffer.setHasAlpha(true);
|
| + }
|
| +}
|
| +
|
| +size_t PNGImageDecoder::decodeFrameCount()
|
| +{
|
| + return parse() ? m_reader->imagesCount() : m_frameBufferCache.size();
|
| +}
|
| +
|
| +void PNGImageDecoder::decode(size_t index)
|
| +{
|
| + if (!m_reader || failed())
|
| + return;
|
| +
|
| + Vector<size_t> framesToDecode;
|
| + size_t frameToDecode = index;
|
| + do {
|
| + framesToDecode.append(frameToDecode);
|
| + frameToDecode = m_frameBufferCache[frameToDecode].requiredPreviousFrameIndex();
|
| + } while (frameToDecode != kNotFound && m_frameBufferCache[frameToDecode].status() != ImageFrame::FrameComplete);
|
| +
|
| + for (auto i = framesToDecode.rbegin(); i != framesToDecode.rend(); ++i) {
|
| + m_currentFrame = *i;
|
| + if (!m_reader->decode(*m_data, m_currentFrame)) {
|
| + setFailed();
|
| + break;
|
| + }
|
| +
|
| + // We need more data to continue decoding.
|
| + if (m_frameBufferCache[m_currentFrame].status() != ImageFrame::FrameComplete)
|
| + break;
|
| + }
|
| +}
|
| +
|
| +void PNGImageDecoder::initializeNewFrame(size_t index)
|
| +{
|
| + ImageFrame* buffer = &m_frameBufferCache[index];
|
| + bool frameRectIsOpaque = (buffer->status() == ImageFrame::FrameComplete) ? !m_reader->hasAlpha() : false;
|
| + buffer->setRequiredPreviousFrameIndex(findRequiredPreviousFrame(index, frameRectIsOpaque));
|
| + const PNGImageReader::FrameInfo* frame = m_reader->frameAtIndex(index);
|
| + IntRect frameRect(frame->xOffset, frame->yOffset, frame->width, frame->height);
|
| + buffer->setOriginalFrameRect(intersection(frameRect, IntRect(IntPoint(), size())));
|
| + buffer->setDuration(frame->duration);
|
| + buffer->setDisposalMethod((frame->dispose == 2) ? ImageFrame::DisposeOverwritePrevious : (frame->dispose == 1) ? ImageFrame::DisposeOverwriteBgcolor : ImageFrame::DisposeKeep);
|
| + buffer->setAlphaBlendSource((frame->blend == 1) ? ImageFrame::BlendAtopPreviousFrame : ImageFrame::BlendAtopBgcolor);
|
| }
|
|
|
| -inline bool isComplete(const PNGImageDecoder* decoder)
|
| +bool PNGImageDecoder::initFrameBuffer()
|
| {
|
| - return decoder->frameIsCompleteAtIndex(0);
|
| + ImageFrame& buffer = m_frameBufferCache[m_currentFrame];
|
| +
|
| + const size_t requiredPreviousFrameIndex = buffer.requiredPreviousFrameIndex();
|
| + if (requiredPreviousFrameIndex == kNotFound) {
|
| + // This frame doesn't rely on any previous data.
|
| + buffer.zeroFillPixelData();
|
| + if (!m_currentFrame)
|
| + buffer.setHasAlpha(false);
|
| + } else {
|
| + const ImageFrame& prevBuffer = m_frameBufferCache[requiredPreviousFrameIndex];
|
| + ASSERT(prevBuffer.status() == ImageFrame::FrameComplete);
|
| +
|
| + // Preserve the last frame as the starting state for this frame.
|
| + if (!buffer.copyBitmapData(prevBuffer))
|
| + return setFailed();
|
| +
|
| + if (prevBuffer.disposalMethod() == ImageFrame::DisposeOverwriteBgcolor) {
|
| + // We want to clear the previous frame to transparent, without
|
| + // affecting pixels in the image outside of the frame.
|
| + const IntRect& prevRect = prevBuffer.originalFrameRect();
|
| + ASSERT(!prevRect.contains(IntRect(IntPoint(), size())));
|
| + buffer.zeroFillFrameRect(prevRect);
|
| + }
|
| + }
|
| + return true;
|
| }
|
|
|
| -void PNGImageDecoder::decode(bool onlySize)
|
| +bool PNGImageDecoder::parse()
|
| {
|
| if (failed())
|
| - return;
|
| + return false;
|
|
|
| if (!m_reader)
|
| m_reader = adoptPtr(new PNGImageReader(this));
|
|
|
| // If we couldn't decode the image but have received all the data, decoding
|
| // has failed.
|
| - if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived())
|
| + if (!m_reader->parse(*m_data) && isAllDataReceived())
|
| setFailed();
|
|
|
| - // If decoding is done or failed, we don't need the PNGImageReader anymore.
|
| - if (isComplete(this) || failed())
|
| + if (failed()) {
|
| m_reader.clear();
|
| + return false;
|
| + }
|
| + return true;
|
| }
|
|
|
| } // namespace blink
|
|
|