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 */ AnimationFrameBuffer(size_t aBatch,size_t aStartFrame)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 AnimationFrameBuffer(const AnimationFrameBuffer & aOther)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 ~AnimationFrameBuffer()75 virtual ~AnimationFrameBuffer() {} 76 77 /** 78 * @returns True if frames post-advance may be discarded and redecoded on 79 * demand, else false. 80 */ MayDiscard()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 */ IsRecycling()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 */ SizeKnown()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 */ Size()104 size_t Size() const { return mSize; } 105 106 /** 107 * @returns True if encountered an error during redecode which should cause 108 * the caller to stop inserting frames. 109 */ HasRedecodeError()110 bool HasRedecodeError() const { return mRedecodeError; } 111 112 /** 113 * @returns The current frame index we have advanced to. 114 */ Displayed()115 size_t Displayed() const { return mGetIndex; } 116 117 /** 118 * @returns Outstanding frames desired from the decoder. 119 */ PendingDecode()120 size_t PendingDecode() const { return mPending; } 121 122 /** 123 * @returns Outstanding frames to advance internally. 124 */ PendingAdvance()125 size_t PendingAdvance() const { return mAdvance; } 126 127 /** 128 * @returns Number of frames we request to be decoded each time it decides we 129 * need more. 130 */ Batch()131 size_t Batch() const { return mBatch; } 132 133 /** 134 * Resets the currently displayed frame of the frame buffer to the beginning. 135 * 136 * @returns True if the caller should restart the decoder. 137 */ Reset()138 bool Reset() { 139 mGetIndex = 0; 140 mAdvance = 0; 141 return ResetInternal(); 142 } 143 144 /** 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. 148 * 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. 152 * 153 * @param aExpectedFrame The frame we expect to have advanced to. This is 154 * used for confirmation purposes (e.g. asserts). 155 * 156 * @returns True if the caller should restart the decoder. 157 */ AdvanceTo(size_t aExpectedFrame)158 bool AdvanceTo(size_t aExpectedFrame) { 159 MOZ_ASSERT(mAdvance == 0); 160 161 if (++mGetIndex == mSize && mSizeKnown) { 162 mGetIndex = 0; 163 } 164 MOZ_ASSERT(mGetIndex == aExpectedFrame); 165 166 bool hasPending = mPending > 0; 167 AdvanceInternal(); 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; 171 } 172 173 /** 174 * Inserts a frame into the frame buffer. 175 * 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. 179 * 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. 182 * 183 * @param aFrame The frame to insert into the buffer. 184 * 185 * @returns True if the decoder should decode another frame. 186 */ Insert(RefPtr<imgFrame> && aFrame)187 InsertStatus Insert(RefPtr<imgFrame>&& aFrame) { 188 MOZ_ASSERT(mPending > 0); 189 MOZ_ASSERT(aFrame); 190 191 --mPending; 192 bool retain = InsertInternal(std::move(aFrame)); 193 194 if (mAdvance > 0 && mSize > 1) { 195 --mAdvance; 196 ++mGetIndex; 197 AdvanceInternal(); 198 } 199 200 if (!retain) { 201 return mPending > 0 ? InsertStatus::DISCARD_CONTINUE 202 : InsertStatus::DISCARD_YIELD; 203 } 204 205 return mPending > 0 ? InsertStatus::CONTINUE : InsertStatus::YIELD; 206 } 207 208 /** 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). 213 * 214 * @param aFrame The frame index to access. 215 * 216 * @returns The frame, if available. 217 */ 218 virtual imgFrame* Get(size_t aFrame, bool aForDisplay) = 0; 219 220 /** 221 * @returns True if the first frame of the animation (not of the queue) is 222 * available/finished, else false. 223 */ 224 virtual bool IsFirstFrameFinished() const = 0; 225 226 /** 227 * @returns True if the last inserted frame matches the given frame, else 228 * false. 229 */ 230 virtual bool IsLastInsertedFrame(imgFrame* aFrame) const = 0; 231 232 /** 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. 237 * 238 * @returns True if the decoder should decode another frame. 239 */ 240 virtual bool MarkComplete(const gfx::IntRect& aFirstFrameRefreshArea) = 0; 241 242 typedef ISurfaceProvider::AddSizeOfCbData AddSizeOfCbData; 243 typedef ISurfaceProvider::AddSizeOfCb AddSizeOfCb; 244 245 /** 246 * Accumulate the total cost of all the frames in the buffer. 247 */ 248 virtual void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, 249 const AddSizeOfCb& aCallback) = 0; 250 251 /** 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. 255 * 256 * @returns The frame to be recycled, if available. 257 */ RecycleFrame(gfx::IntRect & aRecycleRect)258 virtual RawAccessFrameRef RecycleFrame(gfx::IntRect& aRecycleRect) { 259 MOZ_ASSERT(!mRecycling); 260 return RawAccessFrameRef(); 261 } 262 263 protected: 264 /** 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. 268 * 269 * @returns True if the caller should continue as normal, false if the discard 270 * threshold was crossed and we should change queue types. 271 */ 272 virtual bool InsertInternal(RefPtr<imgFrame>&& aFrame) = 0; 273 274 /** 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. 278 */ 279 virtual void AdvanceInternal() = 0; 280 281 /** 282 * Discard any frames as necessary for the reset. mPending may be adjusted to 283 * request more frames. 284 * 285 * @returns True if the caller should resume decoding new frames, else false. 286 */ 287 virtual bool ResetInternal() = 0; 288 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. 292 size_t mSize; 293 294 // The minimum number of frames that we want buffered ahead of the display. 295 size_t mBatch; 296 297 // The sequential index of the frame we have advanced to. 298 size_t mGetIndex; 299 300 // The number of frames we need to auto-advance to synchronize with the 301 // caller. 302 size_t mAdvance; 303 304 // The number of frames to decode before we stop. 305 size_t mPending; 306 307 // True if the total number of frames for the animation is known. 308 bool mSizeKnown; 309 310 // True if this buffer may discard frames. 311 bool mMayDiscard; 312 313 // True if we encountered an error while redecoding. 314 bool mRedecodeError; 315 316 // True if this buffer is recycling frames. 317 bool mRecycling; 318 }; 319 320 /** 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. 324 */ 325 class AnimationFrameRetainedBuffer final : public AnimationFrameBuffer { 326 public: 327 /** 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. 333 * 334 * @param aBatch See AnimationFrameBuffer::AnimationFrameBuffer. 335 * 336 * @param aStartFrame See AnimationFrameBuffer::AnimationFrameBuffer. 337 */ 338 AnimationFrameRetainedBuffer(size_t aThreshold, size_t aBatch, 339 size_t aCurrentFrame); 340 341 /** 342 * @returns Maximum number of frames before we start discarding previous 343 * frames post-advance. 344 */ Threshold()345 size_t Threshold() const { return mThreshold; } 346 347 /** 348 * @returns The frames of this animation, in order. Each element will always 349 * contain a valid frame. 350 */ Frames()351 const nsTArray<RefPtr<imgFrame>>& Frames() const { return mFrames; } 352 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; 359 360 private: 361 friend class AnimationFrameDiscardingQueue; 362 friend class AnimationFrameRecyclingQueue; 363 364 bool InsertInternal(RefPtr<imgFrame>&& aFrame) override; 365 void AdvanceInternal() override; 366 bool ResetInternal() override; 367 368 // The frames of this animation, in order. 369 nsTArray<RefPtr<imgFrame>> mFrames; 370 371 // The maximum number of frames we can have before discarding. 372 size_t mThreshold; 373 }; 374 375 /** 376 * An AnimationFrameDiscardingQueue will only retain up to mBatch * 2 frames. 377 * When the animation advances, it will discard the old current frame. 378 */ 379 class AnimationFrameDiscardingQueue : public AnimationFrameBuffer { 380 public: 381 explicit AnimationFrameDiscardingQueue(AnimationFrameRetainedBuffer&& aQueue); 382 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; 389 Display()390 const std::deque<RefPtr<imgFrame>>& Display() const { return mDisplay; } FirstFrame()391 const imgFrame* FirstFrame() const { return mFirstFrame; } PendingInsert()392 size_t PendingInsert() const { return mInsertIndex; } 393 394 protected: 395 bool InsertInternal(RefPtr<imgFrame>&& aFrame) override; 396 void AdvanceInternal() override; 397 bool ResetInternal() override; 398 399 /// The sequential index of the frame we inserting next. 400 size_t mInsertIndex; 401 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; 405 406 /// The first frame which is never discarded, and preferentially reused. 407 RefPtr<imgFrame> mFirstFrame; 408 }; 409 410 /** 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 416 * data. 417 */ 418 class AnimationFrameRecyclingQueue final 419 : public AnimationFrameDiscardingQueue { 420 public: 421 explicit AnimationFrameRecyclingQueue(AnimationFrameRetainedBuffer&& aQueue); 422 423 bool MarkComplete(const gfx::IntRect& aFirstFrameRefreshArea) override; 424 void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, 425 const AddSizeOfCb& aCallback) override; 426 427 RawAccessFrameRef RecycleFrame(gfx::IntRect& aRecycleRect) override; 428 429 struct RecycleEntry { RecycleEntryRecycleEntry430 explicit RecycleEntry(const gfx::IntRect& aDirtyRect) 431 : mDirtyRect(aDirtyRect) {} 432 RecycleEntryRecycleEntry433 RecycleEntry(RecycleEntry&& aOther) 434 : mFrame(std::move(aOther.mFrame)), mDirtyRect(aOther.mDirtyRect) {} 435 436 RecycleEntry& operator=(RecycleEntry&& aOther) { 437 mFrame = std::move(aOther.mFrame); 438 mDirtyRect = aOther.mDirtyRect; 439 return *this; 440 } 441 442 RecycleEntry(const RecycleEntry& aOther) = delete; 443 RecycleEntry& operator=(const RecycleEntry& aOther) = delete; 444 445 RefPtr<imgFrame> mFrame; // The frame containing the buffer to recycle. 446 gfx::IntRect mDirtyRect; // The dirty rect of the frame itself. 447 }; 448 Recycle()449 const std::deque<RecycleEntry>& Recycle() const { return mRecycle; } FirstFrameRefreshArea()450 const gfx::IntRect& FirstFrameRefreshArea() const { 451 return mFirstFrameRefreshArea; 452 } 453 454 protected: 455 void AdvanceInternal() override; 456 bool ResetInternal() override; 457 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; 462 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; 466 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; 471 }; 472 473 } // namespace image 474 } // namespace mozilla 475 476 #endif // mozilla_image_AnimationFrameBuffer_h 477