1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef mozilla_image_AnimationFrameBuffer_h
7 #define mozilla_image_AnimationFrameBuffer_h
9 #include "ISurfaceProvider.h"
16 * An AnimationFrameBuffer owns the frames outputted by an animated image
17 * decoder as well as directing its owner on how to drive the decoder,
18 * whether to produce more or to stop.
20 * This should be subclassed by the different types of queues, depending on
21 * what behaviour is desired.
23 class AnimationFrameBuffer
{
25 enum class InsertStatus
: uint8_t {
26 YIELD
, // No more frames required at this time.
27 CONTINUE
, // Continue decoding more frames.
28 DISCARD_YIELD
, // Crossed threshold, switch to discarding structure
29 // and stop decoding more frames.
30 DISCARD_CONTINUE
// Crossed threshold, switch to discarding structure
31 // and continue decoding more frames.
35 * @param aBatch Number of frames we request to be decoded each time it
36 * decides we need more.
38 * @param aStartFrame The starting frame for the animation. The frame buffer
39 * will auto-advance (and thus keep the decoding pipeline
40 * going) until it has reached this frame. Useful when the
41 * animation was progressing, but the surface was
42 * discarded, and we had to redecode.
44 AnimationFrameBuffer(size_t aBatch
, size_t aStartFrame
)
48 mAdvance(aStartFrame
),
52 mRedecodeError(false),
54 if (mBatch
> SIZE_MAX
/ 4) {
55 // Batch size is so big, we will just end up decoding the whole animation.
56 mBatch
= SIZE_MAX
/ 4;
57 } else if (mBatch
< 1) {
58 // Never permit a batch size smaller than 1. We always want to be asking
59 // for at least one frame to start.
64 AnimationFrameBuffer(const AnimationFrameBuffer
& aOther
)
65 : mSize(aOther
.mSize
),
66 mBatch(aOther
.mBatch
),
67 mGetIndex(aOther
.mGetIndex
),
68 mAdvance(aOther
.mAdvance
),
69 mPending(aOther
.mPending
),
70 mSizeKnown(aOther
.mSizeKnown
),
71 mMayDiscard(aOther
.mMayDiscard
),
72 mRedecodeError(aOther
.mRedecodeError
),
73 mRecycling(aOther
.mRecycling
) {}
75 virtual ~AnimationFrameBuffer() {}
78 * @returns True if frames post-advance may be discarded and redecoded on
81 bool MayDiscard() const { return mMayDiscard
; }
84 * @returns True if frames post-advance may be reused after displaying, else
85 * false. Implies MayDiscard().
87 bool IsRecycling() const {
88 MOZ_ASSERT_IF(mRecycling
, mMayDiscard
);
93 * @returns True if the frame buffer was ever marked as complete. This implies
94 * that the total number of frames is known and may be gotten from
97 bool SizeKnown() const { return mSizeKnown
; }
100 * @returns The total number of frames in the animation. If SizeKnown() is
101 * true, then this is a constant, else it is just the total number of
102 * frames we have decoded thus far.
104 size_t Size() const { return mSize
; }
107 * @returns The first frame refresh area. This is used instead of the dirty
108 * rect for the last frame when transitioning back to the first frame.
110 const gfx::IntRect
& FirstFrameRefreshArea() const {
111 return mFirstFrameRefreshArea
;
115 * @returns True if encountered an error during redecode which should cause
116 * the caller to stop inserting frames.
118 bool HasRedecodeError() const { return mRedecodeError
; }
121 * @returns The current frame index we have advanced to.
123 size_t Displayed() const { return mGetIndex
; }
126 * @returns Outstanding frames desired from the decoder.
128 size_t PendingDecode() const { return mPending
; }
131 * @returns Outstanding frames to advance internally.
133 size_t PendingAdvance() const { return mAdvance
; }
136 * @returns Number of frames we request to be decoded each time it decides we
139 size_t Batch() const { return mBatch
; }
142 * Resets the currently displayed frame of the frame buffer to the beginning.
144 * @returns True if the caller should restart the decoder.
149 return ResetInternal();
153 * Advance the currently displayed frame of the frame buffer. If it reaches
154 * the end, it will loop back to the beginning. It should not be called unless
155 * a call to Get has returned a valid frame for the next frame index.
157 * As we advance, the number of frames we have buffered ahead of the current
158 * will shrink. Once that becomes too few, we will request a batch-sized set
159 * of frames to be decoded from the decoder.
161 * @param aExpectedFrame The frame we expect to have advanced to. This is
162 * used for confirmation purposes (e.g. asserts).
164 * @returns True if the caller should restart the decoder.
166 bool AdvanceTo(size_t aExpectedFrame
) {
167 MOZ_ASSERT(mAdvance
== 0);
169 if (++mGetIndex
== mSize
&& mSizeKnown
) {
172 MOZ_ASSERT(mGetIndex
== aExpectedFrame
);
174 bool hasPending
= mPending
> 0;
176 // Restart the decoder if we transitioned from no pending frames being
177 // decoded, to some pending frames to be decoded.
178 return !hasPending
&& mPending
> 0;
182 * Inserts a frame into the frame buffer.
184 * Once we have a sufficient number of frames buffered relative to the
185 * currently displayed frame, it will return YIELD to indicate the caller
186 * should stop decoding. Otherwise it will return CONTINUE.
188 * If we cross the threshold, it will return DISCARD_YIELD or DISCARD_CONTINUE
189 * to indicate that the caller should switch to a new queue type.
191 * @param aFrame The frame to insert into the buffer.
193 * @returns True if the decoder should decode another frame.
195 InsertStatus
Insert(RefPtr
<imgFrame
>&& aFrame
) {
196 MOZ_ASSERT(mPending
> 0);
200 bool retain
= InsertInternal(std::move(aFrame
));
202 if (mAdvance
> 0 && mSize
> 1) {
209 return mPending
> 0 ? InsertStatus::DISCARD_CONTINUE
210 : InsertStatus::DISCARD_YIELD
;
213 return mPending
> 0 ? InsertStatus::CONTINUE
: InsertStatus::YIELD
;
217 * Access a specific frame from the frame buffer. It should generally access
218 * frames in sequential order, increasing in tandem with AdvanceTo calls. The
219 * first frame may be accessed at any time. The access order should start with
220 * the same value as that given in Initialize (aStartFrame).
222 * @param aFrame The frame index to access.
224 * @returns The frame, if available.
226 virtual imgFrame
* Get(size_t aFrame
, bool aForDisplay
) = 0;
229 * @returns True if the first frame of the animation (not of the queue) is
230 * available/finished, else false.
232 virtual bool IsFirstFrameFinished() const = 0;
235 * @returns True if the last inserted frame matches the given frame, else
238 virtual bool IsLastInsertedFrame(imgFrame
* aFrame
) const = 0;
241 * This should be called after the last frame has been inserted. If the buffer
242 * is discarding old frames, it may request more frames to be decoded. In this
243 * case that means the decoder should start again from the beginning. This
244 * return value should be used in preference to that of the Insert call.
246 * @returns True if the decoder should decode another frame.
248 virtual bool MarkComplete(const gfx::IntRect
& aFirstFrameRefreshArea
) = 0;
250 typedef ISurfaceProvider::AddSizeOfCbData AddSizeOfCbData
;
251 typedef ISurfaceProvider::AddSizeOfCb AddSizeOfCb
;
254 * Accumulate the total cost of all the frames in the buffer.
256 virtual void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf
,
257 const AddSizeOfCb
& aCallback
) = 0;
260 * Request a recycled frame buffer, and if available, set aRecycleRect to be
261 * the dirty rect between the contents of the recycled frame, and the restore
262 * frame (e.g. what we composite on top of) for the next frame to be created.
264 * @returns The frame to be recycled, if available.
266 virtual RawAccessFrameRef
RecycleFrame(gfx::IntRect
& aRecycleRect
) {
267 MOZ_ASSERT(!mRecycling
);
268 return RawAccessFrameRef();
273 * Perform the actual insertion of the given frame into the underlying buffer
274 * representation. mGetIndex shall be the index of the frame we are inserting,
275 * and mSize and mPending have already been adjusted as needed.
277 * @returns True if the caller should continue as normal, false if the discard
278 * threshold was crossed and we should change queue types.
280 virtual bool InsertInternal(RefPtr
<imgFrame
>&& aFrame
) = 0;
283 * Advance from the current frame to the immediately adjacent next frame.
284 * mGetIndex shall be the the index of the new current frame after advancing.
285 * mPending may be adjusted to request more frames.
287 virtual void AdvanceInternal() = 0;
290 * Discard any frames as necessary for the reset. mPending may be adjusted to
291 * request more frames.
293 * @returns True if the caller should resume decoding new frames, else false.
295 virtual bool ResetInternal() = 0;
297 /// The first frame refresh area. This is used instead of the dirty rect for
298 /// the last frame when transitioning back to the first frame.
299 gfx::IntRect mFirstFrameRefreshArea
;
301 // The total number of frames in the animation. If mSizeKnown is true, it is
302 // the actual total regardless of how many frames are available, otherwise it
303 // is the total number of inserted frames.
306 // The minimum number of frames that we want buffered ahead of the display.
309 // The sequential index of the frame we have advanced to.
312 // The number of frames we need to auto-advance to synchronize with the
316 // The number of frames to decode before we stop.
319 // True if the total number of frames for the animation is known.
322 // True if this buffer may discard frames.
325 // True if we encountered an error while redecoding.
328 // True if this buffer is recycling frames.
333 * An AnimationFrameRetainedBuffer will retain all of the frames inserted into
334 * it. Once it crosses its maximum number of frames, it will recommend
335 * conversion to a discarding queue.
337 class AnimationFrameRetainedBuffer final
: public AnimationFrameBuffer
{
340 * @param aThreshold Maximum number of frames that may be stored in the frame
341 * buffer before it may discard already displayed frames.
342 * Once exceeded, it will discard the previous frame to the
343 * current frame whenever Advance is called. It always
344 * retains the first frame.
346 * @param aBatch See AnimationFrameBuffer::AnimationFrameBuffer.
348 * @param aStartFrame See AnimationFrameBuffer::AnimationFrameBuffer.
350 AnimationFrameRetainedBuffer(size_t aThreshold
, size_t aBatch
,
351 size_t aCurrentFrame
);
354 * @returns Maximum number of frames before we start discarding previous
355 * frames post-advance.
357 size_t Threshold() const { return mThreshold
; }
360 * @returns The frames of this animation, in order. Each element will always
361 * contain a valid frame.
363 const nsTArray
<RefPtr
<imgFrame
>>& Frames() const { return mFrames
; }
365 imgFrame
* Get(size_t aFrame
, bool aForDisplay
) override
;
366 bool IsFirstFrameFinished() const override
;
367 bool IsLastInsertedFrame(imgFrame
* aFrame
) const override
;
368 bool MarkComplete(const gfx::IntRect
& aFirstFrameRefreshArea
) override
;
369 void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf
,
370 const AddSizeOfCb
& aCallback
) override
;
373 friend class AnimationFrameDiscardingQueue
;
374 friend class AnimationFrameRecyclingQueue
;
376 bool InsertInternal(RefPtr
<imgFrame
>&& aFrame
) override
;
377 void AdvanceInternal() override
;
378 bool ResetInternal() override
;
380 // The frames of this animation, in order.
381 nsTArray
<RefPtr
<imgFrame
>> mFrames
;
383 // The maximum number of frames we can have before discarding.
388 * An AnimationFrameDiscardingQueue will only retain up to mBatch * 2 frames.
389 * When the animation advances, it will discard the old current frame.
391 class AnimationFrameDiscardingQueue
: public AnimationFrameBuffer
{
393 explicit AnimationFrameDiscardingQueue(AnimationFrameRetainedBuffer
&& aQueue
);
395 imgFrame
* Get(size_t aFrame
, bool aForDisplay
) final
;
396 bool IsFirstFrameFinished() const final
;
397 bool IsLastInsertedFrame(imgFrame
* aFrame
) const final
;
398 bool MarkComplete(const gfx::IntRect
& aFirstFrameRefreshArea
) override
;
399 void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf
,
400 const AddSizeOfCb
& aCallback
) override
;
402 const std::deque
<RefPtr
<imgFrame
>>& Display() const { return mDisplay
; }
403 const imgFrame
* FirstFrame() const { return mFirstFrame
; }
404 size_t PendingInsert() const { return mInsertIndex
; }
407 bool InsertInternal(RefPtr
<imgFrame
>&& aFrame
) override
;
408 void AdvanceInternal() override
;
409 bool ResetInternal() override
;
411 /// The sequential index of the frame we inserting next.
414 /// Queue storing frames to be displayed by the animator. The first frame in
415 /// the queue is the currently displayed frame.
416 std::deque
<RefPtr
<imgFrame
>> mDisplay
;
418 /// The first frame which is never discarded, and preferentially reused.
419 RefPtr
<imgFrame
> mFirstFrame
;
423 * An AnimationFrameRecyclingQueue will only retain up to mBatch * 2 frames.
424 * When the animation advances, it will place the old current frame into a
425 * recycling queue to be reused for a future allocation. This only works for
426 * animated images where we decoded full sized frames into their own buffers,
427 * so that the buffers are all identically sized and contain the complete frame
430 class AnimationFrameRecyclingQueue final
431 : public AnimationFrameDiscardingQueue
{
433 explicit AnimationFrameRecyclingQueue(AnimationFrameRetainedBuffer
&& aQueue
);
435 void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf
,
436 const AddSizeOfCb
& aCallback
) override
;
438 RawAccessFrameRef
RecycleFrame(gfx::IntRect
& aRecycleRect
) override
;
440 struct RecycleEntry
{
441 explicit RecycleEntry(const gfx::IntRect
& aDirtyRect
)
442 : mDirtyRect(aDirtyRect
) {}
444 RecycleEntry(RecycleEntry
&& aOther
)
445 : mFrame(std::move(aOther
.mFrame
)), mDirtyRect(aOther
.mDirtyRect
) {}
447 RecycleEntry
& operator=(RecycleEntry
&& aOther
) {
448 mFrame
= std::move(aOther
.mFrame
);
449 mDirtyRect
= aOther
.mDirtyRect
;
453 RecycleEntry(const RecycleEntry
& aOther
) = delete;
454 RecycleEntry
& operator=(const RecycleEntry
& aOther
) = delete;
456 RefPtr
<imgFrame
> mFrame
; // The frame containing the buffer to recycle.
457 gfx::IntRect mDirtyRect
; // The dirty rect of the frame itself.
460 const std::deque
<RecycleEntry
>& Recycle() const { return mRecycle
; }
463 void AdvanceInternal() override
;
464 bool ResetInternal() override
;
466 /// Queue storing frames to be recycled by the decoder to produce its future
467 /// frames. May contain up to mBatch frames, where the last frame in the queue
468 /// is adjacent to the first frame in the mDisplay queue.
469 std::deque
<RecycleEntry
> mRecycle
;
471 /// Force recycled frames to use the first frame refresh area as their dirty
472 /// rect. This is used when we are recycling frames from the end of an
473 /// animation to produce frames at the beginning of an animation.
474 bool mForceUseFirstFrameRefreshArea
;
478 } // namespace mozilla
480 #endif // mozilla_image_AnimationFrameBuffer_h