skia2/include/android/SkAnimatedImage.h

180 lines
5.3 KiB
C
Raw Normal View History

/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkAnimatedImage_DEFINED
#define SkAnimatedImage_DEFINED
#include "include/codec/SkCodecAnimation.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkDrawable.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkRect.h"
class SkAndroidCodec;
class SkImage;
class SkPicture;
/**
* Thread unsafe drawable for drawing animated images (e.g. GIF).
*/
class SK_API SkAnimatedImage : public SkDrawable {
public:
/**
* Create an SkAnimatedImage from the SkAndroidCodec.
*
* Returns null on failure to allocate pixels. On success, this will
* decode the first frame.
*
* @param info Width and height may require scaling.
* @param cropRect Rectangle to crop to after scaling.
* @param postProcess Picture to apply after scaling and cropping.
*/
static sk_sp<SkAnimatedImage> Make(std::unique_ptr<SkAndroidCodec>,
const SkImageInfo& info, SkIRect cropRect, sk_sp<SkPicture> postProcess);
/**
* Simpler version that uses the default size, no cropping, and no postProcess.
*/
static sk_sp<SkAnimatedImage> Make(std::unique_ptr<SkAndroidCodec>);
~SkAnimatedImage() override;
/**
* Reset the animation to the beginning.
*/
void reset();
/**
* Whether the animation completed.
*
* Returns true after all repetitions are complete, or an error stops the
* animation. Gets reset to false if the animation is restarted.
*/
bool isFinished() const { return fFinished; }
/**
* Returned by decodeNextFrame and currentFrameDuration if the animation
* is not running.
*/
static constexpr int kFinished = -1;
/**
* Decode the next frame.
*
* If the animation is on the last frame or has hit an error, returns
* kFinished.
*/
int decodeNextFrame();
/**
* Returns the current frame as an SkImage. The SkImage will not change
* after it has been returned.
* If there is no current frame, nullptr will be returned.
*/
sk_sp<SkImage> getCurrentFrame();
/**
* How long to display the current frame.
*
* Useful for the first frame, for which decodeNextFrame is called
* internally.
*/
int currentFrameDuration() {
return fCurrentFrameDuration;
}
/**
* Change the repetition count.
*
* By default, the image will repeat the number of times indicated in the
* encoded data.
*
* Use SkCodec::kRepetitionCountInfinite for infinite, and 0 to show all
* frames once and then stop.
*/
void setRepetitionCount(int count);
/**
* Return the currently set repetition count.
*/
int getRepetitionCount() const {
return fRepetitionCount;
}
/**
* Return the total number of frames in the animation.
*/
int getFrameCount() const { return fFrameCount; }
protected:
SkRect onGetBounds() override;
void onDraw(SkCanvas*) override;
private:
struct Frame {
SkBitmap fBitmap;
int fIndex;
SkCodecAnimation::DisposalMethod fDisposalMethod;
// init() may have to create a new SkPixelRef, if the
// current one is already in use by another owner (e.g.
// an SkPicture). This determines whether to copy the
// existing one to the new one.
enum class OnInit {
// Restore the image from the old SkPixelRef to the
// new one.
kRestoreIfNecessary,
// No need to restore.
kNoRestore,
};
Frame();
bool init(const SkImageInfo& info, OnInit);
bool copyTo(Frame*) const;
};
std::unique_ptr<SkAndroidCodec> fCodec;
Handle EXIF orientation in SkAnimatedImage Bug: skia:10914 Bug: b/163595585 In a WebP image, it is possible to combine animation with an EXIF orientation. While SkAndroidCodec attempts to handle orientation itself by decoding into temporary memory and then drawing through a matrix, this doesn't work directly when compositing a P-frame into a prior frame. SkAnimatedImage already uses an SkMatrix to handle cropping and scaling, so update that matrix to include the orientation. Make SkAnimatedImage a friend of SkAndroidCodec. This allows the former to have the same ExifOrientationBehavior specified by the latter, and to recreate the latter so it does not try to handle the orientation itself. Clip SkAnimatedImage to its bounds. Android's AnimatedImageDrawable performs its own clip, but this makes a crop rect work for other clients. Update getCurrentFrame to take cropping, scaling, and orientation into account. This method is used by CanvasKit, which does not use cropping or scaling, but will now properly orient an animation with an EXIF orientation. Add a GM that exercises the various combinations of ways SkAnimatedImage can be used: - via newPictureSnapshot (as in Android) versus getCurrentFrame - scaling down to a dimension that can be output from the SkAndroidCodec, versus up, which SkAnimatedImage scales - with a crop rect - with a post processor Change-Id: If1854e9aea23fc4afddf75d39132b38e3fbc6071 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/333223 Commit-Queue: Leon Scroggins <scroggo@google.com> Reviewed-by: Derek Sollenberger <djsollen@google.com>
2020-11-06 22:05:36 +00:00
SkImageInfo fDecodeInfo;
const SkIRect fCropRect;
const sk_sp<SkPicture> fPostProcess;
const int fFrameCount;
Handle EXIF orientation in SkAnimatedImage Bug: skia:10914 Bug: b/163595585 In a WebP image, it is possible to combine animation with an EXIF orientation. While SkAndroidCodec attempts to handle orientation itself by decoding into temporary memory and then drawing through a matrix, this doesn't work directly when compositing a P-frame into a prior frame. SkAnimatedImage already uses an SkMatrix to handle cropping and scaling, so update that matrix to include the orientation. Make SkAnimatedImage a friend of SkAndroidCodec. This allows the former to have the same ExifOrientationBehavior specified by the latter, and to recreate the latter so it does not try to handle the orientation itself. Clip SkAnimatedImage to its bounds. Android's AnimatedImageDrawable performs its own clip, but this makes a crop rect work for other clients. Update getCurrentFrame to take cropping, scaling, and orientation into account. This method is used by CanvasKit, which does not use cropping or scaling, but will now properly orient an animation with an EXIF orientation. Add a GM that exercises the various combinations of ways SkAnimatedImage can be used: - via newPictureSnapshot (as in Android) versus getCurrentFrame - scaling down to a dimension that can be output from the SkAndroidCodec, versus up, which SkAnimatedImage scales - with a crop rect - with a post processor Change-Id: If1854e9aea23fc4afddf75d39132b38e3fbc6071 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/333223 Commit-Queue: Leon Scroggins <scroggo@google.com> Reviewed-by: Derek Sollenberger <djsollen@google.com>
2020-11-06 22:05:36 +00:00
SkMatrix fMatrix;
int fSampleSize;
bool fFinished;
int fCurrentFrameDuration;
Frame fDisplayFrame;
Frame fDecodingFrame;
Frame fRestoreFrame;
int fRepetitionCount;
int fRepetitionsCompleted;
SkAnimatedImage(std::unique_ptr<SkAndroidCodec>, const SkImageInfo& requestedInfo,
SkIRect cropRect, sk_sp<SkPicture> postProcess);
int computeNextFrame(int current, bool* animationEnded);
double finish();
Handle EXIF orientation in SkAnimatedImage Bug: skia:10914 Bug: b/163595585 In a WebP image, it is possible to combine animation with an EXIF orientation. While SkAndroidCodec attempts to handle orientation itself by decoding into temporary memory and then drawing through a matrix, this doesn't work directly when compositing a P-frame into a prior frame. SkAnimatedImage already uses an SkMatrix to handle cropping and scaling, so update that matrix to include the orientation. Make SkAnimatedImage a friend of SkAndroidCodec. This allows the former to have the same ExifOrientationBehavior specified by the latter, and to recreate the latter so it does not try to handle the orientation itself. Clip SkAnimatedImage to its bounds. Android's AnimatedImageDrawable performs its own clip, but this makes a crop rect work for other clients. Update getCurrentFrame to take cropping, scaling, and orientation into account. This method is used by CanvasKit, which does not use cropping or scaling, but will now properly orient an animation with an EXIF orientation. Add a GM that exercises the various combinations of ways SkAnimatedImage can be used: - via newPictureSnapshot (as in Android) versus getCurrentFrame - scaling down to a dimension that can be output from the SkAndroidCodec, versus up, which SkAnimatedImage scales - with a crop rect - with a post processor Change-Id: If1854e9aea23fc4afddf75d39132b38e3fbc6071 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/333223 Commit-Queue: Leon Scroggins <scroggo@google.com> Reviewed-by: Derek Sollenberger <djsollen@google.com>
2020-11-06 22:05:36 +00:00
/**
* True if there is no crop, orientation, or post decoding scaling.
*/
bool simple() const { return fMatrix.isIdentity() && !fPostProcess
&& fCropRect == fDecodeInfo.bounds(); }
/**
* Returns the current frame as an SkImage.
*
* Like getCurrentFrame, but only returns the raw data from the internal SkBitmap. (i.e. no
* scaling, orientation-correction or cropping.) If simple(), this is the final output.
*/
sk_sp<SkImage> getCurrentFrameSimple();
using INHERITED = SkDrawable;
};
#endif // SkAnimatedImage_DEFINED