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

Commit 3490228e authored by Leon Scroggins III's avatar Leon Scroggins III Committed by Android (Google) Code Review
Browse files

Merge "Use a native buffer for decoding images." into klp-dev

parents bf143ff1 7315f1ba
Loading
Loading
Loading
Loading
+10 −5
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@
#include "BitmapFactory.h"
#include "NinePatchPeeker.h"
#include "SkData.h"
#include "SkFrontBufferedStream.h"
#include "SkImageDecoder.h"
#include "SkImageRef_ashmem.h"
#include "SkImageRef_GlobalPool.h"
@@ -120,7 +121,7 @@ static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale) {
    }
}

static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStreamRewindable* stream,
        int sampleSize, bool ditherImage) {

    SkImageRef* pr;
@@ -465,13 +466,17 @@ static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteA
        jobject padding, jobject options) {

    jobject bitmap = NULL;
    SkAutoTUnref<SkStreamRewindable> stream(GetRewindableStream(env, is, storage));
    SkAutoTUnref<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage));

    if (stream.get()) {
        // Need to buffer enough input to be able to rewind as much as might be read by a decoder
        // trying to determine the stream's format. Currently the most is 64, read by
        // SkImageDecoder_libwebp.
        // FIXME: Get this number from SkImageDecoder
        SkAutoTUnref<SkStreamRewindable> bufferedStream(SkFrontBufferedStream::Create(stream, 64));
        SkASSERT(bufferedStream.get() != NULL);
        // for now we don't allow purgeable with java inputstreams
        // FIXME: GetRewindableStream may have made a copy, in which case
        // purgeable should be allowed.
        bitmap = doDecode(env, stream, padding, options, false, false);
        bitmap = doDecode(env, bufferedStream, padding, options, false, false);
    }
    return bitmap;
}
+1 −169
Original line number Diff line number Diff line
@@ -5,18 +5,12 @@
#include "SkStream.h"
#include "SkTypes.h"
#include "Utils.h"
#include <androidfw/Asset.h>

static jmethodID    gInputStream_resetMethodID;
static jmethodID    gInputStream_markMethodID;
static jmethodID    gInputStream_markSupportedMethodID;
static jmethodID    gInputStream_readMethodID;
static jmethodID    gInputStream_skipMethodID;

class RewindableJavaStream;

/**
 *  Non-rewindable wrapper for a Java InputStream.
 *  Wrapper for a Java InputStream.
 */
class JavaInputStreamAdaptor : public SkStream {
public:
@@ -64,25 +58,6 @@ public:
    }

private:
    // Does not override rewind, since a JavaInputStreamAdaptor's interface
    // does not support rewinding. RewindableJavaStream, which is a friend,
    // will be able to call this method to rewind.
    bool doRewind() {
        JNIEnv* env = fEnv;

        fBytesRead = 0;
        fIsAtEnd = false;

        env->CallVoidMethod(fJavaInputStream, gInputStream_resetMethodID);
        if (env->ExceptionCheck()) {
            env->ExceptionDescribe();
            env->ExceptionClear();
            SkDebugf("------- reset threw an exception\n");
            return false;
        }
        return true;
    }

    size_t doRead(void* buffer, size_t size) {
        JNIEnv* env = fEnv;
        size_t bytesRead = 0;
@@ -148,9 +123,6 @@ private:
    size_t      fCapacity;
    size_t      fBytesRead;
    bool        fIsAtEnd;

    // Allows access to doRewind and fBytesRead.
    friend class RewindableJavaStream;
};

SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
@@ -190,123 +162,6 @@ SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream,
    return adaptor_to_mem_stream(adaptor.get());
}

/**
 *  Wrapper for a Java InputStream which is rewindable and
 *  has a length.
 */
class RewindableJavaStream : public SkStreamRewindable {
public:
    // RewindableJavaStream takes ownership of adaptor.
    RewindableJavaStream(JavaInputStreamAdaptor* adaptor, size_t length)
        : fAdaptor(adaptor)
        , fLength(length) {
        SkASSERT(fAdaptor != NULL);
    }

    virtual ~RewindableJavaStream() {
        fAdaptor->unref();
    }

    virtual bool rewind() {
        return fAdaptor->doRewind();
    }

    virtual size_t read(void* buffer, size_t size) {
        return fAdaptor->read(buffer, size);
    }

