AnimationFrameBuffer.h (16887B)
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/. */ 5 6 #ifndef mozilla_image_AnimationFrameBuffer_h 7 #define mozilla_image_AnimationFrameBuffer_h 8 9 #include "ISurfaceProvider.h" 10 #include <deque> 11 12 namespace mozilla { 13 namespace image { 14 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. 19 * 20 * This should be subclassed by the different types of queues, depending on 21 * what behaviour is desired. 22 */ 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. 32 }; 33 34 /** 35 * @param aBatch Number of frames we request to be decoded each time it 36 * decides we need more. 37 * 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. 43 */ 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; 61 } 62 } 63 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) {} 74 75 virtual ~AnimationFrameBuffer() {} 76 77 /** 78 * @returns True if frames post-advance may be discarded and redecoded on 79 * demand, else false. 80 */ 81 bool MayDiscard() const { return mMayDiscard; } 82 83 /** 84 * @returns True if frames post-advance may be reused after displaying, else 85 * false. Implies MayDiscard(). 86 */ 87 bool IsRecycling() const { 88 MOZ_ASSERT_IF(mRecycling, mMayDiscard); 89 return mRecycling; 90 } 91 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(). 96 */ 97 bool SizeKnown() const { return mSizeKnown; } 98 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. 103 */ 104 size_t Size() const { return mSize; } 105 106 /** 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. 109 */ 110 const gfx::IntRect& FirstFrameRefreshArea() const { 111 return mFirstFrameRefreshArea; 112 } 113 114 /** 115 * @returns True if encountered an error during redecode which should cause 116 * the caller to stop inserting frames. 117 */ 118 bool HasRedecodeError() const { return mRedecodeError; } 119 120 /** 121 * @returns The current frame index we have advanced to. 122 */ 123 size_t Displayed() const { return mGetIndex; } 124 125 /** 126 * @returns Outstanding frames desired from the decoder. 127 */ 128 size_t PendingDecode() const { return mPending; } 129 130 /** 131 * @returns Outstanding frames to advance internally. 132 */ 133 size_t PendingAdvance() const { return mAdvance; } 134 135 /** 136 * @returns Number of frames we request to be decoded each time it decides we 137 * need more. 138 */ 139 size_t Batch() const { return mBatch; } 140 141 /** 142 * Resets the currently displayed frame of the frame buffer to the beginning. 143 * 144 * @returns True if the caller should restart the decoder. 145 */ 146 bool Reset() { 147 mGetIndex = 0; 148 mAdvance = 0; 149 return ResetInternal(); 150 } 151 152 /** 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. 156 * 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. 160 * 161 * @param aExpectedFrame The frame we expect to have advanced to. This is 162 * used for confirmation purposes (e.g. asserts). 163 * 164 * @returns True if the caller should restart the decoder. 165 */ 166 bool AdvanceTo(size_t aExpectedFrame) { 167 MOZ_ASSERT(mAdvance == 0); 168 169 if (++mGetIndex == mSize && mSizeKnown) { 170 mGetIndex = 0; 171 } 172 MOZ_ASSERT(mGetIndex == aExpectedFrame); 173 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; 179 } 180 181 /** 182 * Inserts a frame into the frame buffer. 183 * 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. 187 * 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. 190 * 191 * @param aFrame The frame to insert into the buffer. 192 * 193 * @returns True if the decoder should decode another frame. 194 */ 195 InsertStatus Insert(RefPtr<imgFrame>&& aFrame) { 196 MOZ_ASSERT(mPending > 0); 197 MOZ_ASSERT(aFrame); 198 199 --mPending; 200 bool retain = InsertInternal(std::move(aFrame)); 201 202 if (mAdvance > 0 && mSize > 1) { 203 --mAdvance; 204 ++mGetIndex; 205 AdvanceInternal(); 206 } 207 208 if (!retain) { 209 return mPending > 0 ? InsertStatus::DISCARD_CONTINUE 210 : InsertStatus::DISCARD_YIELD; 211 } 212 213 return mPending > 0 ? InsertStatus::CONTINUE : InsertStatus::YIELD; 214 } 215 216 /** 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). 221 * 222 * @param aFrame The frame index to access. 223 * 224 * @returns The frame, if available. 225 */ 226 virtual imgFrame* Get(size_t aFrame, bool aForDisplay) = 0; 227 228 /** 229 * @returns True if the first frame of the animation (not of the queue) is 230 * available/finished, else false. 231 */ 232 virtual bool IsFirstFrameFinished() const = 0; 233 234 /** 235 * @returns True if the last inserted frame matches the given frame, else 236 * false. 237 */ 238 virtual bool IsLastInsertedFrame(imgFrame* aFrame) const = 0; 239 240 /** 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. 245 * 246 * @returns True if the decoder should decode another frame. 247 */ 248 virtual bool MarkComplete(const gfx::IntRect& aFirstFrameRefreshArea) = 0; 249 250 typedef ISurfaceProvider::AddSizeOfCbData AddSizeOfCbData; 251 typedef ISurfaceProvider::AddSizeOfCb AddSizeOfCb; 252 253 /** 254 * Accumulate the total cost of all the frames in the buffer. 255 */ 256 virtual void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, 257 const AddSizeOfCb& aCallback) = 0; 258 259 /** 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. 263 * 264 * @returns The frame to be recycled, if available. 265 */ 266 virtual RawAccessFrameRef RecycleFrame(gfx::IntRect& aRecycleRect) { 267 MOZ_ASSERT(!mRecycling); 268 return RawAccessFrameRef(); 269 } 270 271 protected: 272 /** 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. 276 * 277 * @returns True if the caller should continue as normal, false if the discard 278 * threshold was crossed and we should change queue types. 279 */ 280 virtual bool InsertInternal(RefPtr<imgFrame>&& aFrame) = 0; 281 282 /** 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. 286 */ 287 virtual void AdvanceInternal() = 0; 288 289 /** 290 * Discard any frames as necessary for the reset. mPending may be adjusted to 291 * request more frames. 292 * 293 * @returns True if the caller should resume decoding new frames, else false. 294 */ 295 virtual bool ResetInternal() = 0; 296 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; 300 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; 305 306 // The minimum number of frames that we want buffered ahead of the display. 307 size_t mBatch; 308 309 // The sequential index of the frame we have advanced to. 310 size_t mGetIndex; 311 312 // The number of frames we need to auto-advance to synchronize with the 313 // caller. 314 size_t mAdvance; 315 316 // The number of frames to decode before we stop. 317 size_t mPending; 318 319 // True if the total number of frames for the animation is known. 320 bool mSizeKnown; 321 322 // True if this buffer may discard frames. 323 bool mMayDiscard; 324 325 // True if we encountered an error while redecoding. 326 bool mRedecodeError; 327 328 // True if this buffer is recycling frames. 329 bool mRecycling; 330 }; 331 332 /** 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. 336 */ 337 class AnimationFrameRetainedBuffer final : public AnimationFrameBuffer { 338 public: 339 /** 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. 345 * 346 * @param aBatch See AnimationFrameBuffer::AnimationFrameBuffer. 347 * 348 * @param aStartFrame See AnimationFrameBuffer::AnimationFrameBuffer. 349 */ 350 AnimationFrameRetainedBuffer(size_t aThreshold, size_t aBatch, 351 size_t aCurrentFrame); 352 353 /** 354 * @returns Maximum number of frames before we start discarding previous 355 * frames post-advance. 356 */ 357 size_t Threshold() const { return mThreshold; } 358 359 /** 360 * @returns The frames of this animation, in order. Each element will always 361 * contain a valid frame. 362 */ 363 const nsTArray<RefPtr<imgFrame>>& Frames() const { return mFrames; } 364 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; 371 372 private: 373 friend class AnimationFrameDiscardingQueue; 374 friend class AnimationFrameRecyclingQueue; 375 376 bool InsertInternal(RefPtr<imgFrame>&& aFrame) override; 377 void AdvanceInternal() override; 378 bool ResetInternal() override; 379 380 // The frames of this animation, in order. 381 nsTArray<RefPtr<imgFrame>> mFrames; 382 383 // The maximum number of frames we can have before discarding. 384 size_t mThreshold; 385 }; 386 387 /** 388 * An AnimationFrameDiscardingQueue will only retain up to mBatch * 2 frames. 389 * When the animation advances, it will discard the old current frame. 390 */ 391 class AnimationFrameDiscardingQueue : public AnimationFrameBuffer { 392 public: 393 explicit AnimationFrameDiscardingQueue(AnimationFrameRetainedBuffer&& aQueue); 394 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; 401 402 const std::deque<RefPtr<imgFrame>>& Display() const { return mDisplay; } 403 const imgFrame* FirstFrame() const { return mFirstFrame; } 404 size_t PendingInsert() const { return mInsertIndex; } 405 406 protected: 407 bool InsertInternal(RefPtr<imgFrame>&& aFrame) override; 408 void AdvanceInternal() override; 409 bool ResetInternal() override; 410 411 /// The sequential index of the frame we inserting next. 412 size_t mInsertIndex; 413 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; 417 418 /// The first frame which is never discarded, and preferentially reused. 419 RefPtr<imgFrame> mFirstFrame; 420 }; 421 422 /** 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. 429 */ 430 class AnimationFrameRecyclingQueue final 431 : public AnimationFrameDiscardingQueue { 432 public: 433 explicit AnimationFrameRecyclingQueue(AnimationFrameRetainedBuffer&& aQueue); 434 435 void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, 436 const AddSizeOfCb& aCallback) override; 437 438 RawAccessFrameRef RecycleFrame(gfx::IntRect& aRecycleRect) override; 439 440 struct RecycleEntry { 441 explicit RecycleEntry(const gfx::IntRect& aDirtyRect) 442 : mDirtyRect(aDirtyRect) {} 443 444 RecycleEntry(RecycleEntry&& aOther) 445 : mFrame(std::move(aOther.mFrame)), mDirtyRect(aOther.mDirtyRect) {} 446 447 RecycleEntry& operator=(RecycleEntry&& aOther) { 448 mFrame = std::move(aOther.mFrame); 449 mDirtyRect = aOther.mDirtyRect; 450 return *this; 451 } 452 453 RecycleEntry(const RecycleEntry& aOther) = delete; 454 RecycleEntry& operator=(const RecycleEntry& aOther) = delete; 455 456 RefPtr<imgFrame> mFrame; // The frame containing the buffer to recycle. 457 gfx::IntRect mDirtyRect; // The dirty rect of the frame itself. 458 }; 459 460 const std::deque<RecycleEntry>& Recycle() const { return mRecycle; } 461 462 protected: 463 void AdvanceInternal() override; 464 bool ResetInternal() override; 465 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; 470 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; 475 }; 476 477 } // namespace image 478 } // namespace mozilla 479 480 #endif // mozilla_image_AnimationFrameBuffer_h