Bug 1835529 [wpt PR 40276] - Update wpt metadata, a=testonly
[gecko.git] / image / AnimationFrameBuffer.h
blobb812fe4630e91a3bca910e86749e87acc263c4bb
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"
10 #include <deque>
12 namespace mozilla {
13 namespace image {
15 /**
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 {
24 public:
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.
34 /**
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)
45 : mSize(0),
46 mBatch(aBatch),
47 mGetIndex(0),
48 mAdvance(aStartFrame),
49 mPending(0),
50 mSizeKnown(false),
51 mMayDiscard(false),
52 mRedecodeError(false),
53 mRecycling(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.
60 mBatch = 1;
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() {}
77 /**
78 * @returns True if frames post-advance may be discarded and redecoded on
79 * demand, else false.
81 bool MayDiscard() const { return mMayDiscard; }
83 /**
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);
89 return mRecycling;
92 /**
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
95 * Frames().Length().
97 bool SizeKnown() const { return mSizeKnown; }
99 /**
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
137 * need more.
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.
146 bool Reset() {
147 mGetIndex = 0;
148 mAdvance = 0;
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) {
170 mGetIndex = 0;
172 MOZ_ASSERT(mGetIndex == aExpectedFrame);
174 bool hasPending = mPending > 0;
175 AdvanceInternal();
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);
197 MOZ_ASSERT(aFrame);
199 --mPending;
200 bool retain = InsertInternal(std::move(aFrame));
202 if (mAdvance > 0 && mSize > 1) {
203 --mAdvance;
204 ++mGetIndex;
205 AdvanceInternal();
208 if (!retain) {
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
236 * false.
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();
271 protected:
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.
304 size_t mSize;
306 // The minimum number of frames that we want buffered ahead of the display.
307 size_t mBatch;
309 // The sequential index of the frame we have advanced to.
310 size_t mGetIndex;
312 // The number of frames we need to auto-advance to synchronize with the
313 // caller.
314 size_t mAdvance;
316 // The number of frames to decode before we stop.
317 size_t mPending;
319 // True if the total number of frames for the animation is known.
320 bool mSizeKnown;
322 // True if this buffer may discard frames.
323 bool mMayDiscard;
325 // True if we encountered an error while redecoding.
326 bool mRedecodeError;
328 // True if this buffer is recycling frames.
329 bool mRecycling;
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 {
338 public:
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;
372 private:
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.
384 size_t mThreshold;
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 {
392 public:
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; }
406 protected:
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.
412 size_t mInsertIndex;
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
428 * data.
430 class AnimationFrameRecyclingQueue final
431 : public AnimationFrameDiscardingQueue {
432 public:
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;
450 return *this;
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; }
462 protected:
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;
477 } // namespace image
478 } // namespace mozilla
480 #endif // mozilla_image_AnimationFrameBuffer_h