FrameAnimator.h (11670B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_image_FrameAnimator_h 8 #define mozilla_image_FrameAnimator_h 9 10 #include "mozilla/Maybe.h" 11 #include "mozilla/StaticPrefs_image.h" 12 #include "mozilla/TimeStamp.h" 13 #include "gfxTypes.h" 14 #include "imgFrame.h" 15 #include "nsCOMPtr.h" 16 #include "nsRect.h" 17 #include "SurfaceCache.h" 18 19 namespace mozilla { 20 namespace image { 21 22 class RasterImage; 23 class DrawableSurface; 24 25 class AnimationState { 26 public: 27 explicit AnimationState(uint16_t aAnimationMode) 28 : mFrameCount(0), 29 mCurrentAnimationFrameIndex(0), 30 mLoopRemainingCount(-1), 31 mLoopCount(-1), 32 mFirstFrameTimeout(FrameTimeout::FromRawMilliseconds(0)), 33 mAnimationMode(aAnimationMode), 34 mHasBeenDecoded(false), 35 mHasRequestedDecode(false), 36 mIsCurrentlyDecoded(false), 37 mCompositedFrameInvalid(false), 38 mDiscarded(false) {} 39 40 /** 41 * Call this whenever a decode completes, a decode starts, or the image is 42 * discarded. It will update the internal state. Specifically mDiscarded, 43 * mCompositedFrameInvalid, and mIsCurrentlyDecoded. If aAllowInvalidation 44 * is true then returns a rect to invalidate. 45 */ 46 const gfx::IntRect UpdateState(RasterImage* aImage, const gfx::IntSize& aSize, 47 bool aAllowInvalidation = true); 48 49 private: 50 const gfx::IntRect UpdateStateInternal(LookupResult& aResult, 51 const gfx::IntSize& aSize, 52 bool aAllowInvalidation = true); 53 54 public: 55 /** 56 * Call when a decode of this image has been completed. 57 */ 58 void NotifyDecodeComplete(); 59 60 /** 61 * Returns true if this image has been fully decoded before. 62 */ 63 bool GetHasBeenDecoded() { return mHasBeenDecoded; } 64 65 /** 66 * Returns true if this image has ever requested a decode before. 67 */ 68 bool GetHasRequestedDecode() { return mHasRequestedDecode; } 69 70 /** 71 * Returns true if this image has been discarded and a decoded has not yet 72 * been created to redecode it. 73 */ 74 bool IsDiscarded() { return mDiscarded; } 75 76 /** 77 * Sets the composited frame as valid or invalid. 78 */ 79 void SetCompositedFrameInvalid(bool aInvalid) { 80 MOZ_ASSERT(!aInvalid || 81 StaticPrefs::image_mem_animated_discardable_AtStartup()); 82 mCompositedFrameInvalid = aInvalid; 83 } 84 85 /** 86 * Returns whether the composited frame is valid to draw to the screen. 87 */ 88 bool GetCompositedFrameInvalid() { return mCompositedFrameInvalid; } 89 90 /** 91 * Returns whether the image is currently full decoded.. 92 */ 93 bool GetIsCurrentlyDecoded() { return mIsCurrentlyDecoded; } 94 95 /** 96 * Call when you need to re-start animating. Ensures we start from the first 97 * frame. 98 */ 99 void ResetAnimation(); 100 101 /** 102 * The animation mode of the image. 103 * 104 * Constants defined in imgIContainer.idl. 105 */ 106 void SetAnimationMode(uint16_t aAnimationMode); 107 108 /// Update the number of frames of animation this image is known to have. 109 void UpdateKnownFrameCount(uint32_t aFrameCount); 110 111 /// @return the number of frames of animation we know about so far. 112 uint32_t KnownFrameCount() const { return mFrameCount; } 113 114 /// @return the number of frames this animation has, if we know for sure. 115 /// (In other words, if decoding is finished.) Otherwise, returns Nothing(). 116 Maybe<uint32_t> FrameCount() const; 117 118 /** 119 * Get or set the area of the image to invalidate when we loop around to the 120 * first frame. 121 */ 122 void SetFirstFrameRefreshArea(const gfx::IntRect& aRefreshArea); 123 gfx::IntRect FirstFrameRefreshArea() const { return mFirstFrameRefreshArea; } 124 125 /** 126 * If the animation frame time has not yet been set, set it to 127 * TimeStamp::Now(). 128 */ 129 void InitAnimationFrameTimeIfNecessary(); 130 131 /** 132 * Set the animation frame time to @aTime. 133 */ 134 void SetAnimationFrameTime(const TimeStamp& aTime); 135 136 /** 137 * Set the animation frame time to @aTime if we are configured to stop the 138 * animation when not visible and aTime is later than the current time. 139 * Returns true if the time was updated, else false. 140 */ 141 bool MaybeAdvanceAnimationFrameTime(const TimeStamp& aTime); 142 143 /** 144 * The current frame we're on, from 0 to (numFrames - 1). 145 */ 146 uint32_t GetCurrentAnimationFrameIndex() const; 147 148 /* 149 * Set number of times to loop the image. 150 * @note -1 means loop forever. 151 */ 152 void SetLoopCount(int32_t aLoopCount) { mLoopCount = aLoopCount; } 153 int32_t LoopCount() const { return mLoopCount; } 154 155 /// Set the @aLength of a single loop through this image. 156 void SetLoopLength(FrameTimeout aLength) { mLoopLength = Some(aLength); } 157 158 /** 159 * @return the length of a single loop of this image. If this image is not 160 * finished decoding, is not animated, or it is animated but does not loop, 161 * returns FrameTimeout::Forever(). 162 */ 163 FrameTimeout LoopLength() const; 164 165 /* 166 * Get or set the timeout for the first frame. This is used to allow animation 167 * scheduling even before a full decode runs for this image. 168 */ 169 void SetFirstFrameTimeout(FrameTimeout aTimeout) { 170 mFirstFrameTimeout = aTimeout; 171 } 172 FrameTimeout FirstFrameTimeout() const { return mFirstFrameTimeout; } 173 174 private: 175 friend class FrameAnimator; 176 177 //! Area of the first frame that needs to be redrawn on subsequent loops. 178 gfx::IntRect mFirstFrameRefreshArea; 179 180 //! the time that the animation advanced to the current frame 181 TimeStamp mCurrentAnimationFrameTime; 182 183 //! The number of frames of animation this image has. 184 uint32_t mFrameCount; 185 186 //! The current frame index we're on, in the range [0, mFrameCount). 187 uint32_t mCurrentAnimationFrameIndex; 188 189 //! number of loops remaining before animation stops (-1 no stop) 190 int32_t mLoopRemainingCount; 191 192 //! The total number of loops for the image. 193 int32_t mLoopCount; 194 195 //! The length of a single loop through this image. 196 Maybe<FrameTimeout> mLoopLength; 197 198 //! The timeout for the first frame of this image. 199 FrameTimeout mFirstFrameTimeout; 200 201 //! The animation mode of this image. Constants defined in imgIContainer. 202 uint16_t mAnimationMode; 203 204 /** 205 * The following four bools (mHasBeenDecoded, mIsCurrentlyDecoded, 206 * mCompositedFrameInvalid, mDiscarded) track the state of the image with 207 * regards to decoding. They all start out false, including mDiscarded, 208 * because we want to treat being discarded differently from "not yet decoded 209 * for the first time". 210 * 211 * (When we are decoding the image for the first time we want to show the 212 * image at the speed of data coming in from the network or the speed 213 * specified in the image file, whichever is slower. But when redecoding we 214 * want to show nothing until the frame for the current time has been 215 * decoded. The prevents the user from seeing the image "fast forward" 216 * to the expected spot.) 217 * 218 * When the image is decoded for the first time mHasBeenDecoded and 219 * mIsCurrentlyDecoded get set to true. When the image is discarded 220 * mIsCurrentlyDecoded gets set to false, and mCompositedFrameInvalid 221 * & mDiscarded get set to true. When we create a decoder to redecode the 222 * image mDiscarded gets set to false. mCompositedFrameInvalid gets set to 223 * false when we are able to advance to the frame that should be showing 224 * for the current time. mIsCurrentlyDecoded gets set to true when the 225 * redecode finishes. 226 */ 227 228 //! Whether this image has been decoded at least once. 229 bool mHasBeenDecoded; 230 231 //! Whether this image has ever requested a decode. 232 bool mHasRequestedDecode; 233 234 //! Whether this image is currently fully decoded. 235 bool mIsCurrentlyDecoded; 236 237 //! Whether the composited frame is valid to draw to the screen, note that 238 //! the composited frame can exist and be filled with image data but not 239 //! valid to draw to the screen. 240 bool mCompositedFrameInvalid; 241 242 //! Whether this image is currently discarded. Only set to true after the 243 //! image has been decoded at least once. 244 bool mDiscarded; 245 }; 246 247 /** 248 * RefreshResult is used to let callers know how the state of the animation 249 * changed during a call to FrameAnimator::RequestRefresh(). 250 */ 251 struct RefreshResult { 252 RefreshResult() : mFrameAdvanced(false), mAnimationFinished(false) {} 253 254 /// Merges another RefreshResult's changes into this RefreshResult. 255 void Accumulate(const RefreshResult& aOther) { 256 mFrameAdvanced = mFrameAdvanced || aOther.mFrameAdvanced; 257 mAnimationFinished = mAnimationFinished || aOther.mAnimationFinished; 258 mDirtyRect = mDirtyRect.Union(aOther.mDirtyRect); 259 } 260 261 // The region of the image that has changed. 262 gfx::IntRect mDirtyRect; 263 264 // If true, we changed frames at least once. Note that, due to looping, we 265 // could still have ended up on the same frame! 266 bool mFrameAdvanced : 1; 267 268 // Whether the animation has finished playing. 269 bool mAnimationFinished : 1; 270 }; 271 272 class FrameAnimator { 273 public: 274 FrameAnimator(RasterImage* aImage, const gfx::IntSize& aSize) 275 : mImage(aImage), mSize(aSize) { 276 MOZ_COUNT_CTOR(FrameAnimator); 277 } 278 279 MOZ_COUNTED_DTOR(FrameAnimator) 280 281 /** 282 * Call when you need to re-start animating. Ensures we start from the first 283 * frame. 284 */ 285 void ResetAnimation(AnimationState& aState); 286 287 /** 288 * Re-evaluate what frame we're supposed to be on, and do whatever blending 289 * is necessary to get us to that frame. 290 * 291 * Returns the result of that blending, including whether the current frame 292 * changed and what the resulting dirty rectangle is. 293 */ 294 RefreshResult RequestRefresh(AnimationState& aState, const TimeStamp& aTime); 295 296 /** 297 * Get the full frame for the current frame of the animation (it may or may 298 * not have required compositing). It may not be available because it hasn't 299 * been decoded yet, in which case we return an empty LookupResult. 300 */ 301 LookupResult GetCompositedFrame(AnimationState& aState, bool aMarkUsed); 302 303 private: // methods 304 /** 305 * Advances the animation. Typically, this will advance a single frame, but it 306 * may advance multiple frames. This may happen if we have infrequently 307 * "ticking" refresh drivers (e.g. in background tabs), or extremely short- 308 * lived animation frames. 309 * 310 * @param aTime the time that the animation should advance to. This will 311 * typically be <= TimeStamp::Now(). 312 * 313 * @param aCurrentFrame the currently displayed frame of the animation. If 314 * we advance, it will replace aCurrentFrame with the 315 * new current frame we advanced to. 316 * 317 * @returns a RefreshResult that shows whether the frame was successfully 318 * advanced, and its resulting dirty rect. 319 */ 320 RefreshResult AdvanceFrame(AnimationState& aState, DrawableSurface& aFrames, 321 RefPtr<imgFrame>& aCurrentFrame, TimeStamp aTime); 322 323 /** 324 * Get the time the frame we're currently displaying is supposed to end. 325 * 326 * In the error case (like if the requested frame is not currently 327 * decoded), returns None(). 328 */ 329 TimeStamp GetCurrentImgFrameEndTime(AnimationState& aState, 330 FrameTimeout aCurrentTimeout) const; 331 332 private: // data 333 //! A weak pointer to our owning image. 334 RasterImage* mImage; 335 336 //! The intrinsic size of the image. 337 gfx::IntSize mSize; 338 }; 339 340 } // namespace image 341 } // namespace mozilla 342 343 #endif // mozilla_image_FrameAnimator_h