Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4ef66849 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes Ie2ec98d9,I81ce2bef

* changes:
  Make ImageDecoder return animated Drawables
  Ensure that PostProcess Canvas is released
parents 0e4bc3f2 671cce26
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -119,6 +119,7 @@ cc_library_shared {
        "android_util_jar_StrictJarFile.cpp",
        "android_graphics_Canvas.cpp",
        "android_graphics_Picture.cpp",
        "android/graphics/AnimatedImageDrawable.cpp",
        "android/graphics/Bitmap.cpp",
        "android/graphics/BitmapFactory.cpp",
        "android/graphics/ByteBufferStreamAdaptor.cpp",
+2 −0
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env);
extern int register_android_graphics_GraphicBuffer(JNIEnv* env);
extern int register_android_graphics_Graphics(JNIEnv* env);
extern int register_android_graphics_ImageDecoder(JNIEnv*);
extern int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv*);
extern int register_android_graphics_Interpolator(JNIEnv* env);
extern int register_android_graphics_MaskFilter(JNIEnv* env);
extern int register_android_graphics_Movie(JNIEnv* env);
@@ -1396,6 +1397,7 @@ static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_android_graphics_FontFamily),
    REG_JNI(register_android_graphics_GraphicBuffer),
    REG_JNI(register_android_graphics_ImageDecoder),
    REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable),
    REG_JNI(register_android_graphics_Interpolator),
    REG_JNI(register_android_graphics_MaskFilter),
    REG_JNI(register_android_graphics_Matrix),
+154 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "GraphicsJNI.h"
#include "ImageDecoder.h"
#include "core_jni_helpers.h"

#include <hwui/Canvas.h>
#include <SkAndroidCodec.h>
#include <SkAnimatedImage.h>
#include <SkColorFilter.h>
#include <SkPicture.h>
#include <SkPictureRecorder.h>

using namespace android;

struct AnimatedImageDrawable {
    sk_sp<SkAnimatedImage> mDrawable;
    SkPaint                mPaint;
};

// Note: jpostProcess holds a handle to the ImageDecoder.
static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/,
                                           jlong nativeImageDecoder, jobject jpostProcess,
                                           jint width, jint height, jobject jsubset) {
    if (nativeImageDecoder == 0) {
        doThrowIOE(env, "Cannot create AnimatedImageDrawable from null!");
        return 0;
    }

    auto* imageDecoder = reinterpret_cast<ImageDecoder*>(nativeImageDecoder);
    auto info = imageDecoder->mCodec->getInfo();
    const SkISize scaledSize = SkISize::Make(width, height);
    SkIRect subset;
    if (jsubset) {
        GraphicsJNI::jrect_to_irect(env, jsubset, &subset);
    } else {
        subset = SkIRect::MakeWH(width, height);
    }

    sk_sp<SkPicture> picture;
    if (jpostProcess) {
        SkRect bounds = SkRect::MakeWH(subset.width(), subset.height());

        SkPictureRecorder recorder;
        SkCanvas* skcanvas = recorder.beginRecording(bounds);
        std::unique_ptr<Canvas> canvas(Canvas::create_canvas(skcanvas));
        postProcessAndRelease(env, jpostProcess, std::move(canvas), bounds.width(),
                              bounds.height());
        if (env->ExceptionCheck()) {
            return 0;
        }
        picture = recorder.finishRecordingAsPicture();
    }

    std::unique_ptr<AnimatedImageDrawable> drawable(new AnimatedImageDrawable);
    drawable->mDrawable = SkAnimatedImage::Make(std::move(imageDecoder->mCodec),
                scaledSize, subset, std::move(picture));
    if (!drawable->mDrawable) {
        doThrowIOE(env, "Failed to create drawable");
        return 0;
    }
    drawable->mDrawable->start();

    return reinterpret_cast<jlong>(drawable.release());
}

static void AnimatedImageDrawable_destruct(AnimatedImageDrawable* drawable) {
    delete drawable;
}

static jlong AnimatedImageDrawable_nGetNativeFinalizer(JNIEnv* /*env*/, jobject /*clazz*/) {
    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&AnimatedImageDrawable_destruct));
}

static jlong AnimatedImageDrawable_nDraw(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
                                         jlong canvasPtr, jlong msecs) {
    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
    double timeToNextUpdate = drawable->mDrawable->update(msecs);
    auto* canvas = reinterpret_cast<Canvas*>(canvasPtr);
    canvas->drawAnimatedImage(drawable->mDrawable.get(), 0, 0, &drawable->mPaint);
    return (jlong) timeToNextUpdate;
}

