Merge "Replace stream wrap-function w/ more specific ones" into klp-dev
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 @@
// 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 @@
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 @@
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 @@
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 @@
*/
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 8867a11..6646579 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -76,27 +76,6 @@
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;
@@ -161,14 +140,12 @@
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;
}
@@ -176,14 +153,14 @@
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 bool rewind() {
+ 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;
+ }
+
+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 @@
}
if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications.
+ fIsAtEnd = true;
break; // eof
}
@@ -92,58 +148,19 @@
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 @@
"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 @@
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());
+}
+
+/**
+ * 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);
}
- return new JavaInputStreamAdaptor(env, stream, storage);
+ 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 @@
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 @@
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 @@
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 @@
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 1c426fd..1721bee 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -23,7 +23,6 @@
import android.util.Log;
import android.util.TypedValue;
-import java.io.BufferedInputStream;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
@@ -545,28 +544,28 @@
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];
@@ -610,26 +609,41 @@
* 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 @@
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 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(),