Add Android ImageDecoder features to SkAnimatedImage
Bug: b/63909536 Bug: b/63908092 - Scale to an arbitrary size, using the decoding library if it supports it, and Skia otherwise - Crop to a subset - Post-processing with an SkPicture, to facilitate circle masks etc - isRunning, to implement Animatable2 interface in Java Change-Id: I13dbabee8e4a22e5cc193856aa3e94ce23ae4cb5 Reviewed-on: https://skia-review.googlesource.com/94660 Reviewed-by: Derek Sollenberger <djsollen@google.com> Commit-Queue: Leon Scroggins <scroggo@google.com>
This commit is contained in:
parent
9d8abc5816
commit
b1b7f70103
@ -11,8 +11,11 @@
|
||||
#include "SkBitmap.h"
|
||||
#include "SkCodecAnimation.h"
|
||||
#include "SkDrawable.h"
|
||||
#include "SkMatrix.h"
|
||||
#include "SkRect.h"
|
||||
|
||||
class SkAndroidCodec;
|
||||
class SkPicture;
|
||||
|
||||
/**
|
||||
* Thread unsafe drawable for drawing animated images (e.g. GIF).
|
||||
@ -24,6 +27,16 @@ public:
|
||||
*
|
||||
* Returns null on failure to allocate pixels. On success, this will
|
||||
* decode the first frame. It will not animate until start() is called.
|
||||
*
|
||||
* @param scaledSize Size to draw the image, possibly requiring 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>,
|
||||
SkISize scaledSize, 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>);
|
||||
|
||||
@ -46,6 +59,13 @@ public:
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Whether the animation is active.
|
||||
*
|
||||
* If true, update() can be called to animate.
|
||||
*/
|
||||
bool isRunning() const { return fRunning && !fFinished; }
|
||||
|
||||
/**
|
||||
* Update the current time. If the image is animating, this may decode
|
||||
* a new frame.
|
||||
@ -71,6 +91,13 @@ private:
|
||||
};
|
||||
|
||||
std::unique_ptr<SkAndroidCodec> fCodec;
|
||||
const SkISize fScaledSize;
|
||||
const SkImageInfo fDecodeInfo;
|
||||
const SkIRect fCropRect;
|
||||
const sk_sp<SkPicture> fPostProcess;
|
||||
const bool fSimple; // no crop, scale, or postprocess
|
||||
SkMatrix fMatrix; // used only if !fSimple
|
||||
|
||||
bool fFinished;
|
||||
bool fRunning;
|
||||
double fNowMS;
|
||||
@ -78,7 +105,8 @@ private:
|
||||
Frame fActiveFrame;
|
||||
Frame fRestoreFrame;
|
||||
|
||||
SkAnimatedImage(std::unique_ptr<SkAndroidCodec>);
|
||||
SkAnimatedImage(std::unique_ptr<SkAndroidCodec>, SkISize scaledSize,
|
||||
SkImageInfo decodeInfo, SkIRect cropRect, sk_sp<SkPicture> postProcess);
|
||||
|
||||
typedef SkDrawable INHERITED;
|
||||
};
|
||||
|
@ -10,13 +10,26 @@
|
||||
#include "SkCanvas.h"
|
||||
#include "SkCodec.h"
|
||||
#include "SkCodecPriv.h"
|
||||
#include "SkPicture.h"
|
||||
#include "SkPictureRecorder.h"
|
||||
|
||||
sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec) {
|
||||
sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec,
|
||||
SkISize scaledSize, SkIRect cropRect, sk_sp<SkPicture> postProcess) {
|
||||
if (!codec) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto image = sk_sp<SkAnimatedImage>(new SkAnimatedImage(std::move(codec)));
|
||||
SkISize decodeSize = scaledSize;
|
||||
auto decodeInfo = codec->getInfo();
|
||||
if (codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP
|
||||
&& scaledSize.width() < decodeInfo.width()
|
||||
&& scaledSize.height() < decodeInfo.height()) {
|
||||
// libwebp can decode to arbitrary smaller sizes.
|
||||
decodeInfo = decodeInfo.makeWH(decodeSize.width(), decodeSize.height());
|
||||
}
|
||||
|
||||
auto image = sk_sp<SkAnimatedImage>(new SkAnimatedImage(std::move(codec), scaledSize,
|
||||
decodeInfo, cropRect, std::move(postProcess)));
|
||||
if (!image->fActiveFrame.fBitmap.getPixels()) {
|
||||
// tryAllocPixels failed.
|
||||
return nullptr;
|
||||
@ -25,31 +38,60 @@ sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> cod
|
||||
return image;
|
||||
}
|
||||
|
||||
sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec) {
|
||||
if (!codec) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto decodeInfo = codec->getInfo();
|
||||
const auto scaledSize = decodeInfo.dimensions();
|
||||
const auto cropRect = SkIRect::MakeSize(scaledSize);
|
||||
auto image = sk_sp<SkAnimatedImage>(new SkAnimatedImage(std::move(codec), scaledSize,
|
||||
decodeInfo, cropRect, nullptr));
|
||||
|
||||
if (!image->fActiveFrame.fBitmap.getPixels()) {
|
||||
// tryAllocPixels failed.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SkASSERT(image->fSimple);
|
||||
return image;
|
||||
}
|
||||
|
||||
// Sentinel value for starting at the beginning.
|
||||
static constexpr double kInit = -1.0;
|
||||
|
||||
SkAnimatedImage::SkAnimatedImage(std::unique_ptr<SkAndroidCodec> codec)
|
||||
SkAnimatedImage::SkAnimatedImage(std::unique_ptr<SkAndroidCodec> codec, SkISize scaledSize,
|
||||
SkImageInfo decodeInfo, SkIRect cropRect, sk_sp<SkPicture> postProcess)
|
||||
: fCodec(std::move(codec))
|
||||
, fScaledSize(scaledSize)
|
||||
, fDecodeInfo(decodeInfo)
|
||||
, fCropRect(cropRect)
|
||||
, fPostProcess(std::move(postProcess))
|
||||
, fSimple(fScaledSize == fDecodeInfo.dimensions() && !fPostProcess
|
||||
&& fCropRect == fDecodeInfo.bounds())
|
||||
, fFinished(false)
|
||||
, fRunning(false)
|
||||
, fNowMS(kInit)
|
||||
, fRemainingMS(kInit)
|
||||
{
|
||||
if (!fActiveFrame.fBitmap.tryAllocPixels(fCodec->getInfo())) {
|
||||
if (!fActiveFrame.fBitmap.tryAllocPixels(fDecodeInfo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fSimple) {
|
||||
fMatrix = SkMatrix::MakeTrans(-fCropRect.fLeft, -fCropRect.fTop);
|
||||
float scaleX = (float) fScaledSize.width() / fDecodeInfo.width();
|
||||
float scaleY = (float) fScaledSize.height() / fDecodeInfo.height();
|
||||
fMatrix.preConcat(SkMatrix::MakeScale(scaleX, scaleY));
|
||||
}
|
||||
this->update(kInit);
|
||||
}
|
||||
|
||||
SkAnimatedImage::~SkAnimatedImage() { }
|
||||
|
||||
SkRect SkAnimatedImage::onGetBounds() {
|
||||
return SkRect::Make(fCodec->getInfo().bounds());
|
||||
}
|
||||
|
||||
void SkAnimatedImage::onDraw(SkCanvas* canvas) {
|
||||
canvas->drawBitmap(fActiveFrame.fBitmap, 0, 0);
|
||||
return SkRect::MakeIWH(fCropRect.width(), fCropRect.height());
|
||||
}
|
||||
|
||||
SkAnimatedImage::Frame::Frame()
|
||||
@ -216,7 +258,7 @@ double SkAnimatedImage::update(double msecs) {
|
||||
if (dst->getPixels()) {
|
||||
SkAssertResult(dst->setAlphaType(alphaType));
|
||||
} else {
|
||||
auto info = fCodec->getInfo().makeAlphaType(alphaType);
|
||||
auto info = fDecodeInfo.makeAlphaType(alphaType);
|
||||
if (!dst->tryAllocPixels(info)) {
|
||||
fFinished = true;
|
||||
return std::numeric_limits<double>::max();
|
||||
@ -236,3 +278,27 @@ double SkAnimatedImage::update(double msecs) {
|
||||
fActiveFrame.fDisposalMethod = frameInfo.fDisposalMethod;
|
||||
return fRemainingMS + fNowMS;
|
||||
}
|
||||
|
||||
void SkAnimatedImage::onDraw(SkCanvas* canvas) {
|
||||
if (fSimple) {
|
||||
canvas->drawBitmap(fActiveFrame.fBitmap, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
SkRect bounds = this->getBounds();
|
||||
if (fPostProcess) {
|
||||
canvas->saveLayer(&bounds, nullptr);
|
||||
}
|
||||
{
|
||||
SkAutoCanvasRestore acr(canvas, fPostProcess);
|
||||
canvas->concat(fMatrix);
|
||||
SkPaint paint;
|
||||
paint.setBlendMode(SkBlendMode::kSrc);
|
||||
paint.setFilterQuality(kLow_SkFilterQuality);
|
||||
canvas->drawBitmap(fActiveFrame.fBitmap, 0, 0, &paint);
|
||||
}
|
||||
if (fPostProcess) {
|
||||
canvas->drawPicture(fPostProcess);
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user