static void AnimatedImageDrawable_nSetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
                                            jint alpha) {
    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
    drawable->mPaint.setAlpha(alpha);
}

static jlong AnimatedImageDrawable_nGetAlpha(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
    return drawable->mPaint.getAlpha();
}

static void AnimatedImageDrawable_nSetColorFilter(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
                                                  jlong nativeFilter) {
    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
    auto* filter = reinterpret_cast<SkColorFilter*>(nativeFilter);
    drawable->mPaint.setColorFilter(sk_ref_sp(filter));
}

static jboolean AnimatedImageDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
    return drawable->mDrawable->isRunning();
}

static void AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
    drawable->mDrawable->start();
}

static void AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
    drawable->mDrawable->stop();
}

static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
    // FIXME: Report the size of the internal SkBitmap etc.
    return sizeof(drawable);
}

static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
    { "nCreate",             "(JLandroid/graphics/ImageDecoder;IILandroid/graphics/Rect;)J", (void*) AnimatedImageDrawable_nCreate },
    { "nGetNativeFinalizer", "()J",                                                          (void*) AnimatedImageDrawable_nGetNativeFinalizer },
    { "nDraw",               "(JJJ)J",                                                       (void*) AnimatedImageDrawable_nDraw },
    { "nSetAlpha",           "(JI)V",                                                        (void*) AnimatedImageDrawable_nSetAlpha },
    { "nGetAlpha",           "(J)I",                                                         (void*) AnimatedImageDrawable_nGetAlpha },
    { "nSetColorFilter",     "(JJ)V",                                                        (void*) AnimatedImageDrawable_nSetColorFilter },
    { "nIsRunning",          "(J)Z",                                                         (void*) AnimatedImageDrawable_nIsRunning },
    { "nStart",              "(J)V",                                                         (void*) AnimatedImageDrawable_nStart },
    { "nStop",               "(J)V",                                                         (void*) AnimatedImageDrawable_nStop },
    { "nNativeByteSize",     "(J)J",                                                         (void*) AnimatedImageDrawable_nNativeByteSize },
};

int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
    return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable",
            gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods));
}
+39 −56
Original line number Diff line number Diff line
@@ -19,12 +19,11 @@
#include "ByteBufferStreamAdaptor.h"
#include "CreateJavaOutputStreamAdaptor.h"
#include "GraphicsJNI.h"
#include "NinePatchPeeker.h"
#include "ImageDecoder.h"
#include "Utils.h"
#include "core_jni_helpers.h"

#include <hwui/Bitmap.h>
#include <hwui/Canvas.h>

#include <SkAndroidCodec.h>
#include <SkEncodedImageFormat.h>
@@ -43,34 +42,14 @@ static jclass gIncomplete_class;
static jclass    gCorrupt_class;
static jclass    gCanvas_class;
static jmethodID gImageDecoder_constructorMethodID;
static jmethodID gImageDecoder_postProcessMethodID;
static jmethodID gPoint_constructorMethodID;
static jmethodID gIncomplete_constructorMethodID;
static jmethodID gCorrupt_constructorMethodID;
static jmethodID gCallback_onPartialImageMethodID;
static jmethodID gPostProcess_postProcessMethodID;
static jmethodID gCanvas_constructorMethodID;
static jmethodID gCanvas_releaseMethodID;

struct ImageDecoder {
    // These need to stay in sync with ImageDecoder.java's Allocator constants.
    enum Allocator {
        kDefault_Allocator      = 0,
        kSoftware_Allocator     = 1,
        kSharedMemory_Allocator = 2,
        kHardware_Allocator     = 3,
    };

    // These need to stay in sync with PixelFormat.java's Format constants.
    enum PixelFormat {
        kUnknown     =  0,
        kTranslucent = -3,
        kOpaque      = -1,
    };

    NinePatchPeeker mPeeker;
    std::unique_ptr<SkAndroidCodec> mCodec;
};

