tor-browser

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

FrameAnimator.h (11670B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 *
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef mozilla_image_FrameAnimator_h
      8 #define mozilla_image_FrameAnimator_h
      9 
     10 #include "mozilla/Maybe.h"
     11 #include "mozilla/StaticPrefs_image.h"
     12 #include "mozilla/TimeStamp.h"
     13 #include "gfxTypes.h"
     14 #include "imgFrame.h"
     15 #include "nsCOMPtr.h"
     16 #include "nsRect.h"
     17 #include "SurfaceCache.h"
     18 
     19 namespace mozilla {
     20 namespace image {
     21 
     22 class RasterImage;
     23 class DrawableSurface;
     24 
     25 class AnimationState {
     26 public:
     27  explicit AnimationState(uint16_t aAnimationMode)
     28      : mFrameCount(0),
     29        mCurrentAnimationFrameIndex(0),
     30        mLoopRemainingCount(-1),
     31        mLoopCount(-1),
     32        mFirstFrameTimeout(FrameTimeout::FromRawMilliseconds(0)),
     33        mAnimationMode(aAnimationMode),
     34        mHasBeenDecoded(false),
     35        mHasRequestedDecode(false),
     36        mIsCurrentlyDecoded(false),
     37        mCompositedFrameInvalid(false),
     38        mDiscarded(false) {}
     39 
     40  /**
     41   * Call this whenever a decode completes, a decode starts, or the image is
     42   * discarded. It will update the internal state. Specifically mDiscarded,
     43   * mCompositedFrameInvalid, and mIsCurrentlyDecoded. If aAllowInvalidation
     44   * is true then returns a rect to invalidate.
     45   */
     46  const gfx::IntRect UpdateState(RasterImage* aImage, const gfx::IntSize& aSize,
     47                                 bool aAllowInvalidation = true);
     48 
     49 private:
     50  const gfx::IntRect UpdateStateInternal(LookupResult& aResult,
     51                                         const gfx::IntSize& aSize,
     52                                         bool aAllowInvalidation = true);
     53 
     54 public:
     55  /**
     56   * Call when a decode of this image has been completed.
     57   */
     58  void NotifyDecodeComplete();
     59 
     60  /**
     61   * Returns true if this image has been fully decoded before.
     62   */
     63  bool GetHasBeenDecoded() { return mHasBeenDecoded; }
     64 
     65  /**
     66   * Returns true if this image has ever requested a decode before.
     67   */
     68  bool GetHasRequestedDecode() { return mHasRequestedDecode; }
     69 
     70  /**
     71   * Returns true if this image has been discarded and a decoded has not yet
     72   * been created to redecode it.
     73   */
     74  bool IsDiscarded() { return mDiscarded; }
     75 
     76  /**
     77   * Sets the composited frame as valid or invalid.
     78   */
     79  void SetCompositedFrameInvalid(bool aInvalid) {
     80    MOZ_ASSERT(!aInvalid ||
     81               StaticPrefs::image_mem_animated_discardable_AtStartup());
     82    mCompositedFrameInvalid = aInvalid;
     83  }
     84 
     85  /**
     86   * Returns whether the composited frame is valid to draw to the screen.
     87   */
     88  bool GetCompositedFrameInvalid() { return mCompositedFrameInvalid; }
     89 
     90  /**
     91   * Returns whether the image is currently full decoded..
     92   */
     93  bool GetIsCurrentlyDecoded() { return mIsCurrentlyDecoded; }
     94 
     95  /**
     96   * Call when you need to re-start animating. Ensures we start from the first
     97   * frame.
     98   */
     99  void ResetAnimation();
    100 
    101  /**
    102   * The animation mode of the image.
    103   *
    104   * Constants defined in imgIContainer.idl.
    105   */
    106  void SetAnimationMode(uint16_t aAnimationMode);
    107 
    108  /// Update the number of frames of animation this image is known to have.
    109  void UpdateKnownFrameCount(uint32_t aFrameCount);
    110 
    111  /// @return the number of frames of animation we know about so far.
    112  uint32_t KnownFrameCount() const { return mFrameCount; }
    113 
    114  /// @return the number of frames this animation has, if we know for sure.
    115  /// (In other words, if decoding is finished.) Otherwise, returns Nothing().
    116  Maybe<uint32_t> FrameCount() const;
    117 
    118  /**
    119   * Get or set the area of the image to invalidate when we loop around to the
    120   * first frame.
    121   */
    122  void SetFirstFrameRefreshArea(const gfx::IntRect& aRefreshArea);
    123  gfx::IntRect FirstFrameRefreshArea() const { return mFirstFrameRefreshArea; }
    124 
    125  /**
    126   * If the animation frame time has not yet been set, set it to
    127   * TimeStamp::Now().
    128   */
    129  void InitAnimationFrameTimeIfNecessary();
    130 
    131  /**
    132   * Set the animation frame time to @aTime.
    133   */
    134  void SetAnimationFrameTime(const TimeStamp& aTime);
    135 
    136  /**
    137   * Set the animation frame time to @aTime if we are configured to stop the
    138   * animation when not visible and aTime is later than the current time.
    139   * Returns true if the time was updated, else false.
    140   */
    141  bool MaybeAdvanceAnimationFrameTime(const TimeStamp& aTime);
    142 
    143  /**
    144   * The current frame we're on, from 0 to (numFrames - 1).
    145   */
    146  uint32_t GetCurrentAnimationFrameIndex() const;
    147 
    148  /*
    149   * Set number of times to loop the image.
    150   * @note -1 means loop forever.
    151   */
    152  void SetLoopCount(int32_t aLoopCount) { mLoopCount = aLoopCount; }
    153  int32_t LoopCount() const { return mLoopCount; }
    154 
    155  /// Set the @aLength of a single loop through this image.
    156  void SetLoopLength(FrameTimeout aLength) { mLoopLength = Some(aLength); }
    157 
    158  /**
    159   * @return the length of a single loop of this image. If this image is not
    160   * finished decoding, is not animated, or it is animated but does not loop,
    161   * returns FrameTimeout::Forever().
    162   */
    163  FrameTimeout LoopLength() const;
    164 
    165  /*
    166   * Get or set the timeout for the first frame. This is used to allow animation
    167   * scheduling even before a full decode runs for this image.
    168   */
    169  void SetFirstFrameTimeout(FrameTimeout aTimeout) {
    170    mFirstFrameTimeout = aTimeout;
    171  }
    172  FrameTimeout FirstFrameTimeout() const { return mFirstFrameTimeout; }
    173 
    174 private:
    175  friend class FrameAnimator;
    176 
    177  //! Area of the first frame that needs to be redrawn on subsequent loops.
    178  gfx::IntRect mFirstFrameRefreshArea;
    179 
    180  //! the time that the animation advanced to the current frame
    181  TimeStamp mCurrentAnimationFrameTime;
    182 
    183  //! The number of frames of animation this image has.
    184  uint32_t mFrameCount;
    185 
    186  //! The current frame index we're on, in the range [0, mFrameCount).
    187  uint32_t mCurrentAnimationFrameIndex;
    188 
    189  //! number of loops remaining before animation stops (-1 no stop)
    190  int32_t mLoopRemainingCount;
    191 
    192  //! The total number of loops for the image.
    193  int32_t mLoopCount;
    194 
    195  //! The length of a single loop through this image.
    196  Maybe<FrameTimeout> mLoopLength;
    197 
    198  //! The timeout for the first frame of this image.
    199  FrameTimeout mFirstFrameTimeout;
    200 
    201  //! The animation mode of this image. Constants defined in imgIContainer.
    202  uint16_t mAnimationMode;
    203 
    204  /**
    205   * The following four bools (mHasBeenDecoded, mIsCurrentlyDecoded,
    206   * mCompositedFrameInvalid, mDiscarded) track the state of the image with
    207   * regards to decoding. They all start out false, including mDiscarded,
    208   * because we want to treat being discarded differently from "not yet decoded
    209   * for the first time".
    210   *
    211   * (When we are decoding the image for the first time we want to show the
    212   * image at the speed of data coming in from the network or the speed
    213   * specified in the image file, whichever is slower. But when redecoding we
    214   * want to show nothing until the frame for the current time has been
    215   * decoded. The prevents the user from seeing the image "fast forward"
    216   * to the expected spot.)
    217   *
    218   * When the image is decoded for the first time mHasBeenDecoded and
    219   * mIsCurrentlyDecoded get set to true. When the image is discarded
    220   * mIsCurrentlyDecoded gets set to false, and mCompositedFrameInvalid
    221   * & mDiscarded get set to true. When we create a decoder to redecode the
    222   * image mDiscarded gets set to false. mCompositedFrameInvalid gets set to
    223   * false when we are able to advance to the frame that should be showing
    224   * for the current time. mIsCurrentlyDecoded gets set to true when the
    225   * redecode finishes.
    226   */
    227 
    228  //! Whether this image has been decoded at least once.
    229  bool mHasBeenDecoded;
    230 
    231  //! Whether this image has ever requested a decode.
    232  bool mHasRequestedDecode;
    233 
    234  //! Whether this image is currently fully decoded.
    235  bool mIsCurrentlyDecoded;
    236 
    237  //! Whether the composited frame is valid to draw to the screen, note that
    238  //! the composited frame can exist and be filled with image data but not
    239  //! valid to draw to the screen.
    240  bool mCompositedFrameInvalid;
    241 
    242  //! Whether this image is currently discarded. Only set to true after the
    243  //! image has been decoded at least once.
    244  bool mDiscarded;
    245 };
    246 
    247 /**
    248 * RefreshResult is used to let callers know how the state of the animation
    249 * changed during a call to FrameAnimator::RequestRefresh().
    250 */
    251 struct RefreshResult {
    252  RefreshResult() : mFrameAdvanced(false), mAnimationFinished(false) {}
    253 
    254  /// Merges another RefreshResult's changes into this RefreshResult.
    255  void Accumulate(const RefreshResult& aOther) {
    256    mFrameAdvanced = mFrameAdvanced || aOther.mFrameAdvanced;
    257    mAnimationFinished = mAnimationFinished || aOther.mAnimationFinished;
    258    mDirtyRect = mDirtyRect.Union(aOther.mDirtyRect);
    259  }
    260 
    261  // The region of the image that has changed.
    262  gfx::IntRect mDirtyRect;
    263 
    264  // If true, we changed frames at least once. Note that, due to looping, we
    265  // could still have ended up on the same frame!
    266  bool mFrameAdvanced : 1;
    267 
    268  // Whether the animation has finished playing.
    269  bool mAnimationFinished : 1;
    270 };
    271 
    272 class FrameAnimator {
    273 public:
    274  FrameAnimator(RasterImage* aImage, const gfx::IntSize& aSize)
    275      : mImage(aImage), mSize(aSize) {
    276    MOZ_COUNT_CTOR(FrameAnimator);
    277  }
    278 
    279  MOZ_COUNTED_DTOR(FrameAnimator)
    280 
    281  /**
    282   * Call when you need to re-start animating. Ensures we start from the first
    283   * frame.
    284   */
    285  void ResetAnimation(AnimationState& aState);
    286 
    287  /**
    288   * Re-evaluate what frame we're supposed to be on, and do whatever blending
    289   * is necessary to get us to that frame.
    290   *
    291   * Returns the result of that blending, including whether the current frame
    292   * changed and what the resulting dirty rectangle is.
    293   */
    294  RefreshResult RequestRefresh(AnimationState& aState, const TimeStamp& aTime);
    295 
    296  /**
    297   * Get the full frame for the current frame of the animation (it may or may
    298   * not have required compositing). It may not be available because it hasn't
    299   * been decoded yet, in which case we return an empty LookupResult.
    300   */
    301  LookupResult GetCompositedFrame(AnimationState& aState, bool aMarkUsed);
    302 
    303 private:  // methods
    304  /**
    305   * Advances the animation. Typically, this will advance a single frame, but it
    306   * may advance multiple frames. This may happen if we have infrequently
    307   * "ticking" refresh drivers (e.g. in background tabs), or extremely short-
    308   * lived animation frames.
    309   *
    310   * @param aTime the time that the animation should advance to. This will
    311   *              typically be <= TimeStamp::Now().
    312   *
    313   * @param aCurrentFrame the currently displayed frame of the animation. If
    314   *                      we advance, it will replace aCurrentFrame with the
    315   *                      new current frame we advanced to.
    316   *
    317   * @returns a RefreshResult that shows whether the frame was successfully
    318   *          advanced, and its resulting dirty rect.
    319   */
    320  RefreshResult AdvanceFrame(AnimationState& aState, DrawableSurface& aFrames,
    321                             RefPtr<imgFrame>& aCurrentFrame, TimeStamp aTime);
    322 
    323  /**
    324   * Get the time the frame we're currently displaying is supposed to end.
    325   *
    326   * In the error case (like if the requested frame is not currently
    327   * decoded), returns None().
    328   */
    329  TimeStamp GetCurrentImgFrameEndTime(AnimationState& aState,
    330                                      FrameTimeout aCurrentTimeout) const;
    331 
    332 private:  // data
    333  //! A weak pointer to our owning image.
    334  RasterImage* mImage;
    335 
    336  //! The intrinsic size of the image.
    337  gfx::IntSize mSize;
    338 };
    339 
    340 }  // namespace image
    341 }  // namespace mozilla
    342 
    343 #endif  // mozilla_image_FrameAnimator_h