diff options
author | Leon Scroggins III <scroggo@google.com> | 2013-08-29 10:24:21 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2013-08-29 10:24:21 -0700 |
commit | f7142e3e8bfba982ad73a7c7a6a992491b7cfb43 (patch) | |
tree | d865e27f612159e371787b6bd3df45ae1ba4a8b1 | |
parent | f5b43bdc6231df8b646d5bbb215e639230f37260 (diff) | |
parent | af1725190fbb8dd7c29726f8b7c072f3af734aed (diff) | |
download | frameworks_base-f7142e3e8bfba982ad73a7c7a6a992491b7cfb43.zip frameworks_base-f7142e3e8bfba982ad73a7c7a6a992491b7cfb43.tar.gz frameworks_base-f7142e3e8bfba982ad73a7c7a6a992491b7cfb43.tar.bz2 |
am af172519: am 4b299312: Merge "Replace stream wrap-function w/ more specific ones" into klp-dev
* commit 'af1725190fbb8dd7c29726f8b7c072f3af734aed':
Replace stream wrap-function w/ more specific ones
-rw-r--r-- | core/jni/android/graphics/Bitmap.cpp | 1 | ||||
-rw-r--r-- | core/jni/android/graphics/BitmapFactory.cpp | 55 | ||||
-rw-r--r-- | core/jni/android/graphics/BitmapRegionDecoder.cpp | 43 | ||||
-rw-r--r-- | core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp | 276 | ||||
-rw-r--r-- | core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h | 66 | ||||
-rw-r--r-- | core/jni/android/graphics/Movie.cpp | 13 | ||||
-rw-r--r-- | core/jni/android/graphics/Picture.cpp | 8 | ||||
-rw-r--r-- | core/jni/android/graphics/Utils.cpp | 48 | ||||
-rw-r--r-- | core/jni/android/graphics/Utils.h | 13 | ||||
-rw-r--r-- | graphics/java/android/graphics/BitmapFactory.java | 74 | ||||
-rw-r--r-- | graphics/java/android/graphics/BitmapRegionDecoder.java | 7 |
11 files changed, 420 insertions, 184 deletions
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index fd9fbae..eea9ee1 100644 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -5,6 +5,7 @@ #include "GraphicsJNI.h"
#include "SkDither.h"
#include "SkUnPreMultiply.h"
+#include "SkStream.h"
#include <binder/Parcel.h>
#include "android_os_Parcel.h"
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index c433874..16beb02 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -202,7 +202,7 @@ private: // since we "may" create a purgeable imageref, we require the stream be ref'able // i.e. dynamically allocated, since its lifetime may exceed the current stack // frame. -static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, +static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options, bool allowPurgeable, bool forcePurgeable = false) { int sampleSize = 1; @@ -459,26 +459,17 @@ static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteA jobject padding, jobject options) { jobject bitmap = NULL; - SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0); + SkAutoTUnref<SkStreamRewindable> stream(GetRewindableStream(env, is, storage)); - if (stream) { + if (stream.get()) { // 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); - stream->unref(); } return bitmap; } -static ssize_t getFDSize(int fd) { - off64_t curr = ::lseek64(fd, 0, SEEK_CUR); - if (curr < 0) { - return 0; - } - size_t size = ::lseek(fd, 0, SEEK_END); - ::lseek64(fd, curr, SEEK_SET); - return size; -} - static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor, jobject padding, jobject bitmapFactoryOptions) { @@ -512,44 +503,16 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fi return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD); } -/* make a deep copy of the asset, and return it as a stream, or NULL if there - was an error. - */ -static SkStream* copyAssetToStream(Asset* asset) { - // if we could "ref/reopen" the asset, we may not need to copy it here - off64_t size = asset->seek(0, SEEK_SET); - if ((off64_t)-1 == size) { - SkDebugf("---- copyAsset: asset rewind failed\n"); - return NULL; - } - - size = asset->getLength(); - if (size <= 0) { - SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size); - return NULL; - } - - SkStream* stream = new SkMemoryStream(size); - void* data = const_cast<void*>(stream->getMemoryBase()); - off64_t len = asset->read(data, size); - if (len != size) { - SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len); - delete stream; - stream = NULL; - } - return stream; -} - static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jint native_asset, jobject padding, jobject options) { - SkStream* stream; + SkStreamRewindable* stream; Asset* asset = reinterpret_cast<Asset*>(native_asset); bool forcePurgeable = optionsPurgeable(env, options); if (forcePurgeable) { // if we could "ref/reopen" the asset, we may not need to copy it here // and we could assume optionsShareable, since assets are always RO - stream = copyAssetToStream(asset); + stream = CopyAssetToStream(asset); if (stream == NULL) { return NULL; } @@ -559,7 +522,7 @@ static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jint native_asset, stream = new AssetStreamAdaptor(asset); } SkAutoUnref aur(stream); - return doDecode(env, stream, padding, options, true, forcePurgeable); + return doDecode(env, stream, padding, options, forcePurgeable, forcePurgeable); } static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, @@ -572,7 +535,7 @@ static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, */ bool purgeable = optionsPurgeable(env, options) && !optionsJustBounds(env, options); AutoJavaByteArray ar(env, byteArray); - SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable); + SkMemoryStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable); SkAutoUnref aur(stream); return doDecode(env, stream, NULL, options, purgeable); } diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp index cbb9b92..b8bb637 100644 --- a/core/jni/android/graphics/BitmapRegionDecoder.cpp +++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp @@ -75,27 +75,6 @@ private: int fHeight; }; -static SkMemoryStream* buildSkMemoryStream(SkStream *stream) { - size_t bufferSize = 4096; - size_t streamLen = 0; - size_t len; - char* data = (char*)sk_malloc_throw(bufferSize); - - while ((len = stream->read(data + streamLen, - bufferSize - streamLen)) != 0) { - streamLen += len; - if (streamLen == bufferSize) { - bufferSize *= 2; - data = (char*)sk_realloc_throw(data, bufferSize); - } - } - data = (char*)sk_realloc_throw(data, streamLen); - - SkMemoryStream* streamMem = new SkMemoryStream(); - streamMem->setMemoryOwned(data, streamLen); - return streamMem; -} - static jobject createBitmapRegionDecoder(JNIEnv* env, SkStream* stream) { SkImageDecoder* decoder = SkImageDecoder::Factory(stream); int width, height; @@ -160,14 +139,12 @@ static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz, jbyteArray storage, // byte[] jboolean isShareable) { jobject brd = NULL; - SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024); + // for now we don't allow shareable with java inputstreams + SkStream* stream = CopyJavaInputStream(env, is, storage); if (stream) { - // for now we don't allow shareable with java inputstreams - SkMemoryStream* mStream = buildSkMemoryStream(stream); - brd = createBitmapRegionDecoder(env, mStream); - SkSafeUnref(mStream); // the decoder now holds a reference - stream->unref(); + brd = createBitmapRegionDecoder(env, stream); + stream->unref(); // the decoder now holds a reference } return brd; } @@ -175,14 +152,14 @@ static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz, static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz, jint native_asset, // Asset jboolean isShareable) { - SkStream* stream, *assStream; Asset* asset = reinterpret_cast<Asset*>(native_asset); - assStream = new AssetStreamAdaptor(asset); - stream = buildSkMemoryStream(assStream); - assStream->unref(); + SkAutoTUnref<SkMemoryStream> stream(CopyAssetToStream(asset)); + if (NULL == stream.get()) { + return NULL; + } - jobject brd = createBitmapRegionDecoder(env, stream); - SkSafeUnref(stream); // the decoder now holds a reference + jobject brd = createBitmapRegionDecoder(env, stream.get()); + // The decoder now holds a reference to stream. return brd; } diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp index aa4cbde..797d155 100644 --- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp +++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp @@ -1,28 +1,83 @@ #include "CreateJavaOutputStreamAdaptor.h" +#include "JNIHelp.h" +#include "SkData.h" +#include "SkRefCnt.h" +#include "SkStream.h" +#include "SkTypes.h" +#include "Utils.h" +#include <androidfw/Asset.h> #define RETURN_NULL_IF_NULL(value) \ do { if (!(value)) { SkASSERT(0); return NULL; } } while (false) +#define RETURN_ZERO_IF_NULL(value) \ + do { if (!(value)) { SkASSERT(0); return 0; } } while (false) + static jmethodID gInputStream_resetMethodID; static jmethodID gInputStream_markMethodID; -static jmethodID gInputStream_availableMethodID; +static jmethodID gInputStream_markSupportedMethodID; static jmethodID gInputStream_readMethodID; static jmethodID gInputStream_skipMethodID; +class RewindableJavaStream; + +/** + * Non-rewindable wrapper for a Java InputStream. + */ class JavaInputStreamAdaptor : public SkStream { public: JavaInputStreamAdaptor(JNIEnv* env, jobject js, jbyteArray ar) : fEnv(env), fJavaInputStream(js), fJavaByteArray(ar) { SkASSERT(ar); - fCapacity = env->GetArrayLength(ar); + fCapacity = env->GetArrayLength(ar); SkASSERT(fCapacity > 0); - fBytesRead = 0; + fBytesRead = 0; + fIsAtEnd = false; + } + + virtual size_t read(void* buffer, size_t size) { + JNIEnv* env = fEnv; + if (NULL == buffer) { + if (0 == size) { + return 0; + } else { + /* InputStream.skip(n) can return <=0 but still not be at EOF + If we see that value, we need to call read(), which will + block if waiting for more data, or return -1 at EOF + */ + size_t amountSkipped = 0; + do { + size_t amount = this->doSkip(size - amountSkipped); + if (0 == amount) { + char tmp; + amount = this->doRead(&tmp, 1); + if (0 == amount) { + // if read returned 0, we're at EOF + fIsAtEnd = true; + break; + } + } + amountSkipped += amount; + } while (amountSkipped < size); + return amountSkipped; + } + } + return this->doRead(buffer, size); + } + + virtual bool isAtEnd() const { + return fIsAtEnd; } - virtual bool rewind() { +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()) { @@ -53,6 +108,7 @@ public: } if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications. + fIsAtEnd = true; break; // eof } @@ -92,58 +148,19 @@ public: return (size_t)skipped; } - size_t doSize() { - JNIEnv* env = fEnv; - jint avail = env->CallIntMethod(fJavaInputStream, - gInputStream_availableMethodID); - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - env->ExceptionClear(); - SkDebugf("------- available threw an exception\n"); - avail = 0; - } - return avail; - } - - virtual size_t read(void* buffer, size_t size) { - JNIEnv* env = fEnv; - if (NULL == buffer) { - if (0 == size) { - return this->doSize(); - } else { - /* InputStream.skip(n) can return <=0 but still not be at EOF - If we see that value, we need to call read(), which will - block if waiting for more data, or return -1 at EOF - */ - size_t amountSkipped = 0; - do { - size_t amount = this->doSkip(size - amountSkipped); - if (0 == amount) { - char tmp; - amount = this->doRead(&tmp, 1); - if (0 == amount) { - // if read returned 0, we're at EOF - break; - } - } - amountSkipped += amount; - } while (amountSkipped < size); - return amountSkipped; - } - } - return this->doRead(buffer, size); - } - -private: JNIEnv* fEnv; jobject fJavaInputStream; // the caller owns this object jbyteArray fJavaByteArray; // the caller owns this object size_t fCapacity; size_t fBytesRead; + bool fIsAtEnd; + + // Allows access to doRewind and fBytesRead. + friend class RewindableJavaStream; }; -SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, - jbyteArray storage, int markSize) { +SkStream* WrapJavaInputStream(JNIEnv* env, jobject stream, + jbyteArray storage) { static bool gInited; if (!gInited) { @@ -154,8 +171,8 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, "reset", "()V"); gInputStream_markMethodID = env->GetMethodID(inputStream_Clazz, "mark", "(I)V"); - gInputStream_availableMethodID = env->GetMethodID(inputStream_Clazz, - "available", "()I"); + gInputStream_markSupportedMethodID = env->GetMethodID(inputStream_Clazz, + "markSupported", "()Z"); gInputStream_readMethodID = env->GetMethodID(inputStream_Clazz, "read", "([BII)I"); gInputStream_skipMethodID = env->GetMethodID(inputStream_Clazz, @@ -163,18 +180,167 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, RETURN_NULL_IF_NULL(gInputStream_resetMethodID); RETURN_NULL_IF_NULL(gInputStream_markMethodID); - RETURN_NULL_IF_NULL(gInputStream_availableMethodID); + RETURN_NULL_IF_NULL(gInputStream_markSupportedMethodID); RETURN_NULL_IF_NULL(gInputStream_readMethodID); RETURN_NULL_IF_NULL(gInputStream_skipMethodID); gInited = true; } - if (markSize) { - env->CallVoidMethod(stream, gInputStream_markMethodID, markSize); + return new JavaInputStreamAdaptor(env, stream, storage); +} + +static SkMemoryStream* adaptor_to_mem_stream(SkStream* adaptor) { + SkASSERT(adaptor != NULL); + SkDynamicMemoryWStream wStream; + const int bufferSize = 256 * 1024; // 256 KB, same as ViewStateSerializer. + uint8_t buffer[bufferSize]; + do { + size_t bytesRead = adaptor->read(buffer, bufferSize); + wStream.write(buffer, bytesRead); + } while (!adaptor->isAtEnd()); + SkAutoTUnref<SkData> data(wStream.copyToData()); + return new SkMemoryStream(data.get()); +} + +SkMemoryStream* CopyJavaInputStream(JNIEnv* env, jobject stream, + jbyteArray storage) { + SkAutoTUnref<SkStream> adaptor(WrapJavaInputStream(env, stream, storage)); + if (NULL == adaptor.get()) { + return NULL; } + return adaptor_to_mem_stream(adaptor.get()); +} - return new JavaInputStreamAdaptor(env, stream, storage); +/** + * 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; +}; + +/** + * 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) { + static jclass byteArrayInputStream_Clazz; + static jfieldID countField; + static jfieldID posField; + + byteArrayInputStream_Clazz = env->FindClass("java/io/ByteArrayInputStream"); + RETURN_ZERO_IF_NULL(byteArrayInputStream_Clazz); + + countField = env->GetFieldID(byteArrayInputStream_Clazz, "count", "I"); + RETURN_ZERO_IF_NULL(byteArrayInputStream_Clazz); + posField = env->GetFieldID(byteArrayInputStream_Clazz, "pos", "I"); + RETURN_ZERO_IF_NULL(byteArrayInputStream_Clazz); + + if (env->IsInstanceOf(jstream, byteArrayInputStream_Clazz)) { + // Return the remaining length, to keep the same behavior of using the rest of the + // stream. + return env->GetIntField(jstream, countField) - env->GetIntField(jstream, posField); + } + 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(WrapJavaInputStream(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()); +} + +android::AssetStreamAdaptor* CheckForAssetStream(JNIEnv* env, jobject jstream) { + static jclass assetInputStream_Clazz; + static jmethodID getAssetIntMethodID; + + assetInputStream_Clazz = env->FindClass("android/content/res/AssetManager$AssetInputStream"); + RETURN_NULL_IF_NULL(assetInputStream_Clazz); + + getAssetIntMethodID = env->GetMethodID(assetInputStream_Clazz, "getAssetInt", "()I"); + RETURN_NULL_IF_NULL(getAssetIntMethodID); + + if (!env->IsInstanceOf(jstream, assetInputStream_Clazz)) { + return NULL; + } + + jint jasset = env->CallIntMethod(jstream, getAssetIntMethodID); + android::Asset* a = reinterpret_cast<android::Asset*>(jasset); + if (NULL == a) { + jniThrowNullPointerException(env, "NULL native asset"); + return NULL; + } + return new android::AssetStreamAdaptor(a); } /////////////////////////////////////////////////////////////////////////////// diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h index c34c96a..5218dc5 100644 --- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h +++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h @@ -3,10 +3,70 @@ //#include <android_runtime/AndroidRuntime.h> #include "jni.h" -#include "SkStream.h" -SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, - jbyteArray storage, int markSize = 0); +namespace android { + class AssetStreamAdaptor; +} + +class SkMemoryStream; +class SkStream; +class SkStreamRewindable; +class SkWStream; + +/** + * Return an adaptor from a Java InputStream to an SkStream. + * @param env JNIEnv object. + * @param stream Pointer to Java InputStream. + * @param storage Java byte array for retrieving data from the + * Java InputStream. + * @return SkStream Simple subclass of SkStream which supports its + * basic methods like reading. Only valid until the calling + * function returns, since the Java InputStream is not managed + * by the SkStream. + */ +SkStream* WrapJavaInputStream(JNIEnv* env, jobject stream, + jbyteArray storage); + +/** + * Copy 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 SkMemoryStream The data in stream will be copied to a new + * SkMemoryStream. + * FIXME: Could return a more generic return type if ViewStateSerializer + * did not require an SkMemoryStream. + */ +SkMemoryStream* 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 WrapJavaInputStream. + */ +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); diff --git a/core/jni/android/graphics/Movie.cpp b/core/jni/android/graphics/Movie.cpp index 4f64ff8..2eae841 100644 --- a/core/jni/android/graphics/Movie.cpp +++ b/core/jni/android/graphics/Movie.cpp @@ -1,8 +1,10 @@ +#include "ScopedLocalRef.h" #include "SkMovie.h" #include "SkStream.h" #include "GraphicsJNI.h" #include "SkTemplates.h" #include "SkUtils.h" +#include "Utils.h" #include "CreateJavaOutputStreamAdaptor.h" #include <androidfw/Asset.h> @@ -83,9 +85,14 @@ static jobject movie_decodeStream(JNIEnv* env, jobject clazz, jobject istream) { NPE_CHECK_RETURN_ZERO(env, istream); - // what is the lifetime of the array? Can the skstream hold onto it? - jbyteArray byteArray = env->NewByteArray(16*1024); - SkStream* strm = CreateJavaInputStreamAdaptor(env, istream, byteArray); + 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); + } if (NULL == strm) { return 0; } diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp index 9c02219..dff2b18 100644 --- a/core/jni/android/graphics/Picture.cpp +++ b/core/jni/android/graphics/Picture.cpp @@ -20,6 +20,7 @@ #include "SkCanvas.h" #include "SkPicture.h" +#include "SkStream.h" #include "SkTemplates.h" #include "CreateJavaOutputStreamAdaptor.h" @@ -38,10 +39,9 @@ public: static SkPicture* deserialize(JNIEnv* env, jobject, jobject jstream, jbyteArray jstorage) { SkPicture* picture = NULL; - SkStream* strm = CreateJavaInputStreamAdaptor(env, jstream, jstorage); - if (strm) { - picture = SkPicture::CreateFromStream(strm); - delete strm; + SkAutoTUnref<SkStream> strm(WrapJavaInputStream(env, jstream, jstorage)); + if (strm.get()) { + picture = SkPicture::CreateFromStream(strm.get()); } return picture; } diff --git a/core/jni/android/graphics/Utils.cpp b/core/jni/android/graphics/Utils.cpp index cf6977e..b7d1f3a 100644 --- a/core/jni/android/graphics/Utils.cpp +++ b/core/jni/android/graphics/Utils.cpp @@ -28,12 +28,28 @@ bool AssetStreamAdaptor::rewind() { return true; } +size_t AssetStreamAdaptor::getLength() const { + return fAsset->getLength(); +} + +bool AssetStreamAdaptor::isAtEnd() const { + return fAsset->getRemainingLength() == 0; +} + +SkStreamRewindable* AssetStreamAdaptor::duplicate() const { + SkASSERT(false); + // Cannot create a duplicate, since each AssetStreamAdaptor + // would be modifying the Asset. + //return new AssetStreamAdaptor(fAsset); + return NULL; +} + size_t AssetStreamAdaptor::read(void* buffer, size_t size) { ssize_t amount; if (NULL == buffer) { - if (0 == size) { // caller is asking us for our total length - return fAsset->getLength(); + if (0 == size) { + return 0; } // asset->seek returns new total offset // we want to return amount that was skipped @@ -62,6 +78,34 @@ size_t AssetStreamAdaptor::read(void* buffer, size_t size) { return amount; } +SkMemoryStream* android::CopyAssetToStream(Asset* asset) { + if (NULL == asset) { + return NULL; + } + + off64_t size = asset->seek(0, SEEK_SET); + if ((off64_t)-1 == size) { + SkDebugf("---- copyAsset: asset rewind failed\n"); + return NULL; + } + + size = asset->getLength(); + if (size <= 0) { + SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size); + return NULL; + } + + SkMemoryStream* stream = new SkMemoryStream(size); + void* data = const_cast<void*>(stream->getMemoryBase()); + off64_t len = asset->read(data, size); + if (len != size) { + SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len); + delete stream; + stream = NULL; + } + return stream; +} + jobject android::nullObjectReturn(const char msg[]) { if (msg) { SkDebugf("--- %s\n", msg); diff --git a/core/jni/android/graphics/Utils.h b/core/jni/android/graphics/Utils.h index 75ceaa2..a1ac72a 100644 --- a/core/jni/android/graphics/Utils.h +++ b/core/jni/android/graphics/Utils.h @@ -26,16 +26,27 @@ namespace android { -class AssetStreamAdaptor : public SkStream { +class AssetStreamAdaptor : public SkStreamRewindable { public: AssetStreamAdaptor(Asset* a) : fAsset(a) {} virtual bool rewind(); virtual size_t read(void* buffer, size_t size); + virtual bool hasLength() const { return true; } + virtual size_t getLength() const; + virtual bool isAtEnd() const; + virtual SkStreamRewindable* duplicate() const; private: Asset* fAsset; }; +/** + * Make a deep copy of the asset, and return it as a stream, or NULL if there + * was an error. + * FIXME: If we could "ref/reopen" the asset, we may not need to copy it here. + */ + +SkMemoryStream* CopyAssetToStream(Asset*); /** Restore the file descriptor's offset in our destructor */ diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index 5ace987..ea53f20 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -23,7 +23,6 @@ import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; -import java.io.BufferedInputStream; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.IOException; @@ -550,28 +549,28 @@ public class BitmapFactory { return null; } - Bitmap bm; + Bitmap bm = null; Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap"); try { - // we need mark/reset to work properly - if (!is.markSupported()) { - is = new BufferedInputStream(is, DECODE_BUFFER_SIZE); - } - - // so we can call reset() if a given codec gives up after reading up to - // this many bytes. FIXME: need to find out from the codecs what this - // value should be. - is.mark(1024); - + boolean decodeGenericStream = true; if (is instanceof AssetManager.AssetInputStream) { final int asset = ((AssetManager.AssetInputStream) is).getAssetInt(); bm = nativeDecodeAsset(asset, outPadding, opts); - } else { - // pass some temp storage down to the native code. 1024 is made up, - // but should be large enough to avoid too many small calls back - // into is.read(...) This number is not related to the value passed - // to mark(...) above. + // Do not follow the normal case. + decodeGenericStream = false; + } else if (is instanceof FileInputStream) { + try { + FileDescriptor fd = ((FileInputStream) is).getFD(); + // decodeFileDescriptor will take care of throwing the IAE and + // calling setDensityFromOptions. + return decodeFileDescriptor(fd, outPadding, opts); + } catch (IOException e) { + // Fall through to nativeDecodeStream. + } + } + + if (decodeGenericStream) { byte [] tempStorage = null; if (opts != null) tempStorage = opts.inTempStorage; if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE]; @@ -615,26 +614,41 @@ public class BitmapFactory { * no bitmap is returned (null) then padding is * unchanged. * @param opts null-ok; Options that control downsampling and whether the - * image should be completely decoded, or just is size returned. + * image should be completely decoded, or just its size returned. * @return the decoded bitmap, or null */ public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) { - if (nativeIsSeekable(fd)) { - Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts); + Bitmap bm; + + Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeFileDescriptor"); + try { + if (nativeIsSeekable(fd)) { + 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); + } finally { + try { + fis.close(); + } catch (Throwable t) {/* ignore */} + } + } + if (bm == null && opts != null && opts.inBitmap != null) { throw new IllegalArgumentException("Problem decoding into existing bitmap"); } - return bm; - } else { - FileInputStream fis = new FileInputStream(fd); - try { - return decodeStream(fis, outPadding, opts); - } finally { - try { - fis.close(); - } catch (Throwable t) {/* ignore */} - } + + setDensityFromOptions(bm, opts); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); } + return bm; } /** diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java index b38d107..3524b25 100644 --- a/graphics/java/android/graphics/BitmapRegionDecoder.java +++ b/graphics/java/android/graphics/BitmapRegionDecoder.java @@ -17,7 +17,6 @@ package android.graphics; import android.content.res.AssetManager; -import java.io.BufferedInputStream; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.IOException; @@ -108,12 +107,6 @@ public final class BitmapRegionDecoder { */ public static BitmapRegionDecoder newInstance(InputStream is, boolean isShareable) throws IOException { - // we need mark/reset to work properly in JNI - - if (!is.markSupported()) { - is = new BufferedInputStream(is, 16 * 1024); - } - if (is instanceof AssetManager.AssetInputStream) { return nativeNewInstance( ((AssetManager.AssetInputStream) is).getAssetInt(), |