blob: 373e893b9a25eb5343fef2cb2de70cfe06c780d1 [file] [log] [blame]
Leon Scroggins III671cce22018-01-14 16:52:17 -05001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "GraphicsJNI.h"
18#include "ImageDecoder.h"
Leon Scroggins III127d31a2018-01-19 12:29:47 -050019#include "Utils.h"
Leon Scroggins III671cce22018-01-14 16:52:17 -050020
Leon Scroggins III671cce22018-01-14 16:52:17 -050021#include <SkAndroidCodec.h>
22#include <SkAnimatedImage.h>
23#include <SkColorFilter.h>
Kevin Lubick1175dc02022-02-28 12:41:27 -050024#include <SkEncodedImageFormat.h>
Leon Scroggins III671cce22018-01-14 16:52:17 -050025#include <SkPicture.h>
26#include <SkPictureRecorder.h>
Kevin Lubick1175dc02022-02-28 12:41:27 -050027#include <SkRect.h>
28#include <SkRefCnt.h>
Leon Scroggins III5b7f4262018-01-26 11:03:54 -050029#include <hwui/AnimatedImageDrawable.h>
Leon Scroggins III753a56f2019-12-11 11:02:15 -050030#include <hwui/ImageDecoder.h>
Leon Scroggins III5b7f4262018-01-26 11:03:54 -050031#include <hwui/Canvas.h>
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -050032#include <utils/Looper.h>
Leon Scroggins III671cce22018-01-14 16:52:17 -050033
34using namespace android;
35
John Reck3998a012021-07-28 11:04:30 -040036static jclass gAnimatedImageDrawableClass;
37static jmethodID gAnimatedImageDrawable_callOnAnimationEndMethodID;
Leon Scroggins III671cce22018-01-14 16:52:17 -050038
39// Note: jpostProcess holds a handle to the ImageDecoder.
40static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/,
41 jlong nativeImageDecoder, jobject jpostProcess,
Leon Scroggins IIIeac14232019-02-04 10:30:22 -050042 jint width, jint height, jlong colorSpaceHandle,
43 jboolean extended, jobject jsubset) {
Leon Scroggins III671cce22018-01-14 16:52:17 -050044 if (nativeImageDecoder == 0) {
45 doThrowIOE(env, "Cannot create AnimatedImageDrawable from null!");
46 return 0;
47 }
48
49 auto* imageDecoder = reinterpret_cast<ImageDecoder*>(nativeImageDecoder);
Leon Scroggins III671cce22018-01-14 16:52:17 -050050 SkIRect subset;
51 if (jsubset) {
52 GraphicsJNI::jrect_to_irect(env, jsubset, &subset);
53 } else {
54 subset = SkIRect::MakeWH(width, height);
55 }
56
Leon Scroggins IIIeb3b38e2018-03-20 11:11:13 -040057 bool hasRestoreFrame = false;
Leon Scroggins IIIeac14232019-02-04 10:30:22 -050058 if (imageDecoder->mCodec->getEncodedFormat() != SkEncodedImageFormat::kWEBP) {
Leon Scroggins IIIeb3b38e2018-03-20 11:11:13 -040059 const int frameCount = imageDecoder->mCodec->codec()->getFrameCount();
60 for (int i = 0; i < frameCount; ++i) {
61 SkCodec::FrameInfo frameInfo;
62 if (!imageDecoder->mCodec->codec()->getFrameInfo(i, &frameInfo)) {
63 doThrowIOE(env, "Failed to read frame info!");
64 return 0;
65 }
66 if (frameInfo.fDisposalMethod == SkCodecAnimation::DisposalMethod::kRestorePrevious) {
67 hasRestoreFrame = true;
68 break;
69 }
70 }
71 }
72
Leon Scroggins IIIeac14232019-02-04 10:30:22 -050073 auto info = imageDecoder->mCodec->getInfo().makeWH(width, height)
74 .makeColorSpace(GraphicsJNI::getNativeColorSpace(colorSpaceHandle));
75 if (extended) {
76 info = info.makeColorType(kRGBA_F16_SkColorType);
77 }
78
Leon Scroggins IIIeb3b38e2018-03-20 11:11:13 -040079 size_t bytesUsed = info.computeMinByteSize();
80 // SkAnimatedImage has one SkBitmap for decoding, plus an extra one if there is a
81 // kRestorePrevious frame. AnimatedImageDrawable has two SkPictures storing the current
82 // frame and the next frame. (The former assumes that the image is animated, and the
83 // latter assumes that it is drawn to a hardware canvas.)
84 bytesUsed *= hasRestoreFrame ? 4 : 3;
Leon Scroggins III671cce22018-01-14 16:52:17 -050085 sk_sp<SkPicture> picture;
86 if (jpostProcess) {
87 SkRect bounds = SkRect::MakeWH(subset.width(), subset.height());
88
89 SkPictureRecorder recorder;
90 SkCanvas* skcanvas = recorder.beginRecording(bounds);
91 std::unique_ptr<Canvas> canvas(Canvas::create_canvas(skcanvas));
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -050092 postProcessAndRelease(env, jpostProcess, std::move(canvas));
Leon Scroggins III671cce22018-01-14 16:52:17 -050093 if (env->ExceptionCheck()) {
94 return 0;
95 }
96 picture = recorder.finishRecordingAsPicture();
Leon Scroggins IIIeb3b38e2018-03-20 11:11:13 -040097 bytesUsed += picture->approximateBytesUsed();
Leon Scroggins III671cce22018-01-14 16:52:17 -050098 }
99
Derek Sollenberger2d142132018-01-22 10:25:26 -0500100
101 sk_sp<SkAnimatedImage> animatedImg = SkAnimatedImage::Make(std::move(imageDecoder->mCodec),
Leon Scroggins IIIeac14232019-02-04 10:30:22 -0500102 info, subset,
Derek Sollenberger2d142132018-01-22 10:25:26 -0500103 std::move(picture));
104 if (!animatedImg) {
Leon Scroggins III671cce22018-01-14 16:52:17 -0500105 doThrowIOE(env, "Failed to create drawable");
106 return 0;
107 }
Leon Scroggins III671cce22018-01-14 16:52:17 -0500108
Leon Scroggins IIIeb3b38e2018-03-20 11:11:13 -0400109 bytesUsed += sizeof(animatedImg.get());
110
111 sk_sp<AnimatedImageDrawable> drawable(new AnimatedImageDrawable(std::move(animatedImg),
112 bytesUsed));
Leon Scroggins III671cce22018-01-14 16:52:17 -0500113 return reinterpret_cast<jlong>(drawable.release());
114}
115
116static void AnimatedImageDrawable_destruct(AnimatedImageDrawable* drawable) {
Derek Sollenberger2d142132018-01-22 10:25:26 -0500117 SkSafeUnref(drawable);
Leon Scroggins III671cce22018-01-14 16:52:17 -0500118}
119
120static jlong AnimatedImageDrawable_nGetNativeFinalizer(JNIEnv* /*env*/, jobject /*clazz*/) {
121 return static_cast<jlong>(reinterpret_cast<uintptr_t>(&AnimatedImageDrawable_destruct));
122}
123
Leon Scroggins III5b7f4262018-01-26 11:03:54 -0500124// Java's FINISHED relies on this being -1
125static_assert(SkAnimatedImage::kFinished == -1);
126
Leon Scroggins III671cce22018-01-14 16:52:17 -0500127static jlong AnimatedImageDrawable_nDraw(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
Derek Sollenberger2d142132018-01-22 10:25:26 -0500128 jlong canvasPtr) {
Leon Scroggins III671cce22018-01-14 16:52:17 -0500129 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
Leon Scroggins III671cce22018-01-14 16:52:17 -0500130 auto* canvas = reinterpret_cast<Canvas*>(canvasPtr);
Derek Sollenberger2d142132018-01-22 10:25:26 -0500131 return (jlong) canvas->drawAnimatedImage(drawable);
Leon Scroggins III671cce22018-01-14 16:52:17 -0500132}
133
134static void AnimatedImageDrawable_nSetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
135 jint alpha) {
136 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
Derek Sollenberger2d142132018-01-22 10:25:26 -0500137 drawable->setStagingAlpha(alpha);
Leon Scroggins III671cce22018-01-14 16:52:17 -0500138}
139
140static jlong AnimatedImageDrawable_nGetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
141 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
Derek Sollenberger2d142132018-01-22 10:25:26 -0500142 return drawable->getStagingAlpha();
Leon Scroggins III671cce22018-01-14 16:52:17 -0500143}
144
145static void AnimatedImageDrawable_nSetColorFilter(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
146 jlong nativeFilter) {
147 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
148 auto* filter = reinterpret_cast<SkColorFilter*>(nativeFilter);
Derek Sollenberger2d142132018-01-22 10:25:26 -0500149 drawable->setStagingColorFilter(sk_ref_sp(filter));
Leon Scroggins III671cce22018-01-14 16:52:17 -0500150}
151
152static jboolean AnimatedImageDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
153 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
Derek Sollenberger2d142132018-01-22 10:25:26 -0500154 return drawable->isRunning();
Leon Scroggins III671cce22018-01-14 16:52:17 -0500155}
156
Leon Scroggins III057c91a2018-01-24 13:02:16 -0500157static jboolean AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
Leon Scroggins III671cce22018-01-14 16:52:17 -0500158 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
Leon Scroggins III057c91a2018-01-24 13:02:16 -0500159 return drawable->start();
Leon Scroggins III671cce22018-01-14 16:52:17 -0500160}
161
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500162static jboolean AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
Leon Scroggins III671cce22018-01-14 16:52:17 -0500163 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500164 return drawable->stop();
Leon Scroggins III671cce22018-01-14 16:52:17 -0500165}
166
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500167// Java's LOOP_INFINITE relies on this being the same.
168static_assert(SkCodec::kRepetitionCountInfinite == -1);
169
Leon Scroggins III6de55b82018-02-23 16:11:37 -0500170static jint AnimatedImageDrawable_nGetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
Leon Scroggins III1474b782018-02-23 09:38:12 -0500171 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
172 return drawable->getRepetitionCount();
173}
174
Leon Scroggins III6de55b82018-02-23 16:11:37 -0500175static void AnimatedImageDrawable_nSetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
176 jint loopCount) {
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500177 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
178 drawable->setRepetitionCount(loopCount);
179}
180
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500181class InvokeListener : public MessageHandler {
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500182public:
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500183 InvokeListener(JNIEnv* env, jobject javaObject) {
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500184 LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK);
John Reck3998a012021-07-28 11:04:30 -0400185 mCallbackRef = env->NewGlobalRef(javaObject);
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500186 }
187
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500188 ~InvokeListener() override {
Leon Scroggins IIIf97b29d22020-04-06 12:01:01 -0400189 auto* env = requireEnv(mJvm);
John Reck3998a012021-07-28 11:04:30 -0400190 env->DeleteGlobalRef(mCallbackRef);
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500191 }
192
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500193 virtual void handleMessage(const Message&) override {
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500194 auto* env = get_env_or_die(mJvm);
John Reck3998a012021-07-28 11:04:30 -0400195 env->CallStaticVoidMethod(gAnimatedImageDrawableClass,
196 gAnimatedImageDrawable_callOnAnimationEndMethodID, mCallbackRef);
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500197 }
198
199private:
200 JavaVM* mJvm;
John Reck3998a012021-07-28 11:04:30 -0400201 jobject mCallbackRef;
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500202};
203
204class JniAnimationEndListener : public OnAnimationEndListener {
205public:
206 JniAnimationEndListener(sp<Looper>&& looper, JNIEnv* env, jobject javaObject) {
207 mListener = new InvokeListener(env, javaObject);
208 mLooper = std::move(looper);
209 }
210
211 void onAnimationEnd() override { mLooper->sendMessage(mListener, 0); }
212
213private:
214 sp<InvokeListener> mListener;
215 sp<Looper> mLooper;
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500216};
217
218static void AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv* env, jobject /*clazz*/,
219 jlong nativePtr, jobject jdrawable) {
220 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500221 if (!jdrawable) {
222 drawable->setOnAnimationEndListener(nullptr);
223 } else {
224 sp<Looper> looper = Looper::getForThread();
225 if (!looper.get()) {
226 doThrowISE(env,
227 "Must set AnimatedImageDrawable's AnimationCallback on a thread with a "
228 "looper!");
229 return;
230 }
231
232 drawable->setOnAnimationEndListener(
233 std::make_unique<JniAnimationEndListener>(std::move(looper), env, jdrawable));
234 }
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500235}
236
Leon Scroggins IIIeb3b38e2018-03-20 11:11:13 -0400237static jlong AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
Leon Scroggins III671cce22018-01-14 16:52:17 -0500238 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
Leon Scroggins IIIeb3b38e2018-03-20 11:11:13 -0400239 return drawable->byteSize();
Leon Scroggins III671cce22018-01-14 16:52:17 -0500240}
241
Leon Scroggins III5e8447f2018-03-05 14:22:51 -0500242static void AnimatedImageDrawable_nSetMirrored(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
243 jboolean mirrored) {
244 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
245 drawable->setStagingMirrored(mirrored);
246}
247
Leon Scroggins IIIbe969ef2021-01-20 15:28:39 -0500248static void AnimatedImageDrawable_nSetBounds(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
249 jobject jrect) {
250 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
251 SkRect rect;
252 GraphicsJNI::jrect_to_rect(env, jrect, &rect);
253 drawable->setStagingBounds(rect);
254}
255
Leon Scroggins III671cce22018-01-14 16:52:17 -0500256static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
John Reck3998a012021-07-28 11:04:30 -0400257 {"nCreate", "(JLandroid/graphics/ImageDecoder;IIJZLandroid/graphics/Rect;)J",
258 (void*)AnimatedImageDrawable_nCreate},
259 {"nGetNativeFinalizer", "()J", (void*)AnimatedImageDrawable_nGetNativeFinalizer},
260 {"nDraw", "(JJ)J", (void*)AnimatedImageDrawable_nDraw},
261 {"nSetAlpha", "(JI)V", (void*)AnimatedImageDrawable_nSetAlpha},
262 {"nGetAlpha", "(J)I", (void*)AnimatedImageDrawable_nGetAlpha},
263 {"nSetColorFilter", "(JJ)V", (void*)AnimatedImageDrawable_nSetColorFilter},
264 {"nIsRunning", "(J)Z", (void*)AnimatedImageDrawable_nIsRunning},
265 {"nStart", "(J)Z", (void*)AnimatedImageDrawable_nStart},
266 {"nStop", "(J)Z", (void*)AnimatedImageDrawable_nStop},
267 {"nGetRepeatCount", "(J)I", (void*)AnimatedImageDrawable_nGetRepeatCount},
268 {"nSetRepeatCount", "(JI)V", (void*)AnimatedImageDrawable_nSetRepeatCount},
269 {"nSetOnAnimationEndListener", "(JLjava/lang/ref/WeakReference;)V",
270 (void*)AnimatedImageDrawable_nSetOnAnimationEndListener},
271 {"nNativeByteSize", "(J)J", (void*)AnimatedImageDrawable_nNativeByteSize},
272 {"nSetMirrored", "(JZ)V", (void*)AnimatedImageDrawable_nSetMirrored},
273 {"nSetBounds", "(JLandroid/graphics/Rect;)V", (void*)AnimatedImageDrawable_nSetBounds},
Leon Scroggins III671cce22018-01-14 16:52:17 -0500274};
275
276int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
John Reck3998a012021-07-28 11:04:30 -0400277 gAnimatedImageDrawableClass = reinterpret_cast<jclass>(env->NewGlobalRef(
278 FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable")));
279 gAnimatedImageDrawable_callOnAnimationEndMethodID =
280 GetStaticMethodIDOrDie(env, gAnimatedImageDrawableClass, "callOnAnimationEnd",
281 "(Ljava/lang/ref/WeakReference;)V");
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500282
Leon Scroggins III671cce22018-01-14 16:52:17 -0500283 return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable",
284 gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods));
285}
286