Backed out changeset 5d8fe1ab20de (bug 1900175) as requested by bobowen on element...
[gecko.git] / image / FrameAnimator.h
blob40538a3bebaf02ee6de836915d6b6b1acce0ccde
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
7 #ifndef mozilla_image_FrameAnimator_h
8 #define mozilla_image_FrameAnimator_h
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"
19 namespace mozilla {
20 namespace image {
22 class RasterImage;
23 class DrawableSurface;
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) {}
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.
46 const gfx::IntRect UpdateState(RasterImage* aImage, const gfx::IntSize& aSize,
47 bool aAllowInvalidation = true);
49 private:
50 const gfx::IntRect UpdateStateInternal(LookupResult& aResult,
51 const gfx::IntSize& aSize,
52 bool aAllowInvalidation = true);
54 public:
55 /**
56 * Call when a decode of this image has been completed.
58 void NotifyDecodeComplete();
60 /**
61 * Returns true if this image has been fully decoded before.
63 bool GetHasBeenDecoded() { return mHasBeenDecoded; }
65 /**
66 * Returns true if this image has ever requested a decode before.
68 bool GetHasRequestedDecode() { return mHasRequestedDecode; }
70 /**
71 * Returns true if this image has been discarded and a decoded has not yet
72 * been created to redecode it.
74 bool IsDiscarded() { return mDiscarded; }
76 /**
77 * Sets the composited frame as valid or invalid.
79 void SetCompositedFrameInvalid(bool aInvalid) {
80 MOZ_ASSERT(!aInvalid ||
81 StaticPrefs::image_mem_animated_discardable_AtStartup());
82 mCompositedFrameInvalid = aInvalid;
85 /**
86 * Returns whether the composited frame is valid to draw to the screen.
88 bool GetCompositedFrameInvalid() { return mCompositedFrameInvalid; }
90 /**
91 * Returns whether the image is currently full decoded..
93 bool GetIsCurrentlyDecoded() { return mIsCurrentlyDecoded; }
95 /**
96 * Call when you need to re-start animating. Ensures we start from the first
97 * frame.
99 void ResetAnimation();
102 * The animation mode of the image.
104 * Constants defined in imgIContainer.idl.
106 void SetAnimationMode(uint16_t aAnimationMode);
108 /// Update the number of frames of animation this image is known to have.
109 void UpdateKnownFrameCount(uint32_t aFrameCount);
111 /// @return the number of frames of animation we know about so far.
112 uint32_t KnownFrameCount() const { return mFrameCount; }
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;
119 * Get or set the area of the image to invalidate when we loop around to the
120 * first frame.
122 void SetFirstFrameRefreshArea(const gfx::IntRect& aRefreshArea);
123 gfx::IntRect FirstFrameRefreshArea() const { return mFirstFrameRefreshArea; }
126 * If the animation frame time has not yet been set, set it to
127 * TimeStamp::Now().
129 void InitAnimationFrameTimeIfNecessary();
132 * Set the animation frame time to @aTime.
134 void SetAnimationFrameTime(const TimeStamp& aTime);
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.
141 bool MaybeAdvanceAnimationFrameTime(const TimeStamp& aTime);
144 * The current frame we're on, from 0 to (numFrames - 1).
146 uint32_t GetCurrentAnimationFrameIndex() const;
149 * Set number of times to loop the image.
150 * @note -1 means loop forever.
152 void SetLoopCount(int32_t aLoopCount) { mLoopCount = aLoopCount; }
153 int32_t LoopCount() const { return mLoopCount; }
155 /// Set the @aLength of a single loop through this image.
156 void SetLoopLength(FrameTimeout aLength) { mLoopLength = Some(aLength); }
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().
163 FrameTimeout LoopLength() const;
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.
169 void SetFirstFrameTimeout(FrameTimeout aTimeout) {
170 mFirstFrameTimeout = aTimeout;
172 FrameTimeout FirstFrameTimeout() const { return mFirstFrameTimeout; }
174 private:
175 friend class FrameAnimator;
177 //! Area of the first frame that needs to be redrawn on subsequent loops.
178 gfx::IntRect mFirstFrameRefreshArea;
180 //! the time that the animation advanced to the current frame
181 TimeStamp mCurrentAnimationFrameTime;
183 //! The number of frames of animation this image has.
184 uint32_t mFrameCount;
186 //! The current frame index we're on, in the range [0, mFrameCount).
187 uint32_t mCurrentAnimationFrameIndex;
189 //! number of loops remaining before animation stops (-1 no stop)
190 int32_t mLoopRemainingCount;
192 //! The total number of loops for the image.
193 int32_t mLoopCount;
195 //! The length of a single loop through this image.
196 Maybe<FrameTimeout> mLoopLength;
198 //! The timeout for the first frame of this image.
199 FrameTimeout mFirstFrameTimeout;
201 //! The animation mode of this image. Constants defined in imgIContainer.
202 uint16_t mAnimationMode;
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".
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.)
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.
228 //! Whether this image has been decoded at least once.
229 bool mHasBeenDecoded;
231 //! Whether this image has ever requested a decode.
232 bool mHasRequestedDecode;
234 //! Whether this image is currently fully decoded.
235 bool mIsCurrentlyDecoded;
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;
242 //! Whether this image is currently discarded. Only set to true after the
243 //! image has been decoded at least once.
244 bool mDiscarded;
248 * RefreshResult is used to let callers know how the state of the animation
249 * changed during a call to FrameAnimator::RequestRefresh().
251 struct RefreshResult {
252 RefreshResult() : mFrameAdvanced(false), mAnimationFinished(false) {}
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);
261 // The region of the image that has changed.
262 gfx::IntRect mDirtyRect;
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;
268 // Whether the animation has finished playing.
269 bool mAnimationFinished : 1;
272 class FrameAnimator {
273 public:
274 FrameAnimator(RasterImage* aImage, const gfx::IntSize& aSize)
275 : mImage(aImage), mSize(aSize) {
276 MOZ_COUNT_CTOR(FrameAnimator);
279 MOZ_COUNTED_DTOR(FrameAnimator)
282 * Call when you need to re-start animating. Ensures we start from the first
283 * frame.
285 void ResetAnimation(AnimationState& aState);
288 * Re-evaluate what frame we're supposed to be on, and do whatever blending
289 * is necessary to get us to that frame.
291 * Returns the result of that blending, including whether the current frame
292 * changed and what the resulting dirty rectangle is.
294 RefreshResult RequestRefresh(AnimationState& aState, const TimeStamp& aTime);
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.
301 LookupResult GetCompositedFrame(AnimationState& aState, bool aMarkUsed);
303 private: // methods
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.
310 * @param aTime the time that the animation should advance to. This will
311 * typically be <= TimeStamp::Now().
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.
317 * @returns a RefreshResult that shows whether the frame was successfully
318 * advanced, and its resulting dirty rect.
320 RefreshResult AdvanceFrame(AnimationState& aState, DrawableSurface& aFrames,
321 RefPtr<imgFrame>& aCurrentFrame, TimeStamp aTime);
324 * Get the time the frame we're currently displaying is supposed to end.
326 * In the error case (like if the requested frame is not currently
327 * decoded), returns None().
329 TimeStamp GetCurrentImgFrameEndTime(AnimationState& aState,
330 FrameTimeout aCurrentTimeout) const;
332 private: // data
333 //! A weak pointer to our owning image.
334 RasterImage* mImage;
336 //! The intrinsic size of the image.
337 gfx::IntSize mSize;
340 } // namespace image
341 } // namespace mozilla
343 #endif // mozilla_image_FrameAnimator_h