Coverage Report

Created: 2018-09-25 14:53

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