diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index f9bb2331482d1..9c20de2e7eaa5 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -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 stream(GetRewindableStream(env, is, storage)); + SkAutoTUnref 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 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; } diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp index f773f59688fb2..da8f083059eb6 100644 --- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp +++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp @@ -5,18 +5,12 @@ #include "SkStream.h" #include "SkTypes.h" #include "Utils.h" -#include -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 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(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(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"); diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h index fcc0c9a5d60fc..ecd270f0927d9 100644 --- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h +++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h @@ -4,10 +4,6 @@ //#include #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 diff --git a/core/jni/android/graphics/Movie.cpp b/core/jni/android/graphics/Movie.cpp index 2eae84132a962..feb2decd3528c 100644 --- a/core/jni/android/graphics/Movie.cpp +++ b/core/jni/android/graphics/Movie.cpp @@ -1,4 +1,5 @@ #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(native_asset); + if (asset == NULL) return NULL; + SkAutoTUnref 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 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 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 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;", diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index 23606a1894240..c8ace44c2c516 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -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(); diff --git a/graphics/java/android/graphics/Movie.java b/graphics/java/android/graphics/Movie.java index 4a334531e0701..9419faf15ff80 100644 --- a/graphics/java/android/graphics/Movie.java +++ b/graphics/java/android/graphics/Movie.java @@ -16,12 +16,13 @@ package android.graphics; +import android.content.res.AssetManager; import java.io.InputStream; import java.io.FileInputStream; public class Movie { private final int mNativeMovie; - + private Movie(int nativeMovie) { if (nativeMovie == 0) { throw new RuntimeException("native movie creation failed"); @@ -42,7 +43,20 @@ public class Movie { draw(canvas, x, y, null); } - public static native Movie decodeStream(InputStream is); + public static Movie decodeStream(InputStream is) { + if (is == null) { + return null; + } + if (is instanceof AssetManager.AssetInputStream) { + final int asset = ((AssetManager.AssetInputStream) is).getAssetInt(); + return nativeDecodeAsset(asset); + } + + return nativeDecodeStream(is); + } + + private static native Movie nativeDecodeAsset(int asset); + private static native Movie nativeDecodeStream(InputStream is); public static native Movie decodeByteArray(byte[] data, int offset, int length);