[canvaskit] Create an SkImage from a frame of an SkAnimatedImage

Flutter on the web wants to be able to extract arbitrary frames from
an animated image and pass those into functions like:
drawAtlas, drawImage, drawImageRect

This should allow that to happen w/o having to add lots of variants like
drawAnimatedImage. If this sticks, is drawAnimatedImage still useful?
(maybe it saves a copy?)

Change-Id: I99d7045c5dea61d0a1bd6d335c88e7517f2c4fc2
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/263020
Commit-Queue: Kevin Lubick <kjlubick@google.com>
Reviewed-by: Leon Scroggins <scroggo@google.com>
Reviewed-by: Mike Reed <reed@google.com>
This commit is contained in:
Kevin Lubick 2020-01-08 13:29:31 -05:00 committed by Skia Commit-Bot
parent 1d457b7d95
commit f5bc8fba95
6 changed files with 70 additions and 0 deletions

View File

@ -8,6 +8,8 @@ Milestone 81
<Insert new notes here- top is most recent.>
* Added SkAnimatedImage::getCurrentFrame()
* Add support to create an SkSurface from an MTKView, with delayed acquisition of
the MTLDrawable.
Entry point: SkSurface::MakeFromMTKView

View File

@ -15,6 +15,7 @@
#include "include/core/SkRect.h"
class SkAndroidCodec;
class SkImage;
class SkPicture;
/**
@ -82,6 +83,13 @@ public:
*/
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.
*

View File

@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
build.
- Experimental Runtime shader available for custom builds.
- WebP support.
- `SkAnimatedImage.getCurrentFrame` which returns an SkImage.
### Fixed
- `CanvasKit.SaveLayerInitWithPrevious` and `CanvasKit.SaveLayerF16ColorType` constants.

View File

@ -869,6 +869,7 @@ EMSCRIPTEN_BINDINGS(Skia) {
class_<SkAnimatedImage>("SkAnimatedImage")
.smart_ptr<sk_sp<SkAnimatedImage>>("sk_sp<SkAnimatedImage>")
.function("decodeNextFrame", &SkAnimatedImage::decodeNextFrame)
.function("getCurrentFrame", &SkAnimatedImage::getCurrentFrame)
.function("getFrameCount", &SkAnimatedImage::getFrameCount)
.function("getRepetitionCount", &SkAnimatedImage::getRepetitionCount)
.function("height", optional_override([](SkAnimatedImage& self)->int32_t {

View File

@ -271,6 +271,53 @@ describe('Core canvas behavior', function() {
});
});
it('can use various image filters on animated images', function(done) {
const imgPromise = fetch('/assets/flightAnim.gif')
.then((response) => response.arrayBuffer());
Promise.all([imgPromise, LoadCanvasKit]).then((values) => {
const gifData = values[0];
expect(gifData).toBeTruthy();
catchException(done, () => {
let img = CanvasKit.MakeAnimatedImageFromEncoded(gifData);
expect(img).toBeTruthy();
const surface = CanvasKit.MakeCanvasSurface('test');
expect(surface).toBeTruthy('Could not make surface');
if (!surface) {
done();
return;
}
img.decodeNextFrame();
img.decodeNextFrame();
const canvas = surface.getCanvas();
canvas.clear(CanvasKit.WHITE);
const paint = new CanvasKit.SkPaint();
paint.setAntiAlias(true);
paint.setColor(CanvasKit.Color(0, 255, 0, 1.0));
const redCF = CanvasKit.SkColorFilter.MakeBlend(
CanvasKit.Color(255, 0, 0, 0.1), CanvasKit.BlendMode.SrcOver);
const redIF = CanvasKit.SkImageFilter.MakeColorFilter(redCF, null);
const blurIF = CanvasKit.SkImageFilter.MakeBlur(8, 0.2, CanvasKit.TileMode.Decal, null);
const combined = CanvasKit.SkImageFilter.MakeCompose(redIF, blurIF);
paint.setImageFilter(combined);
const frame = img.getCurrentFrame();
canvas.drawImage(frame, 100, 50, paint);
surface.flush();
paint.delete();
redIF.delete();
redCF.delete();
blurIF.delete();
combined.delete();
frame.delete();
img.delete();
reportSurface(surface, 'animated_filters', done);
})();
});
});
it('can use DecodeCache APIs', function(done) {
LoadCanvasKit.then(catchException(done, () => {
const initialLimit = CanvasKit.getDecodeCacheLimitBytes();

View File

@ -329,6 +329,9 @@ int SkAnimatedImage::decodeNextFrame() {
}
void SkAnimatedImage::onDraw(SkCanvas* canvas) {
// This SkBitmap may be reused later to decode the following frame. But Frame::init
// lazily copies the pixel ref if it has any other references. So it is safe to not
// do a deep copy here.
auto image = SkMakeImageFromRasterBitmap(fDisplayFrame.fBitmap,
kNever_SkCopyPixelsMode);
@ -357,3 +360,11 @@ void SkAnimatedImage::onDraw(SkCanvas* canvas) {
void SkAnimatedImage::setRepetitionCount(int newCount) {
fRepetitionCount = newCount;
}
sk_sp<SkImage> SkAnimatedImage::getCurrentFrame() {
// This SkBitmap may be reused later to decode the following frame. But Frame::init
// lazily copies the pixel ref if it has any other references. So it is safe to not
// do a deep copy here.
return SkMakeImageFromRasterBitmap(fDisplayFrame.fBitmap,
kNever_SkCopyPixelsMode);
}