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"
17 #include "SurfaceCache.h"
23 class DrawableSurface
;
25 class AnimationState
{
27 explicit AnimationState(uint16_t aAnimationMode
)
29 mCurrentAnimationFrameIndex(0),
30 mLoopRemainingCount(-1),
32 mFirstFrameTimeout(FrameTimeout::FromRawMilliseconds(0)),
33 mAnimationMode(aAnimationMode
),
34 mHasBeenDecoded(false),
35 mHasRequestedDecode(false),
36 mIsCurrentlyDecoded(false),
37 mCompositedFrameInvalid(false),
38 mCompositedFrameRequested(false),
42 * Call this whenever a decode completes, a decode starts, or the image is
43 * discarded. It will update the internal state. Specifically mDiscarded,
44 * mCompositedFrameInvalid, and mIsCurrentlyDecoded. If aAllowInvalidation
45 * is true then returns a rect to invalidate.
47 const gfx::IntRect
UpdateState(RasterImage
* aImage
, const gfx::IntSize
& aSize
,
48 bool aAllowInvalidation
= true);
51 const gfx::IntRect
UpdateStateInternal(LookupResult
& aResult
,
52 const gfx::IntSize
& aSize
,
53 bool aAllowInvalidation
= true);
57 * Call when a decode of this image has been completed.
59 void NotifyDecodeComplete();
62 * Returns true if this image has been fully decoded before.
64 bool GetHasBeenDecoded() { return mHasBeenDecoded
; }
67 * Returns true if this image has ever requested a decode before.
69 bool GetHasRequestedDecode() { return mHasRequestedDecode
; }
72 * Returns true if this image has been discarded and a decoded has not yet
73 * been created to redecode it.
75 bool IsDiscarded() { return mDiscarded
; }
78 * Sets the composited frame as valid or invalid.
80 void SetCompositedFrameInvalid(bool aInvalid
) {
81 MOZ_ASSERT(!aInvalid
||
82 StaticPrefs::image_mem_animated_discardable_AtStartup());
83 mCompositedFrameInvalid
= aInvalid
;
87 * Returns whether the composited frame is valid to draw to the screen.
89 bool GetCompositedFrameInvalid() { return mCompositedFrameInvalid
; }
92 * Returns whether the image is currently full decoded..
94 bool GetIsCurrentlyDecoded() { return mIsCurrentlyDecoded
; }
97 * Call when you need to re-start animating. Ensures we start from the first
100 void ResetAnimation();
103 * The animation mode of the image.
105 * Constants defined in imgIContainer.idl.
107 void SetAnimationMode(uint16_t aAnimationMode
);
109 /// Update the number of frames of animation this image is known to have.
110 void UpdateKnownFrameCount(uint32_t aFrameCount
);
112 /// @return the number of frames of animation we know about so far.
113 uint32_t KnownFrameCount() const { return mFrameCount
; }
115 /// @return the number of frames this animation has, if we know for sure.
116 /// (In other words, if decoding is finished.) Otherwise, returns Nothing().
117 Maybe
<uint32_t> FrameCount() const;
120 * Get or set the area of the image to invalidate when we loop around to the
123 void SetFirstFrameRefreshArea(const gfx::IntRect
& aRefreshArea
);
124 gfx::IntRect
FirstFrameRefreshArea() const { return mFirstFrameRefreshArea
; }
127 * If the animation frame time has not yet been set, set it to
130 void InitAnimationFrameTimeIfNecessary();
133 * Set the animation frame time to @aTime.
135 void SetAnimationFrameTime(const TimeStamp
& aTime
);
138 * Set the animation frame time to @aTime if we are configured to stop the
139 * animation when not visible and aTime is later than the current time.
140 * Returns true if the time was updated, else false.
142 bool MaybeAdvanceAnimationFrameTime(const TimeStamp
& aTime
);
145 * The current frame we're on, from 0 to (numFrames - 1).
147 uint32_t GetCurrentAnimationFrameIndex() const;
150 * Set number of times to loop the image.
151 * @note -1 means loop forever.
153 void SetLoopCount(int32_t aLoopCount
) { mLoopCount
= aLoopCount
; }
154 int32_t LoopCount() const { return mLoopCount
; }
156 /// Set the @aLength of a single loop through this image.
157 void SetLoopLength(FrameTimeout aLength
) { mLoopLength
= Some(aLength
); }
160 * @return the length of a single loop of this image. If this image is not
161 * finished decoding, is not animated, or it is animated but does not loop,
162 * returns FrameTimeout::Forever().
164 FrameTimeout
LoopLength() const;
167 * Get or set the timeout for the first frame. This is used to allow animation
168 * scheduling even before a full decode runs for this image.
170 void SetFirstFrameTimeout(FrameTimeout aTimeout
) {
171 mFirstFrameTimeout
= aTimeout
;
173 FrameTimeout
FirstFrameTimeout() const { return mFirstFrameTimeout
; }
176 friend class FrameAnimator
;
178 //! Area of the first frame that needs to be redrawn on subsequent loops.
179 gfx::IntRect mFirstFrameRefreshArea
;
181 //! the time that the animation advanced to the current frame
182 TimeStamp mCurrentAnimationFrameTime
;
184 //! The number of frames of animation this image has.
185 uint32_t mFrameCount
;
187 //! The current frame index we're on, in the range [0, mFrameCount).
188 uint32_t mCurrentAnimationFrameIndex
;
190 //! number of loops remaining before animation stops (-1 no stop)
191 int32_t mLoopRemainingCount
;
193 //! The total number of loops for the image.
196 //! The length of a single loop through this image.
197 Maybe
<FrameTimeout
> mLoopLength
;
199 //! The timeout for the first frame of this image.
200 FrameTimeout mFirstFrameTimeout
;
202 //! The animation mode of this image. Constants defined in imgIContainer.
203 uint16_t mAnimationMode
;
206 * The following four bools (mHasBeenDecoded, mIsCurrentlyDecoded,
207 * mCompositedFrameInvalid, mDiscarded) track the state of the image with
208 * regards to decoding. They all start out false, including mDiscarded,
209 * because we want to treat being discarded differently from "not yet decoded
210 * for the first time".
212 * (When we are decoding the image for the first time we want to show the
213 * image at the speed of data coming in from the network or the speed
214 * specified in the image file, whichever is slower. But when redecoding we
215 * want to show nothing until the frame for the current time has been
216 * decoded. The prevents the user from seeing the image "fast forward"
217 * to the expected spot.)
219 * When the image is decoded for the first time mHasBeenDecoded and
220 * mIsCurrentlyDecoded get set to true. When the image is discarded
221 * mIsCurrentlyDecoded gets set to false, and mCompositedFrameInvalid
222 * & mDiscarded get set to true. When we create a decoder to redecode the
223 * image mDiscarded gets set to false. mCompositedFrameInvalid gets set to
224 * false when we are able to advance to the frame that should be showing
225 * for the current time. mIsCurrentlyDecoded gets set to true when the
229 //! Whether this image has been decoded at least once.
230 bool mHasBeenDecoded
;
232 //! Whether this image has ever requested a decode.
233 bool mHasRequestedDecode
;
235 //! Whether this image is currently fully decoded.
236 bool mIsCurrentlyDecoded
;
238 //! Whether the composited frame is valid to draw to the screen, note that
239 //! the composited frame can exist and be filled with image data but not
240 //! valid to draw to the screen.
241 bool mCompositedFrameInvalid
;
243 //! Whether the composited frame was requested from the animator since the
244 //! last time we advanced the animation.
245 bool mCompositedFrameRequested
;
247 //! Whether this image is currently discarded. Only set to true after the
248 //! image has been decoded at least once.
253 * RefreshResult is used to let callers know how the state of the animation
254 * changed during a call to FrameAnimator::RequestRefresh().
256 struct RefreshResult
{
257 RefreshResult() : mFrameAdvanced(false), mAnimationFinished(false) {}
259 /// Merges another RefreshResult's changes into this RefreshResult.
260 void Accumulate(const RefreshResult
& aOther
) {
261 mFrameAdvanced
= mFrameAdvanced
|| aOther
.mFrameAdvanced
;
262 mAnimationFinished
= mAnimationFinished
|| aOther
.mAnimationFinished
;
263 mDirtyRect
= mDirtyRect
.Union(aOther
.mDirtyRect
);
266 // The region of the image that has changed.
267 gfx::IntRect mDirtyRect
;
269 // If true, we changed frames at least once. Note that, due to looping, we
270 // could still have ended up on the same frame!
271 bool mFrameAdvanced
: 1;
273 // Whether the animation has finished playing.
274 bool mAnimationFinished
: 1;
277 class FrameAnimator
{
279 FrameAnimator(RasterImage
* aImage
, const gfx::IntSize
& aSize
)
280 : mImage(aImage
), mSize(aSize
) {
281 MOZ_COUNT_CTOR(FrameAnimator
);
284 MOZ_COUNTED_DTOR(FrameAnimator
)
287 * Call when you need to re-start animating. Ensures we start from the first
290 void ResetAnimation(AnimationState
& aState
);
293 * Re-evaluate what frame we're supposed to be on, and do whatever blending
294 * is necessary to get us to that frame.
296 * Returns the result of that blending, including whether the current frame
297 * changed and what the resulting dirty rectangle is.
299 RefreshResult
RequestRefresh(AnimationState
& aState
, const TimeStamp
& aTime
);
302 * Get the full frame for the current frame of the animation (it may or may
303 * not have required compositing). It may not be available because it hasn't
304 * been decoded yet, in which case we return an empty LookupResult.
306 LookupResult
GetCompositedFrame(AnimationState
& aState
, bool aMarkUsed
);
310 * Advances the animation. Typically, this will advance a single frame, but it
311 * may advance multiple frames. This may happen if we have infrequently
312 * "ticking" refresh drivers (e.g. in background tabs), or extremely short-
313 * lived animation frames.
315 * @param aTime the time that the animation should advance to. This will
316 * typically be <= TimeStamp::Now().
318 * @param aCurrentFrame the currently displayed frame of the animation. If
319 * we advance, it will replace aCurrentFrame with the
320 * new current frame we advanced to.
322 * @returns a RefreshResult that shows whether the frame was successfully
323 * advanced, and its resulting dirty rect.
325 RefreshResult
AdvanceFrame(AnimationState
& aState
, DrawableSurface
& aFrames
,
326 RefPtr
<imgFrame
>& aCurrentFrame
, TimeStamp aTime
);
329 * Get the time the frame we're currently displaying is supposed to end.
331 * In the error case (like if the requested frame is not currently
332 * decoded), returns None().
334 TimeStamp
GetCurrentImgFrameEndTime(AnimationState
& aState
,
335 FrameTimeout aCurrentTimeout
) const;
338 //! A weak pointer to our owning image.
341 //! The intrinsic size of the image.
346 } // namespace mozilla
348 #endif // mozilla_image_FrameAnimator_h