| Index: Source/core/platform/image-decoders/gif/GIFImageDecoder.cpp
|
| diff --git a/Source/core/platform/image-decoders/gif/GIFImageDecoder.cpp b/Source/core/platform/image-decoders/gif/GIFImageDecoder.cpp
|
| index 705a6b1d68b3aff518943a289d8f0505c8dc5ead..2b5603b00cd4a02cefcc6cf131cfd959c9903aff 100644
|
| --- a/Source/core/platform/image-decoders/gif/GIFImageDecoder.cpp
|
| +++ b/Source/core/platform/image-decoders/gif/GIFImageDecoder.cpp
|
| @@ -29,6 +29,7 @@
|
| #include <limits>
|
| #include "core/platform/PlatformInstrumentation.h"
|
| #include "core/platform/image-decoders/gif/GIFImageReader.h"
|
| +#include "wtf/NotFound.h"
|
| #include "wtf/PassOwnPtr.h"
|
|
|
| namespace WebCore {
|
| @@ -57,14 +58,14 @@ void GIFImageDecoder::setData(SharedBuffer* data, bool allDataReceived)
|
| bool GIFImageDecoder::isSizeAvailable()
|
| {
|
| if (!ImageDecoder::isSizeAvailable())
|
| - decode(0, GIFSizeQuery);
|
| + parse(GIFSizeQuery);
|
|
|
| return ImageDecoder::isSizeAvailable();
|
| }
|
|
|
| size_t GIFImageDecoder::frameCount()
|
| {
|
| - decode(std::numeric_limits<unsigned>::max(), GIFFrameCountQuery);
|
| + parse(GIFFrameCountQuery);
|
| return m_frameBufferCache.size();
|
| }
|
|
|
| @@ -109,7 +110,7 @@ ImageFrame* GIFImageDecoder::frameBufferAtIndex(size_t index)
|
| ImageFrame& frame = m_frameBufferCache[index];
|
| if (frame.status() != ImageFrame::FrameComplete) {
|
| PlatformInstrumentation::willDecodeImage("GIF");
|
| - decode(index + 1, GIFFullQuery);
|
| + decode(index);
|
| PlatformInstrumentation::didDecodeImage();
|
| }
|
| return &frame;
|
| @@ -133,57 +134,7 @@ bool GIFImageDecoder::setFailed()
|
| return ImageDecoder::setFailed();
|
| }
|
|
|
| -void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame)
|
| -{
|
| - // In some cases, like if the decoder was destroyed while animating, we
|
| - // can be asked to clear more frames than we currently have.
|
| - if (m_frameBufferCache.isEmpty())
|
| - return; // Nothing to do.
|
| -
|
| - // 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
|
| - // pointless), it's partial (so we don't want to clear it anyway), or the
|
| - // cache could be enlarged with a future setData() call and it could be
|
| - // needed to construct the next frame (see comments below). Callers can
|
| - // always use ImageSource::clear(true, ...) to completely free the memory in
|
| - // this case.
|
| - clearBeforeFrame = std::min(clearBeforeFrame, m_frameBufferCache.size() - 1);
|
| - const Vector<ImageFrame>::iterator end(m_frameBufferCache.begin() + clearBeforeFrame);
|
| -
|
| - // We need to preserve frames such that:
|
| - // * We don't clear |end|
|
| - // * We don't clear the frame we're currently decoding
|
| - // * We don't clear any frame from which a future initFrameBuffer() call
|
| - // will copy bitmap data
|
| - // All other frames can be cleared. Because of the constraints on when
|
| - // ImageSource::clear() can be called (see ImageSource.h), we're guaranteed
|
| - // 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
|
| - // 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
|
| - // 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();
|
| - }
|
| -
|
| - // 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();
|
| - }
|
| -}
|
| -
|
| -bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, const Vector<unsigned char>& rowBuffer, size_t width, size_t rowNumber, unsigned repeatCount, bool writeTransparentPixels)
|
| +bool GIFImageDecoder::haveDecodedRow(size_t frameIndex, const Vector<unsigned char>& rowBuffer, size_t width, size_t rowNumber, unsigned repeatCount, bool writeTransparentPixels)
|
| {
|
| const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex);
|
| // The pixel data and coordinates supplied to us are relative to the frame's
|
| @@ -246,7 +197,12 @@ bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, const Vector<unsigned
|
| return true;
|
| }
|
|
|
| -bool GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, ImageFrame::FrameDisposalMethod disposalMethod)
|
| +bool GIFImageDecoder::parseCompleted() const
|
| +{
|
| + return m_reader && m_reader->parseCompleted();
|
| +}
|
| +
|
| +bool GIFImageDecoder::frameComplete(size_t frameIndex)
|
| {
|
| // Initialize the frame if necessary. Some GIFs insert do-nothing frames,
|
| // in which case we never reach haveDecodedRow() before getting here.
|
| @@ -255,26 +211,20 @@ bool GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration,
|
| return false; // initFrameBuffer() has already called setFailed().
|
|
|
| buffer.setStatus(ImageFrame::FrameComplete);
|
| - buffer.setDuration(frameDuration);
|
| - buffer.setDisposalMethod(disposalMethod);
|
|
|
| if (!m_currentBufferSawAlpha) {
|
| // 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(), size())))
|
| + if (buffer.originalFrameRect().contains(IntRect(IntPoint(), size()))) {
|
| buffer.setHasAlpha(false);
|
| - else if (frameIndex) {
|
| + buffer.setRequiredPreviousFrameIndex(notFound);
|
| + } else if (buffer.requiredPreviousFrameIndex() != notFound) {
|
| // Tricky case. This frame does not have alpha only if everywhere
|
| // outside its rect doesn't have alpha. To know whether this is
|
| // 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
|
| - // 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))
|
| - prevBuffer = &m_frameBufferCache[--frameIndex];
|
| + const ImageFrame* prevBuffer = &m_frameBufferCache[buffer.requiredPreviousFrameIndex()];
|
| + ASSERT(prevBuffer->disposalMethod() != ImageFrame::DisposeOverwritePrevious);
|
|
|
| // Now, if we're at a DisposeNotSpecified or DisposeKeep frame, then
|
| // we can say we have no alpha if that frame had no alpha. But
|
| @@ -292,14 +242,17 @@ bool GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration,
|
| return true;
|
| }
|
|
|
| -void GIFImageDecoder::gifComplete()
|
| +void GIFImageDecoder::clearFrameBuffer(size_t frameIndex)
|
| {
|
| - // Cache the repetition count, which is now as authoritative as it's ever
|
| - // going to be.
|
| - repetitionCount();
|
| + if (m_reader && m_frameBufferCache[frameIndex].status() == ImageFrame::FramePartial) {
|
| + // Reset the state of the partial frame in the reader so that the frame
|
| + // can be decoded again when requested.
|
| + m_reader->clearDecodeState(frameIndex);
|
| + }
|
| + ImageDecoder::clearFrameBuffer(frameIndex);
|
| }
|
|
|
| -void GIFImageDecoder::decode(unsigned haltAtFrame, GIFQuery query)
|
| +void GIFImageDecoder::parse(GIFParseQuery query)
|
| {
|
| if (failed())
|
| return;
|
| @@ -309,97 +262,102 @@ void GIFImageDecoder::decode(unsigned haltAtFrame, GIFQuery query)
|
| m_reader->setData(m_data);
|
| }
|
|
|
| - if (query == GIFSizeQuery) {
|
| - if (!m_reader->decode(GIFSizeQuery, haltAtFrame))
|
| - setFailed();
|
| - return;
|
| - }
|
| -
|
| - if (!m_reader->decode(GIFFrameCountQuery, haltAtFrame)) {
|
| + if (!m_reader->parse(query)) {
|
| setFailed();
|
| 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;
|
| + for (size_t i = oldSize; i < m_reader->imagesCount(); ++i) {
|
| + ImageFrame& buffer = m_frameBufferCache[i];
|
| + const GIFFrameContext* frameContext = m_reader->frameContext(i);
|
| + buffer.setPremultiplyAlpha(m_premultiplyAlpha);
|
| + buffer.setRequiredPreviousFrameIndex(findRequiredPreviousFrame(i));
|
| + buffer.setDuration(frameContext->delayTime);
|
| + buffer.setDisposalMethod(frameContext->disposalMethod);
|
|
|
| - if (!m_reader->decode(GIFFullQuery, haltAtFrame)) {
|
| - setFailed();
|
| + // Initialize the frame rect in our buffer.
|
| + 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);
|
| +
|
| + buffer.setOriginalFrameRect(frameRect);
|
| + }
|
| +}
|
| +
|
| +void GIFImageDecoder::decode(size_t frameIndex)
|
| +{
|
| + parse(GIFFrameCountQuery);
|
| +
|
| + if (failed())
|
| return;
|
| +
|
| + Vector<size_t> framesToDecode;
|
| + size_t frameToDecode = frameIndex;
|
| + do {
|
| + framesToDecode.append(frameToDecode);
|
| + frameToDecode = m_frameBufferCache[frameToDecode].requiredPreviousFrameIndex();
|
| + } while (frameToDecode != notFound && m_frameBufferCache[frameToDecode].status() != ImageFrame::FrameComplete);
|
| +
|
| + // The |rend| variable is needed by some compilers that can't correctly
|
| + // select from const and non-const versions of overloaded functions.
|
| + // Can remove the variable if Android compiler can compile
|
| + // 'iter != framesToDecode.rend()'.
|
| + Vector<size_t>::const_reverse_iterator rend = framesToDecode.rend();
|
| + for (Vector<size_t>::const_reverse_iterator iter = framesToDecode.rbegin(); iter != rend; ++iter) {
|
| + size_t frameIndex = *iter;
|
| + if (!m_reader->decode(frameIndex)) {
|
| + setFailed();
|
| + return;
|
| + }
|
| +
|
| + // We need more data to continue decoding.
|
| + if (m_frameBufferCache[frameIndex].status() != ImageFrame::FrameComplete)
|
| + break;
|
| }
|
|
|
| // It is also a fatal error if all data is received and we have decoded all
|
| // frames available but the file is truncated.
|
| - if (haltAtFrame >= m_frameBufferCache.size() && isAllDataReceived() && m_reader && !m_reader->parseCompleted())
|
| + if (frameIndex >= m_frameBufferCache.size() - 1 && isAllDataReceived() && m_reader && !m_reader->parseCompleted())
|
| setFailed();
|
| }
|
|
|
| -bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex)
|
| +bool GIFImageDecoder::initFrameBuffer(size_t frameIndex)
|
| {
|
| // Initialize the frame rect in our buffer.
|
| const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex);
|
| - 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];
|
| - buffer->setOriginalFrameRect(frameRect);
|
|
|
| - if (!frameIndex) {
|
| - // This is the first frame, so we're not relying on any previous data.
|
| + size_t requiredPreviousFrameIndex = buffer->requiredPreviousFrameIndex();
|
| + if (requiredPreviousFrameIndex == notFound) {
|
| + // This frame doesn't rely on any previous data.
|
| if (!buffer->setSize(size().width(), size().height()))
|
| 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
|
| - // 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.)
|
| - const ImageFrame* prevBuffer = &m_frameBufferCache[--frameIndex];
|
| - ImageFrame::FrameDisposalMethod prevMethod = prevBuffer->disposalMethod();
|
| - while (frameIndex && (prevMethod == ImageFrame::DisposeOverwritePrevious)) {
|
| - prevBuffer = &m_frameBufferCache[--frameIndex];
|
| - prevMethod = prevBuffer->disposalMethod();
|
| - }
|
| + const ImageFrame* prevBuffer = &m_frameBufferCache[requiredPreviousFrameIndex];
|
| ASSERT(prevBuffer->status() == ImageFrame::FrameComplete);
|
|
|
| - if ((prevMethod == ImageFrame::DisposeNotSpecified) || (prevMethod == ImageFrame::DisposeKeep)) {
|
| - // Preserve the last frame as the starting state for this frame.
|
| - if (!buffer->copyBitmapData(*prevBuffer))
|
| - return setFailed();
|
| - } else {
|
| + // 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();
|
| - const IntSize& bufferSize = size();
|
| - if (!frameIndex || prevRect.contains(IntRect(IntPoint(), size()))) {
|
| - // 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()))
|
| - return setFailed();
|
| - } else {
|
| - // Copy the whole previous buffer, then clear just its frame.
|
| - if (!buffer->copyBitmapData(*prevBuffer))
|
| - return setFailed();
|
| - for (int y = prevRect.y(); y < prevRect.maxY(); ++y) {
|
| - for (int x = prevRect.x(); x < prevRect.maxX(); ++x)
|
| - buffer->setRGBA(x, y, 0, 0, 0, 0);
|
| - }
|
| - if ((prevRect.width() > 0) && (prevRect.height() > 0))
|
| - buffer->setHasAlpha(true);
|
| + ASSERT(!prevRect.contains(IntRect(IntPoint(), size())));
|
| + for (int y = prevRect.y(); y < prevRect.maxY(); ++y) {
|
| + for (int x = prevRect.x(); x < prevRect.maxX(); ++x)
|
| + buffer->setRGBA(x, y, 0, 0, 0, 0);
|
| }
|
| + if ((prevRect.width() > 0) && (prevRect.height() > 0))
|
| + buffer->setHasAlpha(true);
|
| }
|
| }
|
|
|
|
|