tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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