diff options
author | Leon Scroggins III <scroggo@google.com> | 2018-01-16 15:26:35 -0500 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-01-17 17:46:17 +0000 |
commit | b1b7f70103378aff5e9a315e7788866b648ab4b1 (patch) | |
tree | 5e9c63445bbd5c7ebb301273e929e942ad4642a0 | |
parent | 9d8abc5816d9e5533e7943c52fa6856177f482b2 (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.h | 30 | ||||
-rw-r--r-- | src/android/SkAnimatedImage.cpp | 84 |
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(); + } +} |