blob: 0f80c55d0ed0fd5e5cb5f20500e65cc65441a976 [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
Leon Scroggins III671cce22018-01-14 16:52:17 -050017#include <SkAndroidCodec.h>
18#include <SkAnimatedImage.h>
19#include <SkColorFilter.h>
Kevin Lubick1175dc02022-02-28 12:41:27 -050020#include <SkEncodedImageFormat.h>
Leon Scroggins III671cce22018-01-14 16:52:17 -050021#include <SkPicture.h>
22#include <SkPictureRecorder.h>
Kevin Lubick1175dc02022-02-28 12:41:27 -050023#include <SkRect.h>
24#include <SkRefCnt.h>
Leon Scroggins III5b7f4262018-01-26 11:03:54 -050025#include <hwui/AnimatedImageDrawable.h>
26#include <hwui/Canvas.h>
Nader Jawadf0ea9e12023-05-02 15:18:00 -070027#include <hwui/ImageDecoder.h>
Jerome Gaillard01f8f4f2024-03-19 14:24:35 +000028#ifdef __ANDROID__
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -050029#include <utils/Looper.h>
Jerome Gaillard01f8f4f2024-03-19 14:24:35 +000030#endif
Leon Scroggins III671cce22018-01-14 16:52:17 -050031
Nader Jawadf0ea9e12023-05-02 15:18:00 -070032#include "ColorFilter.h"
33#include "GraphicsJNI.h"
34#include "ImageDecoder.h"
35#include "Utils.h"
36
Leon Scroggins III671cce22018-01-14 16:52:17 -050037using namespace android;
38
John Reck3998a012021-07-28 11:04:30 -040039static jclass gAnimatedImageDrawableClass;
40static jmethodID gAnimatedImageDrawable_callOnAnimationEndMethodID;
Leon Scroggins III671cce22018-01-14 16:52:17 -050041
42// Note: jpostProcess holds a handle to the ImageDecoder.
43static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/,
44 jlong nativeImageDecoder, jobject jpostProcess,
Leon Scroggins IIIeac14232019-02-04 10:30:22 -050045 jint width, jint height, jlong colorSpaceHandle,
46 jboolean extended, jobject jsubset) {
Leon Scroggins III671cce22018-01-14 16:52:17 -050047 if (nativeImageDecoder == 0) {
48 doThrowIOE(env, "Cannot create AnimatedImageDrawable from null!");
49 return 0;
50 }
51
52 auto* imageDecoder = reinterpret_cast<ImageDecoder*>(nativeImageDecoder);
Leon Scroggins III671cce22018-01-14 16:52:17 -050053 SkIRect subset;
54 if (jsubset) {
55 GraphicsJNI::jrect_to_irect(env, jsubset, &subset);
56 } else {
57 subset = SkIRect::MakeWH(width, height);
58 }
59
Leon Scroggins IIIeb3b38e2018-03-20 11:11:13 -040060 bool hasRestoreFrame = false;
Leon Scroggins IIIeac14232019-02-04 10:30:22 -050061 if (imageDecoder->mCodec->getEncodedFormat() != SkEncodedImageFormat::kWEBP) {
Leon Scroggins IIIeb3b38e2018-03-20 11:11:13 -040062 const int frameCount = imageDecoder->mCodec->codec()->getFrameCount();
63 for (int i = 0; i < frameCount; ++i) {
64 SkCodec::FrameInfo frameInfo;
65 if (!imageDecoder->mCodec->codec()->getFrameInfo(i, &frameInfo)) {
66 doThrowIOE(env, "Failed to read frame info!");
67 return 0;
68 }
69 if (frameInfo.fDisposalMethod == SkCodecAnimation::DisposalMethod::kRestorePrevious) {
70 hasRestoreFrame = true;
71 break;
72 }
73 }
74 }
75
Leon Scroggins IIIeac14232019-02-04 10:30:22 -050076 auto info = imageDecoder->mCodec->getInfo().makeWH(width, height)
77 .makeColorSpace(GraphicsJNI::getNativeColorSpace(colorSpaceHandle));
78 if (extended) {
79 info = info.makeColorType(kRGBA_F16_SkColorType);
80 }
81
Leon Scroggins IIIeb3b38e2018-03-20 11:11:13 -040082 size_t bytesUsed = info.computeMinByteSize();
83 // SkAnimatedImage has one SkBitmap for decoding, plus an extra one if there is a
84 // kRestorePrevious frame. AnimatedImageDrawable has two SkPictures storing the current
85 // frame and the next frame. (The former assumes that the image is animated, and the
86 // latter assumes that it is drawn to a hardware canvas.)
87 bytesUsed *= hasRestoreFrame ? 4 : 3;
Leon Scroggins III671cce22018-01-14 16:52:17 -050088 sk_sp<SkPicture> picture;
89 if (jpostProcess) {
90 SkRect bounds = SkRect::MakeWH(subset.width(), subset.height());
91
92 SkPictureRecorder recorder;
93 SkCanvas* skcanvas = recorder.beginRecording(bounds);
94 std::unique_ptr<Canvas> canvas(Canvas::create_canvas(skcanvas));
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -050095 postProcessAndRelease(env, jpostProcess, std::move(canvas));
Leon Scroggins III671cce22018-01-14 16:52:17 -050096 if (env->ExceptionCheck()) {
97 return 0;
98 }
99 picture = recorder.finishRecordingAsPicture();
Leon Scroggins IIIeb3b38e2018-03-20 11:11:13 -0400100 bytesUsed += picture->approximateBytesUsed();
Leon Scroggins III671cce22018-01-14 16:52:17 -0500101 }
102
John Reck2be87bb2023-04-05 17:33:00 -0400103 SkEncodedImageFormat format = imageDecoder->mCodec->getEncodedFormat();
Derek Sollenberger2d142132018-01-22 10:25:26 -0500104 sk_sp<SkAnimatedImage> animatedImg = SkAnimatedImage::Make(std::move(imageDecoder->mCodec),
Leon Scroggins IIIeac14232019-02-04 10:30:22 -0500105 info, subset,
Derek Sollenberger2d142132018-01-22 10:25:26 -0500106 std::move(picture));
107 if (!animatedImg) {
Leon Scroggins III671cce22018-01-14 16:52:17 -0500108 doThrowIOE(env, "Failed to create drawable");
109 return 0;
110 }
Leon Scroggins III671cce22018-01-14 16:52:17 -0500111
Leon Scroggins IIIeb3b38e2018-03-20 11:11:13 -0400112 bytesUsed += sizeof(animatedImg.get());
113
John Reck2be87bb2023-04-05 17:33:00 -0400114 sk_sp<AnimatedImageDrawable> drawable(
115 new AnimatedImageDrawable(std::move(animatedImg), bytesUsed, format));
Leon Scroggins III671cce22018-01-14 16:52:17 -0500116 return reinterpret_cast<jlong>(drawable.release());
117}
118
119static void AnimatedImageDrawable_destruct(AnimatedImageDrawable* drawable) {
Derek Sollenberger2d142132018-01-22 10:25:26 -0500120 SkSafeUnref(drawable);
Leon Scroggins III671cce22018-01-14 16:52:17 -0500121}
122
123static jlong AnimatedImageDrawable_nGetNativeFinalizer(JNIEnv* /*env*/, jobject /*clazz*/) {
124 return static_cast<jlong>(reinterpret_cast<uintptr_t>(&AnimatedImageDrawable_destruct));
125}
126
Leon Scroggins III5b7f4262018-01-26 11:03:54 -0500127// Java's FINISHED relies on this being -1
128static_assert(SkAnimatedImage::kFinished == -1);
129
Leon Scroggins III671cce22018-01-14 16:52:17 -0500130static jlong AnimatedImageDrawable_nDraw(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
Derek Sollenberger2d142132018-01-22 10:25:26 -0500131 jlong canvasPtr) {
Leon Scroggins III671cce22018-01-14 16:52:17 -0500132 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
Leon Scroggins III671cce22018-01-14 16:52:17 -0500133 auto* canvas = reinterpret_cast<Canvas*>(canvasPtr);
Derek Sollenberger2d142132018-01-22 10:25:26 -0500134 return (jlong) canvas->drawAnimatedImage(drawable);
Leon Scroggins III671cce22018-01-14 16:52:17 -0500135}
136
137static void AnimatedImageDrawable_nSetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
138 jint alpha) {
139 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
Derek Sollenberger2d142132018-01-22 10:25:26 -0500140 drawable->setStagingAlpha(alpha);
Leon Scroggins III671cce22018-01-14 16:52:17 -0500141}
142
143static jlong AnimatedImageDrawable_nGetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
144 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
Derek Sollenberger2d142132018-01-22 10:25:26 -0500145 return drawable->getStagingAlpha();
Leon Scroggins III671cce22018-01-14 16:52:17 -0500146}
147
148static void AnimatedImageDrawable_nSetColorFilter(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
149 jlong nativeFilter) {
150 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
Nader Jawadf0ea9e12023-05-02 15:18:00 -0700151 auto filter = uirenderer::ColorFilter::fromJava(nativeFilter);
152 auto skColorFilter = filter != nullptr ? filter->getInstance() : sk_sp<SkColorFilter>();
153 drawable->setStagingColorFilter(skColorFilter);
Leon Scroggins III671cce22018-01-14 16:52:17 -0500154}
155
156static jboolean AnimatedImageDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
157 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
Derek Sollenberger2d142132018-01-22 10:25:26 -0500158 return drawable->isRunning();
Leon Scroggins III671cce22018-01-14 16:52:17 -0500159}
160
Leon Scroggins III057c91a2018-01-24 13:02:16 -0500161static jboolean AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
Leon Scroggins III671cce22018-01-14 16:52:17 -0500162 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
Leon Scroggins III057c91a2018-01-24 13:02:16 -0500163 return drawable->start();
Leon Scroggins III671cce22018-01-14 16:52:17 -0500164}
165
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500166static jboolean AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
Leon Scroggins III671cce22018-01-14 16:52:17 -0500167 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500168 return drawable->stop();
Leon Scroggins III671cce22018-01-14 16:52:17 -0500169}
170
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500171// Java's LOOP_INFINITE relies on this being the same.
172static_assert(SkCodec::kRepetitionCountInfinite == -1);
173
Leon Scroggins III6de55b82018-02-23 16:11:37 -0500174static jint AnimatedImageDrawable_nGetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
Leon Scroggins III1474b782018-02-23 09:38:12 -0500175 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
176 return drawable->getRepetitionCount();
177}
178
Leon Scroggins III6de55b82018-02-23 16:11:37 -0500179static void AnimatedImageDrawable_nSetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
180 jint loopCount) {
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500181 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
182 drawable->setRepetitionCount(loopCount);
183}
184
Jerome Gaillard01f8f4f2024-03-19 14:24:35 +0000185#ifndef __ANDROID__
186struct Message {
187 Message(int w) {}
188};
189
190class MessageHandler : public virtual RefBase {
191protected:
192 virtual ~MessageHandler() override {}
193
194public:
195 /**
196 * Handles a message.
197 */
198 virtual void handleMessage(const Message& message) = 0;
199};
200#endif
201
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500202class InvokeListener : public MessageHandler {
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500203public:
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500204 InvokeListener(JNIEnv* env, jobject javaObject) {
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500205 LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK);
John Reck3998a012021-07-28 11:04:30 -0400206 mCallbackRef = env->NewGlobalRef(javaObject);
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500207 }
208
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500209 ~InvokeListener() override {
Leon Scroggins IIIf97b29d22020-04-06 12:01:01 -0400210 auto* env = requireEnv(mJvm);
John Reck3998a012021-07-28 11:04:30 -0400211 env->DeleteGlobalRef(mCallbackRef);
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500212 }
213
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500214 virtual void handleMessage(const Message&) override {
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500215 auto* env = get_env_or_die(mJvm);
John Reck3998a012021-07-28 11:04:30 -0400216 env->CallStaticVoidMethod(gAnimatedImageDrawableClass,
217 gAnimatedImageDrawable_callOnAnimationEndMethodID, mCallbackRef);
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500218 }
219
220private:
221 JavaVM* mJvm;
John Reck3998a012021-07-28 11:04:30 -0400222 jobject mCallbackRef;
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500223};
224
225class JniAnimationEndListener : public OnAnimationEndListener {
Jerome Gaillard01f8f4f2024-03-19 14:24:35 +0000226#ifdef __ANDROID__
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500227public:
228 JniAnimationEndListener(sp<Looper>&& looper, JNIEnv* env, jobject javaObject) {
229 mListener = new InvokeListener(env, javaObject);
230 mLooper = std::move(looper);
231 }
232
233 void onAnimationEnd() override { mLooper->sendMessage(mListener, 0); }
234
235private:
236 sp<InvokeListener> mListener;
237 sp<Looper> mLooper;
Jerome Gaillard01f8f4f2024-03-19 14:24:35 +0000238#else
239public:
240 JniAnimationEndListener(JNIEnv* env, jobject javaObject) {
241 mListener = new InvokeListener(env, javaObject);
242 }
243
244 void onAnimationEnd() override { mListener->handleMessage(0); }
245
246private:
247 sp<InvokeListener> mListener;
248#endif
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500249};
250
251static void AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv* env, jobject /*clazz*/,
252 jlong nativePtr, jobject jdrawable) {
253 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500254 if (!jdrawable) {
255 drawable->setOnAnimationEndListener(nullptr);
256 } else {
Jerome Gaillard01f8f4f2024-03-19 14:24:35 +0000257#ifdef __ANDROID__
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500258 sp<Looper> looper = Looper::getForThread();
259 if (!looper.get()) {
260 doThrowISE(env,
261 "Must set AnimatedImageDrawable's AnimationCallback on a thread with a "
262 "looper!");
263 return;
264 }
265
266 drawable->setOnAnimationEndListener(
267 std::make_unique<JniAnimationEndListener>(std::move(looper), env, jdrawable));
Jerome Gaillard01f8f4f2024-03-19 14:24:35 +0000268#else
269 drawable->setOnAnimationEndListener(
270 std::make_unique<JniAnimationEndListener>(env, jdrawable));
271#endif
Leon Scroggins IIIbeaf5d92018-01-26 11:03:54 -0500272 }
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500273}
274
Leon Scroggins IIIeb3b38e2018-03-20 11:11:13 -0400275static jlong AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
Leon Scroggins III671cce22018-01-14 16:52:17 -0500276 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
Leon Scroggins IIIeb3b38e2018-03-20 11:11:13 -0400277 return drawable->byteSize();
Leon Scroggins III671cce22018-01-14 16:52:17 -0500278}
279
Leon Scroggins III5e8447f2018-03-05 14:22:51 -0500280static void AnimatedImageDrawable_nSetMirrored(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
281 jboolean mirrored) {
282 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
283 drawable->setStagingMirrored(mirrored);
284}
285
Leon Scroggins IIIbe969ef2021-01-20 15:28:39 -0500286static void AnimatedImageDrawable_nSetBounds(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
287 jobject jrect) {
288 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
289 SkRect rect;
290 GraphicsJNI::jrect_to_rect(env, jrect, &rect);
291 drawable->setStagingBounds(rect);
292}
293
Leon Scroggins III671cce22018-01-14 16:52:17 -0500294static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
John Reck3998a012021-07-28 11:04:30 -0400295 {"nCreate", "(JLandroid/graphics/ImageDecoder;IIJZLandroid/graphics/Rect;)J",
296 (void*)AnimatedImageDrawable_nCreate},
297 {"nGetNativeFinalizer", "()J", (void*)AnimatedImageDrawable_nGetNativeFinalizer},
298 {"nDraw", "(JJ)J", (void*)AnimatedImageDrawable_nDraw},
299 {"nSetAlpha", "(JI)V", (void*)AnimatedImageDrawable_nSetAlpha},
300 {"nGetAlpha", "(J)I", (void*)AnimatedImageDrawable_nGetAlpha},
301 {"nSetColorFilter", "(JJ)V", (void*)AnimatedImageDrawable_nSetColorFilter},
302 {"nIsRunning", "(J)Z", (void*)AnimatedImageDrawable_nIsRunning},
303 {"nStart", "(J)Z", (void*)AnimatedImageDrawable_nStart},
304 {"nStop", "(J)Z", (void*)AnimatedImageDrawable_nStop},
305 {"nGetRepeatCount", "(J)I", (void*)AnimatedImageDrawable_nGetRepeatCount},
306 {"nSetRepeatCount", "(JI)V", (void*)AnimatedImageDrawable_nSetRepeatCount},
307 {"nSetOnAnimationEndListener", "(JLjava/lang/ref/WeakReference;)V",
308 (void*)AnimatedImageDrawable_nSetOnAnimationEndListener},
309 {"nNativeByteSize", "(J)J", (void*)AnimatedImageDrawable_nNativeByteSize},
310 {"nSetMirrored", "(JZ)V", (void*)AnimatedImageDrawable_nSetMirrored},
311 {"nSetBounds", "(JLandroid/graphics/Rect;)V", (void*)AnimatedImageDrawable_nSetBounds},
Leon Scroggins III671cce22018-01-14 16:52:17 -0500312};
313
314int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
John Reck3998a012021-07-28 11:04:30 -0400315 gAnimatedImageDrawableClass = reinterpret_cast<jclass>(env->NewGlobalRef(
316 FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable")));
317 gAnimatedImageDrawable_callOnAnimationEndMethodID =
318 GetStaticMethodIDOrDie(env, gAnimatedImageDrawableClass, "callOnAnimationEnd",
319 "(Ljava/lang/ref/WeakReference;)V");
Leon Scroggins III127d31a2018-01-19 12:29:47 -0500320
Leon Scroggins III671cce22018-01-14 16:52:17 -0500321 return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable",
322 gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods));
323}
324