Index: Source/core/platform/image-decoders/ImageDecoder.cpp |
diff --git a/Source/core/platform/image-decoders/ImageDecoder.cpp b/Source/core/platform/image-decoders/ImageDecoder.cpp |
index b98e45a3fa8f93c9a9ccfaba21d6749ce34e02fa..9eb1de3127844150adcd41ad5b4f28dc3183e77d 100644 |
--- a/Source/core/platform/image-decoders/ImageDecoder.cpp |
+++ b/Source/core/platform/image-decoders/ImageDecoder.cpp |
@@ -127,7 +127,7 @@ bool ImageDecoder::frameIsCompleteAtIndex(size_t index) const |
unsigned ImageDecoder::frameBytesAtIndex(size_t index) const |
{ |
- if (m_frameBufferCache.size() <= index) |
+ if (m_frameBufferCache.size() <= index || m_frameBufferCache[index].status() == ImageFrame::FrameEmpty) |
return 0; |
// FIXME: Use the dimension of the requested frame. |
return m_size.area() * sizeof(ImageFrame::PixelData); |
@@ -140,4 +140,73 @@ void ImageDecoder::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const |
info.addMember(m_frameBufferCache, "frameBufferCache"); |
} |
+size_t ImageDecoder::clearCacheExceptFrame(size_t clearExceptFrame) |
+{ |
+ // Don't clear if there are no frames or only one frame. |
+ if (m_frameBufferCache.size() <= 1) |
+ return 0; |
+ |
+ // 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. |
+ while ((clearExceptFrame < m_frameBufferCache.size()) && (m_frameBufferCache[clearExceptFrame].status() == ImageFrame::FrameEmpty)) |
+ clearExceptFrame = m_frameBufferCache[clearExceptFrame].requiredPreviousFrameIndex(); |
+ |
+ size_t frameBytesCleared = 0; |
+ for (size_t i = 0; i < m_frameBufferCache.size(); ++i) { |
+ if (i != clearExceptFrame) { |
+ frameBytesCleared += frameBytesAtIndex(i); |
+ clearFrameBuffer(i); |
+ } |
+ } |
+ return frameBytesCleared; |
+} |
+ |
+void ImageDecoder::clearFrameBuffer(size_t frameIndex) |
+{ |
+ m_frameBufferCache[frameIndex].clearPixelData(); |
+} |
+ |
+size_t ImageDecoder::findRequiredPreviousFrame(size_t frameIndex) |
+{ |
+ ASSERT(frameIndex <= m_frameBufferCache.size()); |
+ 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. |
+ size_t prevFrame = frameIndex - 1; |
+ const ImageFrame* prevBuffer = &m_frameBufferCache[prevFrame]; |
+ ASSERT(prevBuffer->requiredPreviousFrameIndexValid()); |
+ |
+ switch (prevBuffer->disposalMethod()) { |
+ case ImageFrame::DisposeNotSpecified: |
+ case ImageFrame::DisposeKeep: |
+ // prevFrame will be used as the starting state for this frame. |
+ // FIXME: Be even smarter by checking the frame sizes and/or alpha-containing regions. |
+ return prevFrame; |
+ case ImageFrame::DisposeOverwritePrevious: |
+ // 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 and |
+ // return the required previous frame of it. |
+ return prevBuffer->requiredPreviousFrameIndex(); |
+ case ImageFrame::DisposeOverwriteBgcolor: |
+ // If the previous frame fills the whole image, then the current frame |
+ // can be decoded alone. Likewise, if the previous frame could be |
+ // decoded without reference to any prior frame, the starting state for |
+ // this frame is a blank frame, so it can again be decoded alone. |
+ // Otherwise, the previous frame contributes to this frame. |
+ return (prevBuffer->originalFrameRect().contains(IntRect(IntPoint(), size())) |
+ || (prevBuffer->requiredPreviousFrameIndex() == notFound)) ? notFound : prevFrame; |
+ default: |
+ ASSERT_NOT_REACHED(); |
+ return notFound; |
+ } |
+} |
+ |
} // namespace WebCore |