Index: third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp |
diff --git a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp |
index 1aa5b1f62ec31626cce8f7f99840de72abfc0791..eff0a2bfbdd8f9c864395db240f4244784dda2ba 100644 |
--- a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp |
+++ b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.cpp |
@@ -50,10 +50,57 @@ PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, |
size_t maxDecodedBytes, |
size_t offset) |
: ImageDecoder(alphaOption, colorOptions, maxDecodedBytes), |
- m_offset(offset) {} |
+ m_offset(offset), |
+ m_currentFrame(0) {} |
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[prevFrame].getDisposalMethod() == |
+ ImageFrame::DisposeOverwritePrevious) |
+ prevFrame = m_frameBufferCache[prevFrame].requiredPreviousFrameIndex(); |
+ |
+ while (clearExceptFrame != kNotFound && |
+ m_frameBufferCache[clearExceptFrame].getStatus() != |
+ 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; |
+} |
+ |
inline float pngFixedToFloat(png_fixed_point x) { |
return ((float)x) * 0.00001f; |
} |
@@ -111,22 +158,7 @@ inline sk_sp<SkColorSpace> readColorSpace(png_structp png, png_infop info) { |
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); |
@@ -200,19 +232,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) |
- // Passing '0' tells png_process_data_pause() not to 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, |
@@ -222,7 +241,7 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, |
return; |
// Initialize the framebuffer if needed. |
- ImageFrame& buffer = m_frameBufferCache[0]; |
+ ImageFrame& buffer = m_frameBufferCache[m_currentFrame]; |
if (buffer.getStatus() == ImageFrame::FrameEmpty) { |
png_structp png = m_reader->pngPtr(); |
if (!buffer.setSizeAndColorSpace(size().width(), size().height(), |
@@ -233,9 +252,12 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, |
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()); |
+ 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; |
@@ -245,8 +267,10 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, |
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). |
@@ -298,6 +322,10 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, |
png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); |
} |
+ // Only do incremental image display for the first frame. |
+ if (m_currentFrame) |
+ return; |
+ |
// Write the decoded row pixels to the frame buffer. The repetitive |
// form of the row write loops is for speed. |
ImageFrame::PixelData* const dstRow = buffer.getAddr(0, y); |
@@ -363,28 +391,203 @@ 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.getAlphaBlendSource(); |
+ |
+ 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 srcPtr = row; |
+ ImageFrame::PixelData* dstRow = buffer.getAddr(rect.x(), y); |
+ if (hasAlpha) { |
+ if (SkColorSpaceXform* xform = colorTransform()) { |
+ SkColorSpaceXform::ColorFormat colorFormat = |
+ SkColorSpaceXform::kRGBA_8888_ColorFormat; |
+ xform->apply(colorFormat, srcPtr, colorFormat, srcPtr, rect.width(), |
+ kUnpremul_SkAlphaType); |
+ } |
+ |
+ if (blendMethod == ImageFrame::BlendAtopBgcolor) { |
+ if (buffer.premultiplyAlpha()) { |
+ for (auto *dstPixel = dstRow; dstPixel < dstRow + rect.width(); |
+ dstPixel++, srcPtr += 4) { |
+ buffer.setRGBAPremultiply(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], |
+ srcPtr[3]); |
+ alphaMask &= srcPtr[3]; |
+ } |
+ } else { |
+ for (auto *dstPixel = dstRow; dstPixel < dstRow + rect.width(); |
+ dstPixel++, srcPtr += 4) { |
+ buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], |
+ srcPtr[3]); |
+ alphaMask &= srcPtr[3]; |
+ } |
+ } |
+ } else { |
+ if (buffer.premultiplyAlpha()) { |
+ for (auto *dstPixel = dstRow; dstPixel < dstRow + rect.width(); |
+ dstPixel++, srcPtr += 4) { |
+ buffer.overRGBAPremultiply(dstPixel, srcPtr[0], srcPtr[1], |
+ srcPtr[2], srcPtr[3]); |
+ alphaMask &= srcPtr[3]; |
+ } |
+ } else { |
+ for (auto *dstPixel = dstRow; dstPixel < dstRow + rect.width(); |
+ dstPixel++, srcPtr += 4) { |
+ buffer.overRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], |
+ srcPtr[3]); |
+ alphaMask &= srcPtr[3]; |
+ } |
+ } |
+ } |
+ } else { |
+ for (auto *dstPixel = dstRow; dstPixel < dstRow + rect.width(); |
+ dstPixel++, srcPtr += 3) { |
+ buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], 255); |
+ } |
+ |
+ if (SkColorSpaceXform* xform = colorTransform()) { |
+ xform->apply(xformColorFormat(), dstRow, xformColorFormat(), dstRow, |
+ rect.width(), kOpaque_SkAlphaType); |
+ } |
+ } |
+ } |
+ |
+ 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->getDisposalMethod() == |
+ ImageFrame::DisposeOverwritePrevious)) |
+ prevBuffer = &m_frameBufferCache[--frameIndex]; |
+ if ((prevBuffer->getDisposalMethod() == |
+ ImageFrame::DisposeOverwriteBgcolor) && |
+ !prevBuffer->hasAlpha() && |
+ buffer.originalFrameRect().contains(prevBuffer->originalFrameRect())) |
+ buffer.setHasAlpha(false); |
+ } |
+ } else if (blendMethod == ImageFrame::BlendAtopBgcolor && |
+ !buffer.hasAlpha()) { |
+ buffer.setHasAlpha(true); |
+ } |
} |
-inline bool isComplete(const PNGImageDecoder* decoder) { |
- return decoder->frameIsCompleteAtIndex(0); |
+size_t PNGImageDecoder::decodeFrameCount() { |
+ return parse() ? m_reader->imagesCount() : m_frameBufferCache.size(); |
} |
-void PNGImageDecoder::decode(bool onlySize) { |
- if (failed()) |
+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].getStatus() != |
+ 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].getStatus() != |
+ ImageFrame::FrameComplete) |
+ break; |
+ } |
+} |
+ |
+void PNGImageDecoder::initializeNewFrame(size_t index) { |
+ ImageFrame* buffer = &m_frameBufferCache[index]; |
+ bool frameRectIsOpaque = buffer->getStatus() == ImageFrame::FrameComplete |
+ ? !m_reader->hasAlpha() |
+ : false; |
+ buffer->setRequiredPreviousFrameIndex( |
+ findRequiredPreviousFrame(index, frameRectIsOpaque)); |
+ const PNGImageReader::FrameInfo* frame = m_reader->frameInfo(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); |
+} |
+ |
+bool PNGImageDecoder::initFrameBuffer() { |
+ 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 { |
+ ImageFrame& prevBuffer = m_frameBufferCache[requiredPreviousFrameIndex]; |
+ DCHECK(prevBuffer.getStatus() == ImageFrame::FrameComplete); |
+ |
+ // Preserve the last frame as the starting state for this frame. |
+ if (!buffer.takeBitmapDataIfWritable(&prevBuffer)) { |
+ if (!buffer.copyBitmapData(prevBuffer)) |
+ return setFailed(); |
+ } |
+ |
+ if (prevBuffer.getDisposalMethod() == 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(); |
+ DCHECK(!prevRect.contains(IntRect(IntPoint(), size()))); |
+ buffer.zeroFillFrameRect(prevRect); |
+ } |
+ } |
+ return true; |
+} |
+ |
+bool PNGImageDecoder::parse() { |
+ if (failed()) |
+ return false; |
+ |
if (!m_reader) |
m_reader = wrapUnique(new PNGImageReader(this, m_offset)); |
// 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.reset(); |
+ return false; |
+ } |
+ return true; |
} |
} // namespace blink |