Line data Source code
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/MemoryReporting.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 : #include "gfxPrefs.h"
19 :
20 : namespace mozilla {
21 : namespace image {
22 :
23 : class RasterImage;
24 : class DrawableSurface;
25 :
26 0 : class AnimationState
27 : {
28 : public:
29 2 : explicit AnimationState(uint16_t aAnimationMode)
30 2 : : mFrameCount(0)
31 : , mCurrentAnimationFrameIndex(0)
32 : , mLoopRemainingCount(-1)
33 : , mLoopCount(-1)
34 : , mFirstFrameTimeout(FrameTimeout::FromRawMilliseconds(0))
35 : , mAnimationMode(aAnimationMode)
36 : , mHasBeenDecoded(false)
37 : , mIsCurrentlyDecoded(false)
38 : , mCompositedFrameInvalid(false)
39 2 : , mDiscarded(false)
40 2 : { }
41 :
42 : /**
43 : * Call this whenever a decode completes, a decode starts, or the image is
44 : * discarded. It will update the internal state. Specifically mDiscarded,
45 : * mCompositedFrameInvalid, and mIsCurrentlyDecoded. If aAllowInvalidation
46 : * is true then returns a rect to invalidate.
47 : */
48 : const gfx::IntRect UpdateState(bool aAnimationFinished,
49 : RasterImage *aImage,
50 : const gfx::IntSize& aSize,
51 : bool aAllowInvalidation = true);
52 : private:
53 : const gfx::IntRect UpdateStateInternal(LookupResult& aResult,
54 : bool aAnimationFinished,
55 : const gfx::IntSize& aSize,
56 : bool aAllowInvalidation = true);
57 :
58 : public:
59 : /**
60 : * Call when a decode of this image has been completed.
61 : */
62 : void NotifyDecodeComplete();
63 :
64 : /**
65 : * Returns true if this image has been fully decoded before.
66 : */
67 0 : bool GetHasBeenDecoded() { return mHasBeenDecoded; }
68 :
69 : /**
70 : * Returns true if this image has been discarded and a decoded has not yet
71 : * been created to redecode it.
72 : */
73 20 : bool IsDiscarded() { return mDiscarded; }
74 :
75 : /**
76 : * Sets the composited frame as valid or invalid.
77 : */
78 : void SetCompositedFrameInvalid(bool aInvalid) {
79 : MOZ_ASSERT(!aInvalid || gfxPrefs::ImageMemAnimatedDiscardable());
80 : mCompositedFrameInvalid = aInvalid;
81 : }
82 :
83 : /**
84 : * Returns whether the composited frame is valid to draw to the screen.
85 : */
86 0 : bool GetCompositedFrameInvalid() {
87 0 : return mCompositedFrameInvalid;
88 : }
89 :
90 : /**
91 : * Returns whether the image is currently full decoded..
92 : */
93 0 : bool GetIsCurrentlyDecoded() {
94 0 : return mIsCurrentlyDecoded;
95 : }
96 :
97 : /**
98 : * Call when you need to re-start animating. Ensures we start from the first
99 : * frame.
100 : */
101 : void ResetAnimation();
102 :
103 : /**
104 : * The animation mode of the image.
105 : *
106 : * Constants defined in imgIContainer.idl.
107 : */
108 : void SetAnimationMode(uint16_t aAnimationMode);
109 :
110 : /// Update the number of frames of animation this image is known to have.
111 : void UpdateKnownFrameCount(uint32_t aFrameCount);
112 :
113 : /// @return the number of frames of animation we know about so far.
114 62 : uint32_t KnownFrameCount() const { return mFrameCount; }
115 :
116 : /// @return the number of frames this animation has, if we know for sure.
117 : /// (In other words, if decoding is finished.) Otherwise, returns Nothing().
118 : Maybe<uint32_t> FrameCount() const;
119 :
120 : /**
121 : * Get or set the area of the image to invalidate when we loop around to the
122 : * first frame.
123 : */
124 : void SetFirstFrameRefreshArea(const gfx::IntRect& aRefreshArea);
125 0 : gfx::IntRect FirstFrameRefreshArea() const { return mFirstFrameRefreshArea; }
126 :
127 : /**
128 : * If the animation frame time has not yet been set, set it to
129 : * TimeStamp::Now().
130 : */
131 : void InitAnimationFrameTimeIfNecessary();
132 :
133 : /**
134 : * Set the animation frame time to @aTime.
135 : */
136 : void SetAnimationFrameTime(const TimeStamp& aTime);
137 :
138 : /**
139 : * The current frame we're on, from 0 to (numFrames - 1).
140 : */
141 : uint32_t GetCurrentAnimationFrameIndex() const;
142 :
143 : /*
144 : * Set number of times to loop the image.
145 : * @note -1 means loop forever.
146 : */
147 4 : void SetLoopCount(int32_t aLoopCount) { mLoopCount = aLoopCount; }
148 22 : int32_t LoopCount() const { return mLoopCount; }
149 :
150 : /// Set the @aLength of a single loop through this image.
151 2 : void SetLoopLength(FrameTimeout aLength) { mLoopLength = Some(aLength); }
152 :
153 : /**
154 : * @return the length of a single loop of this image. If this image is not
155 : * finished decoding, is not animated, or it is animated but does not loop,
156 : * returns FrameTimeout::Forever().
157 : */
158 : FrameTimeout LoopLength() const;
159 :
160 : /*
161 : * Get or set the timeout for the first frame. This is used to allow animation
162 : * scheduling even before a full decode runs for this image.
163 : */
164 4 : void SetFirstFrameTimeout(FrameTimeout aTimeout) { mFirstFrameTimeout = aTimeout; }
165 6 : FrameTimeout FirstFrameTimeout() const { return mFirstFrameTimeout; }
166 :
167 : private:
168 : friend class FrameAnimator;
169 :
170 : //! Area of the first frame that needs to be redrawn on subsequent loops.
171 : gfx::IntRect mFirstFrameRefreshArea;
172 :
173 : //! the time that the animation advanced to the current frame
174 : TimeStamp mCurrentAnimationFrameTime;
175 :
176 : //! The number of frames of animation this image has.
177 : uint32_t mFrameCount;
178 :
179 : //! The current frame index we're on, in the range [0, mFrameCount).
180 : uint32_t mCurrentAnimationFrameIndex;
181 :
182 : //! number of loops remaining before animation stops (-1 no stop)
183 : int32_t mLoopRemainingCount;
184 :
185 : //! The total number of loops for the image.
186 : int32_t mLoopCount;
187 :
188 : //! The length of a single loop through this image.
189 : Maybe<FrameTimeout> mLoopLength;
190 :
191 : //! The timeout for the first frame of this image.
192 : FrameTimeout mFirstFrameTimeout;
193 :
194 : //! The animation mode of this image. Constants defined in imgIContainer.
195 : uint16_t mAnimationMode;
196 :
197 : /**
198 : * The following four bools (mHasBeenDecoded, mIsCurrentlyDecoded,
199 : * mCompositedFrameInvalid, mDiscarded) track the state of the image with
200 : * regards to decoding. They all start out false, including mDiscarded,
201 : * because we want to treat being discarded differently from "not yet decoded
202 : * for the first time".
203 : *
204 : * (When we are decoding the image for the first time we want to show the
205 : * image at the speed of data coming in from the network or the speed
206 : * specified in the image file, whichever is slower. But when redecoding we
207 : * want to show nothing until the frame for the current time has been
208 : * decoded. The prevents the user from seeing the image "fast forward"
209 : * to the expected spot.)
210 : *
211 : * When the image is decoded for the first time mHasBeenDecoded and
212 : * mIsCurrentlyDecoded get set to true. When the image is discarded
213 : * mIsCurrentlyDecoded gets set to false, and mCompositedFrameInvalid
214 : * & mDiscarded get set to true. When we create a decoder to redecode the
215 : * image mDiscarded gets set to false. mCompositedFrameInvalid gets set to
216 : * false when we are able to advance to the frame that should be showing
217 : * for the current time. mIsCurrentlyDecoded gets set to true when the
218 : * redecode finishes.
219 : */
220 :
221 : //! Whether this image has been decoded at least once.
222 : bool mHasBeenDecoded;
223 :
224 : //! Whether this image is currently fully decoded.
225 : bool mIsCurrentlyDecoded;
226 :
227 : //! Whether the composited frame is valid to draw to the screen, note that
228 : //! the composited frame can exist and be filled with image data but not
229 : //! valid to draw to the screen.
230 : bool mCompositedFrameInvalid;
231 :
232 : //! Whether this image is currently discarded. Only set to true after the
233 : //! image has been decoded at least once.
234 : bool mDiscarded;
235 : };
236 :
237 : /**
238 : * RefreshResult is used to let callers know how the state of the animation
239 : * changed during a call to FrameAnimator::RequestRefresh().
240 : */
241 : struct RefreshResult
242 : {
243 42 : RefreshResult()
244 42 : : mFrameAdvanced(false)
245 42 : , mAnimationFinished(false)
246 42 : { }
247 :
248 : /// Merges another RefreshResult's changes into this RefreshResult.
249 22 : void Accumulate(const RefreshResult& aOther)
250 : {
251 22 : mFrameAdvanced = mFrameAdvanced || aOther.mFrameAdvanced;
252 22 : mAnimationFinished = mAnimationFinished || aOther.mAnimationFinished;
253 22 : mDirtyRect = mDirtyRect.Union(aOther.mDirtyRect);
254 22 : }
255 :
256 : // The region of the image that has changed.
257 : gfx::IntRect mDirtyRect;
258 :
259 : // If true, we changed frames at least once. Note that, due to looping, we
260 : // could still have ended up on the same frame!
261 : bool mFrameAdvanced : 1;
262 :
263 : // Whether the animation has finished playing.
264 : bool mAnimationFinished : 1;
265 : };
266 :
267 : class FrameAnimator
268 : {
269 : public:
270 2 : FrameAnimator(RasterImage* aImage, const gfx::IntSize& aSize)
271 2 : : mImage(aImage)
272 : , mSize(aSize)
273 2 : , mLastCompositedFrameIndex(-1)
274 : {
275 2 : MOZ_COUNT_CTOR(FrameAnimator);
276 2 : }
277 :
278 0 : ~FrameAnimator()
279 0 : {
280 0 : MOZ_COUNT_DTOR(FrameAnimator);
281 0 : }
282 :
283 : /**
284 : * Re-evaluate what frame we're supposed to be on, and do whatever blending
285 : * is necessary to get us to that frame.
286 : *
287 : * Returns the result of that blending, including whether the current frame
288 : * changed and what the resulting dirty rectangle is.
289 : */
290 : RefreshResult RequestRefresh(AnimationState& aState,
291 : const TimeStamp& aTime,
292 : bool aAnimationFinished);
293 :
294 : /**
295 : * Get the full frame for the current frame of the animation (it may or may
296 : * not have required compositing). It may not be available because it hasn't
297 : * been decoded yet, in which case we return an empty LookupResult.
298 : */
299 : LookupResult GetCompositedFrame(AnimationState& aState);
300 :
301 : /**
302 : * Collect an accounting of the memory occupied by the compositing surfaces we
303 : * use during animation playback. All of the actual animation frames are
304 : * stored in the SurfaceCache, so we don't need to report them here.
305 : */
306 : void CollectSizeOfCompositingSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
307 : MallocSizeOf aMallocSizeOf) const;
308 :
309 : private: // methods
310 : /**
311 : * Advances the animation. Typically, this will advance a single frame, but it
312 : * may advance multiple frames. This may happen if we have infrequently
313 : * "ticking" refresh drivers (e.g. in background tabs), or extremely short-
314 : * lived animation frames.
315 : *
316 : * @param aTime the time that the animation should advance to. This will
317 : * typically be <= TimeStamp::Now().
318 : *
319 : * @returns a RefreshResult that shows whether the frame was successfully
320 : * advanced, and its resulting dirty rect.
321 : */
322 : RefreshResult AdvanceFrame(AnimationState& aState,
323 : DrawableSurface& aFrames,
324 : TimeStamp aTime);
325 :
326 : /**
327 : * Get the @aIndex-th frame in the frame index, ignoring results of blending.
328 : */
329 : RawAccessFrameRef GetRawFrame(DrawableSurface& aFrames,
330 : uint32_t aFrameNum) const;
331 :
332 : /// @return the given frame's timeout if it is available
333 : Maybe<FrameTimeout> GetTimeoutForFrame(AnimationState& aState,
334 : DrawableSurface& aFrames,
335 : uint32_t aFrameNum) const;
336 :
337 : /**
338 : * Get the time the frame we're currently displaying is supposed to end.
339 : *
340 : * In the error case (like if the requested frame is not currently
341 : * decoded), returns None().
342 : */
343 : Maybe<TimeStamp> GetCurrentImgFrameEndTime(AnimationState& aState,
344 : DrawableSurface& aFrames) const;
345 :
346 : bool DoBlend(DrawableSurface& aFrames,
347 : gfx::IntRect* aDirtyRect,
348 : uint32_t aPrevFrameIndex,
349 : uint32_t aNextFrameIndex);
350 :
351 : /** Clears an area of <aFrame> with transparent black.
352 : *
353 : * @param aFrameData Target Frame data
354 : * @param aFrameRect The rectangle of the data pointed ot by aFrameData
355 : *
356 : * @note Does also clears the transparency mask
357 : */
358 : static void ClearFrame(uint8_t* aFrameData, const gfx::IntRect& aFrameRect);
359 :
360 : //! @overload
361 : static void ClearFrame(uint8_t* aFrameData, const gfx::IntRect& aFrameRect,
362 : const gfx::IntRect& aRectToClear);
363 :
364 : //! Copy one frame's image and mask into another
365 : static bool CopyFrameImage(const uint8_t* aDataSrc, const gfx::IntRect& aRectSrc,
366 : uint8_t* aDataDest, const gfx::IntRect& aRectDest);
367 :
368 : /**
369 : * Draws one frame's image to into another, at the position specified by
370 : * aSrcRect.
371 : *
372 : * @aSrcData the raw data of the current frame being drawn
373 : * @aSrcRect the size of the source frame, and the position of that frame in
374 : * the composition frame
375 : * @aSrcPaletteLength the length (in bytes) of the palette at the beginning
376 : * of the source data (0 if image is not paletted)
377 : * @aSrcHasAlpha whether the source data represents an image with alpha
378 : * @aDstPixels the raw data of the composition frame where the current frame
379 : * is drawn into (32-bit ARGB)
380 : * @aDstRect the size of the composition frame
381 : * @aBlendMethod the blend method for how to blend src on the composition
382 : * frame.
383 : */
384 : static nsresult DrawFrameTo(const uint8_t* aSrcData,
385 : const gfx::IntRect& aSrcRect,
386 : uint32_t aSrcPaletteLength, bool aSrcHasAlpha,
387 : uint8_t* aDstPixels, const gfx::IntRect& aDstRect,
388 : BlendMethod aBlendMethod,
389 : const Maybe<gfx::IntRect>& aBlendRect);
390 :
391 : private: // data
392 : //! A weak pointer to our owning image.
393 : RasterImage* mImage;
394 :
395 : //! The intrinsic size of the image.
396 : gfx::IntSize mSize;
397 :
398 : /** For managing blending of frames
399 : *
400 : * Some animations will use the compositingFrame to composite images
401 : * and just hand this back to the caller when it is time to draw the frame.
402 : * NOTE: When clearing compositingFrame, remember to set
403 : * lastCompositedFrameIndex to -1. Code assume that if
404 : * lastCompositedFrameIndex >= 0 then compositingFrame exists.
405 : */
406 : RawAccessFrameRef mCompositingFrame;
407 :
408 : /** the previous composited frame, for DISPOSE_RESTORE_PREVIOUS
409 : *
410 : * The Previous Frame (all frames composited up to the current) needs to be
411 : * stored in cases where the image specifies it wants the last frame back
412 : * when it's done with the current frame.
413 : */
414 : RawAccessFrameRef mCompositingPrevFrame;
415 :
416 : //! Track the last composited frame for Optimizations (See DoComposite code)
417 : int32_t mLastCompositedFrameIndex;
418 : };
419 :
420 : } // namespace image
421 : } // namespace mozilla
422 :
423 : #endif // mozilla_image_FrameAnimator_h
|