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