1 #include "CreateJavaOutputStreamAdaptor.h" 2 3 #define RETURN_NULL_IF_NULL(value) \ 4 do { if (!(value)) { SkASSERT(0); return NULL; } } while (false) 5 6 static jclass gInputStream_Clazz; 7 static jmethodID gInputStream_resetMethodID; 8 static jmethodID gInputStream_availableMethodID; 9 static jmethodID gInputStream_readMethodID; 10 static jmethodID gInputStream_skipMethodID; 11 12 class JavaInputStreamAdaptor : public SkStream { 13 public: 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) { 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 jlong skipped = env->CallLongMethod(fJavaInputStream, 80 gInputStream_skipMethodID, (jlong)size); 81 if (env->ExceptionCheck()) { 82 env->ExceptionDescribe(); 83 env->ExceptionClear(); 84 SkDebugf("------- available threw an exception\n"); 85 return 0; 86 } 87 if (skipped < 0) { 88 skipped = 0; 89 } 90 return (size_t)skipped; 91 } 92 93 size_t doSize() { 94 JNIEnv* env = fEnv; 95 jint avail = env->CallIntMethod(fJavaInputStream, 96 gInputStream_availableMethodID); 97 if (env->ExceptionCheck()) { 98 env->ExceptionDescribe(); 99 env->ExceptionClear(); 100 SkDebugf("------- available threw an exception\n"); 101 avail = 0; 102 } 103 return avail; 104 } 105 106 virtual size_t read(void* buffer, size_t size) { 107 JNIEnv* env = fEnv; 108 if (NULL == buffer) { 109 if (0 == size) { 110 return this->doSize(); 111 } else { 112 /* InputStream.skip(n) can return <=0 but still not be at EOF 113 If we see that value, we need to call read(), which will 114 block if waiting for more data, or return -1 at EOF 115 */ 116 size_t amountSkipped = 0; 117 do { 118 size_t amount = this->doSkip(size); 119 if (0 == amount) { 120 char tmp; 121 amount = this->doRead(&tmp, 1); 122 if (0 == amount) { 123 // if read returned 0, we're at EOF 124 break; 125 } 126 } 127 amountSkipped += amount; 128 } while (amountSkipped < size); 129 return amountSkipped; 130 } 131 } 132 return this->doRead(buffer, size); 133 } 134 135 private: 136 JNIEnv* fEnv; 137 jobject fJavaInputStream; // the caller owns this object 138 jbyteArray fJavaByteArray; // the caller owns this object 139 size_t fCapacity; 140 size_t fBytesRead; 141 }; 142 143 SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, 144 jbyteArray storage) { 145 static bool gInited; 146 147 if (!gInited) { 148 gInputStream_Clazz = env->FindClass("java/io/InputStream"); 149 RETURN_NULL_IF_NULL(gInputStream_Clazz); 150 gInputStream_Clazz = (jclass)env->NewGlobalRef(gInputStream_Clazz); 151 152 gInputStream_resetMethodID = env->GetMethodID(gInputStream_Clazz, 153 "reset", "()V"); 154 gInputStream_availableMethodID = env->GetMethodID(gInputStream_Clazz, 155 "available", "()I"); 156 gInputStream_readMethodID = env->GetMethodID(gInputStream_Clazz, 157 "read", "([BII)I"); 158 gInputStream_skipMethodID = env->GetMethodID(gInputStream_Clazz, 159 "skip", "(J)J"); 160 161 RETURN_NULL_IF_NULL(gInputStream_resetMethodID); 162 RETURN_NULL_IF_NULL(gInputStream_availableMethodID); 163 RETURN_NULL_IF_NULL(gInputStream_availableMethodID); 164 RETURN_NULL_IF_NULL(gInputStream_skipMethodID); 165 166 gInited = true; 167 } 168 169 return new JavaInputStreamAdaptor(env, stream, storage); 170 } 171 172 /////////////////////////////////////////////////////////////////////////////// 173 174 static jclass gOutputStream_Clazz; 175 static jmethodID gOutputStream_writeMethodID; 176 static jmethodID gOutputStream_flushMethodID; 177 178 class SkJavaOutputStream : public SkWStream { 179 public: 180 SkJavaOutputStream(JNIEnv* env, jobject stream, jbyteArray storage) 181 : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage) { 182 fCapacity = env->GetArrayLength(storage); 183 } 184 185 virtual bool write(const void* buffer, size_t size) { 186 JNIEnv* env = fEnv; 187 jbyteArray storage = fJavaByteArray; 188 189 while (size > 0) { 190 size_t requested = size; 191 if (requested > fCapacity) { 192 requested = fCapacity; 193 } 194 195 env->SetByteArrayRegion(storage, 0, requested, 196 reinterpret_cast<const jbyte*>(buffer)); 197 if (env->ExceptionCheck()) { 198 env->ExceptionDescribe(); 199 env->ExceptionClear(); 200 SkDebugf("--- write:SetByteArrayElements threw an exception\n"); 201 return false; 202 } 203 204 fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_writeMethodID, 205 storage, 0, requested); 206 if (env->ExceptionCheck()) { 207 env->ExceptionDescribe(); 208 env->ExceptionClear(); 209 SkDebugf("------- write threw an exception\n"); 210 return false; 211 } 212 213 buffer = (void*)((char*)buffer + requested); 214 size -= requested; 215 } 216 return true; 217 } 218 219 virtual void flush() { 220 fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_flushMethodID); 221 } 222 223 private: 224 JNIEnv* fEnv; 225 jobject fJavaOutputStream; // the caller owns this object 226 jbyteArray fJavaByteArray; // the caller owns this object 227 size_t fCapacity; 228 }; 229 230 SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, 231 jbyteArray storage) { 232 static bool gInited; 233 234 if (!gInited) { 235 gOutputStream_Clazz = env->FindClass("java/io/OutputStream"); 236 RETURN_NULL_IF_NULL(gOutputStream_Clazz); 237 gOutputStream_Clazz = (jclass)env->NewGlobalRef(gOutputStream_Clazz); 238 239 gOutputStream_writeMethodID = env->GetMethodID(gOutputStream_Clazz, 240 "write", "([BII)V"); 241 RETURN_NULL_IF_NULL(gOutputStream_writeMethodID); 242 gOutputStream_flushMethodID = env->GetMethodID(gOutputStream_Clazz, 243 "flush", "()V"); 244 RETURN_NULL_IF_NULL(gOutputStream_flushMethodID); 245 246 gInited = true; 247 } 248 249 return new SkJavaOutputStream(env, stream, storage); 250 } 251 252