    virtual bool isAtEnd() const {
        return fAdaptor->isAtEnd();
    }

    virtual size_t getLength() const {
        return fLength;
    }

    virtual bool hasLength() const {
        return true;
    }

    virtual SkStreamRewindable* duplicate() const {
        // Duplicating this stream requires rewinding and
        // reading, which modify this Stream (and could
        // fail, leaving this one invalid).
        SkASSERT(false);
        return NULL;
    }

private:
    JavaInputStreamAdaptor* fAdaptor;
    const size_t            fLength;
};

static jclass   gByteArrayInputStream_Clazz;
static jfieldID gCountField;
static jfieldID gPosField;

/**
 *  If jstream is a ByteArrayInputStream, return its remaining length. Otherwise
 *  return 0.
 */
static size_t get_length_from_byte_array_stream(JNIEnv* env, jobject jstream) {
    if (env->IsInstanceOf(jstream, gByteArrayInputStream_Clazz)) {
        // Return the remaining length, to keep the same behavior of using the rest of the
        // stream.
        return env->GetIntField(jstream, gCountField) - env->GetIntField(jstream, gPosField);
    }
    return 0;
}

/**
 *  If jstream is a class that has a length, return it. Otherwise
 *  return 0.
 *  Only checks for a set of subclasses.
 */
static size_t get_length_if_supported(JNIEnv* env, jobject jstream) {
    size_t len = get_length_from_byte_array_stream(env, jstream);
    if (len > 0) {
        return len;
    }
    return 0;
}

SkStreamRewindable* GetRewindableStream(JNIEnv* env, jobject stream,
                                        jbyteArray storage) {
    SkAutoTUnref<SkStream> adaptor(CreateJavaInputStreamAdaptor(env, stream, storage));
    if (NULL == adaptor.get()) {
        return NULL;
    }

    const size_t length = get_length_if_supported(env, stream);
    if (length > 0 && env->CallBooleanMethod(stream, gInputStream_markSupportedMethodID)) {
        // Set the readLimit for mark to the end of the stream, so it can
        // be rewound regardless of how much has been read.
        env->CallVoidMethod(stream, gInputStream_markMethodID, length);
        // RewindableJavaStream will unref adaptor when it is destroyed.
        return new RewindableJavaStream(static_cast<JavaInputStreamAdaptor*>(adaptor.detach()),
                                        length);
    }

    return adaptor_to_mem_stream(adaptor.get());
}

static jclass       gAssetInputStream_Clazz;
static jmethodID    gGetAssetIntMethodID;

android::AssetStreamAdaptor* CheckForAssetStream(JNIEnv* env, jobject jstream) {
    if (!env->IsInstanceOf(jstream, gAssetInputStream_Clazz)) {
        return NULL;
    }

    jint jasset = env->CallIntMethod(jstream, gGetAssetIntMethodID);
    android::Asset* a = reinterpret_cast<android::Asset*>(jasset);
    if (NULL == a) {
        jniThrowNullPointerException(env, "NULL native asset");
        return NULL;
    }
    return new android::AssetStreamAdaptor(a);
}

///////////////////////////////////////////////////////////////////////////////

static jmethodID    gOutputStream_writeMethodID;
@@ -382,13 +237,6 @@ static jclass findClassCheck(JNIEnv* env, const char classname[]) {
    return clazz;
}

static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
                                const char fieldname[], const char type[]) {
    jfieldID id = env->GetFieldID(clazz, fieldname, type);
    SkASSERT(!env->ExceptionCheck());
    return id;
}

