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,jobject jsubset)36 static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/,
37 jlong nativeImageDecoder, jobject jpostProcess,
38 jint width, jint height, jobject jsubset) {
39 if (nativeImageDecoder == 0) {
40 doThrowIOE(env, "Cannot create AnimatedImageDrawable from null!");
41 return 0;
42 }
43
44 auto* imageDecoder = reinterpret_cast<ImageDecoder*>(nativeImageDecoder);
45 const SkISize scaledSize = SkISize::Make(width, height);
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 auto info = imageDecoder->mCodec->getInfo();
54 bool hasRestoreFrame = false;
55 if (imageDecoder->mCodec->getEncodedFormat() == SkEncodedImageFormat::kWEBP) {
56 if (width < info.width() && height < info.height()) {
57 // WebP will scale its SkBitmap to the scaled size.
58 // FIXME: b/73529447 GIF should do the same.
59 info = info.makeWH(width, height);
60 }
61 } else {
62 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
76 size_t bytesUsed = info.computeMinByteSize();
77 // SkAnimatedImage has one SkBitmap for decoding, plus an extra one if there is a
78 // kRestorePrevious frame. AnimatedImageDrawable has two SkPictures storing the current
79 // frame and the next frame. (The former assumes that the image is animated, and the
80 // latter assumes that it is drawn to a hardware canvas.)
81 bytesUsed *= hasRestoreFrame ? 4 : 3;
82 sk_sp<SkPicture> picture;
83 if (jpostProcess) {
84 SkRect bounds = SkRect::MakeWH(subset.width(), subset.height());
85
86 SkPictureRecorder recorder;
87 SkCanvas* skcanvas = recorder.beginRecording(bounds);
88 std::unique_ptr<Canvas> canvas(Canvas::create_canvas(skcanvas));
89 postProcessAndRelease(env, jpostProcess, std::move(canvas));
90 if (env->ExceptionCheck()) {
91 return 0;
92 }
93 picture = recorder.finishRecordingAsPicture();
94 bytesUsed += picture->approximateBytesUsed();
95 }
96
97
98 sk_sp<SkAnimatedImage> animatedImg = SkAnimatedImage::Make(std::move(imageDecoder->mCodec),
99 scaledSize, subset,
100 std::move(picture));
101 if (!animatedImg) {
102 doThrowIOE(env, "Failed to create drawable");
103 return 0;
104 }
105
106 bytesUsed += sizeof(animatedImg.get());
107
108 sk_sp<AnimatedImageDrawable> drawable(new AnimatedImageDrawable(std::move(animatedImg),
109 bytesUsed));
110 return reinterpret_cast<jlong>(drawable.release());
111 }
112
AnimatedImageDrawable_destruct(AnimatedImageDrawable * drawable)113 static void AnimatedImageDrawable_destruct(AnimatedImageDrawable* drawable) {
114 SkSafeUnref(drawable);
115 }
116
AnimatedImageDrawable_nGetNativeFinalizer(JNIEnv *,jobject)117 static jlong AnimatedImageDrawable_nGetNativeFinalizer(JNIEnv* /*env*/, jobject /*clazz*/) {
118 return static_cast<jlong>(reinterpret_cast<uintptr_t>(&AnimatedImageDrawable_destruct));
119 }
120
121 // Java's FINISHED relies on this being -1
122 static_assert(SkAnimatedImage::kFinished == -1);
123
AnimatedImageDrawable_nDraw(JNIEnv * env,jobject,jlong nativePtr,jlong canvasPtr)124 static jlong AnimatedImageDrawable_nDraw(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
125 jlong canvasPtr) {
126 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
127 auto* canvas = reinterpret_cast<Canvas*>(canvasPtr);
128 return (jlong) canvas->drawAnimatedImage(drawable);
129 }
130
AnimatedImageDrawable_nSetAlpha(JNIEnv * env,jobject,jlong nativePtr,jint alpha)131 static void AnimatedImageDrawable_nSetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
132 jint alpha) {
133 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
134 drawable->setStagingAlpha(alpha);
135 }
136
AnimatedImageDrawable_nGetAlpha(JNIEnv * env,jobject,jlong nativePtr)137 static jlong AnimatedImageDrawable_nGetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
138 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
139 return drawable->getStagingAlpha();
140 }
141
AnimatedImageDrawable_nSetColorFilter(JNIEnv * env,jobject,jlong nativePtr,jlong nativeFilter)142 static void AnimatedImageDrawable_nSetColorFilter(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
143 jlong nativeFilter) {
144 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
145 auto* filter = reinterpret_cast<SkColorFilter*>(nativeFilter);
146 drawable->setStagingColorFilter(sk_ref_sp(filter));
147 }
148
AnimatedImageDrawable_nIsRunning(JNIEnv * env,jobject,jlong nativePtr)149 static jboolean AnimatedImageDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
150 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
151 return drawable->isRunning();
152 }
153
AnimatedImageDrawable_nStart(JNIEnv * env,jobject,jlong nativePtr)154 static jboolean AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
155 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
156 return drawable->start();
157 }
158
AnimatedImageDrawable_nStop(JNIEnv * env,jobject,jlong nativePtr)159 static jboolean AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
160 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
161 return drawable->stop();
162 }
163
164 // Java's LOOP_INFINITE relies on this being the same.
165 static_assert(SkCodec::kRepetitionCountInfinite == -1);
166
AnimatedImageDrawable_nGetRepeatCount(JNIEnv * env,jobject,jlong nativePtr)167 static jint AnimatedImageDrawable_nGetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
168 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
169 return drawable->getRepetitionCount();
170 }
171
AnimatedImageDrawable_nSetRepeatCount(JNIEnv * env,jobject,jlong nativePtr,jint loopCount)172 static void AnimatedImageDrawable_nSetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
173 jint loopCount) {
174 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
175 drawable->setRepetitionCount(loopCount);
176 }
177
178 class InvokeListener : public MessageHandler {
179 public:
InvokeListener(JNIEnv * env,jobject javaObject)180 InvokeListener(JNIEnv* env, jobject javaObject) {
181 LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK);
182 // Hold a weak reference to break a cycle that would prevent GC.
183 mWeakRef = env->NewWeakGlobalRef(javaObject);
184 }
185
~InvokeListener()186 ~InvokeListener() override {
187 auto* env = get_env_or_die(mJvm);
188 env->DeleteWeakGlobalRef(mWeakRef);
189 }
190
handleMessage(const Message &)191 virtual void handleMessage(const Message&) override {
192 auto* env = get_env_or_die(mJvm);
193 jobject localRef = env->NewLocalRef(mWeakRef);
194 if (localRef) {
195 env->CallVoidMethod(localRef, gAnimatedImageDrawable_onAnimationEndMethodID);
196 }
197 }
198
199 private:
200 JavaVM* mJvm;
201 jweak mWeakRef;
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
248 static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
249 { "nCreate", "(JLandroid/graphics/ImageDecoder;IILandroid/graphics/Rect;)J", (void*) AnimatedImageDrawable_nCreate },
250 { "nGetNativeFinalizer", "()J", (void*) AnimatedImageDrawable_nGetNativeFinalizer },
251 { "nDraw", "(JJ)J", (void*) AnimatedImageDrawable_nDraw },
252 { "nSetAlpha", "(JI)V", (void*) AnimatedImageDrawable_nSetAlpha },
253 { "nGetAlpha", "(J)I", (void*) AnimatedImageDrawable_nGetAlpha },
254 { "nSetColorFilter", "(JJ)V", (void*) AnimatedImageDrawable_nSetColorFilter },
255 { "nIsRunning", "(J)Z", (void*) AnimatedImageDrawable_nIsRunning },
256 { "nStart", "(J)Z", (void*) AnimatedImageDrawable_nStart },
257 { "nStop", "(J)Z", (void*) AnimatedImageDrawable_nStop },
258 { "nGetRepeatCount", "(J)I", (void*) AnimatedImageDrawable_nGetRepeatCount },
259 { "nSetRepeatCount", "(JI)V", (void*) AnimatedImageDrawable_nSetRepeatCount },
260 { "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener },
261 { "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize },
262 { "nSetMirrored", "(JZ)V", (void*) AnimatedImageDrawable_nSetMirrored },
263 };
264
register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv * env)265 int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
266 jclass animatedImageDrawable_class = FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable");
267 gAnimatedImageDrawable_onAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "onAnimationEnd", "()V");
268
269 return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable",
270 gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods));
271 }
272
273