Bug 1586038 [wpt PR 19492] - [LayoutNG] Account for relpos offset when adding layout...
[gecko.git] / image / FrameAnimator.h
blob46614f875ed5f43f37e6d89eb096fd34ca08e090
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 mCompositedFrameRequested(false),
39 mDiscarded(false) {}
41 /**
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(bool aAnimationFinished, RasterImage* aImage,
48 const gfx::IntSize& aSize,
49 bool aAllowInvalidation = true);
51 private:
52 const gfx::IntRect UpdateStateInternal(LookupResult& aResult,
53 bool aAnimationFinished,
54 const gfx::IntSize& aSize,
55 bool aAllowInvalidation = true);
57 public:
58 /**
59 * Call when a decode of this image has been completed.
61 void NotifyDecodeComplete();
63 /**
64 * Returns true if this image has been fully decoded before.
66 bool GetHasBeenDecoded() { return mHasBeenDecoded; }
68 /**
69 * Returns true if this image has ever requested a decode before.
71 bool GetHasRequestedDecode() { return mHasRequestedDecode; }
73 /**
74 * Returns true if this image has been discarded and a decoded has not yet
75 * been created to redecode it.
77 bool IsDiscarded() { return mDiscarded; }
79 /**
80 * Sets the composited frame as valid or invalid.
82 void SetCompositedFrameInvalid(bool aInvalid) {
83 MOZ_ASSERT(!aInvalid ||
84 StaticPrefs::image_mem_animated_discardable_AtStartup());
85 mCompositedFrameInvalid = aInvalid;
88 /**
89 * Returns whether the composited frame is valid to draw to the screen.
91 bool GetCompositedFrameInvalid() { return mCompositedFrameInvalid; }
93 /**
94 * Returns whether the image is currently full decoded..
96 bool GetIsCurrentlyDecoded() { return mIsCurrentlyDecoded; }
98 /**
99 * Call when you need to re-start animating. Ensures we start from the first
100 * frame.
102 void ResetAnimation();
105 * The animation mode of the image.
107 * Constants defined in imgIContainer.idl.
109 void SetAnimationMode(uint16_t aAnimationMode);
111 /// Update the number of frames of animation this image is known to have.
112 void UpdateKnownFrameCount(uint32_t aFrameCount);
114 /// @return the number of frames of animation we know about so far.
115 uint32_t KnownFrameCount() const { return mFrameCount; }
117 /// @return the number of frames this animation has, if we know for sure.
118 /// (In other words, if decoding is finished.) Otherwise, returns Nothing().
119 Maybe<uint32_t> FrameCount() const;
122 * Get or set the area of the image to invalidate when we loop around to the
123 * first frame.
125 void SetFirstFrameRefreshArea(const gfx::IntRect& aRefreshArea);
126 gfx::IntRect FirstFrameRefreshArea() const { return mFirstFrameRefreshArea; }
129 * If the animation frame time has not yet been set, set it to
130 * TimeStamp::Now().
132 void InitAnimationFrameTimeIfNecessary();
135 * Set the animation frame time to @aTime.
137 void SetAnimationFrameTime(const TimeStamp& aTime);
140 * Set the animation frame time to @aTime if we are configured to stop the
141 * animation when not visible and aTime is later than the current time.
142 * Returns true if the time was updated, else false.
144 bool MaybeAdvanceAnimationFrameTime(const TimeStamp& aTime);
147 * The current frame we're on, from 0 to (numFrames - 1).
149 uint32_t GetCurrentAnimationFrameIndex() const;
152 * Set number of times to loop the image.
153 * @note -1 means loop forever.
155 void SetLoopCount(int32_t aLoopCount) { mLoopCount = aLoopCount; }
156 int32_t LoopCount() const { return mLoopCount; }
158 /// Set the @aLength of a single loop through this image.
159 void SetLoopLength(FrameTimeout aLength) { mLoopLength = Some(aLength); }
162 * @return the length of a single loop of this image. If this image is not
163 * finished decoding, is not animated, or it is animated but does not loop,
164 * returns FrameTimeout::Forever().
166 FrameTimeout LoopLength() const;
169 * Get or set the timeout for the first frame. This is used to allow animation
170 * scheduling even before a full decode runs for this image.
172 void SetFirstFrameTimeout(FrameTimeout aTimeout) {
173 mFirstFrameTimeout = aTimeout;
175 FrameTimeout FirstFrameTimeout() const { return mFirstFrameTimeout; }
177 private:
178 friend class FrameAnimator;
180 //! Area of the first frame that needs to be redrawn on subsequent loops.
181 gfx::IntRect mFirstFrameRefreshArea;
183 //! the time that the animation advanced to the current frame
184 TimeStamp mCurrentAnimationFrameTime;
186 //! The number of frames of animation this image has.
187 uint32_t mFrameCount;
189 //! The current frame index we're on, in the range [0, mFrameCount).
190 uint32_t mCurrentAnimationFrameIndex;
192 //! number of loops remaining before animation stops (-1 no stop)
193 int32_t mLoopRemainingCount;
195 //! The total number of loops for the image.
196 int32_t mLoopCount;
198 //! The length of a single loop through this image.
199 Maybe<FrameTimeout> mLoopLength;
201 //! The timeout for the first frame of this image.
202 FrameTimeout mFirstFrameTimeout;
204 //! The animation mode of this image. Constants defined in imgIContainer.
205 uint16_t mAnimationMode;
208 * The following four bools (mHasBeenDecoded, mIsCurrentlyDecoded,
209 * mCompositedFrameInvalid, mDiscarded) track the state of the image with
210 * regards to decoding. They all start out false, including mDiscarded,
211 * because we want to treat being discarded differently from "not yet decoded
212 * for the first time".
214 * (When we are decoding the image for the first time we want to show the
215 * image at the speed of data coming in from the network or the speed
216 * specified in the image file, whichever is slower. But when redecoding we
217 * want to show nothing until the frame for the current time has been
218 * decoded. The prevents the user from seeing the image "fast forward"
219 * to the expected spot.)
221 * When the image is decoded for the first time mHasBeenDecoded and
222 * mIsCurrentlyDecoded get set to true. When the image is discarded
223 * mIsCurrentlyDecoded gets set to false, and mCompositedFrameInvalid
224 * & mDiscarded get set to true. When we create a decoder to redecode the
225 * image mDiscarded gets set to false. mCompositedFrameInvalid gets set to
226 * false when we are able to advance to the frame that should be showing
227 * for the current time. mIsCurrentlyDecoded gets set to true when the
228 * redecode finishes.
231 //! Whether this image has been decoded at least once.
232 bool mHasBeenDecoded;
234 //! Whether this image has ever requested a decode.
235 bool mHasRequestedDecode;
237 //! Whether this image is currently fully decoded.
238 bool mIsCurrentlyDecoded;
240 //! Whether the composited frame is valid to draw to the screen, note that
241 //! the composited frame can exist and be filled with image data but not
242 //! valid to draw to the screen.
243 bool mCompositedFrameInvalid;
245 //! Whether the composited frame was requested from the animator since the
246 //! last time we advanced the animation.
247 bool mCompositedFrameRequested;
249 //! Whether this image is currently discarded. Only set to true after the
250 //! image has been decoded at least once.
251 bool mDiscarded;
255 * RefreshResult is used to let callers know how the state of the animation
256 * changed during a call to FrameAnimator::RequestRefresh().
258 struct RefreshResult {
259 RefreshResult() : mFrameAdvanced(false), mAnimationFinished(false) {}
261 /// Merges another RefreshResult's changes into this RefreshResult.
262 void Accumulate(const RefreshResult& aOther) {
263 mFrameAdvanced = mFrameAdvanced || aOther.mFrameAdvanced;
264 mAnimationFinished = mAnimationFinished || aOther.mAnimationFinished;
265 mDirtyRect = mDirtyRect.Union(aOther.mDirtyRect);
268 // The region of the image that has changed.
269 gfx::IntRect mDirtyRect;
271 // If true, we changed frames at least once. Note that, due to looping, we
272 // could still have ended up on the same frame!
273 bool mFrameAdvanced : 1;
275 // Whether the animation has finished playing.
276 bool mAnimationFinished : 1;
279 class FrameAnimator {
280 public:
281 FrameAnimator(RasterImage* aImage, const gfx::IntSize& aSize)
282 : mImage(aImage), mSize(aSize) {
283 MOZ_COUNT_CTOR(FrameAnimator);
286 ~FrameAnimator() { MOZ_COUNT_DTOR(FrameAnimator); }
289 * Call when you need to re-start animating. Ensures we start from the first
290 * frame.
292 void ResetAnimation(AnimationState& aState);
295 * Re-evaluate what frame we're supposed to be on, and do whatever blending
296 * is necessary to get us to that frame.
298 * Returns the result of that blending, including whether the current frame
299 * changed and what the resulting dirty rectangle is.
301 RefreshResult RequestRefresh(AnimationState& aState, const TimeStamp& aTime,
302 bool aAnimationFinished);
305 * Get the full frame for the current frame of the animation (it may or may
306 * not have required compositing). It may not be available because it hasn't
307 * been decoded yet, in which case we return an empty LookupResult.
309 LookupResult GetCompositedFrame(AnimationState& aState, bool aMarkUsed);
311 private: // methods
313 * Advances the animation. Typically, this will advance a single frame, but it
314 * may advance multiple frames. This may happen if we have infrequently
315 * "ticking" refresh drivers (e.g. in background tabs), or extremely short-
316 * lived animation frames.
318 * @param aTime the time that the animation should advance to. This will
319 * typically be <= TimeStamp::Now().
321 * @param aCurrentFrame the currently displayed frame of the animation. If
322 * we advance, it will replace aCurrentFrame with the
323 * new current frame we advanced to.
325 * @returns a RefreshResult that shows whether the frame was successfully
326 * advanced, and its resulting dirty rect.
328 RefreshResult AdvanceFrame(AnimationState& aState, DrawableSurface& aFrames,
329 RefPtr<imgFrame>& aCurrentFrame, TimeStamp aTime);
332 * Get the time the frame we're currently displaying is supposed to end.
334 * In the error case (like if the requested frame is not currently
335 * decoded), returns None().
337 TimeStamp GetCurrentImgFrameEndTime(AnimationState& aState,
338 FrameTimeout aCurrentTimeout) const;
340 private: // data
341 //! A weak pointer to our owning image.
342 RasterImage* mImage;
344 //! The intrinsic size of the image.
345 gfx::IntSize mSize;
348 } // namespace image
349 } // namespace mozilla
351 #endif // mozilla_image_FrameAnimator_h