diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp')
-rw-r--r-- | Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp | 150 |
1 files changed, 79 insertions, 71 deletions
diff --git a/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp b/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp index 07304769c..f8c9893ce 100644 --- a/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp +++ b/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,14 +28,11 @@ #include "GIFImageReader.h" #include <limits> -#include <wtf/PassOwnPtr.h> namespace WebCore { -GIFImageDecoder::GIFImageDecoder(ImageSource::AlphaOption alphaOption, - ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption) +GIFImageDecoder::GIFImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption) : ImageDecoder(alphaOption, gammaAndColorProfileOption) - , m_repetitionCount(cAnimationLoopOnce) { } @@ -43,14 +40,14 @@ GIFImageDecoder::~GIFImageDecoder() { } -void GIFImageDecoder::setData(SharedBuffer* data, bool allDataReceived) +void GIFImageDecoder::setData(SharedBuffer& data, bool allDataReceived) { if (failed()) return; ImageDecoder::setData(data, allDataReceived); if (m_reader) - m_reader->setData(data); + m_reader->setData(&data); } bool GIFImageDecoder::isSizeAvailable() @@ -61,25 +58,25 @@ bool GIFImageDecoder::isSizeAvailable() return ImageDecoder::isSizeAvailable(); } -bool GIFImageDecoder::setSize(unsigned width, unsigned height) +bool GIFImageDecoder::setSize(const IntSize& size) { - if (ImageDecoder::isSizeAvailable() && size() == IntSize(width, height)) + if (ImageDecoder::isSizeAvailable() && this->size() == size) return true; - if (!ImageDecoder::setSize(width, height)) + if (!ImageDecoder::setSize(size)) return false; prepareScaleDataIfNecessary(); return true; } -size_t GIFImageDecoder::frameCount() +size_t GIFImageDecoder::frameCount() const { - decode(std::numeric_limits<unsigned>::max(), GIFFrameCountQuery); + const_cast<GIFImageDecoder*>(this)->decode(std::numeric_limits<unsigned>::max(), GIFFrameCountQuery); return m_frameBufferCache.size(); } -int GIFImageDecoder::repetitionCount() const +RepetitionCount GIFImageDecoder::repetitionCount() const { // This value can arrive at any point in the image data stream. Most GIFs // in the wild declare it near the beginning of the file, so it usually is @@ -106,7 +103,7 @@ int GIFImageDecoder::repetitionCount() const // later in the stream. It is also possible that no frames are in the // stream. In these cases we should just loop once. if (failed() || (m_reader && (!m_reader->imagesCount()))) - m_repetitionCount = cAnimationLoopOnce; + m_repetitionCount = RepetitionCountOnce; else if (m_reader && m_reader->loopCount() != cLoopCountNotSeen) m_repetitionCount = m_reader->loopCount(); return m_repetitionCount; @@ -118,14 +115,14 @@ ImageFrame* GIFImageDecoder::frameBufferAtIndex(size_t index) return 0; ImageFrame& frame = m_frameBufferCache[index]; - if (frame.status() != ImageFrame::FrameComplete) + if (!frame.isComplete()) decode(index + 1, GIFFullQuery); return &frame; } bool GIFImageDecoder::setFailed() { - m_reader.clear(); + m_reader = nullptr; return ImageDecoder::setFailed(); } @@ -136,6 +133,10 @@ void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame) if (m_frameBufferCache.isEmpty()) return; // Nothing to do. + // Lock the decodelock here, as we are going to destroy the GIFImageReader and doing so while + // there's an ongoing decode will cause a crash. + LockHolder locker(m_decodeLock); + // The "-1" here is tricky. It does not mean that |clearBeforeFrame| is the // last frame we wish to preserve, but rather that we never want to clear // the very last frame in the cache: it's empty (so clearing it is @@ -157,26 +158,30 @@ void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame) // not to have non-empty frames after the frame we're currently decoding. // So, scan backwards from |end| as follows: // * If the frame is empty, we're still past any frames we care about. - // * If the frame is complete, but is DisposeOverwritePrevious, we'll + // * If the frame is complete, but is DisposalMethod::RestoreToPrevious, we'll // skip over it in future initFrameBuffer() calls. We can clear it // unless it's |end|, and keep scanning. For any other disposal method, // stop scanning, as we've found the frame initFrameBuffer() will need // next. // * If the frame is partial, we're decoding it, so don't clear it; if it - // has a disposal method other than DisposeOverwritePrevious, stop + // has a disposal method other than DisposalMethod::RestoreToPrevious, stop // scanning, as we'll only need this frame when decoding the next one. Vector<ImageFrame>::iterator i(end); - for (; (i != m_frameBufferCache.begin()) && ((i->status() == ImageFrame::FrameEmpty) || (i->disposalMethod() == ImageFrame::DisposeOverwritePrevious)); --i) { - if ((i->status() == ImageFrame::FrameComplete) && (i != end)) - i->clearPixelData(); + for (; (i != m_frameBufferCache.begin()) && (i->isEmpty() || (i->disposalMethod() == ImageFrame::DisposalMethod::RestoreToPrevious)); --i) { + if (i->isComplete() && (i != end)) + i->clear(); } // Now |i| holds the last frame we need to preserve; clear prior frames. for (Vector<ImageFrame>::iterator j(m_frameBufferCache.begin()); j != i; ++j) { - ASSERT(j->status() != ImageFrame::FramePartial); - if (j->status() != ImageFrame::FrameEmpty) - j->clearPixelData(); + ASSERT(!j->isPartial()); + if (j->isEmpty()) + j->clear(); } + + // When some frames are cleared, the reader is out of sync, since the caller might ask for any frame not + // necessarily in the order expected by the reader. See https://bugs.webkit.org/show_bug.cgi?id=159089. + m_reader = nullptr; } bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, const Vector<unsigned char>& rowBuffer, size_t width, size_t rowNumber, unsigned repeatCount, bool writeTransparentPixels) @@ -210,16 +215,16 @@ bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, const Vector<unsigned // Initialize the frame if necessary. ImageFrame& buffer = m_frameBufferCache[frameIndex]; - if (((buffer.status() == ImageFrame::FrameEmpty) && !initFrameBuffer(frameIndex)) || !buffer.hasPixelData()) + if ((buffer.isEmpty() && !initFrameBuffer(frameIndex)) || !buffer.hasBackingStore()) return false; - ImageFrame::PixelData* currentAddress = buffer.getAddr(xBegin, yBegin); + RGBA32* currentAddress = buffer.backingStore()->pixelAt(xBegin, yBegin); // Write one row's worth of data into the frame. for (int x = xBegin; x < xEnd; ++x) { const unsigned char sourceValue = rowBuffer[(m_scaled ? m_scaledColumns[x] : x) - frameContext->xOffset]; if ((!frameContext->isTransparent || (sourceValue != frameContext->tpixel)) && (sourceValue < colorMapSize)) { const size_t colorIndex = static_cast<size_t>(sourceValue) * 3; - buffer.setRGBA(currentAddress, colorMap[colorIndex], colorMap[colorIndex + 1], colorMap[colorIndex + 2], 255); + buffer.backingStore()->setPixel(currentAddress, colorMap[colorIndex], colorMap[colorIndex + 1], colorMap[colorIndex + 2], 255); } else { m_currentBufferSawAlpha = true; // We may or may not need to write transparent pixels to the buffer. @@ -230,34 +235,36 @@ bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, const Vector<unsigned // beyond the first, or the initial passes will "show through" the // later ones. if (writeTransparentPixels) - buffer.setRGBA(currentAddress, 0, 0, 0, 0); + buffer.backingStore()->setPixel(currentAddress, 0, 0, 0, 0); } ++currentAddress; } // Tell the frame to copy the row data if need be. if (repeatCount > 1) - buffer.copyRowNTimes(xBegin, xEnd, yBegin, yEnd); + buffer.backingStore()->repeatFirstRow(IntRect(xBegin, yBegin, xEnd - xBegin , yEnd - yBegin)); return true; } -bool GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, ImageFrame::FrameDisposalMethod disposalMethod) +bool GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, ImageFrame::DisposalMethod disposalMethod) { // Initialize the frame if necessary. Some GIFs insert do-nothing frames, // in which case we never reach haveDecodedRow() before getting here. ImageFrame& buffer = m_frameBufferCache[frameIndex]; - if ((buffer.status() == ImageFrame::FrameEmpty) && !initFrameBuffer(frameIndex)) + if (buffer.isEmpty() && !initFrameBuffer(frameIndex)) return false; // initFrameBuffer() has already called setFailed(). - buffer.setStatus(ImageFrame::FrameComplete); + buffer.setDecoding(ImageFrame::Decoding::Complete); buffer.setDuration(frameDuration); buffer.setDisposalMethod(disposalMethod); if (!m_currentBufferSawAlpha) { + IntRect rect = buffer.backingStore()->frameRect(); + // The whole frame was non-transparent, so it's possible that the entire // resulting buffer was non-transparent, and we can setHasAlpha(false). - if (buffer.originalFrameRect().contains(IntRect(IntPoint(), scaledSize()))) + if (rect.contains(IntRect(IntPoint(), scaledSize()))) buffer.setHasAlpha(false); else if (frameIndex) { // Tricky case. This frame does not have alpha only if everywhere @@ -265,22 +272,23 @@ bool GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, // true, we check the start state of the frame -- if it doesn't have // alpha, we're safe. // - // First skip over prior DisposeOverwritePrevious frames (since they + // First skip over prior DisposalMethod::RestoreToPrevious frames (since they // don't affect the start state of this frame) the same way we do in // initFrameBuffer(). const ImageFrame* prevBuffer = &m_frameBufferCache[--frameIndex]; - while (frameIndex && (prevBuffer->disposalMethod() == ImageFrame::DisposeOverwritePrevious)) + while (frameIndex && (prevBuffer->disposalMethod() == ImageFrame::DisposalMethod::RestoreToPrevious)) prevBuffer = &m_frameBufferCache[--frameIndex]; - // Now, if we're at a DisposeNotSpecified or DisposeKeep frame, then + // Now, if we're at a DisposalMethod::Unspecified or DisposalMethod::DoNotDispose frame, then // we can say we have no alpha if that frame had no alpha. But // since in initFrameBuffer() we already copied that frame's alpha // state into the current frame's, we need do nothing at all here. // - // The only remaining case is a DisposeOverwriteBgcolor frame. If + // The only remaining case is a DisposalMethod::RestoreToBackground frame. If // it had no alpha, and its rect is contained in the current frame's // rect, we know the current frame has no alpha. - if ((prevBuffer->disposalMethod() == ImageFrame::DisposeOverwriteBgcolor) && !prevBuffer->hasAlpha() && buffer.originalFrameRect().contains(prevBuffer->originalFrameRect())) + IntRect prevRect = prevBuffer->backingStore()->frameRect(); + if ((prevBuffer->disposalMethod() == ImageFrame::DisposalMethod::RestoreToBackground) && !prevBuffer->hasAlpha() && rect.contains(prevRect)) buffer.setHasAlpha(false); } } @@ -294,7 +302,7 @@ void GIFImageDecoder::gifComplete() // going to be. repetitionCount(); - m_reader.clear(); + m_reader = nullptr; } void GIFImageDecoder::decode(unsigned haltAtFrame, GIFQuery query) @@ -302,9 +310,10 @@ void GIFImageDecoder::decode(unsigned haltAtFrame, GIFQuery query) if (failed()) return; + LockHolder locker(m_decodeLock); if (!m_reader) { - m_reader = adoptPtr(new GIFImageReader(this)); - m_reader->setData(m_data); + m_reader = std::make_unique<GIFImageReader>(this); + m_reader->setData(m_data.get()); } if (query == GIFSizeQuery) { @@ -318,10 +327,7 @@ void GIFImageDecoder::decode(unsigned haltAtFrame, GIFQuery query) return; } - const size_t oldSize = m_frameBufferCache.size(); m_frameBufferCache.resize(m_reader->imagesCount()); - for (size_t i = oldSize; i < m_reader->imagesCount(); ++i) - m_frameBufferCache[i].setPremultiplyAlpha(m_premultiplyAlpha); if (query == GIFFrameCountQuery) return; @@ -342,66 +348,68 @@ bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex) // Initialize the frame rect in our buffer. const GIFFrameContext* frameContext = m_reader->frameContext(); IntRect frameRect(frameContext->xOffset, frameContext->yOffset, frameContext->width, frameContext->height); - - // Make sure the frameRect doesn't extend outside the buffer. - if (frameRect.maxX() > size().width()) - frameRect.setWidth(size().width() - frameContext->xOffset); - if (frameRect.maxY() > size().height()) - frameRect.setHeight(size().height() - frameContext->yOffset); - ImageFrame* const buffer = &m_frameBufferCache[frameIndex]; - int left = upperBoundScaledX(frameRect.x()); - int right = lowerBoundScaledX(frameRect.maxX(), left); - int top = upperBoundScaledY(frameRect.y()); - int bottom = lowerBoundScaledY(frameRect.maxY(), top); - buffer->setOriginalFrameRect(IntRect(left, top, right - left, bottom - top)); if (!frameIndex) { // This is the first frame, so we're not relying on any previous data. - if (!buffer->setSize(scaledSize().width(), scaledSize().height())) + if (!buffer->initialize(scaledSize(), m_premultiplyAlpha)) return setFailed(); } else { // The starting state for this frame depends on the previous frame's // disposal method. // - // Frames that use the DisposeOverwritePrevious method are effectively + // Frames that use the DisposalMethod::RestoreToPrevious method are effectively // no-ops in terms of changing the starting state of a frame compared to // the starting state of the previous frame, so skip over them. (If the // first frame specifies this method, it will get treated like - // DisposeOverwriteBgcolor below and reset to a completely empty image.) + // DisposalMethod::RestoreToBackground below and reset to a completely empty image.) const ImageFrame* prevBuffer = &m_frameBufferCache[--frameIndex]; - ImageFrame::FrameDisposalMethod prevMethod = prevBuffer->disposalMethod(); - while (frameIndex && (prevMethod == ImageFrame::DisposeOverwritePrevious)) { + ImageFrame::DisposalMethod prevMethod = prevBuffer->disposalMethod(); + while (frameIndex && (prevMethod == ImageFrame::DisposalMethod::RestoreToPrevious)) { prevBuffer = &m_frameBufferCache[--frameIndex]; prevMethod = prevBuffer->disposalMethod(); } - ASSERT(prevBuffer->status() == ImageFrame::FrameComplete); - if ((prevMethod == ImageFrame::DisposeNotSpecified) || (prevMethod == ImageFrame::DisposeKeep)) { + ASSERT(prevBuffer->isComplete()); + + if ((prevMethod == ImageFrame::DisposalMethod::Unspecified) || (prevMethod == ImageFrame::DisposalMethod::DoNotDispose)) { // Preserve the last frame as the starting state for this frame. - if (!buffer->copyBitmapData(*prevBuffer)) + if (!prevBuffer->backingStore() || !buffer->initialize(*prevBuffer->backingStore())) return setFailed(); } else { // We want to clear the previous frame to transparent, without // affecting pixels in the image outside of the frame. - const IntRect& prevRect = prevBuffer->originalFrameRect(); + IntRect prevRect = prevBuffer->backingStore()->frameRect(); const IntSize& bufferSize = scaledSize(); if (!frameIndex || prevRect.contains(IntRect(IntPoint(), scaledSize()))) { // Clearing the first frame, or a frame the size of the whole // image, results in a completely empty image. - if (!buffer->setSize(bufferSize.width(), bufferSize.height())) + if (!buffer->initialize(bufferSize, m_premultiplyAlpha)) return setFailed(); } else { // Copy the whole previous buffer, then clear just its frame. - if (!buffer->copyBitmapData(*prevBuffer)) + if (!prevBuffer->backingStore() || !buffer->initialize(*prevBuffer->backingStore())) return setFailed(); - buffer->zeroFillFrameRect(prevRect); + buffer->backingStore()->clearRect(prevRect); + buffer->setHasAlpha(true); } } } + // Make sure the frameRect doesn't extend outside the buffer. + if (frameRect.maxX() > size().width()) + frameRect.setWidth(size().width() - frameContext->xOffset); + if (frameRect.maxY() > size().height()) + frameRect.setHeight(size().height() - frameContext->yOffset); + + int left = upperBoundScaledX(frameRect.x()); + int right = lowerBoundScaledX(frameRect.maxX(), left); + int top = upperBoundScaledY(frameRect.y()); + int bottom = lowerBoundScaledY(frameRect.maxY(), top); + buffer->backingStore()->setFrameRect(IntRect(left, top, right - left, bottom - top)); + // Update our status to be partially complete. - buffer->setStatus(ImageFrame::FramePartial); + buffer->setDecoding(ImageFrame::Decoding::Partial); // Reset the alpha pixel tracker for this frame. m_currentBufferSawAlpha = false; |