Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(882)

Unified Diff: Source/core/platform/image-decoders/gif/GIFImageDecoder.cpp

Issue 15350006: Decode GIF image frames on demand. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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);
}
}

Powered by Google App Engine
This is Rietveld 408576698