diff options
-rw-r--r-- | core/jni/Android.bp | 1 | ||||
-rw-r--r-- | core/jni/AndroidRuntime.cpp | 2 | ||||
-rw-r--r-- | core/jni/android/graphics/AnimatedImageDrawable.cpp | 154 | ||||
-rw-r--r-- | core/jni/android/graphics/ImageDecoder.cpp | 55 | ||||
-rw-r--r-- | core/jni/android/graphics/ImageDecoder.h | 55 | ||||
-rw-r--r-- | graphics/java/android/graphics/ImageDecoder.java | 28 | ||||
-rw-r--r-- | graphics/java/android/graphics/drawable/AnimatedImageDrawable.java | 173 | ||||
-rw-r--r-- | libs/hwui/RecordingCanvas.cpp | 5 | ||||
-rw-r--r-- | libs/hwui/RecordingCanvas.h | 2 | ||||
-rw-r--r-- | libs/hwui/SkiaCanvas.cpp | 16 | ||||
-rw-r--r-- | libs/hwui/SkiaCanvas.h | 2 | ||||
-rw-r--r-- | libs/hwui/hwui/Canvas.h | 4 |
12 files changed, 455 insertions, 42 deletions
diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 96f3308ec178..53913f198c1d 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -120,6 +120,7 @@ cc_library_shared { "android_util_jar_StrictJarFile.cpp", "android_graphics_Canvas.cpp", "android_graphics_Picture.cpp", + "android/graphics/AnimatedImageDrawable.cpp", "android/graphics/Bitmap.cpp", "android/graphics/BitmapFactory.cpp", "android/graphics/ByteBufferStreamAdaptor.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 6569b4783e67..ef0e260f2ee3 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -63,6 +63,7 @@ extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env); extern int register_android_graphics_GraphicBuffer(JNIEnv* env); extern int register_android_graphics_Graphics(JNIEnv* env); extern int register_android_graphics_ImageDecoder(JNIEnv*); +extern int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv*); extern int register_android_graphics_Interpolator(JNIEnv* env); extern int register_android_graphics_MaskFilter(JNIEnv* env); extern int register_android_graphics_Movie(JNIEnv* env); @@ -1397,6 +1398,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_FontFamily), REG_JNI(register_android_graphics_GraphicBuffer), REG_JNI(register_android_graphics_ImageDecoder), + REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable), REG_JNI(register_android_graphics_Interpolator), REG_JNI(register_android_graphics_MaskFilter), REG_JNI(register_android_graphics_Matrix), diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp new file mode 100644 index 000000000000..12feaab5c684 --- /dev/null +++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GraphicsJNI.h" +#include "ImageDecoder.h" +#include "core_jni_helpers.h" + +#include <hwui/Canvas.h> +#include <SkAndroidCodec.h> +#include <SkAnimatedImage.h> +#include <SkColorFilter.h> +#include <SkPicture.h> +#include <SkPictureRecorder.h> + +using namespace android; + +struct AnimatedImageDrawable { + sk_sp<SkAnimatedImage> mDrawable; + SkPaint mPaint; +}; + +// Note: jpostProcess holds a handle to the ImageDecoder. +static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/, + jlong nativeImageDecoder, jobject jpostProcess, + jint width, jint height, jobject jsubset) { + if (nativeImageDecoder == 0) { + doThrowIOE(env, "Cannot create AnimatedImageDrawable from null!"); + return 0; + } + + auto* imageDecoder = reinterpret_cast<ImageDecoder*>(nativeImageDecoder); + auto info = imageDecoder->mCodec->getInfo(); + const SkISize scaledSize = SkISize::Make(width, height); + SkIRect subset; + if (jsubset) { + GraphicsJNI::jrect_to_irect(env, jsubset, &subset); + } else { + subset = SkIRect::MakeWH(width, height); + } + + sk_sp<SkPicture> picture; + if (jpostProcess) { + SkRect bounds = SkRect::MakeWH(subset.width(), subset.height()); + + SkPictureRecorder recorder; + SkCanvas* skcanvas = recorder.beginRecording(bounds); + std::unique_ptr<Canvas> canvas(Canvas::create_canvas(skcanvas)); + postProcessAndRelease(env, jpostProcess, std::move(canvas), bounds.width(), + bounds.height()); + if (env->ExceptionCheck()) { + return 0; + } + picture = recorder.finishRecordingAsPicture(); + } + + std::unique_ptr<AnimatedImageDrawable> drawable(new AnimatedImageDrawable); + drawable->mDrawable = SkAnimatedImage::Make(std::move(imageDecoder->mCodec), + scaledSize, subset, std::move(picture)); + if (!drawable->mDrawable) { + doThrowIOE(env, "Failed to create drawable"); + return 0; + } + drawable->mDrawable->start(); + + return reinterpret_cast<jlong>(drawable.release()); +} + +static void AnimatedImageDrawable_destruct(AnimatedImageDrawable* drawable) { + delete drawable; +} + +static jlong AnimatedImageDrawable_nGetNativeFinalizer(JNIEnv* /*env*/, jobject /*clazz*/) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&AnimatedImageDrawable_destruct)); +} + +static jlong AnimatedImageDrawable_nDraw(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jlong canvasPtr, jlong msecs) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + double timeToNextUpdate = drawable->mDrawable->update(msecs); + auto* canvas = reinterpret_cast<Canvas*>(canvasPtr); + canvas->drawAnimatedImage(drawable->mDrawable.get(), 0, 0, &drawable->mPaint); + return (jlong) timeToNextUpdate; +} + +static void AnimatedImageDrawable_nSetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jint alpha) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + drawable->mPaint.setAlpha(alpha); +} + +static jlong AnimatedImageDrawable_nGetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + return drawable->mPaint.getAlpha(); +} + +static void AnimatedImageDrawable_nSetColorFilter(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jlong nativeFilter) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + auto* filter = reinterpret_cast<SkColorFilter*>(nativeFilter); + drawable->mPaint.setColorFilter(sk_ref_sp(filter)); +} + +static jboolean AnimatedImageDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + return drawable->mDrawable->isRunning(); +} + +static void AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + drawable->mDrawable->start(); +} + +static void AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + drawable->mDrawable->stop(); +} + +static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + // FIXME: Report the size of the internal SkBitmap etc. + return sizeof(drawable); +} + +static const JNINativeMethod gAnimatedImageDrawableMethods[] = { + { "nCreate", "(JLandroid/graphics/ImageDecoder;IILandroid/graphics/Rect;)J", (void*) AnimatedImageDrawable_nCreate }, + { "nGetNativeFinalizer", "()J", (void*) AnimatedImageDrawable_nGetNativeFinalizer }, + { "nDraw", "(JJJ)J", (void*) AnimatedImageDrawable_nDraw }, + { "nSetAlpha", "(JI)V", (void*) AnimatedImageDrawable_nSetAlpha }, + { "nGetAlpha", "(J)I", (void*) AnimatedImageDrawable_nGetAlpha }, + { "nSetColorFilter", "(JJ)V", (void*) AnimatedImageDrawable_nSetColorFilter }, + { "nIsRunning", "(J)Z", (void*) AnimatedImageDrawable_nIsRunning }, + { "nStart", "(J)V", (void*) AnimatedImageDrawable_nStart }, + { "nStop", "(J)V", (void*) AnimatedImageDrawable_nStop }, + { "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize }, +}; + +int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) { + return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable", + gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods)); +} + diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp index 9e6803784d95..ed9d0e9e86c3 100644 --- a/core/jni/android/graphics/ImageDecoder.cpp +++ b/core/jni/android/graphics/ImageDecoder.cpp @@ -19,12 +19,11 @@ #include "ByteBufferStreamAdaptor.h" #include "CreateJavaOutputStreamAdaptor.h" #include "GraphicsJNI.h" -#include "NinePatchPeeker.h" +#include "ImageDecoder.h" #include "Utils.h" #include "core_jni_helpers.h" #include <hwui/Bitmap.h> -#include <hwui/Canvas.h> #include <SkAndroidCodec.h> #include <SkEncodedImageFormat.h> @@ -51,26 +50,6 @@ static jmethodID gCallback_onPartialImageMethodID; static jmethodID gCanvas_constructorMethodID; static jmethodID gCanvas_releaseMethodID; -struct ImageDecoder { - // These need to stay in sync with ImageDecoder.java's Allocator constants. - enum Allocator { - kDefault_Allocator = 0, - kSoftware_Allocator = 1, - kSharedMemory_Allocator = 2, - kHardware_Allocator = 3, - }; - - // These need to stay in sync with PixelFormat.java's Format constants. - enum PixelFormat { - kUnknown = 0, - kTranslucent = -3, - kOpaque = -1, - }; - - NinePatchPeeker mPeeker; - std::unique_ptr<SkAndroidCodec> mCodec; -}; - static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) { if (!stream.get()) { doThrowIOE(env, "Failed to create a stream"); @@ -78,7 +57,7 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) { } std::unique_ptr<ImageDecoder> decoder(new ImageDecoder); SkCodec::Result result; - auto codec = SkCodec::MakeFromStream(std::move(stream), &result, &decoder->mPeeker); + auto codec = SkCodec::MakeFromStream(std::move(stream), &result, decoder->mPeeker.get()); if (!codec) { switch (result) { case SkCodec::kIncompleteInput: @@ -90,22 +69,24 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) { SkCodec::ResultToString(result)); doThrowIOE(env, msg.c_str()); break; - } + } return nullptr; } + // FIXME: Avoid parsing the whole image? + const bool animated = codec->getFrameCount() > 1; decoder->mCodec = SkAndroidCodec::MakeFromCodec(std::move(codec)); if (!decoder->mCodec.get()) { doThrowIOE(env, "Could not create AndroidCodec"); return nullptr; } - const auto& info = decoder->mCodec->getInfo(); const int width = info.width(); const int height = info.height(); return env->NewObject(gImageDecoder_class, gImageDecoder_constructorMethodID, - reinterpret_cast<jlong>(decoder.release()), width, height); + reinterpret_cast<jlong>(decoder.release()), width, height, + animated); } static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, @@ -176,8 +157,8 @@ static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/, jby return native_create(env, std::move(stream)); } -static jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, - std::unique_ptr<Canvas> canvas, int width, int height) { +jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas, + int width, int height) { jobject jcanvas = env->NewObject(gCanvas_class, gCanvas_constructorMethodID, reinterpret_cast<jlong>(canvas.get())); if (!jcanvas) { @@ -340,23 +321,23 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong // Ignore ninepatch when post-processing. if (!jpostProcess) { // FIXME: Share more code with BitmapFactory.cpp. - if (decoder->mPeeker.mPatch != nullptr) { + if (decoder->mPeeker->mPatch != nullptr) { if (scale) { - decoder->mPeeker.scale(scaleX, scaleY, desiredWidth, desiredHeight); + decoder->mPeeker->scale(scaleX, scaleY, desiredWidth, desiredHeight); } - size_t ninePatchArraySize = decoder->mPeeker.mPatch->serializedSize(); + size_t ninePatchArraySize = decoder->mPeeker->mPatch->serializedSize(); ninePatchChunk = env->NewByteArray(ninePatchArraySize); if (ninePatchChunk == nullptr) { doThrowOOME(env, "Failed to allocate nine patch chunk."); return nullptr; } - env->SetByteArrayRegion(ninePatchChunk, 0, decoder->mPeeker.mPatchSize, - reinterpret_cast<jbyte*>(decoder->mPeeker.mPatch)); + env->SetByteArrayRegion(ninePatchChunk, 0, decoder->mPeeker->mPatchSize, + reinterpret_cast<jbyte*>(decoder->mPeeker->mPatch)); } - if (decoder->mPeeker.mHasInsets) { - ninePatchInsets = decoder->mPeeker.createNinePatchInsets(env, 1.0f); + if (decoder->mPeeker->mHasInsets) { + ninePatchInsets = decoder->mPeeker->createNinePatchInsets(env, 1.0f); if (ninePatchInsets == nullptr) { doThrowOOME(env, "Failed to allocate nine patch insets."); return nullptr; @@ -497,7 +478,7 @@ static jobject ImageDecoder_nGetSampledSize(JNIEnv* env, jobject /*clazz*/, jlon static void ImageDecoder_nGetPadding(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, jobject outPadding) { auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr); - decoder->mPeeker.getPadding(env, outPadding); + decoder->mPeeker->getPadding(env, outPadding); } static void ImageDecoder_nClose(JNIEnv* /*env*/, jobject /*clazz*/, jlong nativePtr) { @@ -525,7 +506,7 @@ static const JNINativeMethod gImageDecoderMethods[] = { int register_android_graphics_ImageDecoder(JNIEnv* env) { gImageDecoder_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder")); - gImageDecoder_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "<init>", "(JII)V"); + gImageDecoder_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "<init>", "(JIIZ)V"); gImageDecoder_postProcessMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "postProcessAndRelease", "(Landroid/graphics/Canvas;II)I"); gPoint_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Point")); diff --git a/core/jni/android/graphics/ImageDecoder.h b/core/jni/android/graphics/ImageDecoder.h new file mode 100644 index 000000000000..2df71eb19528 --- /dev/null +++ b/core/jni/android/graphics/ImageDecoder.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NinePatchPeeker.h" + +#include <hwui/Canvas.h> + +#include <jni.h> + +class SkAndroidCodec; + +using namespace android; + +struct ImageDecoder { + // These need to stay in sync with ImageDecoder.java's Allocator constants. + enum Allocator { + kDefault_Allocator = 0, + kSoftware_Allocator = 1, + kSharedMemory_Allocator = 2, + kHardware_Allocator = 3, + }; + + // These need to stay in sync with PixelFormat.java's Format constants. + enum PixelFormat { + kUnknown = 0, + kTranslucent = -3, + kOpaque = -1, + }; + + std::unique_ptr<SkAndroidCodec> mCodec; + sk_sp<NinePatchPeeker> mPeeker; + + ImageDecoder() + :mPeeker(new NinePatchPeeker) + {} +}; + +// Creates a Java Canvas object from canvas, calls jimageDecoder's PostProcess on it, and then +// releases the Canvas. +// Caller needs to check for exceptions. +jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas, + int width, int height); diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index f8ca29e48d85..05dadc97a5ba 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -26,6 +26,7 @@ import android.content.ContentResolver; import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.content.res.Resources; +import android.graphics.drawable.AnimatedImageDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.NinePatchDrawable; @@ -294,9 +295,10 @@ public final class ImageDecoder implements AutoCloseable { }; // Fields - private long mNativePtr; - private final int mWidth; - private final int mHeight; + private long mNativePtr; + private final int mWidth; + private final int mHeight; + private final boolean mAnimated; private int mDesiredWidth; private int mDesiredHeight; @@ -322,12 +324,14 @@ public final class ImageDecoder implements AutoCloseable { * called after decoding to delete native resources. */ @SuppressWarnings("unused") - private ImageDecoder(long nativePtr, int width, int height) { + private ImageDecoder(long nativePtr, int width, int height, + boolean animated) { mNativePtr = nativePtr; mWidth = width; mHeight = height; mDesiredWidth = width; mDesiredHeight = height; + mAnimated = animated; mCloseGuard.open("close"); } @@ -726,6 +730,21 @@ public final class ImageDecoder implements AutoCloseable { "Drawable!"); } + if (decoder.mAnimated) { + // AnimatedImageDrawable calls postProcessAndRelease only if + // mPostProcess exists. + ImageDecoder postProcessPtr = decoder.mPostProcess == null ? + null : decoder; + Drawable d = new AnimatedImageDrawable(decoder.mNativePtr, + postProcessPtr, decoder.mDesiredWidth, + decoder.mDesiredHeight, decoder.mCropRect, + decoder.mInputStream, decoder.mAssetFd); + // d has taken ownership of these objects. + decoder.mInputStream = null; + decoder.mAssetFd = null; + return d; + } + Bitmap bm = decoder.decodeBitmap(); Resources res = src.getResources(); if (res == null) { @@ -742,7 +761,6 @@ public final class ImageDecoder implements AutoCloseable { opticalInsets, null); } - // TODO: Handle animation. return new BitmapDrawable(res, bm); } } diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java new file mode 100644 index 000000000000..ce3bd9af73b6 --- /dev/null +++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.drawable; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.res.AssetFileDescriptor; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.os.SystemClock; + +import libcore.io.IoUtils; +import libcore.util.NativeAllocationRegistry; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.Runnable; + +/** + * @hide + */ +public class AnimatedImageDrawable extends Drawable implements Animatable { + private final long mNativePtr; + private final InputStream mInputStream; + private final AssetFileDescriptor mAssetFd; + + private final int mIntrinsicWidth; + private final int mIntrinsicHeight; + + private Runnable mRunnable = new Runnable() { + @Override + public void run() { + invalidateSelf(); + } + }; + + /** + * @hide + * This should only be called by ImageDecoder. + * + * decoder is only non-null if it has a PostProcess + */ + public AnimatedImageDrawable(long nativeImageDecoder, + @Nullable ImageDecoder decoder, int width, int height, Rect cropRect, + InputStream inputStream, AssetFileDescriptor afd) + throws IOException { + mNativePtr = nCreate(nativeImageDecoder, decoder, width, height, cropRect); + mInputStream = inputStream; + mAssetFd = afd; + + if (cropRect == null) { + mIntrinsicWidth = width; + mIntrinsicHeight = height; + } else { + mIntrinsicWidth = cropRect.width(); + mIntrinsicHeight = cropRect.height(); + } + + long nativeSize = nNativeByteSize(mNativePtr); + NativeAllocationRegistry registry = new NativeAllocationRegistry( + AnimatedImageDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize); + registry.registerNativeAllocation(this, mNativePtr); + } + + @Override + protected void finalize() throws Throwable { + // FIXME: It's a shame that we have *both* a native finalizer and a Java + // one. The native one is necessary to report how much memory is being + // used natively, and this one is necessary to close the input. An + // alternative might be to read the entire stream ahead of time, so we + // can eliminate the Java finalizer. + try { + IoUtils.closeQuietly(mInputStream); + IoUtils.closeQuietly(mAssetFd); + } finally { + super.finalize(); + } + } + + @Override + public int getIntrinsicWidth() { + return mIntrinsicWidth; + } + + @Override + public int getIntrinsicHeight() { + return mIntrinsicHeight; + } + + @Override + public void draw(@NonNull Canvas canvas) { + long nextUpdate = nDraw(mNativePtr, canvas.getNativeCanvasWrapper(), + SystemClock.uptimeMillis()); + scheduleSelf(mRunnable, nextUpdate); + } + + @Override + public void setAlpha(@IntRange(from=0,to=255) int alpha) { + if (alpha < 0 || alpha > 255) { + throw new IllegalArgumentException("Alpha must be between 0 and" + + " 255! provided " + alpha); + } + nSetAlpha(mNativePtr, alpha); + } + + @Override + public int getAlpha() { + return nGetAlpha(mNativePtr); + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + long nativeFilter = colorFilter == null ? 0 : colorFilter.getNativeInstance(); + nSetColorFilter(mNativePtr, nativeFilter); + } + + @Override + public @PixelFormat.Opacity int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + // TODO: Add a Constant State? + // @Override + // public @Nullable ConstantState getConstantState() {} + + + // Animatable overrides + @Override + public boolean isRunning() { + return nIsRunning(mNativePtr); + } + + @Override + public void start() { + nStart(mNativePtr); + } + + @Override + public void stop() { + nStop(mNativePtr); + } + + private static native long nCreate(long nativeImageDecoder, + @Nullable ImageDecoder decoder, int width, int height, Rect cropRect) + throws IOException; + private static native long nGetNativeFinalizer(); + private static native long nDraw(long nativePtr, long canvasNativePtr, long msecs); + private static native void nSetAlpha(long nativePtr, int alpha); + private static native int nGetAlpha(long nativePtr); + private static native void nSetColorFilter(long nativePtr, long nativeFilter); + private static native boolean nIsRunning(long nativePtr); + private static native void nStart(long nativePtr); + private static native void nStop(long nativePtr); + private static native long nNativeByteSize(long nativePtr); +} diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 3fb1c0d64abf..fb7b24623568 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -495,6 +495,11 @@ void RecordingCanvas::drawNinePatch(Bitmap& bitmap, const android::Res_png_9patc refPaint(paint), refBitmap(bitmap), refPatch(&patch))); } +void RecordingCanvas::drawAnimatedImage(SkAnimatedImage*, float left, float top, + const SkPaint*) { + // Unimplemented +} + // Text void RecordingCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int glyphCount, const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 3087db0550de..dd06ada9da3d 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -183,6 +183,8 @@ public: virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) override; + virtual void drawAnimatedImage(SkAnimatedImage*, float left, float top, + const SkPaint* paint) override; // Text virtual bool drawTextAbsolutePos() const override { return false; } diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 2e08670a757a..dc274cf50a52 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -23,6 +23,7 @@ #include "hwui/MinikinUtils.h" #include "pipeline/skia/AnimatedDrawables.h" +#include <SkAnimatedImage.h> #include <SkCanvasStateUtils.h> #include <SkColorFilter.h> #include <SkColorSpaceXformCanvas.h> @@ -32,6 +33,7 @@ #include <SkGraphics.h> #include <SkImage.h> #include <SkImagePriv.h> +#include <SkPicture.h> #include <SkRSXform.h> #include <SkShader.h> #include <SkTemplates.h> @@ -723,6 +725,20 @@ void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, floa mCanvas->drawImageLattice(image.get(), lattice, dst, addFilter(paint, &tmpPaint, colorFilter)); } +void SkiaCanvas::drawAnimatedImage(SkAnimatedImage* image, float left, float top, + const SkPaint* paint) { + sk_sp<SkPicture> pic(image->newPictureSnapshot()); + SkMatrix matrixStorage; + SkMatrix* matrix; + if (left == 0.0f && top == 0.0f) { + matrix = nullptr; + } else { + matrixStorage = SkMatrix::MakeTrans(left, top); + matrix = &matrixStorage; + } + mCanvas->drawPicture(pic.get(), matrix, paint); +} + void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) { vectorDrawable->drawStaging(this); } diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 99e676a6fb1e..7137210406fb 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -124,6 +124,8 @@ public: virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) override; + virtual void drawAnimatedImage(SkAnimatedImage*, float left, float top, + const SkPaint* paint) override; virtual bool drawTextAbsolutePos() const override { return true; } virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override; diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index e682a2e226b7..5efd35764635 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -28,6 +28,7 @@ #include <SkCanvas.h> #include <SkMatrix.h> +class SkAnimatedImage; class SkCanvasState; class SkVertices; @@ -237,6 +238,9 @@ public: float dstTop, float dstRight, float dstBottom, const SkPaint* paint) = 0; + virtual void drawAnimatedImage(SkAnimatedImage*, float left, float top, + const SkPaint* paint) = 0; + /** * Specifies if the positions passed to ::drawText are absolute or relative * to the (x,y) value provided. |