📄 bitmapimage.cpp
字号:
if (index >= frameCount()) return true; if (index >= m_frames.size() || !m_frames[index].m_haveMetadata) cacheFrame(index); return m_frames[index].m_isComplete;}float BitmapImage::frameDurationAtIndex(size_t index){ if (index >= frameCount()) return 0; if (index >= m_frames.size() || !m_frames[index].m_haveMetadata) cacheFrame(index); return m_frames[index].m_duration;}bool BitmapImage::frameHasAlphaAtIndex(size_t index){ if (index >= frameCount()) return true; if (index >= m_frames.size() || !m_frames[index].m_haveMetadata) cacheFrame(index); return m_frames[index].m_hasAlpha;}int BitmapImage::repetitionCount(bool imageKnownToBeComplete){ if ((m_repetitionCountStatus == Unknown) || ((m_repetitionCountStatus == Uncertain) && imageKnownToBeComplete)) { // Snag the repetition count. If |imageKnownToBeComplete| is false, the // repetition count may not be accurate yet for GIFs; in this case the // decoder will default to cAnimationLoopOnce, and we'll try and read // the count again once the whole image is decoded. m_repetitionCount = m_source.repetitionCount(); m_repetitionCountStatus = (imageKnownToBeComplete || m_repetitionCount == cAnimationNone) ? Certain : Uncertain; } return m_repetitionCount;}bool BitmapImage::shouldAnimate(){ return (repetitionCount(false) != cAnimationNone && !m_animationFinished && imageObserver());}void BitmapImage::startAnimation(bool catchUpIfNecessary){ if (m_frameTimer || !shouldAnimate() || frameCount() <= 1) return; // Determine time for next frame to start. By ignoring paint and timer lag // in this calculation, we make the animation appear to run at its desired // rate regardless of how fast it's being repainted. const double currentDuration = frameDurationAtIndex(m_currentFrame); const double time = currentTime(); if (m_desiredFrameStartTime == 0) { m_desiredFrameStartTime = time + currentDuration; } else { m_desiredFrameStartTime += currentDuration; // When an animated image is more than five minutes out of date, the // user probably doesn't care about resyncing and we could burn a lot of // time looping through frames below. Just reset the timings. const double cAnimationResyncCutoff = 5 * 60; if ((time - m_desiredFrameStartTime) > cAnimationResyncCutoff) m_desiredFrameStartTime = time + currentDuration; } // Don't advance the animation to an incomplete frame. size_t nextFrame = (m_currentFrame + 1) % frameCount(); if (!m_allDataReceived && !frameIsCompleteAtIndex(nextFrame)) return; // Don't advance past the last frame if we haven't decoded the whole image // yet and our repetition count is potentially unset. The repetition count // in a GIF can potentially come after all the rest of the image data, so // wait on it. if (!m_allDataReceived && repetitionCount(false) == cAnimationLoopOnce && m_currentFrame >= (frameCount() - 1)) return; // The image may load more slowly than it's supposed to animate, so that by // the time we reach the end of the first repetition, we're well behind. // Clamp the desired frame start time in this case, so that we don't skip // frames (or whole iterations) trying to "catch up". This is a tradeoff: // It guarantees users see the whole animation the second time through and // don't miss any repetitions, and is closer to what other browsers do; on // the other hand, it makes animations "less accurate" for pages that try to // sync an image and some other resource (e.g. audio), especially if users // switch tabs (and thus stop drawing the animation, which will pause it) // during that initial loop, then switch back later. if (nextFrame == 0 && m_repetitionsComplete == 0 && m_desiredFrameStartTime < time) m_desiredFrameStartTime = time; if (!catchUpIfNecessary || time < m_desiredFrameStartTime) { // Haven't yet reached time for next frame to start; delay until then. m_frameTimer = new Timer<BitmapImage>(this, &BitmapImage::advanceAnimation); m_frameTimer->startOneShot(std::max(m_desiredFrameStartTime - time, 0.)); } else { // We've already reached or passed the time for the next frame to start. // See if we've also passed the time for frames after that to start, in // case we need to skip some frames entirely. Remember not to advance // to an incomplete frame. for (size_t frameAfterNext = (nextFrame + 1) % frameCount(); frameIsCompleteAtIndex(frameAfterNext); frameAfterNext = (nextFrame + 1) % frameCount()) { // Should we skip the next frame? double frameAfterNextStartTime = m_desiredFrameStartTime + frameDurationAtIndex(nextFrame); if (time < frameAfterNextStartTime) break; // Yes; skip over it without notifying our observers. if (!internalAdvanceAnimation(true)) return; m_desiredFrameStartTime = frameAfterNextStartTime; nextFrame = frameAfterNext; } // Draw the next frame immediately. Note that m_desiredFrameStartTime // may be in the past, meaning the next time through this function we'll // kick off the next advancement sooner than this frame's duration would // suggest. if (internalAdvanceAnimation(false)) { // The image region has been marked dirty, but once we return to our // caller, draw() will clear it, and nothing will cause the // animation to advance again. We need to start the timer for the // next frame running, or the animation can hang. (Compare this // with when advanceAnimation() is called, and the region is dirtied // while draw() is not in the callstack, meaning draw() gets called // to update the region and thus startAnimation() is reached again.) // NOTE: For large images with slow or heavily-loaded systems, // throwing away data as we go (see destroyDecodedData()) means we // can spend so much time re-decoding data above that by the time we // reach here we're behind again. If we let startAnimation() run // the catch-up code again, we can get long delays without painting // as we race the timer, or even infinite recursion. In this // situation the best we can do is to simply change frames as fast // as possible, so force startAnimation() to set a zero-delay timer // and bail out if we're not caught up. startAnimation(false); } }}void BitmapImage::stopAnimation(){ // This timer is used to animate all occurrences of this image. Don't invalidate // the timer unless all renderers have stopped drawing. delete m_frameTimer; m_frameTimer = 0;}void BitmapImage::resetAnimation(){ stopAnimation(); m_currentFrame = 0; m_repetitionsComplete = 0; m_desiredFrameStartTime = 0; m_animationFinished = false; // For extremely large animations, when the animation is reset, we just throw everything away. destroyDecodedDataIfNecessary(true);}void BitmapImage::advanceAnimation(Timer<BitmapImage>*){ internalAdvanceAnimation(false); // At this point the image region has been marked dirty, and if it's // onscreen, we'll soon make a call to draw(), which will call // startAnimation() again to keep the animation moving.}bool BitmapImage::internalAdvanceAnimation(bool skippingFrames){ // Stop the animation. stopAnimation(); // See if anyone is still paying attention to this animation. If not, we don't // advance and will remain suspended at the current frame until the animation is resumed. if (!skippingFrames && imageObserver()->shouldPauseAnimation(this)) return false; ++m_currentFrame; bool advancedAnimation = true; bool destroyAll = false; if (m_currentFrame >= frameCount()) { ++m_repetitionsComplete; // Get the repetition count again. If we weren't able to get a // repetition count before, we should have decoded the whole image by // now, so it should now be available. if (repetitionCount(true) && m_repetitionsComplete >= m_repetitionCount) { m_animationFinished = true; m_desiredFrameStartTime = 0; --m_currentFrame; advancedAnimation = false; } else { m_currentFrame = 0; destroyAll = true; } } destroyDecodedDataIfNecessary(destroyAll); // We need to draw this frame if we advanced to it while not skipping, or if // while trying to skip frames we hit the last frame and thus had to stop. if (skippingFrames != advancedAnimation) imageObserver()->animationAdvanced(this); return advancedAnimation;}}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -