From 6a61141137c7a46d747aa611c9caf62436bc119f Mon Sep 17 00:00:00 2001 From: Chris Craik Date: Wed, 1 Apr 2015 14:54:12 -0700 Subject: Adding Bytebuffer support to rastermill library Change-Id: Ifedaeaec49caffa5add357246ff43e2b870949f0 --- framesequence/Android.mk | 2 + framesequence/jni/FrameSequence.h | 1 + framesequence/jni/FrameSequenceJNI.cpp | 25 +++++++++++- framesequence/jni/FrameSequence_gif.cpp | 5 +++ framesequence/jni/FrameSequence_gif.h | 4 ++ framesequence/jni/FrameSequence_webp.cpp | 47 ++++++++++++++-------- framesequence/jni/FrameSequence_webp.h | 5 +++ framesequence/jni/Registry.cpp | 27 +++++++++---- framesequence/jni/Registry.h | 4 ++ framesequence/jni/Stream.cpp | 28 +++++++++++++ framesequence/jni/Stream.h | 16 ++++++-- .../android/support/rastermill/FrameSequence.java | 15 +++++++ 12 files changed, 149 insertions(+), 30 deletions(-) diff --git a/framesequence/Android.mk b/framesequence/Android.mk index cc2c16c..1b3cf2d 100644 --- a/framesequence/Android.mk +++ b/framesequence/Android.mk @@ -21,6 +21,8 @@ LOCAL_MODULE := android-common-framesequence LOCAL_SDK_VERSION := 8 LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_PROGUARD_FLAG_FILES := proguard.flags + include $(BUILD_STATIC_JAVA_LIBRARY) include $(call all-makefiles-under, $(LOCAL_PATH)) diff --git a/framesequence/jni/FrameSequence.h b/framesequence/jni/FrameSequence.h index 6667cdd..134c81a 100644 --- a/framesequence/jni/FrameSequence.h +++ b/framesequence/jni/FrameSequence.h @@ -49,6 +49,7 @@ public: virtual bool isOpaque() const = 0; virtual int getFrameCount() const = 0; virtual int getDefaultLoopCount() const = 0; + virtual jobject getRawByteBuffer() const = 0; virtual FrameSequenceState* createState() const = 0; }; diff --git a/framesequence/jni/FrameSequenceJNI.cpp b/framesequence/jni/FrameSequenceJNI.cpp index 08a73bc..dfc51ec 100644 --- a/framesequence/jni/FrameSequenceJNI.cpp +++ b/framesequence/jni/FrameSequenceJNI.cpp @@ -53,12 +53,27 @@ static jobject nativeDecodeByteArray(JNIEnv* env, jobject clazz, "couldn't read array bytes"); return NULL; } - MemoryStream stream(bytes + offset, length); + MemoryStream stream(bytes + offset, length, NULL); FrameSequence* frameSequence = FrameSequence::create(&stream); env->ReleasePrimitiveArrayCritical(byteArray, bytes, 0); return createJavaFrameSequence(env, frameSequence); } +static jobject nativeDecodeByteBuffer(JNIEnv* env, jobject clazz, + jobject buf, jint offset, jint limit) { + jobject globalBuf = env->NewGlobalRef(buf); + JavaVM* vm; + env->GetJavaVM(&vm); + MemoryStream stream( + (reinterpret_cast( + env->GetDirectBufferAddress(globalBuf))) + offset, + limit, + globalBuf); + FrameSequence* frameSequence = FrameSequence::create(&stream); + jobject finalSequence = createJavaFrameSequence(env, frameSequence); + return finalSequence; +} + static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject istream, jbyteArray byteArray) { JavaInputStream stream(env, istream, byteArray); @@ -69,6 +84,10 @@ static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, static void nativeDestroyFrameSequence(JNIEnv* env, jobject clazz, jlong frameSequenceLong) { FrameSequence* frameSequence = reinterpret_cast(frameSequenceLong); + jobject buf = frameSequence->getRawByteBuffer(); + if (buf != NULL) { + env->DeleteGlobalRef(buf); + } delete frameSequence; } @@ -123,6 +142,10 @@ static JNINativeMethod gMethods[] = { "([BII)L" JNI_PACKAGE "/FrameSequence;", (void*) nativeDecodeByteArray }, + { "nativeDecodeByteBuffer", + "(Ljava/nio/ByteBuffer;II)L" JNI_PACKAGE "/FrameSequence;", + (void*) nativeDecodeByteBuffer + }, { "nativeDecodeStream", "(Ljava/io/InputStream;[B)L" JNI_PACKAGE "/FrameSequence;", (void*) nativeDecodeStream diff --git a/framesequence/jni/FrameSequence_gif.cpp b/framesequence/jni/FrameSequence_gif.cpp index f3e94df..2188c53 100644 --- a/framesequence/jni/FrameSequence_gif.cpp +++ b/framesequence/jni/FrameSequence_gif.cpp @@ -352,6 +352,10 @@ static bool isGif(void* header, int header_size) { || !memcmp(GIF89_STAMP, header, GIF_STAMP_LEN); } +static bool acceptsBuffers() { + return false; +} + static FrameSequence* createFramesequence(Stream* stream) { return new FrameSequence_gif(stream); } @@ -361,6 +365,7 @@ static RegistryEntry gEntry = { isGif, createFramesequence, NULL, + acceptsBuffers, }; static Registry gRegister(gEntry); diff --git a/framesequence/jni/FrameSequence_gif.h b/framesequence/jni/FrameSequence_gif.h index 8bf57b6..563f5b8 100644 --- a/framesequence/jni/FrameSequence_gif.h +++ b/framesequence/jni/FrameSequence_gif.h @@ -49,6 +49,10 @@ public: return mLoopCount; } + virtual jobject getRawByteBuffer() const { + return NULL; + } + virtual FrameSequenceState* createState() const; GifFileType* getGif() const { return mGif; } diff --git a/framesequence/jni/FrameSequence_webp.cpp b/framesequence/jni/FrameSequence_webp.cpp index 602feb7..c33a7e2 100644 --- a/framesequence/jni/FrameSequence_webp.cpp +++ b/framesequence/jni/FrameSequence_webp.cpp @@ -85,22 +85,28 @@ void FrameSequence_webp::constructDependencyChain() { } FrameSequence_webp::FrameSequence_webp(Stream* stream) { - // Read RIFF header to get file size. - uint8_t riff_header[RIFF_HEADER_SIZE]; - if (stream->read(riff_header, RIFF_HEADER_SIZE) != RIFF_HEADER_SIZE) { - ALOGE("WebP header load failed"); - return; - } - mData.size = CHUNK_HEADER_SIZE + GetLE32(riff_header + TAG_SIZE); - mData.bytes = new uint8_t[mData.size]; - memcpy((void*)mData.bytes, riff_header, RIFF_HEADER_SIZE); - - // Read rest of the bytes. - void* remaining_bytes = (void*)(mData.bytes + RIFF_HEADER_SIZE); - size_t remaining_size = mData.size - RIFF_HEADER_SIZE; - if (stream->read(remaining_bytes, remaining_size) != remaining_size) { - ALOGE("WebP full load failed"); - return; + if (stream->getRawBuffer() != NULL) { + mData.size = stream->getRawBufferSize(); + mData.bytes = stream->getRawBufferAddr(); + mRawByteBuffer = stream->getRawBuffer(); + } else { + // Read RIFF header to get file size. + uint8_t riff_header[RIFF_HEADER_SIZE]; + if (stream->read(riff_header, RIFF_HEADER_SIZE) != RIFF_HEADER_SIZE) { + ALOGE("WebP header load failed"); + return; + } + mData.size = CHUNK_HEADER_SIZE + GetLE32(riff_header + TAG_SIZE); + mData.bytes = new uint8_t[mData.size]; + memcpy((void*)mData.bytes, riff_header, RIFF_HEADER_SIZE); + + // Read rest of the bytes. + void* remaining_bytes = (void*)(mData.bytes + RIFF_HEADER_SIZE); + size_t remaining_size = mData.size - RIFF_HEADER_SIZE; + if (stream->read(remaining_bytes, remaining_size) != remaining_size) { + ALOGE("WebP full load failed"); + return; + } } // Construct demux. @@ -120,8 +126,10 @@ FrameSequence_webp::FrameSequence_webp(Stream* stream) { FrameSequence_webp::~FrameSequence_webp() { WebPDemuxDelete(mDemux); - delete[] mData.bytes; delete[] mIsKeyFrame; + if (mRawByteBuffer == NULL) { + delete[] mData.bytes; + } } FrameSequenceState* FrameSequence_webp::createState() const { @@ -366,6 +374,10 @@ static bool isWebP(void* header, int header_size) { !memcmp("WEBP", header_str + 8, 4); } +static bool acceptsWebPBuffer() { + return true; +} + static FrameSequence* createFramesequence(Stream* stream) { return new FrameSequence_webp(stream); } @@ -375,6 +387,7 @@ static RegistryEntry gEntry = { isWebP, createFramesequence, NULL, + acceptsWebPBuffer, }; static Registry gRegister(gEntry); diff --git a/framesequence/jni/FrameSequence_webp.h b/framesequence/jni/FrameSequence_webp.h index f4fbec0..94dcc3b 100644 --- a/framesequence/jni/FrameSequence_webp.h +++ b/framesequence/jni/FrameSequence_webp.h @@ -51,6 +51,10 @@ public: return mLoopCount; } + virtual jobject getRawByteBuffer() const { + return mRawByteBuffer; + } + virtual FrameSequenceState* createState() const; WebPDemuxer* getDemuxer() const { return mDemux; } @@ -66,6 +70,7 @@ private: uint32_t mFormatFlags; // mIsKeyFrame[i] is true if ith canvas can be constructed without decoding any prior frames. bool* mIsKeyFrame; + jobject mRawByteBuffer; }; // Produces frames of a possibly-animated WebP file for display. diff --git a/framesequence/jni/Registry.cpp b/framesequence/jni/Registry.cpp index 125ac66..e632bb2 100644 --- a/framesequence/jni/Registry.cpp +++ b/framesequence/jni/Registry.cpp @@ -34,15 +34,26 @@ Registry::Registry(const RegistryEntry& entry) { const RegistryEntry* Registry::Find(Stream* stream) { Registry* registry = gHead; - int headerSize = gHeaderBytesRequired; - char header[headerSize]; - headerSize = stream->peek(header, headerSize); - while (registry) { - if (headerSize >= registry->mImpl.requiredHeaderBytes - && registry->mImpl.checkHeader(header, headerSize)) { - return &(registry->mImpl); + + if (stream->getRawBuffer() != NULL) { + while (registry) { + if (registry->mImpl.acceptsBuffer()) { + return &(registry->mImpl); + } + registry = registry->mNext; + } + } else { + int headerSize = gHeaderBytesRequired; + char header[headerSize]; + headerSize = stream->peek(header, headerSize); + while (registry) { + if (headerSize >= registry->mImpl.requiredHeaderBytes + && registry->mImpl.checkHeader(header, headerSize)) { + return &(registry->mImpl); + } + registry = registry->mNext; } - registry = registry->mNext; } return 0; } + diff --git a/framesequence/jni/Registry.h b/framesequence/jni/Registry.h index 571c611..8db43cf 100644 --- a/framesequence/jni/Registry.h +++ b/framesequence/jni/Registry.h @@ -17,6 +17,9 @@ #ifndef RASTERMILL_REGISTRY_H #define RASTERMILL_REGISTRY_H +#include "jni.h" +#include + class FrameSequence; class Decoder; class Stream; @@ -26,6 +29,7 @@ struct RegistryEntry { bool (*checkHeader)(void* header, int header_size); FrameSequence* (*createFrameSequence)(Stream* stream); Decoder* (*createDecoder)(Stream* stream); + bool (*acceptsBuffer)(); }; /** diff --git a/framesequence/jni/Stream.cpp b/framesequence/jni/Stream.cpp index b2e0c39..a576e66 100644 --- a/framesequence/jni/Stream.cpp +++ b/framesequence/jni/Stream.cpp @@ -79,6 +79,34 @@ size_t Stream::read(void* buffer, size_t size) { return bytes_read; } +uint8_t* Stream::getRawBufferAddr() { + return NULL; +} + +jobject Stream::getRawBuffer() { + return NULL; +} + +int Stream::getRawBufferSize() { + return 0; +} + +uint8_t* MemoryStream::getRawBufferAddr() { + return mBuffer; +} + +jobject MemoryStream::getRawBuffer() { + return mRawBuffer; +} + +int MemoryStream::getRawBufferSize() { + if (mRawBuffer != NULL) { + return mRemaining; + } else { + return 0; + } +} + size_t MemoryStream::doRead(void* buffer, size_t size) { size = min(size, mRemaining); memcpy(buffer, mBuffer, size); diff --git a/framesequence/jni/Stream.h b/framesequence/jni/Stream.h index f8f2427..f0f3895 100644 --- a/framesequence/jni/Stream.h +++ b/framesequence/jni/Stream.h @@ -28,6 +28,9 @@ public: size_t peek(void* buffer, size_t size); size_t read(void* buffer, size_t size); + virtual uint8_t* getRawBufferAddr(); + virtual jobject getRawBuffer(); + virtual int getRawBufferSize(); protected: virtual size_t doRead(void* buffer, size_t size) = 0; @@ -40,16 +43,21 @@ private: class MemoryStream : public Stream { public: - MemoryStream(void* buffer, size_t size) : - mBuffer((char*)buffer), - mRemaining(size) {} + MemoryStream(void* buffer, size_t size, jobject buf) : + mBuffer((uint8_t*)buffer), + mRemaining(size), + mRawBuffer(buf) {} + virtual uint8_t* getRawBufferAddr(); + virtual jobject getRawBuffer(); + virtual int getRawBufferSize(); protected: virtual size_t doRead(void* buffer, size_t size); private: - char* mBuffer; + uint8_t* mBuffer; size_t mRemaining; + jobject mRawBuffer; }; class FileStream : public Stream { diff --git a/framesequence/src/android/support/rastermill/FrameSequence.java b/framesequence/src/android/support/rastermill/FrameSequence.java index d2bd128..8ff241f 100644 --- a/framesequence/src/android/support/rastermill/FrameSequence.java +++ b/framesequence/src/android/support/rastermill/FrameSequence.java @@ -17,6 +17,7 @@ package android.support.rastermill; import android.graphics.Bitmap; +import java.nio.ByteBuffer; import java.io.InputStream; @@ -40,6 +41,7 @@ public class FrameSequence { private static native FrameSequence nativeDecodeByteArray(byte[] data, int offset, int length); private static native FrameSequence nativeDecodeStream(InputStream is, byte[] tempStorage); + private static native FrameSequence nativeDecodeByteBuffer(ByteBuffer buffer, int offset, int capacity); private static native void nativeDestroyFrameSequence(long nativeFrameSequence); private static native long nativeCreateState(long nativeFrameSequence); private static native void nativeDestroyState(long nativeState); @@ -69,6 +71,19 @@ public class FrameSequence { return nativeDecodeByteArray(data, offset, length); } + public static FrameSequence decodeByteBuffer(ByteBuffer buffer) { + if (buffer == null) throw new IllegalArgumentException(); + if (!buffer.isDirect()) { + if (buffer.hasArray()) { + byte[] byteArray = buffer.array(); + return decodeByteArray(byteArray, buffer.position(), buffer.remaining()); + } else { + throw new IllegalArgumentException("Cannot have non-direct ByteBuffer with no byte array"); + } + } + return nativeDecodeByteBuffer(buffer, buffer.position(), buffer.remaining()); + } + public static FrameSequence decodeStream(InputStream stream) { if (stream == null) throw new IllegalArgumentException(); byte[] tempStorage = new byte[16 * 1024]; // TODO: use buffer pool -- cgit v1.2.3