static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) {
    if (!stream.get()) {
        doThrowIOE(env, "Failed to create a stream");
@@ -78,7 +57,7 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) {
    }
    std::unique_ptr<ImageDecoder> decoder(new ImageDecoder);
    SkCodec::Result result;
    auto codec = SkCodec::MakeFromStream(std::move(stream), &result, &decoder->mPeeker);
    auto codec = SkCodec::MakeFromStream(std::move(stream), &result, decoder->mPeeker.get());
    if (!codec) {
        switch (result) {
            case SkCodec::kIncompleteInput:
@@ -90,22 +69,24 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) {
                           SkCodec::ResultToString(result));
                doThrowIOE(env, msg.c_str());
                break;
        }

        }
        return nullptr;
    }

    // FIXME: Avoid parsing the whole image?
    const bool animated = codec->getFrameCount() > 1;
    decoder->mCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
    if (!decoder->mCodec.get()) {
        doThrowIOE(env, "Could not create AndroidCodec");
        return nullptr;
    }

    const auto& info = decoder->mCodec->getInfo();
    const int width = info.width();
    const int height = info.height();
    return env->NewObject(gImageDecoder_class, gImageDecoder_constructorMethodID,
                          reinterpret_cast<jlong>(decoder.release()), width, height);
                          reinterpret_cast<jlong>(decoder.release()), width, height,
                          animated);
}

static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/,
@@ -176,6 +157,24 @@ static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/, jby
    return native_create(env, std::move(stream));
}

jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas,
                           int width, int height) {
    jobject jcanvas = env->NewObject(gCanvas_class, gCanvas_constructorMethodID,
                                     reinterpret_cast<jlong>(canvas.get()));
    if (!jcanvas) {
        doThrowOOME(env, "Failed to create Java Canvas for PostProcess!");
        return ImageDecoder::kUnknown;
    }

    // jcanvas now owns canvas.
    canvas.release();

    return env->CallIntMethod(jimageDecoder, gImageDecoder_postProcessMethodID,
                              jcanvas, width, height);
}

// Note: jpostProcess points to an ImageDecoder object if it has a PostProcess object, and nullptr
// otherwise.
static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
                                          jobject jcallback, jobject jpostProcess,
                                          jint desiredWidth, jint desiredHeight, jobject jsubset,
