Chromium Code Reviews| 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 d785aec0341c6f5f782e4ffe3b97e4b74b001224..114bef39febc9aafe96ccc30c794554ab3316b39 100644 |
| --- a/Source/core/platform/image-decoders/gif/GIFImageDecoder.cpp |
| +++ b/Source/core/platform/image-decoders/gif/GIFImageDecoder.cpp |
| @@ -29,7 +29,8 @@ |
| #include <limits> |
| #include "core/platform/PlatformInstrumentation.h" |
| #include "core/platform/image-decoders/gif/GIFImageReader.h" |
| -#include <wtf/PassOwnPtr.h> |
| +#include "wtf/NotFound.h" |
| +#include "wtf/PassOwnPtr.h" |
| namespace WebCore { |
| @@ -57,7 +58,7 @@ void GIFImageDecoder::setData(SharedBuffer* data, bool allDataReceived) |
| bool GIFImageDecoder::isSizeAvailable() |
| { |
| if (!ImageDecoder::isSizeAvailable()) |
| - decode(0, GIFSizeQuery); |
| + parse(GIFSizeQuery); |
| return ImageDecoder::isSizeAvailable(); |
| } |
| @@ -76,7 +77,7 @@ bool GIFImageDecoder::setSize(unsigned width, unsigned height) |
| size_t GIFImageDecoder::frameCount() |
| { |
| - decode(std::numeric_limits<unsigned>::max(), GIFFrameCountQuery); |
| + parse(GIFFrameCountQuery); |
| return m_frameBufferCache.size(); |
| } |
| @@ -121,7 +122,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; |
| @@ -145,57 +146,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 |
| @@ -258,7 +209,7 @@ bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, const Vector<unsigned |
| return true; |
| } |
| -bool GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, ImageFrame::FrameDisposalMethod disposalMethod) |
| +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. |
| @@ -267,26 +218,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(), scaledSize()))) |
| + if (buffer.originalFrameRect().contains(IntRect(IntPoint(), scaledSize()))) { |
| buffer.setHasAlpha(false); |
| - else if (frameIndex) { |
| + buffer.setRequiredPreviousFrameIndex(notFound); |
|
Alpha Left Google
2013/05/22 20:18:09
Knowing this frame doesn't depend on any previous
Xianzhu
2013/05/22 23:43:29
That seems unnecessary. Whether this frame resets
|
| + } 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 |
| @@ -304,14 +249,7 @@ bool GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, |
| return true; |
| } |
| -void GIFImageDecoder::gifComplete() |
| -{ |
| - // Cache the repetition count, which is now as authoritative as it's ever |
| - // going to be. |
| - repetitionCount(); |
| -} |
| - |
| -void GIFImageDecoder::decode(unsigned haltAtFrame, GIFQuery query) |
| +void GIFImageDecoder::parse(GIFParseQuery query) |
| { |
| if (failed()) |
| return; |
| @@ -321,37 +259,62 @@ 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; |
| + // The old last frame buffer may have not been completely parsed last time. |
| + if (oldSize) { |
| + ImageFrame& buffer = m_frameBufferCache[oldSize - 1]; |
| + const GIFFrameContext* frameContext = m_reader->frameContext(oldSize - 1); |
| + buffer.setDuration(frameContext->delayTime); |
| + buffer.setDisposalMethod(frameContext->disposalMethod); |
| + } |
| - if (!m_reader->decode(GIFFullQuery, haltAtFrame)) { |
| - setFailed(); |
| + 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)); |
| + // For the last parsed frame, the following information may be incomplete |
| + // at this time. Will be refreshed later. |
| + buffer.setDuration(frameContext->delayTime); |
|
Alpha Left Google
2013/05/22 20:18:09
This information should always be there for each n
Xianzhu
2013/05/22 23:43:29
Done.
|
| + buffer.setDisposalMethod(frameContext->disposalMethod); |
| + } |
| +} |
| + |
| +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); |
| + |
| + for (size_t i = framesToDecode.size(); i; --i) { |
| + if (!m_reader->decode(framesToDecode[i - 1])) { |
| + setFailed(); |
| + return; |
| + } |
|
Xianzhu
2013/05/22 23:43:29
Just noticed I missed the case of not enough data.
|
| } |
| // 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); |
| @@ -369,53 +332,30 @@ bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex) |
| 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. |
| + |
| + size_t requiredPreviousFrameIndex = m_frameBufferCache[frameIndex].requiredPreviousFrameIndex(); |
| + if (requiredPreviousFrameIndex == notFound) { |
| + // This frame doesn't rely on any previous data. |
| if (!buffer->setSize(scaledSize().width(), scaledSize().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 = 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())) |
| - 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); |
| + 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); |
| } |
| } |