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

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

Merge "ImageDecoder (BitmapFactory 2.0)"

parents c243f23e 0c01dbf8
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -123,6 +123,7 @@ cc_library_shared {
        "android_graphics_Picture.cpp",
        "android_graphics_Picture.cpp",
        "android/graphics/Bitmap.cpp",
        "android/graphics/Bitmap.cpp",
        "android/graphics/BitmapFactory.cpp",
        "android/graphics/BitmapFactory.cpp",
        "android/graphics/ByteBufferStreamAdaptor.cpp",
        "android/graphics/Camera.cpp",
        "android/graphics/Camera.cpp",
        "android/graphics/CanvasProperty.cpp",
        "android/graphics/CanvasProperty.cpp",
        "android/graphics/ColorFilter.cpp",
        "android/graphics/ColorFilter.cpp",
@@ -134,6 +135,7 @@ cc_library_shared {
        "android/graphics/GraphicBuffer.cpp",
        "android/graphics/GraphicBuffer.cpp",
        "android/graphics/Graphics.cpp",
        "android/graphics/Graphics.cpp",
        "android/graphics/HarfBuzzNGFaceSkia.cpp",
        "android/graphics/HarfBuzzNGFaceSkia.cpp",
        "android/graphics/ImageDecoder.cpp",
        "android/graphics/Interpolator.cpp",
        "android/graphics/Interpolator.cpp",
        "android/graphics/MaskFilter.cpp",
        "android/graphics/MaskFilter.cpp",
        "android/graphics/Matrix.cpp",
        "android/graphics/Matrix.cpp",
+4 −0
Original line number Original line Diff line number Diff line
@@ -57,10 +57,12 @@ extern int register_android_os_Process(JNIEnv* env);
extern int register_android_graphics_Bitmap(JNIEnv*);
extern int register_android_graphics_Bitmap(JNIEnv*);
extern int register_android_graphics_BitmapFactory(JNIEnv*);
extern int register_android_graphics_BitmapFactory(JNIEnv*);
extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*);
extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*);
extern int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env);
extern int register_android_graphics_Camera(JNIEnv* env);
extern int register_android_graphics_Camera(JNIEnv* env);
extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env);
extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env);
extern int register_android_graphics_GraphicBuffer(JNIEnv* env);
extern int register_android_graphics_GraphicBuffer(JNIEnv* env);
extern int register_android_graphics_Graphics(JNIEnv* env);
extern int register_android_graphics_Graphics(JNIEnv* env);
extern int register_android_graphics_ImageDecoder(JNIEnv*);
extern int register_android_graphics_Interpolator(JNIEnv* env);
extern int register_android_graphics_Interpolator(JNIEnv* env);
extern int register_android_graphics_MaskFilter(JNIEnv* env);
extern int register_android_graphics_MaskFilter(JNIEnv* env);
extern int register_android_graphics_Movie(JNIEnv* env);
extern int register_android_graphics_Movie(JNIEnv* env);
@@ -1380,6 +1382,7 @@ static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_android_graphics_Bitmap),
    REG_JNI(register_android_graphics_Bitmap),
    REG_JNI(register_android_graphics_BitmapFactory),
    REG_JNI(register_android_graphics_BitmapFactory),
    REG_JNI(register_android_graphics_BitmapRegionDecoder),
    REG_JNI(register_android_graphics_BitmapRegionDecoder),
    REG_JNI(register_android_graphics_ByteBufferStreamAdaptor),
    REG_JNI(register_android_graphics_Camera),
    REG_JNI(register_android_graphics_Camera),
    REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),
    REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),
    REG_JNI(register_android_graphics_CanvasProperty),
    REG_JNI(register_android_graphics_CanvasProperty),
@@ -1387,6 +1390,7 @@ static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_android_graphics_DrawFilter),
    REG_JNI(register_android_graphics_DrawFilter),
    REG_JNI(register_android_graphics_FontFamily),
    REG_JNI(register_android_graphics_FontFamily),
    REG_JNI(register_android_graphics_GraphicBuffer),
    REG_JNI(register_android_graphics_GraphicBuffer),
    REG_JNI(register_android_graphics_ImageDecoder),
    REG_JNI(register_android_graphics_Interpolator),
    REG_JNI(register_android_graphics_Interpolator),
    REG_JNI(register_android_graphics_MaskFilter),
    REG_JNI(register_android_graphics_MaskFilter),
    REG_JNI(register_android_graphics_Matrix),
    REG_JNI(register_android_graphics_Matrix),
