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 9 static jmethodID gInputStream_readMethodID; 10 static jmethodID gInputStream_skipMethodID; 11 12 /** 13 * Wrapper for a Java InputStream. 14 */ 15 class JavaInputStreamAdaptor : public SkStream { 16 public: 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 60 private: 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 size_t requested = size; 67 if (requested > fCapacity) 68 requested = fCapacity; 69 70 jint n = env->CallIntMethod(fJavaInputStream, 71 gInputStream_readMethodID, fJavaByteArray, 0, requested); 72 if (env->ExceptionCheck()) { 73 env->ExceptionDescribe(); 74 env->ExceptionClear(); 75 SkDebugf("---- read threw an exception\n"); 76 return 0; 77 } 78 79 if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications. 80 fIsAtEnd = true; 81 break; // eof 82 } 83 84 env->GetByteArrayRegion(fJavaByteArray, 0, n, 85 reinterpret_cast<jbyte*>(buffer)); 86 if (env->ExceptionCheck()) { 87 env->ExceptionDescribe(); 88 env->ExceptionClear(); 89 SkDebugf("---- read:GetByteArrayRegion threw an exception\n"); 90 return 0; 91 } 92 93 buffer = (void*)((char*)buffer + n); 94 bytesRead += n; 95 size -= n; 96 fBytesRead += n; 97 } while (size != 0); 98 99 return bytesRead; 100 } 101 102 size_t doSkip(size_t size) { 103 JNIEnv* env = fEnv; 104 105 jlong skipped = env->CallLongMethod(fJavaInputStream, 106 gInputStream_skipMethodID, (jlong)size); 107 if (env->ExceptionCheck()) { 108 env->ExceptionDescribe(); 109 env->ExceptionClear(); 110 SkDebugf("------- skip threw an exception\n"); 111 return 0; 112 } 113 if (skipped < 0) { 114 skipped = 0; 115 } 116 117 return (size_t)skipped; 118 } 119 120 JNIEnv* fEnv; 121 jobject fJavaInputStream; // the caller owns this object 122 jbyteArray fJavaByteArray; // the caller owns this object 123 size_t fCapacity; 124 size_t fBytesRead; 125 bool fIsAtEnd; 126 }; 127 128 SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, 129 jbyteArray storage) { 130 return new JavaInputStreamAdaptor(env, stream, storage); 131 } 132 133 134 static SkMemoryStream* adaptor_to_mem_stream(SkStream* stream) { 135 SkASSERT(stream != NULL); 136 size_t bufferSize = 4096; 137 size_t streamLen = 0; 138 size_t len; 139 char* data = (char*)sk_malloc_throw(bufferSize); 140 141 while ((len = stream->read(data + streamLen, 142 bufferSize - streamLen)) != 0) { 143 streamLen += len; 144 if (streamLen == bufferSize) { 145 bufferSize *= 2; 146 data = (char*)sk_realloc_throw(data, bufferSize); 147 } 148 } 149 data = (char*)sk_realloc_throw(data, streamLen); 150 151 SkMemoryStream* streamMem = new SkMemoryStream(); 152 streamMem->setMemoryOwned(data, streamLen); 153 return streamMem; 154 } 155 156 SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream, 157 jbyteArray storage) { 158 SkAutoTUnref<SkStream> adaptor(CreateJavaInputStreamAdaptor(env, stream, storage)); 159 if (NULL == adaptor.get()) { 160 return NULL; 161 } 162 return adaptor_to_mem_stream(adaptor.get()); 163 } 164 165 /////////////////////////////////////////////////////////////////////////////// 166 167 static jmethodID gOutputStream_writeMethodID; 168 static jmethodID gOutputStream_flushMethodID; 169 170 class SkJavaOutputStream : public SkWStream { 171 public: 172 SkJavaOutputStream(JNIEnv* env, jobject stream, jbyteArray storage) 173 : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage) { 174 fCapacity = env->GetArrayLength(storage); 175 } 176 177 virtual bool write(const void* buffer, size_t size) { 178 JNIEnv* env = fEnv; 179 jbyteArray storage = fJavaByteArray; 180 181 while (size > 0) { 182 size_t requested = size; 183 if (requested > fCapacity) { 184 requested = fCapacity; 185 } 186 187 env->SetByteArrayRegion(storage, 0, requested, 188 reinterpret_cast<const jbyte*>(buffer)); 189 if (env->ExceptionCheck()) { 190 env->ExceptionDescribe(); 191 env->ExceptionClear(); 192 SkDebugf("--- write:SetByteArrayElements threw an exception\n"); 193 return false; 194 } 195 196 fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_writeMethodID, 197 storage, 0, requested); 198 if (env->ExceptionCheck()) { 199 env->ExceptionDescribe(); 200 env->ExceptionClear(); 201 SkDebugf("------- write threw an exception\n"); 202 return false; 203 } 204 205 buffer = (void*)((char*)buffer + requested); 206 size -= requested; 207 } 208 return true; 209 } 210 211 virtual void flush() { 212 fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_flushMethodID); 213 } 214 215 private: 216 JNIEnv* fEnv; 217 jobject fJavaOutputStream; // the caller owns this object 218 jbyteArray fJavaByteArray; // the caller owns this object 219 size_t fCapacity; 220 }; 221 222 SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, 223 jbyteArray storage) { 224 static bool gInited; 225 226 if (!gInited) { 227 228 gInited = true; 229 } 230 231 return new SkJavaOutputStream(env, stream, storage); 232 } 233 234 static jclass findClassCheck(JNIEnv* env, const char classname[]) { 235 jclass clazz = env->FindClass(classname); 236 SkASSERT(!env->ExceptionCheck()); 237 return clazz; 238 } 239 240 static jmethodID getMethodIDCheck(JNIEnv* env, jclass clazz, 241 const char methodname[], const char type[]) { 242 jmethodID id = env->GetMethodID(clazz, methodname, type); 243 SkASSERT(!env->ExceptionCheck()); 244 return id; 245 } 246 247 int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env) { 248 jclass inputStream_Clazz = findClassCheck(env, "java/io/InputStream"); 249 gInputStream_readMethodID = getMethodIDCheck(env, inputStream_Clazz, "read", "([BII)I"); 250 gInputStream_skipMethodID = getMethodIDCheck(env, inputStream_Clazz, "skip", "(J)J"); 251 252 jclass outputStream_Clazz = findClassCheck(env, "java/io/OutputStream"); 253 gOutputStream_writeMethodID = getMethodIDCheck(env, outputStream_Clazz, "write", "([BII)V"); 254 gOutputStream_flushMethodID = getMethodIDCheck(env, outputStream_Clazz, "flush", "()V"); 255 256 return 0; 257 } 258