aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Leon Scroggins III <scroggo@google.com>2018-01-16 15:26:35 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-01-17 17:46:17 +0000
commitb1b7f70103378aff5e9a315e7788866b648ab4b1 (patch)
tree5e9c63445bbd5c7ebb301273e929e942ad4642a0
parent9d8abc5816d9e5533e7943c52fa6856177f482b2 (diff)
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>
-rw-r--r--include/android/SkAnimatedImage.h30
-rw-r--r--src/android/SkAnimatedImage.cpp84
2 files changed, 104 insertions, 10 deletions
diff --git a/include/android/SkAnimatedImage.h b/include/android/SkAnimatedImage.h
index 1bd9291057..1c1a2fc039 100644
--- a/include/android/SkAnimatedImage.h
+++ b/include/android/SkAnimatedImage.h
@@ -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>);
@@ -47,6 +60,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;
};
diff --git a/src/android/SkAnimatedImage.cpp b/src/android/SkAnimatedImage.cpp
index 25c020fde8..4c87083957 100644
--- a/src/android/SkAnimatedImage.cpp
+++ b/src/android/SkAnimatedImage.cpp
@@ -10,46 +10,88 @@
#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,
+ SkISize scaledSize, SkIRect cropRect, sk_sp<SkPicture> postProcess) {
+ if (!codec) {
+ return nullptr;
+ }
+
+ 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;
+ }
+
+ return image;
+}
sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec) {
if (!codec) {
return nullptr;
}
- auto image = sk_sp<SkAnimatedImage>(new SkAnimatedImage(std::move(codec)));
+ 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();
+ }
+}