+3 −59
Original line number Original line Diff line number Diff line
@@ -47,9 +47,6 @@ jfieldID gOptions_bitmapFieldID;


jfieldID gBitmap_ninePatchInsetsFieldID;
jfieldID gBitmap_ninePatchInsetsFieldID;


jclass gInsetStruct_class;
jmethodID gInsetStruct_constructorMethodID;

jclass gBitmapConfig_class;
jclass gBitmapConfig_class;
jmethodID gBitmapConfig_nativeToConfigMethodID;
jmethodID gBitmapConfig_nativeToConfigMethodID;


@@ -99,43 +96,6 @@ jstring encodedFormatToString(JNIEnv* env, SkEncodedImageFormat format) {
    return jstr;
    return jstr;
}
}


static void scaleDivRange(int32_t* divs, int count, float scale, int maxValue) {
    for (int i = 0; i < count; i++) {
        divs[i] = int32_t(divs[i] * scale + 0.5f);
        if (i > 0 && divs[i] == divs[i - 1]) {
            divs[i]++; // avoid collisions
        }
    }

    if (CC_UNLIKELY(divs[count - 1] > maxValue)) {
        // if the collision avoidance above put some divs outside the bounds of the bitmap,
        // slide outer stretchable divs inward to stay within bounds
        int highestAvailable = maxValue;
        for (int i = count - 1; i >= 0; i--) {
            divs[i] = highestAvailable;
            if (i > 0 && divs[i] <= divs[i-1]){
                // keep shifting
                highestAvailable = divs[i] - 1;
            } else {
                break;
            }
        }
    }
}

static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale,
        int scaledWidth, int scaledHeight) {
    chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
    chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
    chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
    chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);

    // The max value for the divRange is one pixel less than the actual max to ensure that the size
    // of the last div is not zero. A div of size 0 is considered invalid input and will not render.
    scaleDivRange(chunk->getXDivs(), chunk->numXDivs, scale, scaledWidth - 1);
    scaleDivRange(chunk->getYDivs(), chunk->numYDivs, scale, scaledHeight - 1);
}

