| 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
|
|
|