From f5bc8fba95737140784435f5f6e22aaa0ce0340b Mon Sep 17 00:00:00 2001 From: Kevin Lubick Date: Wed, 8 Jan 2020 13:29:31 -0500 Subject: [PATCH] [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 Reviewed-by: Leon Scroggins Reviewed-by: Mike Reed --- RELEASE_NOTES.txt | 2 + include/android/SkAnimatedImage.h | 8 ++++ modules/canvaskit/CHANGELOG.md | 1 + modules/canvaskit/canvaskit_bindings.cpp | 1 + modules/canvaskit/tests/core.spec.js | 47 ++++++++++++++++++++++++ src/android/SkAnimatedImage.cpp | 11 ++++++ 6 files changed, 70 insertions(+) diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt index d98cf9de53..8719323123 100644 --- a/RELEASE_NOTES.txt +++ b/RELEASE_NOTES.txt @@ -8,6 +8,8 @@ Milestone 81 + * Added SkAnimatedImage::getCurrentFrame() + * Add support to create an SkSurface from an MTKView, with delayed acquisition of the MTLDrawable. Entry point: SkSurface::MakeFromMTKView diff --git a/include/android/SkAnimatedImage.h b/include/android/SkAnimatedImage.h index a3907a6454..d613d294f3 100644 --- a/include/android/SkAnimatedImage.h +++ b/include/android/SkAnimatedImage.h @@ -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 getCurrentFrame(); + /** * How long to display the current frame. * diff --git a/modules/canvaskit/CHANGELOG.md b/modules/canvaskit/CHANGELOG.md index 0d127e9eda..0fd15a5bd2 100644 --- a/modules/canvaskit/CHANGELOG.md +++ b/modules/canvaskit/CHANGELOG.md @@ -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. diff --git a/modules/canvaskit/canvaskit_bindings.cpp b/modules/canvaskit/canvaskit_bindings.cpp index ceca5a205d..81a84807b5 100644 --- a/modules/canvaskit/canvaskit_bindings.cpp +++ b/modules/canvaskit/canvaskit_bindings.cpp @@ -869,6 +869,7 @@ EMSCRIPTEN_BINDINGS(Skia) { class_("SkAnimatedImage") .smart_ptr>("sk_sp") .function("decodeNextFrame", &SkAnimatedImage::decodeNextFrame) + .function("getCurrentFrame", &SkAnimatedImage::getCurrentFrame) .function("getFrameCount", &SkAnimatedImage::getFrameCount) .function("getRepetitionCount", &SkAnimatedImage::getRepetitionCount) .function("height", optional_override([](SkAnimatedImage& self)->int32_t { diff --git a/modules/canvaskit/tests/core.spec.js b/modules/canvaskit/tests/core.spec.js index 5c5d30e41d..d38ac81641 100644 --- a/modules/canvaskit/tests/core.spec.js +++ b/modules/canvaskit/tests/core.spec.js @@ -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(); diff --git a/src/android/SkAnimatedImage.cpp b/src/android/SkAnimatedImage.cpp index 6db6afde4a..3ae8a843ba 100644 --- a/src/android/SkAnimatedImage.cpp +++ b/src/android/SkAnimatedImage.cpp @@ -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 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); +}