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 "GraphicsJNI.h"
18 #include "ImageDecoder.h"
19 #include "Utils.h"
20 
21 #include <SkAndroidCodec.h>
22 #include <SkAnimatedImage.h>
23 #include <SkColorFilter.h>
24 #include <SkEncodedImageFormat.h>
25 #include <SkPicture.h>
26 #include <SkPictureRecorder.h>
27 #include <SkRect.h>
28 #include <SkRefCnt.h>
29 #include <hwui/AnimatedImageDrawable.h>
30 #include <hwui/ImageDecoder.h>
31 #include <hwui/Canvas.h>
32 #include <utils/Looper.h>
33 
34 using namespace android;
35 
36 static jclass gAnimatedImageDrawableClass;
37 static jmethodID gAnimatedImageDrawable_callOnAnimationEndMethodID;
38 
39 // 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)40 static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/,
41                                            jlong nativeImageDecoder, jobject jpostProcess,
42                                            jint width, jint height, jlong colorSpaceHandle,
43                                            jboolean extended, jobject jsubset) {
44     if (nativeImageDecoder == 0) {
45         doThrowIOE(env, "Cannot create AnimatedImageDrawable from null!");
46         return 0;
47     }
48 
49     auto* imageDecoder = reinterpret_cast<ImageDecoder*>(nativeImageDecoder);
50     SkIRect subset;
51     if (jsubset) {
52         GraphicsJNI::jrect_to_irect(env, jsubset, &subset);
53     } else {
54         subset = SkIRect::MakeWH(width, height);
55     }
56 
57     bool hasRestoreFrame = false;
58     if (imageDecoder->mCodec->getEncodedFormat() != SkEncodedImageFormat::kWEBP) {
59         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 
73     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 
79     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;
85     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));
92         postProcessAndRelease(env, jpostProcess, std::move(canvas));
93         if (env->ExceptionCheck()) {
94             return 0;
95         }
96         picture = recorder.finishRecordingAsPicture();
97         bytesUsed += picture->approximateBytesUsed();
98     }
99 
100     SkEncodedImageFormat format = imageDecoder->mCodec->getEncodedFormat();
101     sk_sp<SkAnimatedImage> animatedImg = SkAnimatedImage::Make(std::move(imageDecoder->mCodec),
102                                                                info, subset,
103                                                                std::move(picture));
104     if (!animatedImg) {
105         doThrowIOE(env, "Failed to create drawable");
106         return 0;
107     }
108 
109     bytesUsed += sizeof(animatedImg.get());
110 
111     sk_sp<AnimatedImageDrawable> drawable(
112             new AnimatedImageDrawable(std::move(animatedImg), bytesUsed, format));
113     return reinterpret_cast<jlong>(drawable.release());
114 }
115 
AnimatedImageDrawable_destruct(AnimatedImageDrawable * drawable)116 static void AnimatedImageDrawable_destruct(AnimatedImageDrawable* drawable) {
117     SkSafeUnref(drawable);
118 }
119 
AnimatedImageDrawable_nGetNativeFinalizer(JNIEnv *,jobject)120 static jlong AnimatedImageDrawable_nGetNativeFinalizer(JNIEnv* /*env*/, jobject /*clazz*/) {
121     return static_cast<jlong>(reinterpret_cast<uintptr_t>(&AnimatedImageDrawable_destruct));
122 }
123 
124 // Java's FINISHED relies on this being -1
125 static_assert(SkAnimatedImage::kFinished == -1);
126 
AnimatedImageDrawable_nDraw(JNIEnv * env,jobject,jlong nativePtr,jlong canvasPtr)127 static jlong AnimatedImageDrawable_nDraw(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
128                                          jlong canvasPtr) {
129     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
130     auto* canvas = reinterpret_cast<Canvas*>(canvasPtr);
131     return (jlong) canvas->drawAnimatedImage(drawable);
132 }
133 
AnimatedImageDrawable_nSetAlpha(JNIEnv * env,jobject,jlong nativePtr,jint alpha)134 static void AnimatedImageDrawable_nSetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
135                                             jint alpha) {
136     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
137     drawable->setStagingAlpha(alpha);
138 }
139 
AnimatedImageDrawable_nGetAlpha(JNIEnv * env,jobject,jlong nativePtr)140 static jlong AnimatedImageDrawable_nGetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
141     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
142     return drawable->getStagingAlpha();
143 }
144 
AnimatedImageDrawable_nSetColorFilter(JNIEnv * env,jobject,jlong nativePtr,jlong nativeFilter)145 static 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);
149     drawable->setStagingColorFilter(sk_ref_sp(filter));
150 }
151 
AnimatedImageDrawable_nIsRunning(JNIEnv * env,jobject,jlong nativePtr)152 static jboolean AnimatedImageDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
153     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
154     return drawable->isRunning();
155 }
156 
AnimatedImageDrawable_nStart(JNIEnv * env,jobject,jlong nativePtr)157 static jboolean AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
158     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
159     return drawable->start();
160 }
161 
AnimatedImageDrawable_nStop(JNIEnv * env,jobject,jlong nativePtr)162 static jboolean AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
163     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
164     return drawable->stop();
165 }
166 
167 // Java's LOOP_INFINITE relies on this being the same.
168 static_assert(SkCodec::kRepetitionCountInfinite == -1);
169 
AnimatedImageDrawable_nGetRepeatCount(JNIEnv * env,jobject,jlong nativePtr)170 static jint AnimatedImageDrawable_nGetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
171     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
172     return drawable->getRepetitionCount();
173 }
174 
AnimatedImageDrawable_nSetRepeatCount(JNIEnv * env,jobject,jlong nativePtr,jint loopCount)175 static void AnimatedImageDrawable_nSetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
176                                                   jint loopCount) {
177     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
178     drawable->setRepetitionCount(loopCount);
179 }
180 
181 class InvokeListener : public MessageHandler {
182 public:
InvokeListener(JNIEnv * env,jobject javaObject)183     InvokeListener(JNIEnv* env, jobject javaObject) {
184         LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK);
185         mCallbackRef = env->NewGlobalRef(javaObject);
186     }
187 
~InvokeListener()188     ~InvokeListener() override {
189         auto* env = requireEnv(mJvm);
190         env->DeleteGlobalRef(mCallbackRef);
191     }
192 
handleMessage(const Message &)193     virtual void handleMessage(const Message&) override {
194         auto* env = get_env_or_die(mJvm);
195         env->CallStaticVoidMethod(gAnimatedImageDrawableClass,
196                                   gAnimatedImageDrawable_callOnAnimationEndMethodID, mCallbackRef);
197     }
198 
199 private:
200     JavaVM* mJvm;
201     jobject mCallbackRef;
202 };
203 
204 class JniAnimationEndListener : public OnAnimationEndListener {
205 public:
JniAnimationEndListener(sp<Looper> && looper,JNIEnv * env,jobject javaObject)206     JniAnimationEndListener(sp<Looper>&& looper, JNIEnv* env, jobject javaObject) {
207         mListener = new InvokeListener(env, javaObject);
208         mLooper = std::move(looper);
209     }
210 
onAnimationEnd()211     void onAnimationEnd() override { mLooper->sendMessage(mListener, 0); }
212 
213 private:
214     sp<InvokeListener> mListener;
215     sp<Looper> mLooper;
216 };
217 
AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv * env,jobject,jlong nativePtr,jobject jdrawable)218 static void AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv* env, jobject /*clazz*/,
219                                                              jlong nativePtr, jobject jdrawable) {
220     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
221     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     }
235 }
236 
AnimatedImageDrawable_nNativeByteSize(JNIEnv * env,jobject,jlong nativePtr)237 static jlong AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
238     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
239     return drawable->byteSize();
240 }
241 
AnimatedImageDrawable_nSetMirrored(JNIEnv * env,jobject,jlong nativePtr,jboolean mirrored)242 static 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 
AnimatedImageDrawable_nSetBounds(JNIEnv * env,jobject,jlong nativePtr,jobject jrect)248 static 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 
256 static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
257         {"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},
274 };
275 
register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv * env)276 int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
277     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");
282 
283     return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable",
284             gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods));
285 }
286 
287