Index: third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp |
diff --git a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp |
index cc5954edf9de97a6391a4c904dc4826cc341741d..bf46ddc90833f8c0fe7fa1b33465bbf42a2a3daf 100644 |
--- a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp |
+++ b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp |
@@ -220,15 +220,48 @@ bool GIFImageDecoder::frameComplete(size_t frameIndex) |
size_t GIFImageDecoder::clearCacheExceptFrame(size_t clearExceptFrame) |
{ |
- // We need to preserve frames such that: |
- // 1. We don't clear |clearExceptFrame|; |
- // 2. We don't clear any frame from which a future initFrameBuffer() call |
- // will copy bitmap data. |
- // All other frames can be cleared. |
+ // We expect that after this call, we'll be asked to decode frames after |
+ // this one. So we want to avoid clearing frames such that those requests |
+ // would force re-decoding from the beginning of the image. |
+ // |
+ // When |clearExceptFrame| is e.g. DisposeKeep, simply not clearing that |
+ // frame is sufficient, as the next frame will be based on it, and in |
+ // general future frames can't be based on anything previous. |
+ // |
+ // However, if this frame is DisposeOverwritePrevious, then subsequent |
+ // frames will depend on this frame's required previous frame. In this |
+ // case, we need to preserve both this frame and that one. |
+ size_t clearExceptFrame2 = kNotFound; |
+ if (clearExceptFrame < m_frameBufferCache.size()) { |
+ const ImageFrame& frame = m_frameBufferCache[clearExceptFrame]; |
+ if ((frame.status() != ImageFrame::FrameEmpty) && (frame.disposalMethod() == ImageFrame::DisposeOverwritePrevious)) { |
+ clearExceptFrame2 = clearExceptFrame; |
+ clearExceptFrame = frame.requiredPreviousFrameIndex(); |
+ } |
+ } |
+ |
+ // Now |clearExceptFrame| indicates the frame that future frames will |
+ // depend on. But if decoding is skipping forward past intermediate frames, |
+ // this frame may be FrameEmpty. So we need to keep traversing back through |
+ // the required previous frames until we find the nearest non-empty |
+ // ancestor. Preserving that will minimize the amount of future decoding |
+ // needed. |
while ((clearExceptFrame < m_frameBufferCache.size()) && (m_frameBufferCache[clearExceptFrame].status() == ImageFrame::FrameEmpty)) |
clearExceptFrame = m_frameBufferCache[clearExceptFrame].requiredPreviousFrameIndex(); |
+ return clearCacheExceptTwoFrames(clearExceptFrame, clearExceptFrame2); |
+} |
+ |
- return ImageDecoder::clearCacheExceptFrame(clearExceptFrame); |
+size_t GIFImageDecoder::clearCacheExceptTwoFrames(size_t clearExceptFrame1, size_t clearExceptFrame2) |
+{ |
+ size_t frameBytesCleared = 0; |
+ for (size_t i = 0; i < m_frameBufferCache.size(); ++i) { |
+ if (m_frameBufferCache[i].status() != ImageFrame::FrameEmpty && i != clearExceptFrame1 && i != clearExceptFrame2) { |
+ frameBytesCleared += frameBytesAtIndex(i); |
+ clearFrameBuffer(i); |
+ } |
+ } |
+ return frameBytesCleared; |
} |
void GIFImageDecoder::clearFrameBuffer(size_t frameIndex) |