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             jint requested = 0;
     67             if (size > static_cast<size_t>(fCapacity)) {
     68                 requested = fCapacity;
     69             } else {
     70                 // This is safe because requested is clamped to (jint)
     71                 // fCapacity.
     72                 requested = static_cast<jint>(size);
     73             }
     74 
     75             jint n = env->CallIntMethod(fJavaInputStream,
     76                                         gInputStream_readMethodID, fJavaByteArray, 0, requested);
     77             if (env->ExceptionCheck()) {
     78                 env->ExceptionDescribe();
     79                 env->ExceptionClear();
     80                 SkDebugf("---- read threw an exception\n");
     81                 // Consider the stream to be at the end, since there was an error.
     82                 fIsAtEnd = true;
     83                 return 0;
     84             }
     85 
     86             if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications.
     87                 fIsAtEnd = true;
     88                 break;  // eof
     89             }
     90 
     91             env->GetByteArrayRegion(fJavaByteArray, 0, n,
     92                                     reinterpret_cast<jbyte*>(buffer));
     93             if (env->ExceptionCheck()) {
     94                 env->ExceptionDescribe();
     95                 env->ExceptionClear();
     96                 SkDebugf("---- read:GetByteArrayRegion threw an exception\n");
     97                 // The error was not with the stream itself, but consider it to be at the
     98                 // end, since we do not have a way to recover.
     99                 fIsAtEnd = true;
    100                 return 0;
    101             }
    102 
    103             buffer = (void*)((char*)buffer + n);
    104             bytesRead += n;
    105             size -= n;
    106             fBytesRead += n;
    107         } while (size != 0);
    108 
    109         return bytesRead;
    110     }
    111 
    112     size_t doSkip(size_t size) {
    113         JNIEnv* env = fEnv;
    114 
    115         jlong skipped = env->CallLongMethod(fJavaInputStream,
    116                                             gInputStream_skipMethodID, (jlong)size);
    117         if (env->ExceptionCheck()) {
    118             env->ExceptionDescribe();
    119             env->ExceptionClear();
    120             SkDebugf("------- skip threw an exception\n");
    121             return 0;
    122         }
    123         if (skipped < 0) {
    124             skipped = 0;
    125         }
    126 
    127         return (size_t)skipped;
    128     }
    129 
    130     JNIEnv*     fEnv;
    131     jobject     fJavaInputStream;   // the caller owns this object
    132     jbyteArray  fJavaByteArray;     // the caller owns this object
    133     jint        fCapacity;
    134     size_t      fBytesRead;
    135     bool        fIsAtEnd;
    136 };
    137 
    138 SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
    139                                        jbyteArray storage) {
    140     return new JavaInputStreamAdaptor(env, stream, storage);
    141 }
    142 
    143 
    144 static SkMemoryStream* adaptor_to_mem_stream(SkStream* stream) {
    145     SkASSERT(stream != NULL);
    146     size_t bufferSize = 4096;
    147     size_t streamLen = 0;
    148     size_t len;
    149     char* data = (char*)sk_malloc_throw(bufferSize);
    150 
    151     while ((len = stream->read(data + streamLen,
    152                                bufferSize - streamLen)) != 0) {
    153         streamLen += len;
    154         if (streamLen == bufferSize) {
    155             bufferSize *= 2;
    156             data = (char*)sk_realloc_throw(data, bufferSize);
    157         }
    158     }
    159     data = (char*)sk_realloc_throw(data, streamLen);
    160 
    161     SkMemoryStream* streamMem = new SkMemoryStream();
    162     streamMem->setMemoryOwned(data, streamLen);
    163     return streamMem;
    164 }
    165 
    166 SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream,
    167                                         jbyteArray storage) {
    168     SkAutoTUnref<SkStream> adaptor(CreateJavaInputStreamAdaptor(env, stream, storage));
    169     if (NULL == adaptor.get()) {
    170         return NULL;
    171     }
    172     return adaptor_to_mem_stream(adaptor.get());
    173 }
    174 
    175 ///////////////////////////////////////////////////////////////////////////////
    176 
    177 static jmethodID    gOutputStream_writeMethodID;
    178 static jmethodID    gOutputStream_flushMethodID;
    179 
    180 class SkJavaOutputStream : public SkWStream {
    181 public:
    182     SkJavaOutputStream(JNIEnv* env, jobject stream, jbyteArray storage)
    183         : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage), fBytesWritten(0) {
    184         fCapacity = env->GetArrayLength(storage);
    185     }
    186 
    187     virtual size_t bytesWritten() const {
    188         return fBytesWritten;
    189     }
    190 
    191     virtual bool write(const void* buffer, size_t size) {
    192         JNIEnv* env = fEnv;
    193         jbyteArray storage = fJavaByteArray;
    194 
    195         while (size > 0) {
    196             jint requested = 0;
    197             if (size > static_cast<size_t>(fCapacity)) {
    198                 requested = fCapacity;
    199             } else {
    200                 // This is safe because requested is clamped to (jint)
    201                 // fCapacity.
    202                 requested = static_cast<jint>(size);
    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             fBytesWritten += requested;
    226         }
    227         return true;
    228     }
    229 
    230     virtual void flush() {
    231         fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_flushMethodID);
    232     }
    233 
    234 private:
    235     JNIEnv*     fEnv;
    236     jobject     fJavaOutputStream;  // the caller owns this object
    237     jbyteArray  fJavaByteArray;     // the caller owns this object
    238     jint        fCapacity;
    239     size_t      fBytesWritten;
    240 };
    241 
    242 SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream,
    243                                          jbyteArray storage) {
    244     static bool gInited;
    245 
    246     if (!gInited) {
    247 
    248         gInited = true;
    249     }
    250 
    251     return new SkJavaOutputStream(env, stream, storage);
    252 }
    253 
    254 static jclass findClassCheck(JNIEnv* env, const char classname[]) {
    255     jclass clazz = env->FindClass(classname);
    256     SkASSERT(!env->ExceptionCheck());
    257     return clazz;
    258 }
    259 
    260 static jmethodID getMethodIDCheck(JNIEnv* env, jclass clazz,
    261                                   const char methodname[], const char type[]) {
    262     jmethodID id = env->GetMethodID(clazz, methodname, type);
    263     SkASSERT(!env->ExceptionCheck());
    264     return id;
    265 }
    266 
    267 int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env) {
    268     jclass inputStream_Clazz = findClassCheck(env, "java/io/InputStream");
    269     gInputStream_readMethodID = getMethodIDCheck(env, inputStream_Clazz, "read", "([BII)I");
    270     gInputStream_skipMethodID = getMethodIDCheck(env, inputStream_Clazz, "skip", "(J)J");
    271 
    272     jclass outputStream_Clazz = findClassCheck(env, "java/io/OutputStream");
    273     gOutputStream_writeMethodID = getMethodIDCheck(env, outputStream_Clazz, "write", "([BII)V");
    274     gOutputStream_flushMethodID = getMethodIDCheck(env, outputStream_Clazz, "flush", "()V");
    275 
    276     return 0;
    277 }
    278