Changeset 37612 in webkit


Ignore:
Timestamp:
Oct 15, 2008 1:38:13 PM (16 years ago)
Author:
pkasting@chromium.org
Message:

2008-10-15 Peter Kasting <pkasting@google.com>

Reviewed by David Hyatt.

https://bugs.webkit.org/show_bug.cgi?id=19663 (Second attempt)
Account for paint and timer lag when animating images. Also pretend
that images whose animations were paused (by becoming invisible)
continued to animate, by "catching up" to the correct frame when they're
shown again.

  • platform/graphics/BitmapImage.cpp: (WebCore::BitmapImage::BitmapImage): (WebCore::BitmapImage::destroyDecodedData): (WebCore::BitmapImage::cacheFrame): (WebCore::BitmapImage::frameIsCompleteAtIndex): (WebCore::BitmapImage::frameDurationAtIndex): (WebCore::BitmapImage::frameHasAlphaAtIndex): (WebCore::BitmapImage::repetitionCount): (WebCore::BitmapImage::shouldAnimate): (WebCore::BitmapImage::startAnimation): (WebCore::BitmapImage::resetAnimation): (WebCore::BitmapImage::advanceAnimation): (WebCore::BitmapImage::internalAdvanceAnimation): (WebCore::BitmapImage::notifyObserverAndTrimDecodedData):
  • platform/graphics/BitmapImage.h: (WebCore::FrameData::FrameData): (WebCore::BitmapImage::):
  • platform/graphics/GeneratedImage.h: (WebCore::GeneratedImage::destroyDecodedData):
  • platform/graphics/Image.h:
  • platform/graphics/cairo/ImageCairo.cpp: (WebCore::FrameData::clear): (WebCore::BitmapImage::BitmapImage): (WebCore::BitmapImage::draw):
  • platform/graphics/cg/ImageCG.cpp: (WebCore::FrameData::clear): (WebCore::BitmapImage::BitmapImage): (WebCore::BitmapImage::draw):
  • platform/graphics/cg/PDFDocumentImage.h: (WebCore::PDFDocumentImage::destroyDecodedData):
  • platform/graphics/qt/ImageQt.cpp: (WebCore::FrameData::clear): (WebCore::BitmapImage::draw):
  • platform/graphics/wx/ImageWx.cpp: (WebCore::FrameData::clear): (WebCore::BitmapImage::draw):
  • svg/graphics/SVGImage.h: (WebCore::SVGImage::destroyDecodedData):
