This patch dramatically reduces the memory consumed by animated images. ...
authorhyatt <hyatt@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 8 Mar 2007 08:43:35 +0000 (08:43 +0000)
committerhyatt <hyatt@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 8 Mar 2007 08:43:35 +0000 (08:43 +0000)
        now as >1mb in terms of decoded frame buffer size), we will now aggressively flush previous frames of the
        animated GIF and just re-decode them on the fly if the animation loops.

        Whenever a large animated GIF has its animation reset, we will also just throw out everything and start
        the animation over (in order to get rid of any cached detritus held in the ImageSource).

        With this patch and the sample GIF used to test, WebKit's memory consumption went from 160MB down to 16MB.

        Reviewed by mjs

        * platform/graphics/BitmapImage.cpp:
        (WebCore::BitmapImage::destroyDecodedData):
        (WebCore::BitmapImage::resetAnimation):
        (WebCore::BitmapImage::advanceAnimation):
        * platform/graphics/ImageSource.h:
        * platform/graphics/cg/ImageSourceCG.cpp:
        (WebCore::ImageSource::~ImageSource):
        (WebCore::ImageSource::clear):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@20056 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog
WebCore/platform/graphics/BitmapImage.cpp
WebCore/platform/graphics/ImageSource.h
WebCore/platform/graphics/cg/ImageSourceCG.cpp

index 9b9982e..3bcba6f 100644 (file)
@@ -1,3 +1,25 @@
+2007-03-08  David Hyatt  <hyatt@apple.com>
+
+        This patch dramatically reduces the memory consumed by animated images.  For large animated GIFs (defined for
+        now as >1mb in terms of decoded frame buffer size), we will now aggressively flush previous frames of the
+        animated GIF and just re-decode them on the fly if the animation loops.
+
+        Whenever a large animated GIF has its animation reset, we will also just throw out everything and start
+        the animation over (in order to get rid of any cached detritus held in the ImageSource).
+
+        With this patch and the sample GIF used to test, WebKit's memory consumption went from 160MB down to 16MB.
+
+        Reviewed by mjs
+
+        * platform/graphics/BitmapImage.cpp:
+        (WebCore::BitmapImage::destroyDecodedData):
+        (WebCore::BitmapImage::resetAnimation):
+        (WebCore::BitmapImage::advanceAnimation):
+        * platform/graphics/ImageSource.h:
+        * platform/graphics/cg/ImageSourceCG.cpp:
+        (WebCore::ImageSource::~ImageSource):
+        (WebCore::ImageSource::clear):
+
 2007-03-08  Alexey Proskuryakov  <ap@webkit.org>
 
         Reviewed by Darin.
index 4e76920..2d62982 100644 (file)
 
 namespace WebCore {
 
+// Animated images >1MB are considered large enough that we'll only hang on to
+// one frame at a time.
+const unsigned cLargeAnimationCutoff = 1048576;
+
 BitmapImage::BitmapImage(ImageObserver* observer)
     : Image(observer)
     , m_currentFrame(0)
@@ -72,7 +76,8 @@ void BitmapImage::destroyDecodedData(bool incremental)
             if (m_frames[i].m_frame) {
                 sizeChange -= frameSize;
                 m_frames[i].clear();
-                m_source.destroyFrameAtIndex(i);
+                if (!incremental)
+                    m_source.destroyFrameAtIndex(i);
             }
         }
 
@@ -86,6 +91,13 @@ void BitmapImage::destroyDecodedData(bool incremental)
             if (imageObserver())
                 imageObserver()->decodedSizeChanged(this, sizeChange);
         }
+        
+        if (!incremental && frameCount() * frameSize > cLargeAnimationCutoff) {
+            // Reset the image source, since Image I/O has an underlying cache that it uses
+            // while animating that it seems to never clear.
+            m_source.clear();
+            setData(true);
+        }
     }
 }
 
@@ -215,6 +227,11 @@ void BitmapImage::resetAnimation()
     m_currentFrame = 0;
     m_repetitionsComplete = 0;
     m_animationFinished = false;
+    int frameSize = m_size.width() * m_size.height() * 4;
+    
+    // For extremely large animations, when the animation is reset, we just throw everything away.
+    if (frameCount() * frameSize > cLargeAnimationCutoff)
+        destroyDecodedData();
 }
 
 void BitmapImage::advanceAnimation(Timer<BitmapImage>* timer)
@@ -227,7 +244,7 @@ void BitmapImage::advanceAnimation(Timer<BitmapImage>* timer)
     if (imageObserver()->shouldPauseAnimation(this))
         return;
 
-    m_currentFrame++;
+    size_t previousFrame = m_currentFrame++;
     if (m_currentFrame >= frameCount()) {
         m_repetitionsComplete += 1;
         if (m_repetitionCount && m_repetitionsComplete >= m_repetitionCount) {
@@ -240,7 +257,24 @@ void BitmapImage::advanceAnimation(Timer<BitmapImage>* timer)
 
     // Notify our observer that the animation has advanced.
     imageObserver()->animationAdvanced(this);
+
+    // For large animated images, go ahead and throw away frames as we go to save
+    // footprint.
+    int frameSize = m_size.width() * m_size.height() * 4;
+    if (frameCount() * frameSize > cLargeAnimationCutoff) {
+        // Go ahead and decode the next frame so that it can rely on the previous frame.
+        frameAtIndex(m_currentFrame);
         
+        // Now throw away the previous frame.
+        if (m_frames[previousFrame].m_frame) {
+            m_frames[previousFrame].clear();
+            m_source.destroyFrameAtIndex(previousFrame);
+            m_decodedSize -= frameSize;
+            if (imageObserver())
+                imageObserver()->decodedSizeChanged(this, -frameSize);
+        }
+    }
+    
     // Kick off a timer to move to the next frame.
     m_frameTimer = new Timer<BitmapImage>(this, &BitmapImage::advanceAnimation);
     m_frameTimer->startOneShot(frameDurationAtIndex(m_currentFrame));
index da911e1..04b08df 100644 (file)
@@ -68,6 +68,8 @@ public:
     ImageSource();
     ~ImageSource();
 
+    void clear();
+
     bool initialized() const;
     
     void setData(NativeBytePtr, bool allDataReceived);
index 681e576..c33303a 100644 (file)
@@ -40,8 +40,15 @@ ImageSource::ImageSource()
 
 ImageSource::~ImageSource()
 {
-    if (m_decoder)
+    clear();
+}
+
+void ImageSource::clear()
+{
+    if (m_decoder) {
         CFRelease(m_decoder);
+        m_decoder = 0;
+    }
 }
 
 const CFStringRef kCGImageSourceShouldPreferRGB32 = CFSTR("kCGImageSourceShouldPreferRGB32");