Blame image/FrameAnimator.h

Packit f0b94e
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
Packit f0b94e
 *
Packit f0b94e
 * This Source Code Form is subject to the terms of the Mozilla Public
Packit f0b94e
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit f0b94e
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit f0b94e
Packit f0b94e
#ifndef mozilla_image_FrameAnimator_h
Packit f0b94e
#define mozilla_image_FrameAnimator_h
Packit f0b94e
Packit f0b94e
#include "mozilla/Maybe.h"
Packit f0b94e
#include "mozilla/MemoryReporting.h"
Packit f0b94e
#include "mozilla/TimeStamp.h"
Packit f0b94e
#include "gfxTypes.h"
Packit f0b94e
#include "imgFrame.h"
Packit f0b94e
#include "nsCOMPtr.h"
Packit f0b94e
#include "nsRect.h"
Packit f0b94e
#include "SurfaceCache.h"
Packit f0b94e
#include "gfxPrefs.h"
Packit f0b94e
Packit f0b94e
namespace mozilla {
Packit f0b94e
namespace image {
Packit f0b94e
Packit f0b94e
class RasterImage;
Packit f0b94e
class DrawableSurface;
Packit f0b94e
Packit f0b94e
class AnimationState {
Packit f0b94e
 public:
Packit f0b94e
  explicit AnimationState(uint16_t aAnimationMode)
Packit f0b94e
      : mFrameCount(0),
Packit f0b94e
        mCurrentAnimationFrameIndex(0),
Packit f0b94e
        mLoopRemainingCount(-1),
Packit f0b94e
        mLoopCount(-1),
Packit f0b94e
        mFirstFrameTimeout(FrameTimeout::FromRawMilliseconds(0)),
Packit f0b94e
        mAnimationMode(aAnimationMode),
Packit f0b94e
        mHasBeenDecoded(false),
Packit f0b94e
        mHasRequestedDecode(false),
Packit f0b94e
        mIsCurrentlyDecoded(false),
Packit f0b94e
        mCompositedFrameInvalid(false),
Packit f0b94e
        mDiscarded(false) {}
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Call this whenever a decode completes, a decode starts, or the image is
Packit f0b94e
   * discarded. It will update the internal state. Specifically mDiscarded,
Packit f0b94e
   * mCompositedFrameInvalid, and mIsCurrentlyDecoded. If aAllowInvalidation
Packit f0b94e
   * is true then returns a rect to invalidate.
Packit f0b94e
   */
Packit f0b94e
  const gfx::IntRect UpdateState(bool aAnimationFinished, RasterImage* aImage,
Packit f0b94e
                                 const gfx::IntSize& aSize,
Packit f0b94e
                                 bool aAllowInvalidation = true);
Packit f0b94e
Packit f0b94e
 private:
Packit f0b94e
  const gfx::IntRect UpdateStateInternal(LookupResult& aResult,
Packit f0b94e
                                         bool aAnimationFinished,
Packit f0b94e
                                         const gfx::IntSize& aSize,
Packit f0b94e
                                         bool aAllowInvalidation = true);
Packit f0b94e
Packit f0b94e
 public:
Packit f0b94e
  /**
Packit f0b94e
   * Call when a decode of this image has been completed.
Packit f0b94e
   */
Packit f0b94e
  void NotifyDecodeComplete();
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Returns true if this image has been fully decoded before.
Packit f0b94e
   */
Packit f0b94e
  bool GetHasBeenDecoded() { return mHasBeenDecoded; }
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Returns true if this image has ever requested a decode before.
Packit f0b94e
   */
Packit f0b94e
  bool GetHasRequestedDecode() { return mHasRequestedDecode; }
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Returns true if this image has been discarded and a decoded has not yet
Packit f0b94e
   * been created to redecode it.
Packit f0b94e
   */
Packit f0b94e
  bool IsDiscarded() { return mDiscarded; }
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Sets the composited frame as valid or invalid.
Packit f0b94e
   */
Packit f0b94e
  void SetCompositedFrameInvalid(bool aInvalid) {
Packit f0b94e
    MOZ_ASSERT(!aInvalid || gfxPrefs::ImageMemAnimatedDiscardable());
Packit f0b94e
    mCompositedFrameInvalid = aInvalid;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Returns whether the composited frame is valid to draw to the screen.
Packit f0b94e
   */
Packit f0b94e
  bool GetCompositedFrameInvalid() { return mCompositedFrameInvalid; }
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Returns whether the image is currently full decoded..
Packit f0b94e
   */
Packit f0b94e
  bool GetIsCurrentlyDecoded() { return mIsCurrentlyDecoded; }
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Call when you need to re-start animating. Ensures we start from the first
Packit f0b94e
   * frame.
Packit f0b94e
   */
Packit f0b94e
  void ResetAnimation();
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * The animation mode of the image.
Packit f0b94e
   *
Packit f0b94e
   * Constants defined in imgIContainer.idl.
Packit f0b94e
   */
Packit f0b94e
  void SetAnimationMode(uint16_t aAnimationMode);
Packit f0b94e
Packit f0b94e
  /// Update the number of frames of animation this image is known to have.
Packit f0b94e
  void UpdateKnownFrameCount(uint32_t aFrameCount);
Packit f0b94e
Packit f0b94e
  /// @return the number of frames of animation we know about so far.
Packit f0b94e
  uint32_t KnownFrameCount() const { return mFrameCount; }
Packit f0b94e
Packit f0b94e
  /// @return the number of frames this animation has, if we know for sure.
Packit f0b94e
  /// (In other words, if decoding is finished.) Otherwise, returns Nothing().
Packit f0b94e
  Maybe<uint32_t> FrameCount() const;
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Get or set the area of the image to invalidate when we loop around to the
Packit f0b94e
   * first frame.
Packit f0b94e
   */
Packit f0b94e
  void SetFirstFrameRefreshArea(const gfx::IntRect& aRefreshArea);
Packit f0b94e
  gfx::IntRect FirstFrameRefreshArea() const { return mFirstFrameRefreshArea; }
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * If the animation frame time has not yet been set, set it to
Packit f0b94e
   * TimeStamp::Now().
Packit f0b94e
   */
Packit f0b94e
  void InitAnimationFrameTimeIfNecessary();
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Set the animation frame time to @aTime.
Packit f0b94e
   */
Packit f0b94e
  void SetAnimationFrameTime(const TimeStamp& aTime);
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * The current frame we're on, from 0 to (numFrames - 1).
Packit f0b94e
   */
Packit f0b94e
  uint32_t GetCurrentAnimationFrameIndex() const;
Packit f0b94e
Packit f0b94e
  /*
Packit f0b94e
   * Set number of times to loop the image.
Packit f0b94e
   * @note -1 means loop forever.
Packit f0b94e
   */
Packit f0b94e
  void SetLoopCount(int32_t aLoopCount) { mLoopCount = aLoopCount; }
Packit f0b94e
  int32_t LoopCount() const { return mLoopCount; }
Packit f0b94e
Packit f0b94e
  /// Set the @aLength of a single loop through this image.
Packit f0b94e
  void SetLoopLength(FrameTimeout aLength) { mLoopLength = Some(aLength); }
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * @return the length of a single loop of this image. If this image is not
Packit f0b94e
   * finished decoding, is not animated, or it is animated but does not loop,
Packit f0b94e
   * returns FrameTimeout::Forever().
Packit f0b94e
   */
Packit f0b94e
  FrameTimeout LoopLength() const;
Packit f0b94e
Packit f0b94e
  /*
Packit f0b94e
   * Get or set the timeout for the first frame. This is used to allow animation
Packit f0b94e
   * scheduling even before a full decode runs for this image.
Packit f0b94e
   */
Packit f0b94e
  void SetFirstFrameTimeout(FrameTimeout aTimeout) {
Packit f0b94e
    mFirstFrameTimeout = aTimeout;
Packit f0b94e
  }
Packit f0b94e
  FrameTimeout FirstFrameTimeout() const { return mFirstFrameTimeout; }
Packit f0b94e
Packit f0b94e
 private:
Packit f0b94e
  friend class FrameAnimator;
Packit f0b94e
Packit f0b94e
  //! Area of the first frame that needs to be redrawn on subsequent loops.
Packit f0b94e
  gfx::IntRect mFirstFrameRefreshArea;
Packit f0b94e
Packit f0b94e
  //! the time that the animation advanced to the current frame
Packit f0b94e
  TimeStamp mCurrentAnimationFrameTime;
Packit f0b94e
Packit f0b94e
  //! The number of frames of animation this image has.
Packit f0b94e
  uint32_t mFrameCount;
Packit f0b94e
Packit f0b94e
  //! The current frame index we're on, in the range [0, mFrameCount).
Packit f0b94e
  uint32_t mCurrentAnimationFrameIndex;
Packit f0b94e
Packit f0b94e
  //! number of loops remaining before animation stops (-1 no stop)
Packit f0b94e
  int32_t mLoopRemainingCount;
Packit f0b94e
Packit f0b94e
  //! The total number of loops for the image.
Packit f0b94e
  int32_t mLoopCount;
Packit f0b94e
Packit f0b94e
  //! The length of a single loop through this image.
Packit f0b94e
  Maybe<FrameTimeout> mLoopLength;
Packit f0b94e
Packit f0b94e
  //! The timeout for the first frame of this image.
Packit f0b94e
  FrameTimeout mFirstFrameTimeout;
Packit f0b94e
Packit f0b94e
  //! The animation mode of this image. Constants defined in imgIContainer.
Packit f0b94e
  uint16_t mAnimationMode;
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * The following four bools (mHasBeenDecoded, mIsCurrentlyDecoded,
Packit f0b94e
   * mCompositedFrameInvalid, mDiscarded) track the state of the image with
Packit f0b94e
   * regards to decoding. They all start out false, including mDiscarded,
Packit f0b94e
   * because we want to treat being discarded differently from "not yet decoded
Packit f0b94e
   * for the first time".
Packit f0b94e
   *
Packit f0b94e
   * (When we are decoding the image for the first time we want to show the
Packit f0b94e
   * image at the speed of data coming in from the network or the speed
Packit f0b94e
   * specified in the image file, whichever is slower. But when redecoding we
Packit f0b94e
   * want to show nothing until the frame for the current time has been
Packit f0b94e
   * decoded. The prevents the user from seeing the image "fast forward"
Packit f0b94e
   * to the expected spot.)
Packit f0b94e
   *
Packit f0b94e
   * When the image is decoded for the first time mHasBeenDecoded and
Packit f0b94e
   * mIsCurrentlyDecoded get set to true. When the image is discarded
Packit f0b94e
   * mIsCurrentlyDecoded gets set to false, and mCompositedFrameInvalid
Packit f0b94e
   * & mDiscarded get set to true. When we create a decoder to redecode the
Packit f0b94e
   * image mDiscarded gets set to false. mCompositedFrameInvalid gets set to
Packit f0b94e
   * false when we are able to advance to the frame that should be showing
Packit f0b94e
   * for the current time. mIsCurrentlyDecoded gets set to true when the
Packit f0b94e
   * redecode finishes.
Packit f0b94e
   */
Packit f0b94e
Packit f0b94e
  //! Whether this image has been decoded at least once.
Packit f0b94e
  bool mHasBeenDecoded;
Packit f0b94e
Packit f0b94e
  //! Whether this image has ever requested a decode.
Packit f0b94e
  bool mHasRequestedDecode;
Packit f0b94e
Packit f0b94e
  //! Whether this image is currently fully decoded.
Packit f0b94e
  bool mIsCurrentlyDecoded;
Packit f0b94e
Packit f0b94e
  //! Whether the composited frame is valid to draw to the screen, note that
Packit f0b94e
  //! the composited frame can exist and be filled with image data but not
Packit f0b94e
  //! valid to draw to the screen.
Packit f0b94e
  bool mCompositedFrameInvalid;
Packit f0b94e
Packit f0b94e
  //! Whether this image is currently discarded. Only set to true after the
Packit f0b94e
  //! image has been decoded at least once.
Packit f0b94e
  bool mDiscarded;
Packit f0b94e
};
Packit f0b94e
Packit f0b94e
/**
Packit f0b94e
 * RefreshResult is used to let callers know how the state of the animation
Packit f0b94e
 * changed during a call to FrameAnimator::RequestRefresh().
Packit f0b94e
 */
Packit f0b94e
struct RefreshResult {
Packit f0b94e
  RefreshResult() : mFrameAdvanced(false), mAnimationFinished(false) {}
Packit f0b94e
Packit f0b94e
  /// Merges another RefreshResult's changes into this RefreshResult.
Packit f0b94e
  void Accumulate(const RefreshResult& aOther) {
Packit f0b94e
    mFrameAdvanced = mFrameAdvanced || aOther.mFrameAdvanced;
Packit f0b94e
    mAnimationFinished = mAnimationFinished || aOther.mAnimationFinished;
Packit f0b94e
    mDirtyRect = mDirtyRect.Union(aOther.mDirtyRect);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // The region of the image that has changed.
Packit f0b94e
  gfx::IntRect mDirtyRect;
Packit f0b94e
Packit f0b94e
  // If true, we changed frames at least once. Note that, due to looping, we
Packit f0b94e
  // could still have ended up on the same frame!
Packit f0b94e
  bool mFrameAdvanced : 1;
Packit f0b94e
Packit f0b94e
  // Whether the animation has finished playing.
Packit f0b94e
  bool mAnimationFinished : 1;
Packit f0b94e
};
Packit f0b94e
Packit f0b94e
class FrameAnimator {
Packit f0b94e
 public:
Packit f0b94e
  FrameAnimator(RasterImage* aImage, const gfx::IntSize& aSize)
Packit f0b94e
      : mImage(aImage), mSize(aSize), mLastCompositedFrameIndex(-1) {
Packit f0b94e
    MOZ_COUNT_CTOR(FrameAnimator);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  ~FrameAnimator() { MOZ_COUNT_DTOR(FrameAnimator); }
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Call when you need to re-start animating. Ensures we start from the first
Packit f0b94e
   * frame.
Packit f0b94e
   */
Packit f0b94e
  void ResetAnimation(AnimationState& aState);
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Re-evaluate what frame we're supposed to be on, and do whatever blending
Packit f0b94e
   * is necessary to get us to that frame.
Packit f0b94e
   *
Packit f0b94e
   * Returns the result of that blending, including whether the current frame
Packit f0b94e
   * changed and what the resulting dirty rectangle is.
Packit f0b94e
   */
Packit f0b94e
  RefreshResult RequestRefresh(AnimationState& aState, const TimeStamp& aTime,
Packit f0b94e
                               bool aAnimationFinished);
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Get the full frame for the current frame of the animation (it may or may
Packit f0b94e
   * not have required compositing). It may not be available because it hasn't
Packit f0b94e
   * been decoded yet, in which case we return an empty LookupResult.
Packit f0b94e
   */
Packit f0b94e
  LookupResult GetCompositedFrame(AnimationState& aState);
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Collect an accounting of the memory occupied by the compositing surfaces we
Packit f0b94e
   * use during animation playback. All of the actual animation frames are
Packit f0b94e
   * stored in the SurfaceCache, so we don't need to report them here.
Packit f0b94e
   */
Packit f0b94e
  void CollectSizeOfCompositingSurfaces(
Packit f0b94e
      nsTArray<SurfaceMemoryCounter>& aCounters,
Packit f0b94e
      MallocSizeOf aMallocSizeOf) const;
Packit f0b94e
Packit f0b94e
 private:  // methods
Packit f0b94e
  /**
Packit f0b94e
   * Advances the animation. Typically, this will advance a single frame, but it
Packit f0b94e
   * may advance multiple frames. This may happen if we have infrequently
Packit f0b94e
   * "ticking" refresh drivers (e.g. in background tabs), or extremely short-
Packit f0b94e
   * lived animation frames.
Packit f0b94e
   *
Packit f0b94e
   * @param aTime the time that the animation should advance to. This will
Packit f0b94e
   *              typically be <= TimeStamp::Now().
Packit f0b94e
   *
Packit f0b94e
   * @returns a RefreshResult that shows whether the frame was successfully
Packit f0b94e
   *          advanced, and its resulting dirty rect.
Packit f0b94e
   */
Packit f0b94e
  RefreshResult AdvanceFrame(AnimationState& aState, DrawableSurface& aFrames,
Packit f0b94e
                             TimeStamp aTime);
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Get the @aIndex-th frame in the frame index, ignoring results of blending.
Packit f0b94e
   */
Packit f0b94e
  RawAccessFrameRef GetRawFrame(DrawableSurface& aFrames,
Packit f0b94e
                                uint32_t aFrameNum) const;
Packit f0b94e
Packit f0b94e
  /// @return the given frame's timeout if it is available
Packit f0b94e
  Maybe<FrameTimeout> GetTimeoutForFrame(AnimationState& aState,
Packit f0b94e
                                         DrawableSurface& aFrames,
Packit f0b94e
                                         uint32_t aFrameNum) const;
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Get the time the frame we're currently displaying is supposed to end.
Packit f0b94e
   *
Packit f0b94e
   * In the error case (like if the requested frame is not currently
Packit f0b94e
   * decoded), returns None().
Packit f0b94e
   */
Packit f0b94e
  Maybe<TimeStamp> GetCurrentImgFrameEndTime(AnimationState& aState,
Packit f0b94e
                                             DrawableSurface& aFrames) const;
Packit f0b94e
Packit f0b94e
  bool DoBlend(DrawableSurface& aFrames, gfx::IntRect* aDirtyRect,
Packit f0b94e
               uint32_t aPrevFrameIndex, uint32_t aNextFrameIndex);
Packit f0b94e
Packit f0b94e
  /** Clears an area of <aFrame> with transparent black.
Packit f0b94e
   *
Packit f0b94e
   * @param aFrameData Target Frame data
Packit f0b94e
   * @param aFrameRect The rectangle of the data pointed ot by aFrameData
Packit f0b94e
   *
Packit f0b94e
   * @note Does also clears the transparency mask
Packit f0b94e
   */
Packit f0b94e
  static void ClearFrame(uint8_t* aFrameData, const gfx::IntRect& aFrameRect);
Packit f0b94e
Packit f0b94e
  //! @overload
Packit f0b94e
  static void ClearFrame(uint8_t* aFrameData, const gfx::IntRect& aFrameRect,
Packit f0b94e
                         const gfx::IntRect& aRectToClear);
Packit f0b94e
Packit f0b94e
  //! Copy one frame's image and mask into another
Packit f0b94e
  static bool CopyFrameImage(const uint8_t* aDataSrc,
Packit f0b94e
                             const gfx::IntRect& aRectSrc, uint8_t* aDataDest,
Packit f0b94e
                             const gfx::IntRect& aRectDest);
Packit f0b94e
Packit f0b94e
  /**
Packit f0b94e
   * Draws one frame's image to into another, at the position specified by
Packit f0b94e
   * aSrcRect.
Packit f0b94e
   *
Packit f0b94e
   * @aSrcData the raw data of the current frame being drawn
Packit f0b94e
   * @aSrcRect the size of the source frame, and the position of that frame in
Packit f0b94e
   *           the composition frame
Packit f0b94e
   * @aSrcPaletteLength the length (in bytes) of the palette at the beginning
Packit f0b94e
   *                    of the source data (0 if image is not paletted)
Packit f0b94e
   * @aSrcHasAlpha whether the source data represents an image with alpha
Packit f0b94e
   * @aDstPixels the raw data of the composition frame where the current frame
Packit f0b94e
   *             is drawn into (32-bit ARGB)
Packit f0b94e
   * @aDstRect the size of the composition frame
Packit f0b94e
   * @aBlendMethod the blend method for how to blend src on the composition
Packit f0b94e
   * frame.
Packit f0b94e
   */
Packit f0b94e
  static nsresult DrawFrameTo(const uint8_t* aSrcData,
Packit f0b94e
                              const gfx::IntRect& aSrcRect,
Packit f0b94e
                              uint32_t aSrcPaletteLength, bool aSrcHasAlpha,
Packit f0b94e
                              uint8_t* aDstPixels, const gfx::IntRect& aDstRect,
Packit f0b94e
                              BlendMethod aBlendMethod,
Packit f0b94e
                              const Maybe<gfx::IntRect>& aBlendRect);
Packit f0b94e
Packit f0b94e
 private:  // data
Packit f0b94e
  //! A weak pointer to our owning image.
Packit f0b94e
  RasterImage* mImage;
Packit f0b94e
Packit f0b94e
  //! The intrinsic size of the image.
Packit f0b94e
  gfx::IntSize mSize;
Packit f0b94e
Packit f0b94e
  /** For managing blending of frames
Packit f0b94e
   *
Packit f0b94e
   * Some animations will use the compositingFrame to composite images
Packit f0b94e
   * and just hand this back to the caller when it is time to draw the frame.
Packit f0b94e
   * NOTE: When clearing compositingFrame, remember to set
Packit f0b94e
   *       lastCompositedFrameIndex to -1.  Code assume that if
Packit f0b94e
   *       lastCompositedFrameIndex >= 0 then compositingFrame exists.
Packit f0b94e
   */
Packit f0b94e
  RawAccessFrameRef mCompositingFrame;
Packit f0b94e
Packit f0b94e
  /** the previous composited frame, for DISPOSE_RESTORE_PREVIOUS
Packit f0b94e
   *
Packit f0b94e
   * The Previous Frame (all frames composited up to the current) needs to be
Packit f0b94e
   * stored in cases where the image specifies it wants the last frame back
Packit f0b94e
   * when it's done with the current frame.
Packit f0b94e
   */
Packit f0b94e
  RawAccessFrameRef mCompositingPrevFrame;
Packit f0b94e
Packit f0b94e
  //! Track the last composited frame for Optimizations (See DoComposite code)
Packit f0b94e
  int32_t mLastCompositedFrameIndex;
Packit f0b94e
};
Packit f0b94e
Packit f0b94e
}  // namespace image
Packit f0b94e
}  // namespace mozilla
Packit f0b94e
Packit f0b94e
#endif  // mozilla_image_FrameAnimator_h