Location:
trunk/WebCore
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog

    r37611 r37612  
     12008-10-15  Peter Kasting  <pkasting@google.com>
     2
     3        Reviewed by David Hyatt.
     4
     5        https://bugs.webkit.org/show_bug.cgi?id=19663 (Second attempt)
     6        Account for paint and timer lag when animating images.  Also pretend
     7        that images whose animations were paused (by becoming invisible)
     8        continued to animate, by "catching up" to the correct frame when they're
     9        shown again.
     10
     11        * platform/graphics/BitmapImage.cpp:
     12        (WebCore::BitmapImage::BitmapImage):
     13        (WebCore::BitmapImage::destroyDecodedData):
     14        (WebCore::BitmapImage::cacheFrame):
     15        (WebCore::BitmapImage::frameIsCompleteAtIndex):
     16        (WebCore::BitmapImage::frameDurationAtIndex):
     17        (WebCore::BitmapImage::frameHasAlphaAtIndex):
     18        (WebCore::BitmapImage::repetitionCount):
     19        (WebCore::BitmapImage::shouldAnimate):
     20        (WebCore::BitmapImage::startAnimation):
     21        (WebCore::BitmapImage::resetAnimation):
     22        (WebCore::BitmapImage::advanceAnimation):
     23        (WebCore::BitmapImage::internalAdvanceAnimation):
     24        (WebCore::BitmapImage::notifyObserverAndTrimDecodedData):
     25        * platform/graphics/BitmapImage.h:
     26        (WebCore::FrameData::FrameData):
     27        (WebCore::BitmapImage::):
     28        * platform/graphics/GeneratedImage.h:
     29        (WebCore::GeneratedImage::destroyDecodedData):
     30        * platform/graphics/Image.h:
     31        * platform/graphics/cairo/ImageCairo.cpp:
     32        (WebCore::FrameData::clear):
     33        (WebCore::BitmapImage::BitmapImage):
     34        (WebCore::BitmapImage::draw):
     35        * platform/graphics/cg/ImageCG.cpp:
     36        (WebCore::FrameData::clear):
     37        (WebCore::BitmapImage::BitmapImage):
     38        (WebCore::BitmapImage::draw):
     39        * platform/graphics/cg/PDFDocumentImage.h:
     40        (WebCore::PDFDocumentImage::destroyDecodedData):
     41        * platform/graphics/qt/ImageQt.cpp:
     42        (WebCore::FrameData::clear):
     43        (WebCore::BitmapImage::draw):
     44        * platform/graphics/wx/ImageWx.cpp:
     45        (WebCore::FrameData::clear):
     46        (WebCore::BitmapImage::draw):
     47        * svg/graphics/SVGImage.h:
     48        (WebCore::SVGImage::destroyDecodedData):
     49
    1502008-10-14  Maxime Britto  <britto@apple.com>
    251
  • trunk/WebCore/platform/graphics/BitmapImage.cpp

    r36781 r37612  
    3232#include "IntRect.h"
    3333#include "PlatformString.h"
     34#include "SystemTime.h"
    3435#include "Timer.h"
    3536#include <wtf/Vector.h>
     
    4142// one frame at a time.
    4243const unsigned cLargeAnimationCutoff = 5242880;
     44
     45// When an animated image is more than five minutes out of date, don't try to
     46// resync on repaint, so we don't waste CPU cycles on an edge case the user
     47// doesn't care about.
     48const double cAnimationResyncCutoff = 5 * 60;
    4349
    4450BitmapImage::BitmapImage(ImageObserver* observer)
     
    4753    , m_frames(0)
    4854    , m_frameTimer(0)
    49     , m_repetitionCount(0)
     55    , m_repetitionCount(cAnimationNone)
     56    , m_repetitionCountStatus(Unknown)
    5057    , m_repetitionsComplete(0)
     58    , m_desiredFrameStartTime(0)
    5159    , m_isSolidColor(false)
    52     , m_animatingImageType(true)
    5360    , m_animationFinished(false)
    5461    , m_allDataReceived(false)
     
    6976}
    7077
    71 void BitmapImage::destroyDecodedData(bool incremental)
     78void BitmapImage::destroyDecodedData(bool incremental, bool preserveNearbyFrames)
    7279{
    7380    // Destroy the cached images and release them.
     
    7582        int sizeChange = 0;
    7683        int frameSize = m_size.width() * m_size.height() * 4;
     84        const size_t nextFrame = (preserveNearbyFrames && frameCount()) ? ((m_currentFrame + 1) % frameCount()) : 0;
    7785        for (unsigned i = incremental ? m_frames.size() - 1 : 0; i < m_frames.size(); i++) {
    78             if (m_frames[i].m_frame) {
     86            if (m_frames[i].m_frame && (!preserveNearbyFrames || (i != m_currentFrame && i != nextFrame))) {
    7987                sizeChange -= frameSize;
    8088                m_frames[i].clear();
     
    107115    ASSERT(m_decodedSize == 0 || numFrames > 1);
    108116   
    109     if (!m_frames.size() && shouldAnimate()) {           
    110         // Snag the repetition count.  Note that the repetition count may not be
    111         // accurate yet for GIFs; if we haven't gotten the count from the source
    112         // image yet, it will default to cAnimationLoopOnce, and we'll try and
    113         // read it again once the whole image is decoded.
    114         m_repetitionCount = m_source.repetitionCount();
    115         if (m_repetitionCount == cAnimationNone)
    116             m_animatingImageType = false;
    117     }
    118    
    119117    if (m_frames.size() < numFrames)
    120118        m_frames.grow(numFrames);
     
    124122        checkForSolidColor();
    125123
    126     if (shouldAnimate())
     124    m_frames[index].m_haveMetadata = true;
     125    m_frames[index].m_isComplete = m_source.frameIsCompleteAtIndex(index);
     126    if (repetitionCount(false) != cAnimationNone)
    127127        m_frames[index].m_duration = m_source.frameDurationAtIndex(index);
    128128    m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index);
     
    208208}
    209209
     210bool BitmapImage::frameIsCompleteAtIndex(size_t index)
     211{
     212    if (index >= frameCount())
     213        return true;
     214
     215    if (index >= m_frames.size() || !m_frames[index].m_haveMetadata)
     216        cacheFrame(index);
     217
     218    return m_frames[index].m_isComplete;
     219}
     220
    210221float BitmapImage::frameDurationAtIndex(size_t index)
    211222{
     
    213224        return 0;
    214225
    215     if (index >= m_frames.size() || !m_frames[index].m_frame)
     226    if (index >= m_frames.size() || !m_frames[index].m_haveMetadata)
    216227        cacheFrame(index);
    217228
     
    224235        return true;
    225236
    226     if (index >= m_frames.size() || !m_frames[index].m_frame)
     237    if (index >= m_frames.size() || !m_frames[index].m_haveMetadata)
    227238        cacheFrame(index);
    228239
     
    230241}
    231242
     243int BitmapImage::repetitionCount(bool imageKnownToBeComplete)
     244{
     245    if ((m_repetitionCountStatus == Unknown) || ((m_repetitionCountStatus == Uncertain) && imageKnownToBeComplete)) {
     246        // Snag the repetition count.  If |imageKnownToBeComplete| is false, the
     247        // repetition count may not be accurate yet for GIFs; in this case the
     248        // decoder will default to cAnimationLoopOnce, and we'll try and read
     249        // the count again once the whole image is decoded.
     250        m_repetitionCount = m_source.repetitionCount();
     251        m_repetitionCountStatus = (imageKnownToBeComplete || m_repetitionCount == cAnimationNone) ? Certain : Uncertain;
     252    }
     253    return m_repetitionCount;
     254}
     255
    232256bool BitmapImage::shouldAnimate()
    233257{
    234     return (m_animatingImageType && !m_animationFinished && imageObserver());
    235 }
    236 
    237 void BitmapImage::startAnimation()
     258    return (repetitionCount(false) != cAnimationNone && !m_animationFinished && imageObserver());
     259}
     260
     261void BitmapImage::startAnimation(bool catchUpIfNecessary)
    238262{
    239263    if (m_frameTimer || !shouldAnimate() || frameCount() <= 1)
    240264        return;
    241265
    242     // Don't advance the animation until the current frame has completely loaded.
    243     if (!m_source.frameIsCompleteAtIndex(m_currentFrame))
     266    // Determine time for next frame to start.  By ignoring paint and timer lag
     267    // in this calculation, we make the animation appear to run at its desired
     268    // rate regardless of how fast it's being repainted.
     269    const double currentDuration = frameDurationAtIndex(m_currentFrame);
     270    const double time = currentTime();
     271    if (m_desiredFrameStartTime == 0) {
     272        m_desiredFrameStartTime = time + currentDuration;
     273    } else {
     274        m_desiredFrameStartTime += currentDuration;
     275        // If we're too far behind, the user probably doesn't care about
     276        // resyncing and we could burn a lot of time looping through frames
     277        // below.  Just reset the timings.
     278        if ((time - m_desiredFrameStartTime) > cAnimationResyncCutoff)
     279            m_desiredFrameStartTime = time + currentDuration;
     280    }
     281
     282    // Don't advance the animation to an incomplete frame.
     283    size_t nextFrame = (m_currentFrame + 1) % frameCount();
     284    if (!frameIsCompleteAtIndex(nextFrame))
    244285        return;
    245286
     
    248289    // in a GIF can potentially come after all the rest of the image data, so
    249290    // wait on it.
    250     if (!m_allDataReceived && m_repetitionCount == cAnimationLoopOnce && m_currentFrame >= (frameCount() - 1))
     291    if (!m_allDataReceived && repetitionCount(false) == cAnimationLoopOnce && m_currentFrame >= (frameCount() - 1))
    251292        return;
    252293
    253     m_frameTimer = new Timer<BitmapImage>(this, &BitmapImage::advanceAnimation);
    254     m_frameTimer->startOneShot(frameDurationAtIndex(m_currentFrame));
     294    // The image may load more slowly than it's supposed to animate, so that by
     295    // the time we reach the end of the first repetition, we're well behind.
     296    // Clamp the desired frame start time in this case, so that we don't skip
     297    // frames (or whole iterations) trying to "catch up".  This is a tradeoff:
     298    // It guarantees users see the whole animation the second time through and
     299    // don't miss any repetitions, and is closer to what other browsers do; on
     300    // the other hand, it makes animations "less accurate" for pages that try to
     301    // sync an image and some other resource (e.g. audio), especially if users
     302    // switch tabs (and thus stop drawing the animation, which will pause it)
     303    // during that initial loop, then switch back later.
     304    if (nextFrame == 0 && m_repetitionsComplete == 0 && m_desiredFrameStartTime < time)
     305      m_desiredFrameStartTime = time;
     306
     307    if (!catchUpIfNecessary || time < m_desiredFrameStartTime) {
     308        // Haven't yet reached time for next frame to start; delay until then.
     309        m_frameTimer = new Timer<BitmapImage>(this, &BitmapImage::advanceAnimation);
     310        m_frameTimer->startOneShot(std::max(m_desiredFrameStartTime - time, 0.));
     311    } else {
     312        // We've already reached or passed the time for the next frame to start.
     313        // See if we've also passed the time for frames after that to start, in
     314        // case we need to skip some frames entirely.  Remember not to advance
     315        // to an incomplete frame.
     316        for (size_t frameAfterNext = (nextFrame + 1) % frameCount(); frameIsCompleteAtIndex(frameAfterNext); frameAfterNext = (nextFrame + 1) % frameCount()) {
     317            // Should we skip the next frame?
     318            double frameAfterNextStartTime = m_desiredFrameStartTime + frameDurationAtIndex(nextFrame);
     319            if (time < frameAfterNextStartTime)
     320                break;
     321
     322            // Yes; skip over it without notifying our observers.
     323            if (!internalAdvanceAnimation(true))
     324                return;
     325            m_desiredFrameStartTime = frameAfterNextStartTime;
     326            nextFrame = frameAfterNext;
     327        }
     328
     329        // Draw the next frame immediately.  Note that m_desiredFrameStartTime
     330        // may be in the past, meaning the next time through this function we'll
     331        // kick off the next advancement sooner than this frame's duration would
     332        // suggest.
     333        if (internalAdvanceAnimation(false)) {
     334            // The image region has been marked dirty, but once we return to our
     335            // caller, draw() will clear it, and nothing will cause the
     336            // animation to advance again.  We need to start the timer for the
     337            // next frame running, or the animation can hang.  (Compare this
     338            // with when advanceAnimation() is called, and the region is dirtied
     339            // while draw() is not in the callstack, meaning draw() gets called
     340            // to update the region and thus startAnimation() is reached again.)
     341            // NOTE: For large images with slow or heavily-loaded systems,
     342            // throwing away data as we go (see destroyDecodedData()) means we
     343            // can spend so much time re-decoding data above that by the time we
     344            // reach here we're behind again.  If we let startAnimation() run
     345            // the catch-up code again, we can get long delays without painting
     346            // as we race the timer, or even infinite recursion.  In this
     347            // situation the best we can do is to simply change frames as fast
     348            // as possible, so force startAnimation() to set a zero-delay timer
     349            // and bail out if we're not caught up.
     350            startAnimation(false);
     351        }
     352    }
    255353}
    256354
     
    268366    m_currentFrame = 0;
    269367    m_repetitionsComplete = 0;
     368    m_desiredFrameStartTime = 0;
    270369    m_animationFinished = false;
    271370    int frameSize = m_size.width() * m_size.height() * 4;
     
    278377void BitmapImage::advanceAnimation(Timer<BitmapImage>* timer)
    279378{
     379    internalAdvanceAnimation(false);
     380    // At this point the image region has been marked dirty, and if it's
     381    // onscreen, we'll soon make a call to draw(), which will call
     382    // startAnimation() again to keep the animation moving.
     383}
     384
     385bool BitmapImage::internalAdvanceAnimation(bool skippingFrames)
     386{
    280387    // Stop the animation.
    281388    stopAnimation();
     
    283390    // See if anyone is still paying attention to this animation.  If not, we don't
    284391    // advance and will remain suspended at the current frame until the animation is resumed.
    285     if (imageObserver()->shouldPauseAnimation(this))
    286         return;
     392    if (!skippingFrames && imageObserver()->shouldPauseAnimation(this))
     393        return false;
    287394
    288395    m_currentFrame++;
    289396    if (m_currentFrame >= frameCount()) {
    290         m_repetitionsComplete += 1;
     397        ++m_repetitionsComplete;
    291398        // Get the repetition count again.  If we weren't able to get a
    292399        // repetition count before, we should have decoded the whole image by
    293400        // now, so it should now be available.
    294         m_repetitionCount = m_source.repetitionCount();
    295         if (m_repetitionCount && m_repetitionsComplete >= m_repetitionCount) {
     401        if (repetitionCount(true) && m_repetitionsComplete >= m_repetitionCount) {
    296402            m_animationFinished = true;
     403            m_desiredFrameStartTime = 0;
    297404            m_currentFrame--;
    298             return;
     405            if (skippingFrames) {
     406                // Uh oh.  We tried to skip past the end of the animation.  We'd
     407                // better draw this last frame.
     408                notifyObserverAndTrimDecodedData();
     409            }
     410            return false;
    299411        }
    300412        m_currentFrame = 0;
    301413    }
    302414
     415    if (!skippingFrames)
     416        notifyObserverAndTrimDecodedData();
     417
     418    return true;
     419}
     420
     421void BitmapImage::notifyObserverAndTrimDecodedData()
     422{
    303423    // Notify our observer that the animation has advanced.
    304424    imageObserver()->animationAdvanced(this);
    305425
    306     // For large animated images, go ahead and throw away frames as we go to save
    307     // footprint.
     426    // For large animated images, go ahead and throw away frames as we go to
     427    // save footprint.
    308428    int frameSize = m_size.width() * m_size.height() * 4;
    309429    if (frameCount() * frameSize > cLargeAnimationCutoff) {
    310         // Destroy all of our frames and just redecode every time.
    311         destroyDecodedData();
    312 
    313         // Go ahead and decode the next frame.
    314         frameAtIndex(m_currentFrame);
    315     }
    316    
    317     // We do not advance the animation explicitly.  We rely on a subsequent draw of the image
    318     // to force a request for the next frame via startAnimation().  This allows images that move offscreen while
    319     // scrolling to stop animating (thus saving memory from additional decoded frames and
    320     // CPU time spent doing the decoding).
    321 }
    322 
    323 }
     430        // Destroy all of our frames and just redecode every time.  We save the
     431        // current frame since we'll need it in draw() anyway.
     432        destroyDecodedData(false, true);
     433    }
     434}
     435
     436}
  • trunk/WebCore/platform/graphics/BitmapImage.h

    r36781 r37612  
    6767    FrameData()
    6868        : m_frame(0)
     69        , m_haveMetadata(false)
     70        , m_isComplete(false)
    6971        , m_duration(0)
    7072        , m_hasAlpha(true)
     
    8082
    8183    NativeImagePtr m_frame;
     84    bool m_haveMetadata;
     85    bool m_isComplete;
    8286    float m_duration;
    8387    bool m_hasAlpha;
     
    137141
    138142protected:
     143    enum RepetitionCountStatus {
     144      Unknown,    // We haven't checked the source's repetition count.
     145      Uncertain,  // We have a repetition count, but it might be wrong (some GIFs have a count after the image data, and will report "loop once" until all data has been decoded).
     146      Certain,    // The repetition count is known to be correct.
     147    };
     148
    139149    BitmapImage(NativeImagePtr, ImageObserver* = 0);
    140150    BitmapImage(ImageObserver* = 0);
     
    151161    size_t frameCount();
    152162    NativeImagePtr frameAtIndex(size_t);
     163    bool frameIsCompleteAtIndex(size_t);
    153164    float frameDurationAtIndex(size_t);
    154165    bool frameHasAlphaAtIndex(size_t);
     
    157168    void cacheFrame(size_t index);
    158169
    159     // Called to invalidate all our cached data.  If an image is loading incrementally, we only
    160     // invalidate the last cached frame.
    161     virtual void destroyDecodedData(bool incremental = false);
     170    // Called to invalidate all our cached data.  If an image is loading
     171    // incrementally, we only invalidate the last cached frame.  For large
     172    // animated images, where we throw away the decoded data after every frame,
     173    // |preserveNearbyFrames| can be set to preserve the current frame's data
     174    // and eliminate some unnecessary duplicated decoding work.  This also
     175    // preserves the next frame's data, if available.  In most cases this has no
     176    // effect; either that frame isn't decoded yet, or it's already been
     177    // destroyed by a previous call.  But when we fall behind on the very first
     178    // animation loop and startAnimation() needs to "catch up" one or more
     179    // frames, this briefly preserves some of that decoding work, to ease CPU
     180    // load and make it less likely that we'll keep falling behind.
     181    virtual void destroyDecodedData(bool incremental = false, bool preserveNearbyFrames = false);
    162182
    163183    // Whether or not size is available yet.   
     
    165185
    166186    // Animation.
     187    int repetitionCount(bool imageKnownToBeComplete);  // |imageKnownToBeComplete| should be set if the caller knows the entire image has been decoded.
    167188    bool shouldAnimate();
    168     virtual void startAnimation();
     189    virtual void startAnimation(bool catchUpIfNecessary = true);
    169190    void advanceAnimation(Timer<BitmapImage>*);
    170    
     191
     192    // Function that does the real work of advancing the animation.  When
     193    // skippingFrames is true, we're in the middle of a loop trying to skip over
     194    // a bunch of animation frames, so we should not do things like decode each
     195    // one or notify our observers.
     196    // Returns whether the animation was advanced.
     197    bool internalAdvanceAnimation(bool skippingFrames);
     198
     199    // Helper for internalAdvanceAnimation().
     200    void notifyObserverAndTrimDecodedData();
     201
    171202    // Handle platform-specific data
    172203    void initPlatformData();
     
    186217   
    187218    Timer<BitmapImage>* m_frameTimer;
    188     int m_repetitionCount; // How many total animation loops we should do.
     219    int m_repetitionCount; // How many total animation loops we should do.  This will be cAnimationNone if this image type is incapable of animation.
     220    RepetitionCountStatus m_repetitionCountStatus;
    189221    int m_repetitionsComplete;  // How many repetitions we've finished.
     222    double m_desiredFrameStartTime;  // The system time at which we hope to see the next call to startAnimation().
    190223
    191224#if PLATFORM(MAC)
     
    197230    bool m_isSolidColor;  // Whether or not we are a 1x1 solid image.
    198231
    199     bool m_animatingImageType;  // Whether or not we're an image type that is capable of animating (GIF).
    200232    bool m_animationFinished;  // Whether or not we've completed the entire animation.
    201233
  • trunk/WebCore/platform/graphics/GeneratedImage.h

    r35996 r37612  
    5454
    5555    // Assume that generated content has no decoded data we need to worry about
    56     virtual void destroyDecodedData(bool incremental = false) { }
     56    virtual void destroyDecodedData(bool incremental = false, bool preserveNearbyFrames = false) { }
    5757    virtual unsigned decodedSize() const { return 0; }
    5858
  • trunk/WebCore/platform/graphics/Image.h

    r36120 r37612  
    109109    virtual bool dataChanged(bool allDataReceived) { return false; }
    110110
    111     virtual void destroyDecodedData(bool incremental = false) = 0;
     111    virtual void destroyDecodedData(bool incremental = false, bool preserveNearbyFrames = false) = 0;
    112112    virtual unsigned decodedSize() const = 0;
    113113
  • trunk/WebCore/platform/graphics/cairo/ImageCairo.cpp

    r36781 r37612  
    4444        cairo_surface_destroy(m_frame);
    4545        m_frame = 0;
    46         m_duration = 0.;
    47         m_hasAlpha = true;
     46        // NOTE: We purposefully don't reset metadata here, so that even if we
     47        // throw away previously-decoded data, animation loops can still access
     48        // properties like frame durations without re-decoding.
    4849    }
    4950}
     
    5455    , m_frames(0)
    5556    , m_frameTimer(0)
    56     , m_repetitionCount(0)
     57    , m_repetitionCount(cAnimationNone)
     58    , m_repetitionCountStatus(Unknown)
    5759    , m_repetitionsComplete(0)
    5860    , m_isSolidColor(false)
     
    8385void BitmapImage::draw(GraphicsContext* context, const FloatRect& dst, const FloatRect& src, CompositeOperator op)
    8486{
    85     FloatRect srcRect(src);
    86     FloatRect dstRect(dst);
     87    startAnimation();
    8788
    8889    cairo_surface_t* image = frameAtIndex(m_currentFrame);
    8990    if (!image) // If it's too early we won't have an image yet.
    9091        return;
     92
     93    FloatRect srcRect(src);
     94    FloatRect dstRect(dst);
    9195
    9296    if (mayFillWithSolidColor()) {
     
    131135
    132136    cairo_restore(cr);
    133 
    134     startAnimation();
    135137
    136138    if (imageObserver())
  • trunk/WebCore/platform/graphics/cg/ImageCG.cpp

    r36781 r37612  
    5353        CGImageRelease(m_frame);
    5454        m_frame = 0;
    55         m_duration = 0.0f;
    56         m_hasAlpha = true;
     55        // NOTE: We purposefully don't reset metadata here, so that even if we
     56        // throw away previously-decoded data, animation loops can still access
     57        // properties like frame durations without re-decoding.
    5758    }
    5859}
     
    6768    , m_frames(0)
    6869    , m_frameTimer(0)
    69     , m_repetitionCount(0)
     70    , m_repetitionCount(cAnimationNone)
     71    , m_repetitionCountStatus(Unknown)
    7072    , m_repetitionsComplete(0)
    7173    , m_isSolidColor(false)
    72     , m_animatingImageType(false)
    7374    , m_animationFinished(true)
    7475    , m_allDataReceived(true)
     
    130131void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOp)
    131132{
     133    startAnimation();
     134
    132135    CGImageRef image = frameAtIndex(m_currentFrame);
    133136    if (!image) // If it's too early we won't have an image yet.
     
    194197    ctxt->restore();
    195198
    196     startAnimation();
    197 
    198199    if (imageObserver())
    199200        imageObserver()->didDraw(this);
  • trunk/WebCore/platform/graphics/cg/PDFDocumentImage.h

    r35934 r37612  
    5151        // FIXME: PDF Images are underreporting decoded sizes and will be unable
    5252        // to prune because these functions are not implemented yet.
    53         virtual void destroyDecodedData(bool incremental = false) { }
     53        virtual void destroyDecodedData(bool incremental = false, bool preserveNearbyFrames = false) { }
    5454        virtual unsigned decodedSize() const { return 0; }
    5555
  • trunk/WebCore/platform/graphics/qt/ImageQt.cpp

    r36781 r37612  
    7474    if (m_frame) {
    7575        m_frame = 0;
    76         m_duration = 0.0f;
    77         m_hasAlpha = true;
     76        // NOTE: We purposefully don't reset metadata here, so that even if we
     77        // throw away previously-decoded data, animation loops can still access
     78        // properties like frame durations without re-decoding.
    7879    }
    7980}
     
    109110                       const FloatRect& src, CompositeOperator op)
    110111{
     112    startAnimation();
     113
    111114    QPixmap* image = nativeImageForCurrentFrame();
    112115    if (!image)
     
    132135
    133136    ctxt->restore();
    134 
    135     startAnimation();
    136137}
    137138
  • trunk/WebCore/platform/graphics/wx/ImageWx.cpp

    r36781 r37612  
    5858        delete m_frame;
    5959        m_frame = 0;
    60         m_duration = 0.;
    61         m_hasAlpha = true;
     60        // NOTE: We purposefully don't reset metadata here, so that even if we
     61        // throw away previously-decoded data, animation loops can still access
     62        // properties like frame durations without re-decoding.
    6263    }
    6364}
     
    9899    wxWindowDC* context = ctxt->platformContext();
    99100#endif
     101
     102    startAnimation();
    100103
    101104    wxBitmap* bitmap = frameAtIndex(m_currentFrame);
     
    142145    }
    143146    ctxt->restore();
    144     startAnimation();
    145147}
    146148
  • trunk/WebCore/svg/graphics/SVGImage.h

    r37112 r37612  
    6060        // FIXME: SVGImages are underreporting decoded sizes and will be unable
    6161        // to prune because these functions are not implemented yet.
    62         virtual void destroyDecodedData(bool incremental = false) { }
     62        virtual void destroyDecodedData(bool incremental = false, bool preserveNearbyFrames = false) { }
    6363        virtual unsigned decodedSize() const { return 0; }
    6464
Note: See TracChangeset for help on using the changeset viewer.