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