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 True if encountered an error during redecode which should cause
108 * the caller to stop inserting frames.
110 bool HasRedecodeError() const { return mRedecodeError
; }
113 * @returns The current frame index we have advanced to.
115 size_t Displayed() const { return mGetIndex
; }
118 * @returns Outstanding frames desired from the decoder.
120 size_t PendingDecode() const { return mPending
; }
123 * @returns Outstanding frames to advance internally.
125 size_t PendingAdvance() const { return mAdvance
; }
128 * @returns Number of frames we request to be decoded each time it decides we
131 size_t Batch() const { return mBatch
; }
134 * Resets the currently displayed frame of the frame buffer to the beginning.
136 * @returns True if the caller should restart the decoder.
141 return ResetInternal();
145 * Advance the currently displayed frame of the frame buffer. If it reaches
146 * the end, it will loop back to the beginning. It should not be called unless
147 * a call to Get has returned a valid frame for the next frame index.
149 * As we advance, the number of frames we have buffered ahead of the current
150 * will shrink. Once that becomes too few, we will request a batch-sized set
151 * of frames to be decoded from the decoder.
153 * @param aExpectedFrame The frame we expect to have advanced to. This is
154 * used for confirmation purposes (e.g. asserts).
156 * @returns True if the caller should restart the decoder.
158 bool AdvanceTo(size_t aExpectedFrame
) {
159 MOZ_ASSERT(mAdvance
== 0);
161 if (++mGetIndex
== mSize
&& mSizeKnown
) {
164 MOZ_ASSERT(mGetIndex
== aExpectedFrame
);
166 bool hasPending
= mPending
> 0;
168 // Restart the decoder if we transitioned from no pending frames being
169 // decoded, to some pending frames to be decoded.
170 return !hasPending
&& mPending
> 0;
174 * Inserts a frame into the frame buffer.
176 * Once we have a sufficient number of frames buffered relative to the
177 * currently displayed frame, it will return YIELD to indicate the caller
178 * should stop decoding. Otherwise it will return CONTINUE.
180 * If we cross the threshold, it will return DISCARD_YIELD or DISCARD_CONTINUE
181 * to indicate that the caller should switch to a new queue type.
183 * @param aFrame The frame to insert into the buffer.
185 * @returns True if the decoder should decode another frame.
187 InsertStatus
Insert(RefPtr
<imgFrame
>&& aFrame
) {
188 MOZ_ASSERT(mPending
> 0);
192 bool retain
= InsertInternal(std::move(aFrame
));
194 if (mAdvance
> 0 && mSize
> 1) {
201 return mPending
> 0 ? InsertStatus::DISCARD_CONTINUE
202 : InsertStatus::DISCARD_YIELD
;
205 return mPending
> 0 ? InsertStatus::CONTINUE
: InsertStatus::YIELD
;
209 * Access a specific frame from the frame buffer. It should generally access
210 * frames in sequential order, increasing in tandem with AdvanceTo calls. The
211 * first frame may be accessed at any time. The access order should start with
212 * the same value as that given in Initialize (aStartFrame).
214 * @param aFrame The frame index to access.
216 * @returns The frame, if available.
218 virtual imgFrame
* Get(size_t aFrame
, bool aForDisplay
) = 0;
221 * @returns True if the first frame of the animation (not of the queue) is
222 * available/finished, else false.
224 virtual bool IsFirstFrameFinished() const = 0;
227 * @returns True if the last inserted frame matches the given frame, else
230 virtual bool IsLastInsertedFrame(imgFrame
* aFrame
) const = 0;
233 * This should be called after the last frame has been inserted. If the buffer
234 * is discarding old frames, it may request more frames to be decoded. In this
235 * case that means the decoder should start again from the beginning. This
236 * return value should be used in preference to that of the Insert call.
238 * @returns True if the decoder should decode another frame.
240 virtual bool MarkComplete(const gfx::IntRect
& aFirstFrameRefreshArea
) = 0;
242 typedef ISurfaceProvider::AddSizeOfCbData AddSizeOfCbData
;
243 typedef ISurfaceProvider::AddSizeOfCb AddSizeOfCb
;
246 * Accumulate the total cost of all the frames in the buffer.
248 virtual void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf
,
249 const AddSizeOfCb
& aCallback
) = 0;
252 * Request a recycled frame buffer, and if available, set aRecycleRect to be
253 * the dirty rect between the contents of the recycled frame, and the restore
254 * frame (e.g. what we composite on top of) for the next frame to be created.
256 * @returns The frame to be recycled, if available.
258 virtual RawAccessFrameRef
RecycleFrame(gfx::IntRect
& aRecycleRect
) {
259 MOZ_ASSERT(!mRecycling
);
260 return RawAccessFrameRef();
265 * Perform the actual insertion of the given frame into the underlying buffer
266 * representation. mGetIndex shall be the index of the frame we are inserting,
267 * and mSize and mPending have already been adjusted as needed.
269 * @returns True if the caller should continue as normal, false if the discard
270 * threshold was crossed and we should change queue types.
272 virtual bool InsertInternal(RefPtr
<imgFrame
>&& aFrame
) = 0;
275 * Advance from the current frame to the immediately adjacent next frame.
276 * mGetIndex shall be the the index of the new current frame after advancing.
277 * mPending may be adjusted to request more frames.
279 virtual void AdvanceInternal() = 0;
282 * Discard any frames as necessary for the reset. mPending may be adjusted to
283 * request more frames.
285 * @returns True if the caller should resume decoding new frames, else false.
287 virtual bool ResetInternal() = 0;
289 // The total number of frames in the animation. If mSizeKnown is true, it is
290 // the actual total regardless of how many frames are available, otherwise it
291 // is the total number of inserted frames.
294 // The minimum number of frames that we want buffered ahead of the display.
297 // The sequential index of the frame we have advanced to.
300 // The number of frames we need to auto-advance to synchronize with the
304 // The number of frames to decode before we stop.
307 // True if the total number of frames for the animation is known.
310 // True if this buffer may discard frames.
313 // True if we encountered an error while redecoding.
316 // True if this buffer is recycling frames.
321 * An AnimationFrameRetainedBuffer will retain all of the frames inserted into
322 * it. Once it crosses its maximum number of frames, it will recommend
323 * conversion to a discarding queue.
325 class AnimationFrameRetainedBuffer final
: public AnimationFrameBuffer
{
328 * @param aThreshold Maximum number of frames that may be stored in the frame
329 * buffer before it may discard already displayed frames.
330 * Once exceeded, it will discard the previous frame to the
331 * current frame whenever Advance is called. It always
332 * retains the first frame.
334 * @param aBatch See AnimationFrameBuffer::AnimationFrameBuffer.
336 * @param aStartFrame See AnimationFrameBuffer::AnimationFrameBuffer.
338 AnimationFrameRetainedBuffer(size_t aThreshold
, size_t aBatch
,
339 size_t aCurrentFrame
);
342 * @returns Maximum number of frames before we start discarding previous
343 * frames post-advance.
345 size_t Threshold() const { return mThreshold
; }
348 * @returns The frames of this animation, in order. Each element will always
349 * contain a valid frame.
351 const nsTArray
<RefPtr
<imgFrame
>>& Frames() const { return mFrames
; }
353 imgFrame
* Get(size_t aFrame
, bool aForDisplay
) override
;
354 bool IsFirstFrameFinished() const override
;
355 bool IsLastInsertedFrame(imgFrame
* aFrame
) const override
;
356 bool MarkComplete(const gfx::IntRect
& aFirstFrameRefreshArea
) override
;
357 void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf
,
358 const AddSizeOfCb
& aCallback
) override
;
361 friend class AnimationFrameDiscardingQueue
;
362 friend class AnimationFrameRecyclingQueue
;
364 bool InsertInternal(RefPtr
<imgFrame
>&& aFrame
) override
;
365 void AdvanceInternal() override
;
366 bool ResetInternal() override
;
368 // The frames of this animation, in order.
369 nsTArray
<RefPtr
<imgFrame
>> mFrames
;
371 // The maximum number of frames we can have before discarding.
376 * An AnimationFrameDiscardingQueue will only retain up to mBatch * 2 frames.
377 * When the animation advances, it will discard the old current frame.
379 class AnimationFrameDiscardingQueue
: public AnimationFrameBuffer
{
381 explicit AnimationFrameDiscardingQueue(AnimationFrameRetainedBuffer
&& aQueue
);
383 imgFrame
* Get(size_t aFrame
, bool aForDisplay
) final
;
384 bool IsFirstFrameFinished() const final
;
385 bool IsLastInsertedFrame(imgFrame
* aFrame
) const final
;
386 bool MarkComplete(const gfx::IntRect
& aFirstFrameRefreshArea
) override
;
387 void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf
,
388 const AddSizeOfCb
& aCallback
) override
;
390 const std::deque
<RefPtr
<imgFrame
>>& Display() const { return mDisplay
; }
391 const imgFrame
* FirstFrame() const { return mFirstFrame
; }
392 size_t PendingInsert() const { return mInsertIndex
; }
395 bool InsertInternal(RefPtr
<imgFrame
>&& aFrame
) override
;
396 void AdvanceInternal() override
;
397 bool ResetInternal() override
;
399 /// The sequential index of the frame we inserting next.
402 /// Queue storing frames to be displayed by the animator. The first frame in
403 /// the queue is the currently displayed frame.
404 std::deque
<RefPtr
<imgFrame
>> mDisplay
;
406 /// The first frame which is never discarded, and preferentially reused.
407 RefPtr
<imgFrame
> mFirstFrame
;
411 * An AnimationFrameRecyclingQueue will only retain up to mBatch * 2 frames.
412 * When the animation advances, it will place the old current frame into a
413 * recycling queue to be reused for a future allocation. This only works for
414 * animated images where we decoded full sized frames into their own buffers,
415 * so that the buffers are all identically sized and contain the complete frame
418 class AnimationFrameRecyclingQueue final
419 : public AnimationFrameDiscardingQueue
{
421 explicit AnimationFrameRecyclingQueue(AnimationFrameRetainedBuffer
&& aQueue
);
423 bool MarkComplete(const gfx::IntRect
& aFirstFrameRefreshArea
) override
;
424 void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf
,
425 const AddSizeOfCb
& aCallback
) override
;
427 RawAccessFrameRef
RecycleFrame(gfx::IntRect
& aRecycleRect
) override
;
429 struct RecycleEntry
{
430 explicit RecycleEntry(const gfx::IntRect
& aDirtyRect
)
431 : mDirtyRect(aDirtyRect
) {}
433 RecycleEntry(RecycleEntry
&& aOther
)
434 : mFrame(std::move(aOther
.mFrame
)), mDirtyRect(aOther
.mDirtyRect
) {}
436 RecycleEntry
& operator=(RecycleEntry
&& aOther
) {
437 mFrame
= std::move(aOther
.mFrame
);
438 mDirtyRect
= aOther
.mDirtyRect
;
442 RecycleEntry(const RecycleEntry
& aOther
) = delete;
443 RecycleEntry
& operator=(const RecycleEntry
& aOther
) = delete;
445 RefPtr
<imgFrame
> mFrame
; // The frame containing the buffer to recycle.
446 gfx::IntRect mDirtyRect
; // The dirty rect of the frame itself.
449 const std::deque
<RecycleEntry
>& Recycle() const { return mRecycle
; }
450 const gfx::IntRect
& FirstFrameRefreshArea() const {
451 return mFirstFrameRefreshArea
;
455 void AdvanceInternal() override
;
456 bool ResetInternal() override
;
458 /// Queue storing frames to be recycled by the decoder to produce its future
459 /// frames. May contain up to mBatch frames, where the last frame in the queue
460 /// is adjacent to the first frame in the mDisplay queue.
461 std::deque
<RecycleEntry
> mRecycle
;
463 /// The first frame refresh area. This is used instead of the dirty rect for
464 /// the last frame when transitioning back to the first frame.
465 gfx::IntRect mFirstFrameRefreshArea
;
467 /// Force recycled frames to use the first frame refresh area as their dirty
468 /// rect. This is used when we are recycling frames from the end of an
469 /// animation to produce frames at the beginning of an animation.
470 bool mForceUseFirstFrameRefreshArea
;
474 } // namespace mozilla
476 #endif // mozilla_image_AnimationFrameBuffer_h