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. 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 112 static void AnimatedImageDrawable_destruct(AnimatedImageDrawable* drawable) { 113 SkSafeUnref(drawable); 114 } 115 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 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 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 136 static jlong AnimatedImageDrawable_nGetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { 137 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); 138 return drawable->getStagingAlpha(); 139 } 140 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 148 static jboolean AnimatedImageDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { 149 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); 150 return drawable->isRunning(); 151 } 152 153 static jboolean AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { 154 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); 155 return drawable->start(); 156 } 157 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 166 static jint AnimatedImageDrawable_nGetRepeatCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { 167 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); 168 return drawable->getRepetitionCount(); 169 } 170 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: 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 185 ~InvokeListener() override { 186 auto* env = get_env_or_die(mJvm); 187 env->DeleteWeakGlobalRef(mWeakRef); 188 } 189 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: 205 JniAnimationEndListener(sp<Looper>&& looper, JNIEnv* env, jobject javaObject) { 206 mListener = new InvokeListener(env, javaObject); 207 mLooper = std::move(looper); 208 } 209 210 void onAnimationEnd() override { mLooper->sendMessage(mListener, 0); } 211 212 private: 213 sp<InvokeListener> mListener; 214 sp<Looper> mLooper; 215 }; 216 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 236 static jlong AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { 237 auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); 238 return drawable->byteSize(); 239 } 240 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 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