Changeset 37612 in webkit
- Timestamp:
- Oct 15, 2008 1:38:13 PM (16 years ago)
- Location:
- trunk/WebCore
- Files:
-
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebCore/ChangeLog
r37611 r37612 1 2008-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 1 50 2008-10-14 Maxime Britto <britto@apple.com> 2 51 -
trunk/WebCore/platform/graphics/BitmapImage.cpp
r36781 r37612 32 32 #include "IntRect.h" 33 33 #include "PlatformString.h" 34 #include "SystemTime.h" 34 35 #include "Timer.h" 35 36 #include <wtf/Vector.h> … … 41 42 // one frame at a time. 42 43 const 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. 48 const double cAnimationResyncCutoff = 5 * 60; 43 49 44 50 BitmapImage::BitmapImage(ImageObserver* observer) … … 47 53 , m_frames(0) 48 54 , m_frameTimer(0) 49 , m_repetitionCount(0) 55 , m_repetitionCount(cAnimationNone) 56 , m_repetitionCountStatus(Unknown) 50 57 , m_repetitionsComplete(0) 58 , m_desiredFrameStartTime(0) 51 59 , m_isSolidColor(false) 52 , m_animatingImageType(true)53 60 , m_animationFinished(false) 54 61 , m_allDataReceived(false) … … 69 76 } 70 77 71 void BitmapImage::destroyDecodedData(bool incremental )78 void BitmapImage::destroyDecodedData(bool incremental, bool preserveNearbyFrames) 72 79 { 73 80 // Destroy the cached images and release them. … … 75 82 int sizeChange = 0; 76 83 int frameSize = m_size.width() * m_size.height() * 4; 84 const size_t nextFrame = (preserveNearbyFrames && frameCount()) ? ((m_currentFrame + 1) % frameCount()) : 0; 77 85 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))) { 79 87 sizeChange -= frameSize; 80 88 m_frames[i].clear(); … … 107 115 ASSERT(m_decodedSize == 0 || numFrames > 1); 108 116 109 if (!m_frames.size() && shouldAnimate()) {110 // Snag the repetition count. Note that the repetition count may not be111 // accurate yet for GIFs; if we haven't gotten the count from the source112 // image yet, it will default to cAnimationLoopOnce, and we'll try and113 // 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 119 117 if (m_frames.size() < numFrames) 120 118 m_frames.grow(numFrames); … … 124 122 checkForSolidColor(); 125 123 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) 127 127 m_frames[index].m_duration = m_source.frameDurationAtIndex(index); 128 128 m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index); … … 208 208 } 209 209 210 bool 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 210 221 float BitmapImage::frameDurationAtIndex(size_t index) 211 222 { … … 213 224 return 0; 214 225 215 if (index >= m_frames.size() || !m_frames[index].m_ frame)226 if (index >= m_frames.size() || !m_frames[index].m_haveMetadata) 216 227 cacheFrame(index); 217 228 … … 224 235 return true; 225 236 226 if (index >= m_frames.size() || !m_frames[index].m_ frame)237 if (index >= m_frames.size() || !m_frames[index].m_haveMetadata) 227 238 cacheFrame(index); 228 239 … … 230 241 } 231 242 243 int 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 232 256 bool BitmapImage::shouldAnimate() 233 257 { 234 return ( m_animatingImageType && !m_animationFinished && imageObserver());235 } 236 237 void BitmapImage::startAnimation( )258 return (repetitionCount(false) != cAnimationNone && !m_animationFinished && imageObserver()); 259 } 260 261 void BitmapImage::startAnimation(bool catchUpIfNecessary) 238 262 { 239 263 if (m_frameTimer || !shouldAnimate() || frameCount() <= 1) 240 264 return; 241 265 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)) 244 285 return; 245 286 … … 248 289 // in a GIF can potentially come after all the rest of the image data, so 249 290 // 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)) 251 292 return; 252 293 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 } 255 353 } 256 354 … … 268 366 m_currentFrame = 0; 269 367 m_repetitionsComplete = 0; 368 m_desiredFrameStartTime = 0; 270 369 m_animationFinished = false; 271 370 int frameSize = m_size.width() * m_size.height() * 4; … … 278 377 void BitmapImage::advanceAnimation(Timer<BitmapImage>* timer) 279 378 { 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 385 bool BitmapImage::internalAdvanceAnimation(bool skippingFrames) 386 { 280 387 // Stop the animation. 281 388 stopAnimation(); … … 283 390 // See if anyone is still paying attention to this animation. If not, we don't 284 391 // 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; 287 394 288 395 m_currentFrame++; 289 396 if (m_currentFrame >= frameCount()) { 290 m_repetitionsComplete += 1;397 ++m_repetitionsComplete; 291 398 // Get the repetition count again. If we weren't able to get a 292 399 // repetition count before, we should have decoded the whole image by 293 400 // 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) { 296 402 m_animationFinished = true; 403 m_desiredFrameStartTime = 0; 297 404 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; 299 411 } 300 412 m_currentFrame = 0; 301 413 } 302 414 415 if (!skippingFrames) 416 notifyObserverAndTrimDecodedData(); 417 418 return true; 419 } 420 421 void BitmapImage::notifyObserverAndTrimDecodedData() 422 { 303 423 // Notify our observer that the animation has advanced. 304 424 imageObserver()->animationAdvanced(this); 305 425 306 // For large animated images, go ahead and throw away frames as we go to save307 // footprint.426 // For large animated images, go ahead and throw away frames as we go to 427 // save footprint. 308 428 int frameSize = m_size.width() * m_size.height() * 4; 309 429 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 67 67 FrameData() 68 68 : m_frame(0) 69 , m_haveMetadata(false) 70 , m_isComplete(false) 69 71 , m_duration(0) 70 72 , m_hasAlpha(true) … … 80 82 81 83 NativeImagePtr m_frame; 84 bool m_haveMetadata; 85 bool m_isComplete; 82 86 float m_duration; 83 87 bool m_hasAlpha; … … 137 141 138 142 protected: 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 139 149 BitmapImage(NativeImagePtr, ImageObserver* = 0); 140 150 BitmapImage(ImageObserver* = 0); … … 151 161 size_t frameCount(); 152 162 NativeImagePtr frameAtIndex(size_t); 163 bool frameIsCompleteAtIndex(size_t); 153 164 float frameDurationAtIndex(size_t); 154 165 bool frameHasAlphaAtIndex(size_t); … … 157 168 void cacheFrame(size_t index); 158 169 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); 162 182 163 183 // Whether or not size is available yet. … … 165 185 166 186 // Animation. 187 int repetitionCount(bool imageKnownToBeComplete); // |imageKnownToBeComplete| should be set if the caller knows the entire image has been decoded. 167 188 bool shouldAnimate(); 168 virtual void startAnimation( );189 virtual void startAnimation(bool catchUpIfNecessary = true); 169 190 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 171 202 // Handle platform-specific data 172 203 void initPlatformData(); … … 186 217 187 218 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; 189 221 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(). 190 223 191 224 #if PLATFORM(MAC) … … 197 230 bool m_isSolidColor; // Whether or not we are a 1x1 solid image. 198 231 199 bool m_animatingImageType; // Whether or not we're an image type that is capable of animating (GIF).200 232 bool m_animationFinished; // Whether or not we've completed the entire animation. 201 233 -
trunk/WebCore/platform/graphics/GeneratedImage.h
r35996 r37612 54 54 55 55 // 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) { } 57 57 virtual unsigned decodedSize() const { return 0; } 58 58 -
trunk/WebCore/platform/graphics/Image.h
r36120 r37612 109 109 virtual bool dataChanged(bool allDataReceived) { return false; } 110 110 111 virtual void destroyDecodedData(bool incremental = false ) = 0;111 virtual void destroyDecodedData(bool incremental = false, bool preserveNearbyFrames = false) = 0; 112 112 virtual unsigned decodedSize() const = 0; 113 113 -
trunk/WebCore/platform/graphics/cairo/ImageCairo.cpp
r36781 r37612 44 44 cairo_surface_destroy(m_frame); 45 45 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. 48 49 } 49 50 } … … 54 55 , m_frames(0) 55 56 , m_frameTimer(0) 56 , m_repetitionCount(0) 57 , m_repetitionCount(cAnimationNone) 58 , m_repetitionCountStatus(Unknown) 57 59 , m_repetitionsComplete(0) 58 60 , m_isSolidColor(false) … … 83 85 void BitmapImage::draw(GraphicsContext* context, const FloatRect& dst, const FloatRect& src, CompositeOperator op) 84 86 { 85 FloatRect srcRect(src); 86 FloatRect dstRect(dst); 87 startAnimation(); 87 88 88 89 cairo_surface_t* image = frameAtIndex(m_currentFrame); 89 90 if (!image) // If it's too early we won't have an image yet. 90 91 return; 92 93 FloatRect srcRect(src); 94 FloatRect dstRect(dst); 91 95 92 96 if (mayFillWithSolidColor()) { … … 131 135 132 136 cairo_restore(cr); 133 134 startAnimation();135 137 136 138 if (imageObserver()) -
trunk/WebCore/platform/graphics/cg/ImageCG.cpp
r36781 r37612 53 53 CGImageRelease(m_frame); 54 54 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. 57 58 } 58 59 } … … 67 68 , m_frames(0) 68 69 , m_frameTimer(0) 69 , m_repetitionCount(0) 70 , m_repetitionCount(cAnimationNone) 71 , m_repetitionCountStatus(Unknown) 70 72 , m_repetitionsComplete(0) 71 73 , m_isSolidColor(false) 72 , m_animatingImageType(false)73 74 , m_animationFinished(true) 74 75 , m_allDataReceived(true) … … 130 131 void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOp) 131 132 { 133 startAnimation(); 134 132 135 CGImageRef image = frameAtIndex(m_currentFrame); 133 136 if (!image) // If it's too early we won't have an image yet. … … 194 197 ctxt->restore(); 195 198 196 startAnimation();197 198 199 if (imageObserver()) 199 200 imageObserver()->didDraw(this); -
trunk/WebCore/platform/graphics/cg/PDFDocumentImage.h
r35934 r37612 51 51 // FIXME: PDF Images are underreporting decoded sizes and will be unable 52 52 // 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) { } 54 54 virtual unsigned decodedSize() const { return 0; } 55 55 -
trunk/WebCore/platform/graphics/qt/ImageQt.cpp
r36781 r37612 74 74 if (m_frame) { 75 75 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. 78 79 } 79 80 } … … 109 110 const FloatRect& src, CompositeOperator op) 110 111 { 112 startAnimation(); 113 111 114 QPixmap* image = nativeImageForCurrentFrame(); 112 115 if (!image) … … 132 135 133 136 ctxt->restore(); 134 135 startAnimation();136 137 } 137 138 -
trunk/WebCore/platform/graphics/wx/ImageWx.cpp
r36781 r37612 58 58 delete m_frame; 59 59 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. 62 63 } 63 64 } … … 98 99 wxWindowDC* context = ctxt->platformContext(); 99 100 #endif 101 102 startAnimation(); 100 103 101 104 wxBitmap* bitmap = frameAtIndex(m_currentFrame); … … 142 145 } 143 146 ctxt->restore(); 144 startAnimation();145 147 } 146 148 -
trunk/WebCore/svg/graphics/SVGImage.h
r37112 r37612 60 60 // FIXME: SVGImages are underreporting decoded sizes and will be unable 61 61 // 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) { } 63 63 virtual unsigned decodedSize() const { return 0; } 64 64
Note: See TracChangeset
for help on using the changeset viewer.