am 4b299312: Merge "Replace stream wrap-function w/ more specific ones" into klp-dev
* commit '4b2993123bf9a1c56d0a16b032612834dd418520': Replace stream wrap-function w/ more specific ones
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 @@ 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
@@ -545,28 +544,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];
|
||||
@@ -610,26 +609,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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user