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