Changeset 207386 in webkit
- Timestamp:
- Oct 15, 2016 5:19:22 PM (8 years ago)
- Location:
- trunk
- Files:
-
- 3 added
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r207383 r207386 1 2016-10-15 Said Abou-Hallawa <sabouhallawa@apple.com> 2 3 Delete the animated image catchup code 4 https://bugs.webkit.org/show_bug.cgi?id=163410 5 6 Reviewed by Simon Fraser. 7 8 This animated gif has one red frame, one green frame and two red frames. 9 The test page renders only two frames from this this image on a canvas. The 10 test passes if the second frame (the green one) is rendered on the canvas 11 even if drawImage() is called after the duration of the first frame. 12 13 * fast/images/ordered-animated-image-frames-expected.html: Added. 14 * fast/images/ordered-animated-image-frames.html: Added. 15 * fast/images/resources/animated-red-green-blue.gif: Added. 16 1 17 2016-10-15 Myles C. Maxfield <mmaxfield@apple.com> 2 18 -
trunk/Source/WebCore/ChangeLog
r207384 r207386 1 2016-10-15 Said Abou-Hallawa <sabouhallawa@apple.com> 2 3 Delete the animated image catchup code 4 https://bugs.webkit.org/show_bug.cgi?id=163410 5 6 Reviewed by Simon Fraser. 7 8 This patch fixes two issues in the animated image workflow: 9 10 1) Setting the animation timer should follow the following rules: 11 12 -- Initial case: Which happens before drawing the first frame. We 13 should set the timer to fire after the current_frame_duration. 14 15 -- Late case (Slow animation): This happens if the current_time is 16 past the next_frame_desired_time. In this case we should fire the 17 timer immediately. 18 19 -- Early case (Fast animation): This happens when there is still time 20 before the next_frame_desired_time. In this case we should set the 21 timer to fire after the difference between the next_frame_desired_time 22 and the current_time. 23 24 2) Deleting the code for catching up the current_frame: 25 26 This code used to run in the slow animation case. It was never used 27 on iOS. It was trying to adjust the current_frame according to the 28 current_time as if there were no delay. It turned out that this might 29 cause a bigger delay because most likely the decoder decodes the image 30 frames incrementally; i.e. to decode frame k, it has to have frame 31 (k - 1) decoded. 32 33 Test: fast/images/ordered-animated-image-frames.html 34 35 * platform/graphics/BitmapImage.cpp: 36 (WebCore::BitmapImage::draw): Remove the iOS specific code. 37 (WebCore::BitmapImage::startAnimation): Move the animation finishing code from 38 BitmapImage::internalAdvanceAnimation() to this function. Simplify the timer 39 duration code as it is described above. 40 41 (WebCore::BitmapImage::advanceAnimation): Merge BitmapImage::internalAdvanceAnimation() 42 into this function. 43 44 (WebCore::BitmapImage::resetAnimation): 45 46 (WebCore::BitmapImage::internalAdvanceAnimation): Deleted. 47 * platform/graphics/BitmapImage.h: 48 49 * platform/graphics/Image.cpp: 50 (WebCore::Image::drawTiled): 51 * platform/graphics/Image.h: 52 (WebCore::Image::startAnimation): 53 * svg/graphics/SVGImage.cpp: 54 (WebCore::SVGImage::startAnimation): 55 * svg/graphics/SVGImage.h: 56 Remove the catchup code form the Image and SVGImage classes. 57 1 58 2016-10-15 Darin Adler <darin@apple.com> 2 59 -
trunk/Source/WebCore/platform/graphics/BitmapImage.cpp
r207357 r207386 142 142 return; 143 143 144 #if PLATFORM(IOS)145 startAnimation(DoNotCatchUp);146 #else147 144 startAnimation(); 148 #endif149 145 150 146 Color color = singlePixelSolidColor(); … … 228 224 } 229 225 230 void BitmapImage::startAnimation( CatchUpAnimation catchUpIfNecessary)226 void BitmapImage::startAnimation() 231 227 { 232 228 if (m_frameTimer || !shouldAnimate() || frameCount() <= 1) 233 229 return; 234 230 235 // If we aren't already animating, set now as the animation start time. 236 const double time = monotonicallyIncreasingTime(); 237 if (!m_desiredFrameStartTime) 238 m_desiredFrameStartTime = time; 231 if (m_currentFrame >= frameCount() - 1) { 232 // Don't advance past the last frame if we haven't decoded the whole image 233 // yet and our repetition count is potentially unset. The repetition count 234 // in a GIF can potentially come after all the rest of the image data, so 235 // wait on it. 236 if (!m_source.isAllDataReceived() && repetitionCount() == RepetitionCountOnce) 237 return; 238 239 ++m_repetitionsComplete; 240 241 // Check for the end of animation. 242 if (repetitionCount() != RepetitionCountInfinite && m_repetitionsComplete > repetitionCount()) { 243 m_animationFinished = true; 244 destroyDecodedDataIfNecessary(false); 245 return; 246 } 247 248 destroyDecodedDataIfNecessary(true); 249 } 239 250 240 251 // Don't advance the animation to an incomplete frame. … … 243 254 return; 244 255 245 // Don't advance past the last frame if we haven't decoded the whole image 246 // yet and our repetition count is potentially unset. The repetition count 247 // in a GIF can potentially come after all the rest of the image data, so 248 // wait on it. 249 if (!m_source.isAllDataReceived() && repetitionCount() == RepetitionCountOnce && m_currentFrame >= (frameCount() - 1)) 250 return; 251 252 // Determine time for next frame to start. By ignoring paint and timer lag 253 // in this calculation, we make the animation appear to run at its desired 254 // rate regardless of how fast it's being repainted. 255 const double currentDuration = frameDurationAtIndex(m_currentFrame); 256 m_desiredFrameStartTime += currentDuration; 257 258 #if !PLATFORM(IOS) 259 // When an animated image is more than five minutes out of date, the 260 // user probably doesn't care about resyncing and we could burn a lot of 261 // time looping through frames below. Just reset the timings. 262 const double cAnimationResyncCutoff = 5 * 60; 263 if ((time - m_desiredFrameStartTime) > cAnimationResyncCutoff) 264 m_desiredFrameStartTime = time + currentDuration; 265 #else 266 // Maintaining frame-to-frame delays is more important than 267 // maintaining absolute animation timing, so reset the timings each frame. 268 m_desiredFrameStartTime = time + currentDuration; 269 #endif 270 271 // The image may load more slowly than it's supposed to animate, so that by 272 // the time we reach the end of the first repetition, we're well behind. 273 // Clamp the desired frame start time in this case, so that we don't skip 274 // frames (or whole iterations) trying to "catch up". This is a tradeoff: 275 // It guarantees users see the whole animation the second time through and 276 // don't miss any repetitions, and is closer to what other browsers do; on 277 // the other hand, it makes animations "less accurate" for pages that try to 278 // sync an image and some other resource (e.g. audio), especially if users 279 // switch tabs (and thus stop drawing the animation, which will pause it) 280 // during that initial loop, then switch back later. 281 if (nextFrame == 0 && m_repetitionsComplete == 0 && m_desiredFrameStartTime < time) 256 double time = monotonicallyIncreasingTime(); 257 258 // Handle initial state. 259 if (!m_desiredFrameStartTime) 282 260 m_desiredFrameStartTime = time; 283 261 284 if (catchUpIfNecessary == DoNotCatchUp || time < m_desiredFrameStartTime) { 285 // Haven't yet reached time for next frame to start; delay until then. 286 startTimer(std::max<double>(m_desiredFrameStartTime - time, 0)); 287 return; 288 } 262 // Setting 'm_desiredFrameStartTime' to 'time' means we are late; otherwise we are early. 263 m_desiredFrameStartTime = std::max(time, m_desiredFrameStartTime + frameDurationAtIndex(m_currentFrame)); 289 264 290 265 ASSERT(!m_frameTimer); 291 292 // We've already reached or passed the time for the next frame to start. 293 // See if we've also passed the time for frames after that to start, in 294 // case we need to skip some frames entirely. Remember not to advance 295 // to an incomplete frame. 296 297 #if !LOG_DISABLED 298 size_t startCatchupFrameIndex = nextFrame; 299 #endif 300 301 for (size_t frameAfterNext = (nextFrame + 1) % frameCount(); frameIsCompleteAtIndex(frameAfterNext); frameAfterNext = (nextFrame + 1) % frameCount()) { 302 // Should we skip the next frame? 303 double frameAfterNextStartTime = m_desiredFrameStartTime + frameDurationAtIndex(nextFrame); 304 if (time < frameAfterNextStartTime) 305 break; 306 307 // Yes; skip over it without notifying our observers. If we hit the end while catching up, 308 // tell the observer asynchronously. 309 if (!internalAdvanceAnimation(SkippingFramesToCatchUp)) { 310 m_animationFinishedWhenCatchingUp = true; 311 startTimer(0); 312 LOG(Images, "BitmapImage %p startAnimation catching up from frame %lu, ended", this, startCatchupFrameIndex); 313 return; 314 } 315 m_desiredFrameStartTime = frameAfterNextStartTime; 316 nextFrame = frameAfterNext; 317 } 318 319 LOG(Images, "BitmapImage %p startAnimation catching up jumped from from frame %lu to %d", this, startCatchupFrameIndex, (int)nextFrame - 1); 320 321 // Draw the next frame as soon as possible. Note that m_desiredFrameStartTime 322 // may be in the past, meaning the next time through this function we'll 323 // kick off the next advancement sooner than this frame's duration would suggest. 324 startTimer(0); 266 startTimer(m_desiredFrameStartTime - time); 325 267 } 326 268 327 269 void BitmapImage::advanceAnimation() 328 270 { 329 internalAdvanceAnimation();330 // At this point the image region has been marked dirty, and if it's331 // onscreen, we'll soon make a call to draw(), which will call332 // startAnimation() again to keep the animation moving.333 }334 335 bool BitmapImage::internalAdvanceAnimation(AnimationAdvancement advancement)336 {337 271 clearTimer(); 338 272 339 if (m_animationFinishedWhenCatchingUp) { 273 m_currentFrame = (m_currentFrame + 1) % frameCount(); 274 destroyDecodedDataIfNecessary(false); 275 276 if (imageObserver()) 340 277 imageObserver()->animationAdvanced(this); 341 m_animationFinishedWhenCatchingUp = false;342 return false;343 }344 345 ++m_currentFrame;346 bool advancedAnimation = true;347 bool destroyAll = false;348 if (m_currentFrame >= frameCount()) {349 ++m_repetitionsComplete;350 351 // Get the repetition count again. If we weren't able to get a352 // repetition count before, we should have decoded the whole image by353 // now, so it should now be available.354 if (repetitionCount() != RepetitionCountInfinite && m_repetitionsComplete > repetitionCount()) {355 m_animationFinished = true;356 m_desiredFrameStartTime = 0;357 --m_currentFrame;358 advancedAnimation = false;359 } else {360 m_currentFrame = 0;361 destroyAll = true;362 }363 }364 destroyDecodedDataIfNecessary(destroyAll);365 366 // We need to draw this frame if we advanced to it while not skipping, or if367 // while trying to skip frames we hit the last frame and thus had to stop.368 if (advancement == Normal && advancedAnimation)369 imageObserver()->animationAdvanced(this);370 371 return advancedAnimation;372 278 } 373 279 … … 383 289 stopAnimation(); 384 290 m_currentFrame = 0; 385 m_repetitionsComplete = 0;291 m_repetitionsComplete = RepetitionCountNone; 386 292 m_desiredFrameStartTime = 0; 387 293 m_animationFinished = false; -
trunk/Source/WebCore/platform/graphics/BitmapImage.h
r207357 r207386 153 153 bool shouldAnimate(); 154 154 bool canAnimate(); 155 void startAnimation( CatchUpAnimation = CatchUp) override;155 void startAnimation() override; 156 156 void advanceAnimation(); 157 158 // Function that does the real work of advancing the animation. When159 // skippingFrames is true, we're in the middle of a loop trying to skip over160 // a bunch of animation frames, so we should not do things like decode each161 // one or notify our observers.162 // Returns whether the animation was advanced.163 enum AnimationAdvancement { Normal, SkippingFramesToCatchUp };164 bool internalAdvanceAnimation(AnimationAdvancement = Normal);165 157 166 158 // It may look unusual that there is no start animation call as public API. This is because … … 194 186 double m_desiredFrameStartTime { 0 }; // The system time at which we hope to see the next call to startAnimation(). 195 187 bool m_animationFinished { false }; 196 bool m_animationFinishedWhenCatchingUp { false };197 188 198 189 #if USE(APPKIT) -
trunk/Source/WebCore/platform/graphics/Image.cpp
r207028 r207386 195 195 FloatRect tileRect(FloatPoint(), intrinsicTileSize); 196 196 drawPattern(ctxt, destRect, tileRect, patternTransform, oneTileRect.location(), spacing, op, blendMode); 197 198 #if PLATFORM(IOS)199 startAnimation(DoNotCatchUp);200 #else201 197 startAnimation(); 202 #endif203 198 } 204 199 … … 280 275 FloatPoint patternPhase(dstRect.x() - hPhase, dstRect.y() - vPhase); 281 276 drawPattern(ctxt, dstRect, srcRect, patternTransform, patternPhase, spacing, op); 282 283 #if PLATFORM(IOS)284 startAnimation(DoNotCatchUp);285 #else286 277 startAnimation(); 287 #endif288 278 } 289 279 -
trunk/Source/WebCore/platform/graphics/Image.h
r207357 r207386 127 127 // Animation begins whenever someone draws the image, so startAnimation() is not normally called. 128 128 // It will automatically pause once all observers no longer want to render the image anywhere. 129 enum CatchUpAnimation { DoNotCatchUp, CatchUp }; 130 virtual void startAnimation(CatchUpAnimation = CatchUp) { } 129 virtual void startAnimation() { } 131 130 virtual void stopAnimation() {} 132 131 virtual void resetAnimation() {} -
trunk/Source/WebCore/svg/graphics/SVGImage.cpp
r207357 r207386 373 373 } 374 374 375 // FIXME: support catchUpIfNecessary. 376 void SVGImage::startAnimation(CatchUpAnimation) 375 void SVGImage::startAnimation() 377 376 { 378 377 SVGSVGElement* rootElement = this->rootElement(); -
trunk/Source/WebCore/svg/graphics/SVGImage.h
r207357 r207386 62 62 bool hasRelativeHeight() const final; 63 63 64 void startAnimation( CatchUpAnimation = CatchUp) final;64 void startAnimation() final; 65 65 void stopAnimation() final; 66 66 void resetAnimation() final;
Note: See TracChangeset
for help on using the changeset viewer.