CreateJavaOutputStreamAdaptor.cpp revision 2bb39d7a43fdb28ecdafd65ea4b89dc05c1ad7be
1#include "CreateJavaOutputStreamAdaptor.h" 2#include "JNIHelp.h" 3#include "SkData.h" 4#include "SkRefCnt.h" 5#include "SkStream.h" 6#include "SkTypes.h" 7#include "Utils.h" 8 9static jmethodID gInputStream_readMethodID; 10static jmethodID gInputStream_skipMethodID; 11 12/** 13 * Wrapper for a Java InputStream. 14 */ 15class JavaInputStreamAdaptor : public SkStream { 16public: 17 JavaInputStreamAdaptor(JNIEnv* env, jobject js, jbyteArray ar) 18 : fEnv(env), fJavaInputStream(js), fJavaByteArray(ar) { 19 SkASSERT(ar); 20 fCapacity = env->GetArrayLength(ar); 21 SkASSERT(fCapacity > 0); 22 fBytesRead = 0; 23 fIsAtEnd = false; 24 } 25 26 virtual size_t read(void* buffer, size_t size) { 27 JNIEnv* env = fEnv; 28 if (NULL == buffer) { 29 if (0 == size) { 30 return 0; 31 } else { 32 /* InputStream.skip(n) can return <=0 but still not be at EOF 33 If we see that value, we need to call read(), which will 34 block if waiting for more data, or return -1 at EOF 35 */ 36 size_t amountSkipped = 0; 37 do { 38 size_t amount = this->doSkip(size - amountSkipped); 39 if (0 == amount) { 40 char tmp; 41 amount = this->doRead(&tmp, 1); 42 if (0 == amount) { 43 // if read returned 0, we're at EOF 44 fIsAtEnd = true; 45 break; 46 } 47 } 48 amountSkipped += amount; 49 } while (amountSkipped < size); 50 return amountSkipped; 51 } 52 } 53 return this->doRead(buffer, size); 54 } 55 56 virtual bool isAtEnd() const { 57 return fIsAtEnd; 58 } 59 60private: 61 size_t doRead(void* buffer, size_t size) { 62 JNIEnv* env = fEnv; 63 size_t bytesRead = 0; 64 // read the bytes 65 do { 66 jint requested = 0; 67 if (size > static_cast<size_t>(fCapacity)) { 68 requested = fCapacity; 69 } else { 70 // This is safe because requested is clamped to (jint) 71 // fCapacity. 72 requested = static_cast<jint>(size); 73 } 74 75 jint n = env->CallIntMethod(fJavaInputStream, 76 gInputStream_readMethodID, fJavaByteArray, 0, requested); 77 if (env->ExceptionCheck()) { 78 env->ExceptionDescribe(); 79 env->ExceptionClear(); 80 SkDebugf("---- read threw an exception\n"); 81 return 0; 82 } 83 84 if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications. 85 fIsAtEnd = true; 86 break; // eof 87 } 88 89 env->GetByteArrayRegion(fJavaByteArray, 0, n, 90 reinterpret_cast<jbyte*>(buffer)); 91 if (env->ExceptionCheck()) { 92 env->ExceptionDescribe(); 93 env->ExceptionClear(); 94 SkDebugf("---- read:GetByteArrayRegion threw an exception\n"); 95 return 0; 96 } 97 98 buffer = (void*)((char*)buffer + n); 99 bytesRead += n; 100 size -= n; 101 fBytesRead += n; 102 } while (size != 0); 103 104 return bytesRead; 105 } 106 107 size_t doSkip(size_t size) { 108 JNIEnv* env = fEnv; 109 110 jlong skipped = env->CallLongMethod(fJavaInputStream, 111 gInputStream_skipMethodID, (jlong)size); 112 if (env->ExceptionCheck()) { 113 env->ExceptionDescribe(); 114 env->ExceptionClear(); 115 SkDebugf("------- skip threw an exception\n"); 116 return 0; 117 } 118 if (skipped < 0) { 119 skipped = 0; 120 } 121 122 return (size_t)skipped; 123 } 124 125 JNIEnv* fEnv; 126 jobject fJavaInputStream; // the caller owns this object 127 jbyteArray fJavaByteArray; // the caller owns this object 128 jint fCapacity; 129 size_t fBytesRead; 130 bool fIsAtEnd; 131}; 132 133SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, 134 jbyteArray storage) { 135 return new JavaInputStreamAdaptor(env, stream, storage); 136} 137 138 139static SkMemoryStream* adaptor_to_mem_stream(SkStream* stream) { 140 SkASSERT(stream != NULL); 141 size_t bufferSize = 4096; 142 size_t streamLen = 0; 143 size_t len; 144 char* data = (char*)sk_malloc_throw(bufferSize); 145 146 while ((len = stream->read(data + streamLen, 147 bufferSize - streamLen)) != 0) { 148 streamLen += len; 149 if (streamLen == bufferSize) { 150 bufferSize *= 2; 151 data = (char*)sk_realloc_throw(data, bufferSize); 152 } 153 } 154 data = (char*)sk_realloc_throw(data, streamLen); 155 156 SkMemoryStream* streamMem = new SkMemoryStream(); 157 streamMem->setMemoryOwned(data, streamLen); 158 return streamMem; 159} 160 161SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream, 162 jbyteArray storage) { 163 SkAutoTUnref<SkStream> adaptor(CreateJavaInputStreamAdaptor(env, stream, storage)); 164 if (NULL == adaptor.get()) { 165 return NULL; 166 } 167 return adaptor_to_mem_stream(adaptor.get()); 168} 169 170/////////////////////////////////////////////////////////////////////////////// 171 172static jmethodID gOutputStream_writeMethodID; 173static jmethodID gOutputStream_flushMethodID; 174 175class SkJavaOutputStream : public SkWStream { 176public: 177 SkJavaOutputStream(JNIEnv* env, jobject stream, jbyteArray storage) 178 : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage) { 179 fCapacity = env->GetArrayLength(storage); 180 } 181 182 virtual bool write(const void* buffer, size_t size) { 183 JNIEnv* env = fEnv; 184 jbyteArray storage = fJavaByteArray; 185 186 while (size > 0) { 187 jint requested = 0; 188 if (size > static_cast<size_t>(fCapacity)) { 189 requested = fCapacity; 190 } else { 191 // This is safe because requested is clamped to (jint) 192 // fCapacity. 193 requested = static_cast<jint>(size); 194 } 195 196 env->SetByteArrayRegion(storage, 0, requested, 197 reinterpret_cast<const jbyte*>(buffer)); 198 if (env->ExceptionCheck()) { 199 env->ExceptionDescribe(); 200 env->ExceptionClear(); 201 SkDebugf("--- write:SetByteArrayElements threw an exception\n"); 202 return false; 203 } 204 205 fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_writeMethodID, 206 storage, 0, requested); 207 if (env->ExceptionCheck()) { 208 env->ExceptionDescribe(); 209 env->ExceptionClear(); 210 SkDebugf("------- write threw an exception\n"); 211 return false; 212 } 213 214 buffer = (void*)((char*)buffer + requested); 215 size -= requested; 216 } 217 return true; 218 } 219 220 virtual void flush() { 221 fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_flushMethodID); 222 } 223 224private: 225 JNIEnv* fEnv; 226 jobject fJavaOutputStream; // the caller owns this object 227 jbyteArray fJavaByteArray; // the caller owns this object 228 jint fCapacity; 229}; 230 231SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, 232 jbyteArray storage) { 233 static bool gInited; 234 235 if (!gInited) { 236 237 gInited = true; 238 } 239 240 return new SkJavaOutputStream(env, stream, storage); 241} 242 243static jclass findClassCheck(JNIEnv* env, const char classname[]) { 244 jclass clazz = env->FindClass(classname); 245 SkASSERT(!env->ExceptionCheck()); 246 return clazz; 247} 248 249static jmethodID getMethodIDCheck(JNIEnv* env, jclass clazz, 250 const char methodname[], const char type[]) { 251 jmethodID id = env->GetMethodID(clazz, methodname, type); 252 SkASSERT(!env->ExceptionCheck()); 253 return id; 254} 255 256int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env) { 257 jclass inputStream_Clazz = findClassCheck(env, "java/io/InputStream"); 258 gInputStream_readMethodID = getMethodIDCheck(env, inputStream_Clazz, "read", "([BII)I"); 259 gInputStream_skipMethodID = getMethodIDCheck(env, inputStream_Clazz, "skip", "(J)J"); 260 261 jclass outputStream_Clazz = findClassCheck(env, "java/io/OutputStream"); 262 gOutputStream_writeMethodID = getMethodIDCheck(env, outputStream_Clazz, "write", "([BII)V"); 263 gOutputStream_flushMethodID = getMethodIDCheck(env, outputStream_Clazz, "flush", "()V"); 264 265 return 0; 266} 267