@@ -322,23 +321,23 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
    // Ignore ninepatch when post-processing.
    if (!jpostProcess) {
        // FIXME: Share more code with BitmapFactory.cpp.
        if (decoder->mPeeker.mPatch != nullptr) {
        if (decoder->mPeeker->mPatch != nullptr) {
            if (scale) {
                decoder->mPeeker.scale(scaleX, scaleY, desiredWidth, desiredHeight);
                decoder->mPeeker->scale(scaleX, scaleY, desiredWidth, desiredHeight);
            }
            size_t ninePatchArraySize = decoder->mPeeker.mPatch->serializedSize();
            size_t ninePatchArraySize = decoder->mPeeker->mPatch->serializedSize();
            ninePatchChunk = env->NewByteArray(ninePatchArraySize);
            if (ninePatchChunk == nullptr) {
                doThrowOOME(env, "Failed to allocate nine patch chunk.");
                return nullptr;
            }

            env->SetByteArrayRegion(ninePatchChunk, 0, decoder->mPeeker.mPatchSize,
                                    reinterpret_cast<jbyte*>(decoder->mPeeker.mPatch));
            env->SetByteArrayRegion(ninePatchChunk, 0, decoder->mPeeker->mPatchSize,
                                    reinterpret_cast<jbyte*>(decoder->mPeeker->mPatch));
        }

        if (decoder->mPeeker.mHasInsets) {
            ninePatchInsets = decoder->mPeeker.createNinePatchInsets(env, 1.0f);
        if (decoder->mPeeker->mHasInsets) {
            ninePatchInsets = decoder->mPeeker->createNinePatchInsets(env, 1.0f);
            if (ninePatchInsets == nullptr) {
                doThrowOOME(env, "Failed to allocate nine patch insets.");
                return nullptr;
@@ -399,23 +398,9 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong

    if (jpostProcess) {
        std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm));
        jobject jcanvas = env->NewObject(gCanvas_class, gCanvas_constructorMethodID,
                                         reinterpret_cast<jlong>(canvas.get()));
        if (!jcanvas) {
            doThrowOOME(env, "Failed to create Java Canvas for PostProcess!");
            return nullptr;
        }
        // jcanvas will now own canvas.
        canvas.release();

        jint pixelFormat = env->CallIntMethod(jpostProcess, gPostProcess_postProcessMethodID,
                                              jcanvas, bm.width(), bm.height());
        if (env->ExceptionCheck()) {
            return nullptr;
        }

        // The Canvas objects are no longer needed, and will not remain valid.
        env->CallVoidMethod(jcanvas, gCanvas_releaseMethodID);
        jint pixelFormat = postProcessAndRelease(env, jpostProcess, std::move(canvas),
                                                 bm.width(), bm.height());
        if (env->ExceptionCheck()) {
            return nullptr;
        }
@@ -493,7 +478,7 @@ static jobject ImageDecoder_nGetSampledSize(JNIEnv* env, jobject /*clazz*/, jlon
static void ImageDecoder_nGetPadding(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
                                     jobject outPadding) {
    auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
    decoder->mPeeker.getPadding(env, outPadding);
    decoder->mPeeker->getPadding(env, outPadding);
}

static void ImageDecoder_nClose(JNIEnv* /*env*/, jobject /*clazz*/, jlong nativePtr) {
@@ -511,7 +496,7 @@ static const JNINativeMethod gImageDecoderMethods[] = {
    { "nCreate",        "([BII)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
    { "nCreate",        "(Ljava/io/InputStream;[B)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
    { "nCreate",        "(Ljava/io/FileDescriptor;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
    { "nDecodeBitmap",  "(JLandroid/graphics/ImageDecoder$OnPartialImageListener;Landroid/graphics/PostProcess;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
    { "nDecodeBitmap",  "(JLandroid/graphics/ImageDecoder$OnPartialImageListener;Landroid/graphics/ImageDecoder;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
                                                                 (void*) ImageDecoder_nDecodeBitmap },
    { "nGetSampledSize","(JI)Landroid/graphics/Point;",          (void*) ImageDecoder_nGetSampledSize },
    { "nGetPadding",    "(JLandroid/graphics/Rect;)V",           (void*) ImageDecoder_nGetPadding },
@@ -521,7 +506,8 @@ static const JNINativeMethod gImageDecoderMethods[] = {

int register_android_graphics_ImageDecoder(JNIEnv* env) {
    gImageDecoder_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder"));
    gImageDecoder_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "<init>", "(JII)V");
    gImageDecoder_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "<init>", "(JIIZ)V");
    gImageDecoder_postProcessMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "postProcessAndRelease", "(Landroid/graphics/Canvas;II)I");

    gPoint_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Point"));
    gPoint_constructorMethodID = GetMethodIDOrDie(env, gPoint_class, "<init>", "(II)V");
@@ -535,9 +521,6 @@ int register_android_graphics_ImageDecoder(JNIEnv* env) {
    jclass callback_class = FindClassOrDie(env, "android/graphics/ImageDecoder$OnPartialImageListener");
    gCallback_onPartialImageMethodID = GetMethodIDOrDie(env, callback_class, "onPartialImage", "(Ljava/io/IOException;)Z");

    jclass postProcess_class = FindClassOrDie(env, "android/graphics/PostProcess");
    gPostProcess_postProcessMethodID = GetMethodIDOrDie(env, postProcess_class, "postProcess", "(Landroid/graphics/Canvas;II)I");

    gCanvas_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Canvas"));
    gCanvas_constructorMethodID = GetMethodIDOrDie(env, gCanvas_class, "<init>", "(J)V");
    gCanvas_releaseMethodID = GetMethodIDOrDie(env, gCanvas_class, "release", "()V");
+55 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "NinePatchPeeker.h"

#include <hwui/Canvas.h>

#include <jni.h>

class SkAndroidCodec;

using namespace android;

struct ImageDecoder {
    // These need to stay in sync with ImageDecoder.java's Allocator constants.
    enum Allocator {
        kDefault_Allocator      = 0,
        kSoftware_Allocator     = 1,
        kSharedMemory_Allocator = 2,
        kHardware_Allocator     = 3,
    };

    // These need to stay in sync with PixelFormat.java's Format constants.
    enum PixelFormat {
        kUnknown     =  0,
        kTranslucent = -3,
        kOpaque      = -1,
    };

    std::unique_ptr<SkAndroidCodec> mCodec;
    sk_sp<NinePatchPeeker> mPeeker;

    ImageDecoder()
        :mPeeker(new NinePatchPeeker)
    {}
};

// Creates a Java Canvas object from canvas, calls jimageDecoder's PostProcess on it, and then
// releases the Canvas.
// Caller needs to check for exceptions.
jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas,
                           int width, int height);
Loading