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