class ScaleCheckingAllocator : public SkBitmap::HeapAllocator {
class ScaleCheckingAllocator : public SkBitmap::HeapAllocator {
public:
public:
    ScaleCheckingAllocator(float scale, int size)
    ScaleCheckingAllocator(float scale, int size)
@@ -428,7 +388,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
    jbyteArray ninePatchChunk = NULL;
    jbyteArray ninePatchChunk = NULL;
    if (peeker.mPatch != NULL) {
    if (peeker.mPatch != NULL) {
        if (willScale) {
        if (willScale) {
            scaleNinePatchChunk(peeker.mPatch, scale, scaledWidth, scaledHeight);
            peeker.scale(scale, scale, scaledWidth, scaledHeight);
        }
        }


        size_t ninePatchArraySize = peeker.mPatch->serializedSize();
        size_t ninePatchArraySize = peeker.mPatch->serializedSize();
@@ -448,12 +408,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,


    jobject ninePatchInsets = NULL;
    jobject ninePatchInsets = NULL;
    if (peeker.mHasInsets) {
    if (peeker.mHasInsets) {
        ninePatchInsets = env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID,
        ninePatchInsets = peeker.createNinePatchInsets(env, scale);
                peeker.mOpticalInsets[0], peeker.mOpticalInsets[1],
                peeker.mOpticalInsets[2], peeker.mOpticalInsets[3],
                peeker.mOutlineInsets[0], peeker.mOutlineInsets[1],
                peeker.mOutlineInsets[2], peeker.mOutlineInsets[3],
                peeker.mOutlineRadius, peeker.mOutlineAlpha, scale);
        if (ninePatchInsets == NULL) {
        if (ninePatchInsets == NULL) {
            return nullObjectReturn("nine patch insets == null");
            return nullObjectReturn("nine patch insets == null");
        }
        }
@@ -508,13 +463,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
    }
    }


    if (padding) {
    if (padding) {
        if (peeker.mPatch != NULL) {
        peeker.getPadding(env, padding);
            GraphicsJNI::set_jrect(env, padding,
                    peeker.mPatch->paddingLeft, peeker.mPatch->paddingTop,
                    peeker.mPatch->paddingRight, peeker.mPatch->paddingBottom);
        } else {
            GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
        }
    }
    }


    // If we get here, the outputBitmap should have an installed pixelref.
    // If we get here, the outputBitmap should have an installed pixelref.
@@ -705,11 +654,6 @@ int register_android_graphics_BitmapFactory(JNIEnv* env) {
    gBitmap_ninePatchInsetsFieldID = GetFieldIDOrDie(env, bitmap_class, "mNinePatchInsets",
    gBitmap_ninePatchInsetsFieldID = GetFieldIDOrDie(env, bitmap_class, "mNinePatchInsets",
            "Landroid/graphics/NinePatch$InsetStruct;");
            "Landroid/graphics/NinePatch$InsetStruct;");


    gInsetStruct_class = MakeGlobalRefOrDie(env, FindClassOrDie(env,
        "android/graphics/NinePatch$InsetStruct"));
    gInsetStruct_constructorMethodID = GetMethodIDOrDie(env, gInsetStruct_class, "<init>",
                                                        "(IIIIIIIIFIF)V");

    gBitmapConfig_class = MakeGlobalRefOrDie(env, FindClassOrDie(env,
    gBitmapConfig_class = MakeGlobalRefOrDie(env, FindClassOrDie(env,
            "android/graphics/Bitmap$Config"));
            "android/graphics/Bitmap$Config"));
    gBitmapConfig_nativeToConfigMethodID = GetStaticMethodIDOrDie(env, gBitmapConfig_class,
    gBitmapConfig_nativeToConfigMethodID = GetStaticMethodIDOrDie(env, gBitmapConfig_class,
+323 −0
Original line number Original line Diff line number Diff line
#include "ByteBufferStreamAdaptor.h"
#include "core_jni_helpers.h"

#include <SkStream.h>

using namespace android;

static jmethodID gByteBuffer_getMethodID;
static jmethodID gByteBuffer_setPositionMethodID;

static JNIEnv* get_env_or_die(JavaVM* jvm) {
    JNIEnv* env;
    if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", jvm);
    }
    return env;
}

class ByteBufferStream : public SkStreamAsset {
private:
    ByteBufferStream(JavaVM* jvm, jobject jbyteBuffer, size_t initialPosition, size_t length,
                     jbyteArray storage)
            : mJvm(jvm)
            , mByteBuffer(jbyteBuffer)
            , mPosition(0)
            , mInitialPosition(initialPosition)
            , mLength(length)
            , mStorage(storage) {}

public:
    static ByteBufferStream* Create(JavaVM* jvm, JNIEnv* env, jobject jbyteBuffer,
                                    size_t position, size_t length) {
        // This object outlives its native method call.
        jbyteBuffer = env->NewGlobalRef(jbyteBuffer);
        if (!jbyteBuffer) {
            return nullptr;
        }

        jbyteArray storage = env->NewByteArray(kStorageSize);
        if (!storage) {
            env->DeleteGlobalRef(jbyteBuffer);
            return nullptr;
        }

        // This object outlives its native method call.
        storage = static_cast<jbyteArray>(env->NewGlobalRef(storage));
        if (!storage) {
            env->DeleteGlobalRef(jbyteBuffer);
            return nullptr;
        }

        return new ByteBufferStream(jvm, jbyteBuffer, position, length, storage);
    }

    ~ByteBufferStream() override {
        auto* env = get_env_or_die(mJvm);
        env->DeleteGlobalRef(mByteBuffer);
        env->DeleteGlobalRef(mStorage);
    }

    size_t read(void* buffer, size_t size) override {
        if (size > mLength - mPosition) {
            size = mLength - mPosition;
        }
        if (!size) {
            return 0;
        }

        if (!buffer) {
            return this->setPosition(mPosition + size);
        }

        auto* env = get_env_or_die(mJvm);
        size_t bytesRead = 0;
        do {
            const size_t requested = (size > kStorageSize) ? kStorageSize : size;
            const jint jrequested = static_cast<jint>(requested);
            env->CallObjectMethod(mByteBuffer, gByteBuffer_getMethodID, mStorage, 0, jrequested);
            if (env->ExceptionCheck()) {
                ALOGE("Error in ByteBufferStream::read - was the ByteBuffer modified externally?");
                env->ExceptionDescribe();
                env->ExceptionClear();
                mPosition = mLength;
                return bytesRead;
            }

            env->GetByteArrayRegion(mStorage, 0, requested, reinterpret_cast<jbyte*>(buffer));
            if (env->ExceptionCheck()) {
                ALOGE("Internal error in ByteBufferStream::read");
                env->ExceptionDescribe();
                env->ExceptionClear();
                mPosition = mLength;
                return bytesRead;
            }

            mPosition += requested;
            buffer = reinterpret_cast<void*>(reinterpret_cast<char*>(buffer) + requested);
            bytesRead += requested;
            size -= requested;
        } while (size);
        return bytesRead;
    }

    bool isAtEnd() const override { return mLength == mPosition; }

    // SkStreamRewindable overrides
    bool rewind() override { return this->setPosition(0); }

    SkStreamAsset* onDuplicate() const override {
        // SkStreamRewindable requires overriding this, but it is not called by
        // decoders, so does not need a true implementation. A proper
        // implementation would require duplicating the ByteBuffer, which has
        // its own internal position state.
        return nullptr;
    }

    // SkStreamSeekable overrides
    size_t getPosition() const override { return mPosition; }

    bool seek(size_t position) override {
        return this->setPosition(position > mLength ? mLength : position);
    }

    bool move(long offset) override {
        long newPosition = mPosition + offset;
        if (newPosition < 0) {
            return this->setPosition(0);
        }
        return this->seek(static_cast<size_t>(newPosition));
    }

    SkStreamAsset* onFork() const override {
        // SkStreamSeekable requires overriding this, but it is not called by
        // decoders, so does not need a true implementation. A proper
        // implementation would require duplicating the ByteBuffer, which has
        // its own internal position state.
        return nullptr;
    }

    // SkStreamAsset overrides
    size_t getLength() const override { return mLength; }

private:
    JavaVM*          mJvm;
    jobject          mByteBuffer;
    // Logical position of the SkStream, between 0 and mLength.
    size_t           mPosition;
    // Initial position of mByteBuffer, treated as mPosition 0.
    const size_t     mInitialPosition;
    // Logical length of the SkStream, from mInitialPosition to
    // mByteBuffer.limit().
    const size_t     mLength;

    // Range has already been checked by the caller.
    bool setPosition(size_t newPosition) {
        auto* env = get_env_or_die(mJvm);
        env->CallObjectMethod(mByteBuffer, gByteBuffer_setPositionMethodID,
                              newPosition + mInitialPosition);
        if (env->ExceptionCheck()) {
            ALOGE("Internal error in ByteBufferStream::setPosition");
            env->ExceptionDescribe();
            env->ExceptionClear();
            mPosition = mLength;
            return false;
        }
        mPosition = newPosition;
        return true;
    }

    // FIXME: This is an arbitrary storage size, which should be plenty for
    // some formats (png, gif, many bmps). But for jpeg, the more we can supply
    // in one call the better, and webp really wants all of the data. How to
    // best choose the amount of storage used?
    static constexpr size_t kStorageSize = 4096;
    jbyteArray mStorage;
};

class ByteArrayStream : public SkStreamAsset {
private:
    ByteArrayStream(JavaVM* jvm, jbyteArray jarray, size_t offset, size_t length)
            : mJvm(jvm), mByteArray(jarray), mOffset(offset), mPosition(0), mLength(length) {}

public:
    static ByteArrayStream* Create(JavaVM* jvm, JNIEnv* env, jbyteArray jarray, size_t offset,
                                   size_t length) {
        // This object outlives its native method call.
        jarray = static_cast<jbyteArray>(env->NewGlobalRef(jarray));
        if (!jarray) {
            return nullptr;
        }
        return new ByteArrayStream(jvm, jarray, offset, length);
    }

    ~ByteArrayStream() override {
        auto* env = get_env_or_die(mJvm);
        env->DeleteGlobalRef(mByteArray);
    }

    size_t read(void* buffer, size_t size) override {
        if (size > mLength - mPosition) {
            size = mLength - mPosition;
        }
        if (!size) {
            return 0;
        }

        auto* env = get_env_or_die(mJvm);
        if (buffer) {
            env->GetByteArrayRegion(mByteArray, mPosition + mOffset, size,
                                    reinterpret_cast<jbyte*>(buffer));
            if (env->ExceptionCheck()) {
                ALOGE("Internal error in ByteArrayStream::read");
                env->ExceptionDescribe();
                env->ExceptionClear();
                mPosition = mLength;
                return 0;
            }
        }

        mPosition += size;
        return size;
    }

    bool isAtEnd() const override { return mLength == mPosition; }

    // SkStreamRewindable overrides
    bool rewind() override {
        mPosition = 0;
        return true;
    }
    SkStreamAsset* onDuplicate() const override {
        // SkStreamRewindable requires overriding this, but it is not called by
        // decoders, so does not need a true implementation. Note that a proper
        // implementation is fairly straightforward
        return nullptr;
    }

    // SkStreamSeekable overrides
    size_t getPosition() const override { return mPosition; }

    bool seek(size_t position) override {
        mPosition = (position > mLength) ? mLength : position;
        return true;
    }

    bool move(long offset) override {
        long newPosition = mPosition + offset;
        if (newPosition < 0) {
            return this->seek(0);
        }
        return this->seek(static_cast<size_t>(newPosition));
    }

    SkStreamAsset* onFork() const override {
        // SkStreamSeekable requires overriding this, but it is not called by
        // decoders, so does not need a true implementation. Note that a proper
        // implementation is fairly straightforward
        return nullptr;
    }

    // SkStreamAsset overrides
    size_t getLength() const override { return mLength; }

private:
    JavaVM*      mJvm;
    jbyteArray   mByteArray;
    // Offset in mByteArray. Only used when communicating with Java.
    const size_t mOffset;
    // Logical position of the SkStream, between 0 and mLength.
    size_t       mPosition;
    const size_t mLength;
};

struct release_proc_context {
    JavaVM* jvm;
    jobject jbyteBuffer;
};

std::unique_ptr<SkStream> CreateByteBufferStreamAdaptor(JNIEnv* env, jobject jbyteBuffer,
                                                        size_t position, size_t limit) {
    JavaVM* jvm;
    LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK);

    const size_t length = limit - position;
    void* addr = env->GetDirectBufferAddress(jbyteBuffer);
    if (addr) {
        addr = reinterpret_cast<void*>(reinterpret_cast<char*>(addr) + position);
        jbyteBuffer = env->NewGlobalRef(jbyteBuffer);
        if (!jbyteBuffer) {
            return nullptr;
        }

        auto* context = new release_proc_context{jvm, jbyteBuffer};
        auto releaseProc = [](const void*, void* context) {
            auto* c = reinterpret_cast<release_proc_context*>(context);
            JNIEnv* env = get_env_or_die(c->jvm);
            env->DeleteGlobalRef(c->jbyteBuffer);
            delete c;
        };
        auto data = SkData::MakeWithProc(addr, length, releaseProc, context);
        // The new SkMemoryStream will read directly from addr.
        return std::unique_ptr<SkStream>(new SkMemoryStream(std::move(data)));
    }

    // Non-direct, or direct access is not supported.
    return std::unique_ptr<SkStream>(ByteBufferStream::Create(jvm, env, jbyteBuffer, position,
                                                              length));
}

std::unique_ptr<SkStream> CreateByteArrayStreamAdaptor(JNIEnv* env, jbyteArray array, size_t offset,
                                                       size_t length) {
    JavaVM* jvm;
    LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK);

    return std::unique_ptr<SkStream>(ByteArrayStream::Create(jvm, env, array, offset, length));
}

int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env) {
    jclass byteBuffer_class = FindClassOrDie(env, "java/nio/ByteBuffer");
    gByteBuffer_getMethodID         = GetMethodIDOrDie(env, byteBuffer_class, "get", "([BII)Ljava/nio/ByteBuffer;");
    gByteBuffer_setPositionMethodID = GetMethodIDOrDie(env, byteBuffer_class, "position", "(I)Ljava/nio/Buffer;");
    return true;
}
+37 −0
Original line number Original line Diff line number Diff line
#ifndef _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_
#define _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_

#include <jni.h>
#include <memory>

class SkStream;

/**
 * Create an adaptor for treating a java.nio.ByteBuffer as an SkStream.
 *
 * This will special case direct ByteBuffers, but not the case where a byte[]
 * can be used directly. For that, use CreateByteArrayStreamAdaptor.
 *
 * @param jbyteBuffer corresponding to the java ByteBuffer. This method will
 *      add a global ref.
 * @param initialPosition returned by ByteBuffer.position(). Decoding starts
 *      from here.
 * @param limit returned by ByteBuffer.limit().
 *
 * Returns null on failure.
 */
std::unique_ptr<SkStream> CreateByteBufferStreamAdaptor(JNIEnv*, jobject jbyteBuffer,
                                                        size_t initialPosition, size_t limit);

/**
 * Create an adaptor for treating a Java byte[] as an SkStream.
 *
 * @param offset into the byte[] of the beginning of the data to use.
 * @param length of data to use, starting from offset.
 *
 * Returns null on failure.
 */
std::unique_ptr<SkStream> CreateByteArrayStreamAdaptor(JNIEnv*, jbyteArray array, size_t offset,
                                                       size_t length);

#endif  // _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_
Loading