xref: /aosp_15_r20/frameworks/base/libs/hwui/jni/AnimatedImageDrawable.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
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 <SkAndroidCodec.h>
18 #include <SkAnimatedImage.h>
19 #include <SkColorFilter.h>
20 #include <SkEncodedImageFormat.h>
21 #include <SkPicture.h>
22 #include <SkPictureRecorder.h>
23 #include <SkRect.h>
24 #include <SkRefCnt.h>
25 #include <hwui/AnimatedImageDrawable.h>
26 #include <hwui/Canvas.h>
27 #include <hwui/ImageDecoder.h>
28 #ifdef __ANDROID__
29 #include <utils/Looper.h>
30 #else
31 #include "utils/MessageHandler.h"
32 #endif
33 
34 #include "ColorFilter.h"
35 #include "GraphicsJNI.h"
36 #include "ImageDecoder.h"
37 #include "Utils.h"
38 
39 using namespace android;
40 
41 static jclass gAnimatedImageDrawableClass;
42 static jmethodID gAnimatedImageDrawable_callOnAnimationEndMethodID;
43 
44 // Note: jpostProcess holds a handle to the ImageDecoder.
AnimatedImageDrawable_nCreate(JNIEnv * env,jobject,jlong nativeImageDecoder,jobject jpostProcess,jint width,jint height,jlong colorSpaceHandle,jboolean extended,jobject jsubset)45 static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/,
46                                            jlong nativeImageDecoder, jobject jpostProcess,
47                                            jint width, jint height, jlong colorSpaceHandle,
48                                            jboolean extended, jobject jsubset) {
49     if (nativeImageDecoder == 0) {
50         doThrowIOE(env, "Cannot create AnimatedImageDrawable from null!");
51         return 0;
52     }
53 
54     auto* imageDecoder = reinterpret_cast<ImageDecoder*>(nativeImageDecoder);
55     SkIRect subset;
56     if (jsubset) {
57         GraphicsJNI::jrect_to_irect(env, jsubset, &subset);
58     } else {
59         subset = SkIRect::MakeWH(width, height);
60     }
61 
62     bool hasRestoreFrame = false;
63     if (imageDecoder->mCodec->getEncodedFormat() != SkEncodedImageFormat::kWEBP) {
64         const int frameCount = imageDecoder->mCodec->codec()->getFrameCount();
65         for (int i = 0; i < frameCount; ++i) {
66             SkCodec::FrameInfo frameInfo;
67             if (!imageDecoder->mCodec->codec()->getFrameInfo(i, &frameInfo)) {
68                 doThrowIOE(env, "Failed to read frame info!");
69                 return 0;
70             }
71             if (frameInfo.fDisposalMethod == SkCodecAnimation::DisposalMethod::kRestorePrevious) {
72                 hasRestoreFrame = true;
73                 break;
74             }
75         }
76     }
77 
78     auto info = imageDecoder->mCodec->getInfo().makeWH(width, height)
79         .makeColorSpace(GraphicsJNI::getNativeColorSpace(colorSpaceHandle));
80     if (extended) {
81         info = info.makeColorType(kRGBA_F16_SkColorType);
82     }
83 
84     size_t bytesUsed = info.computeMinByteSize();
85     // SkAnimatedImage has one SkBitmap for decoding, plus an extra one if there is a
86     // kRestorePrevious frame. AnimatedImageDrawable has two SkPictures storing the current
87     // frame and the next frame. (The former assumes that the image is animated, and the
88     // latter assumes that it is drawn to a hardware canvas.)
89     bytesUsed *= hasRestoreFrame ? 4 : 3;
90     sk_sp<SkPicture> picture;
91     if (jpostProcess) {
92         SkRect bounds = SkRect::MakeWH(subset.width(), subset.height());
93 
94         SkPictureRecorder recorder;
95         SkCanvas* skcanvas = recorder.beginRecording(bounds);
96         std::unique_ptr<Canvas> canvas(Canvas::create_canvas(skcanvas));
97         postProcessAndRelease(env, jpostProcess, std::move(canvas));
98         if (env->ExceptionCheck()) {
99             return 0;
100         }
101         picture = recorder.finishRecordingAsPicture();
102         bytesUsed += picture->approximateBytesUsed();
103     }
104 
105     SkEncodedImageFormat format = imageDecoder->mCodec->getEncodedFormat();
106     sk_sp<SkAnimatedImage> animatedImg = SkAnimatedImage::Make(std::move(imageDecoder->mCodec),
107                                                                info, subset,
108                                                                std::move(picture));
109     if (!animatedImg) {
110         doThrowIOE(env, "Failed to create drawable");
111         return 0;
112     }
113 
114     bytesUsed += sizeof(animatedImg.get());
115 
116     sk_sp<AnimatedImageDrawable> drawable(
117             new AnimatedImageDrawable(std::move(animatedImg), bytesUsed, format));
118     return reinterpret_cast<jlong>(drawable.release());
119 }
120 
AnimatedImageDrawable_destruct(AnimatedImageDrawable * drawable)121 static void AnimatedImageDrawable_destruct(AnimatedImageDrawable* drawable) {
122     SkSafeUnref(drawable);
123 }
124 
AnimatedImageDrawable_nGetNativeFinalizer(JNIEnv *,jobject)125 static jlong AnimatedImageDrawable_nGetNativeFinalizer(JNIEnv* /*env*/, jobject /*clazz*/) {
126     return static_cast<jlong>(reinterpret_cast<uintptr_t>(&AnimatedImageDrawable_destruct));
127 }
128 
129 // Java's FINISHED relies on this being -1
130 static_assert(SkAnimatedImage::kFinished == -1);
131 
AnimatedImageDrawable_nDraw(JNIEnv * env,jobject,jlong nativePtr,jlong canvasPtr)132 static jlong AnimatedImageDrawable_nDraw(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
133                                          jlong canvasPtr) {
134     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
135     auto* canvas = reinterpret_cast<Canvas*>(canvasPtr);
136     return (jlong) canvas->drawAnimatedImage(drawable);
137 }
138 
AnimatedImageDrawable_nSetAlpha(JNIEnv * env,jobject,jlong nativePtr,jint alpha)139 static void AnimatedImageDrawable_nSetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
140                                             jint alpha) {
141     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
142     drawable->setStagingAlpha(alpha);
143 }
144 
AnimatedImageDrawable_nGetAlpha(JNIEnv * env,jobject,jlong nativePtr)145 static jlong AnimatedImageDrawable_nGetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
146     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
147     return drawable->getStagingAlpha();
148 }
149 
AnimatedImageDrawable_nSetColorFilter(JNIEnv * env,jobject,jlong nativePtr,jlong nativeFilter)150 static void AnimatedImageDrawable_nSetColorFilter(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
151                                                   jlong nativeFilter) {
152     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
153     auto filter = uirenderer::ColorFilter::fromJava(nativeFilter);
154     auto skColorFilter = filter != nullptr ? filter->getInstance() : sk_sp<SkColorFilter>();
155     drawable->setStagingColorFilter(skColorFilter);
156 }
157 
AnimatedImageDrawable_nIsRunning(JNIEnv * env,jobject,jlong nativePtr)158 static jboolean AnimatedImageDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
159     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
160     return drawable->isRunning();
161 }
162 
AnimatedImageDrawable_nStart(JNIEnv * env,jobject,jlong nativePtr)163 static jboolean AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
164     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
165     return drawable->start();
166 }
167 
AnimatedImageDrawable_nStop(JNIEnv * env,jobject,jlong nativePtr)168 static jboolean AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
169     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
170     return drawable->stop();
171 }
172 
173 // Java's LOOP_INFINITE relies on this being the same.
174 static_assert(SkCodec::kRepetitionCountInfinite == -1);
175 
AnimatedImageDrawable_nGetRepeatCount(JNIEnv * env,jobject,jlong nativePtr)176 static jint AnimatedImageDrawable_nGetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
177     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
178     return drawable->getRepetitionCount();
179 }
180 
AnimatedImageDrawable_nSetRepeatCount(JNIEnv * env,jobject,jlong nativePtr,jint loopCount)181 static void AnimatedImageDrawable_nSetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
182                                                   jint loopCount) {
183     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
184     drawable->setRepetitionCount(loopCount);
185 }
186 
187 class InvokeListener : public MessageHandler {
188 public:
InvokeListener(JNIEnv * env,jobject javaObject)189     InvokeListener(JNIEnv* env, jobject javaObject) {
190         LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK);
191         mCallbackRef = env->NewGlobalRef(javaObject);
192     }
193 
~InvokeListener()194     ~InvokeListener() override {
195         auto* env = requireEnv(mJvm);
196         env->DeleteGlobalRef(mCallbackRef);
197     }
198 
handleMessage(const Message &)199     virtual void handleMessage(const Message&) override {
200         auto* env = get_env_or_die(mJvm);
201         env->CallStaticVoidMethod(gAnimatedImageDrawableClass,
202                                   gAnimatedImageDrawable_callOnAnimationEndMethodID, mCallbackRef);
203     }
204 
205 private:
206     JavaVM* mJvm;
207     jobject mCallbackRef;
208 };
209 
210 class JniAnimationEndListener : public OnAnimationEndListener {
211 #ifdef __ANDROID__
212 public:
JniAnimationEndListener(sp<Looper> && looper,JNIEnv * env,jobject javaObject)213     JniAnimationEndListener(sp<Looper>&& looper, JNIEnv* env, jobject javaObject) {
214         mListener = new InvokeListener(env, javaObject);
215         mLooper = std::move(looper);
216     }
217 
onAnimationEnd()218     void onAnimationEnd() override { mLooper->sendMessage(mListener, 0); }
219 
220 private:
221     sp<InvokeListener> mListener;
222     sp<Looper> mLooper;
223 #else
224 public:
225     JniAnimationEndListener(JNIEnv* env, jobject javaObject) {
226         mListener = new InvokeListener(env, javaObject);
227     }
228 
229     void onAnimationEnd() override { mListener->handleMessage(0); }
230 
231 private:
232     sp<InvokeListener> mListener;
233 #endif
234 };
235 
AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv * env,jobject,jlong nativePtr,jobject jdrawable)236 static void AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv* env, jobject /*clazz*/,
237                                                              jlong nativePtr, jobject jdrawable) {
238     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
239     if (!jdrawable) {
240         drawable->setOnAnimationEndListener(nullptr);
241     } else {
242 #ifdef __ANDROID__
243         sp<Looper> looper = Looper::getForThread();
244         if (!looper.get()) {
245             doThrowISE(env,
246                        "Must set AnimatedImageDrawable's AnimationCallback on a thread with a "
247                        "looper!");
248             return;
249         }
250 
251         drawable->setOnAnimationEndListener(
252                 std::make_unique<JniAnimationEndListener>(std::move(looper), env, jdrawable));
253 #else
254         drawable->setOnAnimationEndListener(
255                 std::make_unique<JniAnimationEndListener>(env, jdrawable));
256 #endif
257     }
258 }
259 
AnimatedImageDrawable_nNativeByteSize(JNIEnv * env,jobject,jlong nativePtr)260 static jlong AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
261     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
262     return drawable->byteSize();
263 }
264 
AnimatedImageDrawable_nSetMirrored(JNIEnv * env,jobject,jlong nativePtr,jboolean mirrored)265 static void AnimatedImageDrawable_nSetMirrored(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
266                                                jboolean mirrored) {
267     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
268     drawable->setStagingMirrored(mirrored);
269 }
270 
AnimatedImageDrawable_nSetBounds(JNIEnv * env,jobject,jlong nativePtr,jobject jrect)271 static void AnimatedImageDrawable_nSetBounds(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
272                                              jobject jrect) {
273     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
274     SkRect rect;
275     GraphicsJNI::jrect_to_rect(env, jrect, &rect);
276     drawable->setStagingBounds(rect);
277 }
278 
AnimatedImageDrawable_nSetFilterBitmap(JNIEnv * env,jobject,jlong nativePtr,jboolean filterBitmap)279 static jboolean AnimatedImageDrawable_nSetFilterBitmap(JNIEnv* env, jobject /*clazz*/,
280                                                        jlong nativePtr, jboolean filterBitmap) {
281     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
282     return drawable->setFilterBitmap(filterBitmap);
283 }
284 
AnimatedImageDrawable_nGetFilterBitmap(JNIEnv * env,jobject,jlong nativePtr)285 static jboolean AnimatedImageDrawable_nGetFilterBitmap(JNIEnv* env, jobject /*clazz*/,
286                                                        jlong nativePtr) {
287     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
288     return drawable->getFilterBitmap();
289 }
290 
291 static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
292         {"nCreate", "(JLandroid/graphics/ImageDecoder;IIJZLandroid/graphics/Rect;)J",
293          (void*)AnimatedImageDrawable_nCreate},
294         {"nGetNativeFinalizer", "()J", (void*)AnimatedImageDrawable_nGetNativeFinalizer},
295         {"nDraw", "(JJ)J", (void*)AnimatedImageDrawable_nDraw},
296         {"nSetAlpha", "(JI)V", (void*)AnimatedImageDrawable_nSetAlpha},
297         {"nGetAlpha", "(J)I", (void*)AnimatedImageDrawable_nGetAlpha},
298         {"nSetColorFilter", "(JJ)V", (void*)AnimatedImageDrawable_nSetColorFilter},
299         {"nIsRunning", "(J)Z", (void*)AnimatedImageDrawable_nIsRunning},
300         {"nStart", "(J)Z", (void*)AnimatedImageDrawable_nStart},
301         {"nStop", "(J)Z", (void*)AnimatedImageDrawable_nStop},
302         {"nGetRepeatCount", "(J)I", (void*)AnimatedImageDrawable_nGetRepeatCount},
303         {"nSetRepeatCount", "(JI)V", (void*)AnimatedImageDrawable_nSetRepeatCount},
304         {"nSetOnAnimationEndListener", "(JLjava/lang/ref/WeakReference;)V",
305          (void*)AnimatedImageDrawable_nSetOnAnimationEndListener},
306         {"nNativeByteSize", "(J)J", (void*)AnimatedImageDrawable_nNativeByteSize},
307         {"nSetMirrored", "(JZ)V", (void*)AnimatedImageDrawable_nSetMirrored},
308         {"nSetBounds", "(JLandroid/graphics/Rect;)V", (void*)AnimatedImageDrawable_nSetBounds},
309         {"nSetFilterBitmap", "(JZ)Z", (void*)AnimatedImageDrawable_nSetFilterBitmap},
310         {"nGetFilterBitmap", "(J)Z", (void*)AnimatedImageDrawable_nGetFilterBitmap},
311 };
312 
register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv * env)313 int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
314     gAnimatedImageDrawableClass = reinterpret_cast<jclass>(env->NewGlobalRef(
315             FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable")));
316     gAnimatedImageDrawable_callOnAnimationEndMethodID =
317             GetStaticMethodIDOrDie(env, gAnimatedImageDrawableClass, "callOnAnimationEnd",
318                                    "(Ljava/lang/ref/WeakReference;)V");
319 
320     return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable",
321             gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods));
322 }
323 
324