Home | History | Annotate | Download | only in graphics
      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