Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(296)

Unified Diff: Source/platform/image-decoders/png/PNGImageDecoder.cpp

Issue 1250373006: Animated PNG implementation (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Another LayoutTests fix. Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698