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..e1da525fdc0550e987c48b1e9ebc68df3e389660 100644 |
| --- a/Source/core/platform/image-decoders/gif/GIFImageDecoder.cpp |
| +++ b/Source/core/platform/image-decoders/gif/GIFImageDecoder.cpp |
| @@ -57,7 +57,7 @@ void GIFImageDecoder::setData(SharedBuffer* data, bool allDataReceived) |
| bool GIFImageDecoder::isSizeAvailable() |
| { |
| if (!ImageDecoder::isSizeAvailable()) |
| - decode(0, GIFSizeQuery); |
| + parse(GIFSizeQuery); |
| return ImageDecoder::isSizeAvailable(); |
| } |
| @@ -76,7 +76,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 +121,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; |
| @@ -195,7 +195,7 @@ void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame) |
| } |
| } |
| -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 +258,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, unsigned frameDuration, ImageFrame::FrameDisposalMethod disposalMethod) |
| { |
| // Initialize the frame if necessary. Some GIFs insert do-nothing frames, |
| // in which case we never reach haveDecodedRow() before getting here. |
| @@ -311,7 +311,7 @@ void GIFImageDecoder::gifComplete() |
| repetitionCount(); |
| } |
| -void GIFImageDecoder::decode(unsigned haltAtFrame, GIFQuery query) |
| +void GIFImageDecoder::parse(GIFParseQuery query) |
| { |
| if (failed()) |
| return; |
| @@ -321,13 +321,7 @@ 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; |
| } |
| @@ -336,22 +330,82 @@ void GIFImageDecoder::decode(unsigned haltAtFrame, GIFQuery query) |
| 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) |
| +void GIFImageDecoder::decode(size_t frameIndex) |
| +{ |
| + parse(GIFFrameCountQuery); |
| + |
| + if (failed()) |
| return; |
| - if (!m_reader->decode(GIFFullQuery, haltAtFrame)) { |
| + BitVector frames(frameIndex); |
|
Alpha Left Google
2013/05/18 00:39:35
Instead of a BitVector what about a vector of fram
Xianzhu
2013/05/20 04:30:59
Done.
|
| + getFramesToDecode(frameIndex, &frames); |
| + |
| + if (!m_reader->decode(frames)) { |
|
Alpha Left Google
2013/05/18 00:39:35
I'd prefer to call m_reader->decode(...) multiple
Xianzhu
2013/05/20 04:30:59
Done.
|
| setFailed(); |
| return; |
| } |
| // 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(); |
|
Alpha Left Google
2013/05/18 00:39:35
The reason for calling setFailed() here is to stop
|
| } |
| -bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex) |
| +void GIFImageDecoder::getFramesToDecode(size_t frameIndex, BitVector* frames) |
| +{ |
| + while (m_frameBufferCache[frameIndex].status() != ImageFrame::FrameComplete) { |
| + frames->set(frameIndex); |
| + frameIndex = getPreviousRequiredFrame(frameIndex); |
| + if (frameIndex == notFound) |
| + break; |
| + } |
| +} |
| + |
| +size_t GIFImageDecoder::getPreviousRequiredFrame(size_t frameIndex) |
|
Alpha Left Google
2013/05/18 00:39:35
I see this method will be called multiple times fo
Xianzhu
2013/05/20 04:30:59
Done.
|
| +{ |
| + if (!frameIndex) { |
| + // The first frame doesn't rely on any previous data. |
| + return notFound; |
| + } |
| + |
| + // 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.) |
| + size_t prevFrame = frameIndex - 1; |
| + const ImageFrame* prevBuffer = &m_frameBufferCache[prevFrame]; |
| + ImageFrame::FrameDisposalMethod prevMethod = prevBuffer->disposalMethod(); |
| + while (prevFrame && (prevMethod == ImageFrame::DisposeOverwritePrevious)) { |
| + prevBuffer = &m_frameBufferCache[--prevFrame]; |
| + prevMethod = prevBuffer->disposalMethod(); |
| + } |
| + |
| + if (prevMethod == ImageFrame::DisposeNotSpecified || prevMethod == ImageFrame::DisposeKeep) { |
| + // prevFrame will be used as the starting state for this frame. |
| + return prevFrame; |
| + } |
| + |
| + ASSERT(prevMethod == ImageFrame::DisposeOverwriteBgcolor); |
| + const IntRect& prevRect = prevBuffer->originalFrameRect(); |
| + const IntSize& bufferSize = scaledSize(); |
| + if (!frameIndex || prevRect.contains(IntRect(IntPoint(), scaledSize()))) { |
| + // The first frame or a frame covering the whole image will be cleared |
| + // to background, so this frame doesn't rely on any previous data. |
| + return notFound; |
| + } |
| + |
| + // prevFrame only clears part of the image to background, so it contributes |
| + // to the starting state of this frame. |
| + return prevFrame; |
| +} |
| + |
| +bool GIFImageDecoder::initFrameBuffer(size_t frameIndex) |
| { |
| // Initialize the frame rect in our buffer. |
| const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex); |
| @@ -369,53 +423,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 previousRequiredFrame = getPreviousRequiredFrame(frameIndex); |
| + if (previousRequiredFrame == 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[previousRequiredFrame]; |
| 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); |
| } |
| } |