Loading core/jni/android/graphics/Bitmap.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -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" Loading core/jni/android/graphics/BitmapFactory.cpp +9 −46 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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; } Loading @@ -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, Loading @@ -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); } Loading core/jni/android/graphics/BitmapRegionDecoder.cpp +10 −33 Original line number Diff line number Diff line Loading @@ -76,27 +76,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; Loading Loading @@ -161,14 +140,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; } Loading @@ -176,14 +153,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; } Loading core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp +221 −55 Original line number Diff line number Diff line #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) Loading @@ -17,12 +32,52 @@ public: fCapacity = env->GetArrayLength(ar); SkASSERT(fCapacity > 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()) { Loading Loading @@ -53,6 +108,7 @@ public: } if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications. fIsAtEnd = true; break; // eof } Loading Loading @@ -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) { Loading @@ -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, Loading @@ -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); } 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); } 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); } /////////////////////////////////////////////////////////////////////////////// Loading core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h +63 −3 Original line number Diff line number Diff line Loading @@ -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); Loading Loading
core/jni/android/graphics/Bitmap.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -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" Loading
core/jni/android/graphics/BitmapFactory.cpp +9 −46 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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; } Loading @@ -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, Loading @@ -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); } Loading
core/jni/android/graphics/BitmapRegionDecoder.cpp +10 −33 Original line number Diff line number Diff line Loading @@ -76,27 +76,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; Loading Loading @@ -161,14 +140,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; } Loading @@ -176,14 +153,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; } Loading
core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp +221 −55 Original line number Diff line number Diff line #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) Loading @@ -17,12 +32,52 @@ public: fCapacity = env->GetArrayLength(ar); SkASSERT(fCapacity > 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()) { Loading Loading @@ -53,6 +108,7 @@ public: } if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications. fIsAtEnd = true; break; // eof } Loading Loading @@ -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) { Loading @@ -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, Loading @@ -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); } 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); } 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); } /////////////////////////////////////////////////////////////////////////////// Loading
core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h +63 −3 Original line number Diff line number Diff line Loading @@ -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); Loading