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