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