static jmethodID getMethodIDCheck(JNIEnv* env, jclass clazz,
                                  const char methodname[], const char type[]) {
    jmethodID id = env->GetMethodID(clazz, methodname, type);
@@ -398,25 +246,9 @@ static jmethodID getMethodIDCheck(JNIEnv* env, jclass clazz,

int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env) {
    jclass inputStream_Clazz = findClassCheck(env, "java/io/InputStream");
    gInputStream_resetMethodID = getMethodIDCheck(env, inputStream_Clazz, "reset", "()V");
    gInputStream_markMethodID = getMethodIDCheck(env, inputStream_Clazz, "mark", "(I)V");
    gInputStream_markSupportedMethodID = getMethodIDCheck(env, inputStream_Clazz, "markSupported", "()Z");
    gInputStream_readMethodID = getMethodIDCheck(env, inputStream_Clazz, "read", "([BII)I");
    gInputStream_skipMethodID = getMethodIDCheck(env, inputStream_Clazz, "skip", "(J)J");

    gByteArrayInputStream_Clazz = findClassCheck(env, "java/io/ByteArrayInputStream");
    // Ref gByteArrayInputStream_Clazz so we can continue to refer to it when
    // calling IsInstance.
    gByteArrayInputStream_Clazz = (jclass) env->NewGlobalRef(gByteArrayInputStream_Clazz);
    gCountField = getFieldIDCheck(env, gByteArrayInputStream_Clazz, "count", "I");
    gPosField = getFieldIDCheck(env, gByteArrayInputStream_Clazz, "pos", "I");

    gAssetInputStream_Clazz = findClassCheck(env, "android/content/res/AssetManager$AssetInputStream");
    // Ref gAssetInputStream_Clazz so we can continue to refer to it when
    // calling IsInstance.
    gAssetInputStream_Clazz = (jclass) env->NewGlobalRef(gAssetInputStream_Clazz);
    gGetAssetIntMethodID = getMethodIDCheck(env, gAssetInputStream_Clazz, "getAssetInt", "()I");

    jclass outputStream_Clazz = findClassCheck(env, "java/io/OutputStream");
    gOutputStream_writeMethodID = getMethodIDCheck(env, outputStream_Clazz, "write", "([BII)V");
    gOutputStream_flushMethodID = getMethodIDCheck(env, outputStream_Clazz, "flush", "()V");
+2 −31
Original line number Diff line number Diff line
@@ -4,10 +4,6 @@
//#include <android_runtime/AndroidRuntime.h>
#include "jni.h"

namespace android {
    class AssetStreamAdaptor;
}

class SkMemoryStream;
class SkStream;
class SkStreamRewindable;
@@ -15,6 +11,7 @@ class SkWStream;

/**
 *  Return an adaptor from a Java InputStream to an SkStream.
 *  Does not support rewind.
 *  @param env JNIEnv object.
 *  @param stream Pointer to Java InputStream.
 *  @param storage Java byte array for retrieving data from the
@@ -28,7 +25,7 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
                                       jbyteArray storage);

/**
 *  Copy a Java InputStream.
 *  Copy a Java InputStream. The result will be rewindable.
 *  @param env JNIEnv object.
 *  @param stream Pointer to Java InputStream.
 *  @param storage Java byte array for retrieving data from the
@@ -39,32 +36,6 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream,
                                        jbyteArray storage);

/**
 *  Get a rewindable stream from a Java InputStream.
 *  @param env JNIEnv object.
 *  @param stream Pointer to Java InputStream.
 *  @param storage Java byte array for retrieving data from the
 *      Java InputStream.
 *  @return SkStreamRewindable Either a wrapper around the Java
 *      InputStream, if possible, or a copy which is rewindable.
 *      Since it may be a wrapper, must not be used after the
 *      caller returns, like the result of CreateJavaInputStreamAdaptor.
 */
SkStreamRewindable* GetRewindableStream(JNIEnv* env, jobject stream,
                                        jbyteArray storage);

/**
 *  If the Java InputStream is an AssetInputStream, return an adaptor.
 *  This should not be used after the calling function returns, since
 *  the caller may close the asset. Returns NULL if the stream is
 *  not an AssetInputStream.
 *  @param env JNIEnv object.
 *  @param stream Pointer to Java InputStream.
 *  @return AssetStreamAdaptor representing the InputStream, or NULL.
 *      Must not be held onto.
 */
android::AssetStreamAdaptor* CheckForAssetStream(JNIEnv* env, jobject stream);

SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream,
                                         jbyteArray storage);
#endif
+23 −10
Original line number Diff line number Diff line
#include "ScopedLocalRef.h"
#include "SkFrontBufferedStream.h"
#include "SkMovie.h"
#include "SkStream.h"
#include "GraphicsJNI.h"
@@ -81,23 +82,33 @@ static void movie_draw(JNIEnv* env, jobject movie, jobject canvas,
    c->drawBitmap(b, sx, sy, p);
}

static jobject movie_decodeAsset(JNIEnv* env, jobject clazz, jint native_asset) {
    android::Asset* asset = reinterpret_cast<android::Asset*>(native_asset);
    if (asset == NULL) return NULL;
    SkAutoTUnref<SkStreamRewindable> stream (new android::AssetStreamAdaptor(asset));
    SkMovie* moov = SkMovie::DecodeStream(stream.get());
    return create_jmovie(env, moov);
}

static jobject movie_decodeStream(JNIEnv* env, jobject clazz, jobject istream) {

    NPE_CHECK_RETURN_ZERO(env, istream);

    SkStreamRewindable* strm = CheckForAssetStream(env, istream);
    jbyteArray byteArray = NULL;
    ScopedLocalRef<jbyteArray> scoper(env, NULL);
    if (NULL == strm) {
        byteArray = env->NewByteArray(16*1024);
        scoper.reset(byteArray);
        strm = GetRewindableStream(env, istream, byteArray);
    }
    jbyteArray byteArray = env->NewByteArray(16*1024);
    ScopedLocalRef<jbyteArray> scoper(env, byteArray);
    SkStream* strm = CreateJavaInputStreamAdaptor(env, istream, byteArray);
    if (NULL == strm) {
        return 0;
    }

    SkMovie* moov = SkMovie::DecodeStream(strm);
    // Need to buffer enough input to be able to rewind as much as might be read by a decoder
    // trying to determine the stream's format. The only decoder for movies is GIF, which
    // will only read 6.
    // FIXME: Get this number from SkImageDecoder
    SkAutoTUnref<SkStreamRewindable> bufferedStream(SkFrontBufferedStream::Create(strm, 6));
    SkASSERT(bufferedStream.get() != NULL);

    SkMovie* moov = SkMovie::DecodeStream(bufferedStream);
    strm->unref();
    return create_jmovie(env, moov);
}
@@ -135,7 +146,9 @@ static JNINativeMethod gMethods[] = {
    {   "setTime",  "(I)Z", (void*)movie_setTime  },
    {   "draw",     "(Landroid/graphics/Canvas;FFLandroid/graphics/Paint;)V",
                            (void*)movie_draw  },
    { "decodeStream", "(Ljava/io/InputStream;)Landroid/graphics/Movie;",
    { "nativeDecodeAsset", "(I)Landroid/graphics/Movie;",
                            (void*)movie_decodeAsset },
    { "nativeDecodeStream", "(Ljava/io/InputStream;)Landroid/graphics/Movie;",
                            (void*)movie_decodeStream },
    { "nativeDestructor","(I)V", (void*)movie_destructor },
    { "decodeByteArray", "([BII)Landroid/graphics/Movie;",
+14 −10
Original line number Diff line number Diff line
@@ -569,10 +569,7 @@ public class BitmapFactory {
                final int asset = ((AssetManager.AssetInputStream) is).getAssetInt();
                bm = nativeDecodeAsset(asset, outPadding, opts);
            } else {
                byte [] tempStorage = null;
                if (opts != null) tempStorage = opts.inTempStorage;
                if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE];
                bm = nativeDecodeStream(is, tempStorage, outPadding, opts);
                bm = decodeStreamInternal(is, outPadding, opts);
            }

            if (bm == null && opts != null && opts.inBitmap != null) {
@@ -587,6 +584,18 @@ public class BitmapFactory {
        return bm;
    }

    /**
     * Private helper function for decoding an InputStream natively. Buffers the input enough to
     * do a rewind as needed, and supplies temporary storage if necessary. is MUST NOT be null.
     */
    private static Bitmap decodeStreamInternal(InputStream is, Rect outPadding, Options opts) {
        // ASSERT(is != null);
        byte [] tempStorage = null;
        if (opts != null) tempStorage = opts.inTempStorage;
        if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE];
        return nativeDecodeStream(is, tempStorage, outPadding, opts);
    }

    /**
     * Decode an input stream into a bitmap. If the input stream is null, or
     * cannot be used to decode a bitmap, the function returns null.
@@ -624,13 +633,8 @@ public class BitmapFactory {
                bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
            } else {
                FileInputStream fis = new FileInputStream(fd);
                // FIXME: If nativeDecodeStream grabbed the pointer to tempStorage
                // from Options, this code would not need to be duplicated.
                byte [] tempStorage = null;
                if (opts != null) tempStorage = opts.inTempStorage;
                if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE];
                try {
                    bm = nativeDecodeStream(fis, tempStorage, outPadding, opts);
                    bm = decodeStreamInternal(fis, outPadding, opts);
                } finally {
                    try {
                        fis.close();
Loading