CreateJavaOutputStreamAdaptor.cpp revision 7315f1baee19476363235127bc1438e2a291fa15
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        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
60private:
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
128SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
129                                       jbyteArray storage) {
130    return new JavaInputStreamAdaptor(env, stream, storage);
131}
132
133
134static 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
156SkStreamRewindable* 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
167static jmethodID    gOutputStream_writeMethodID;
168static jmethodID    gOutputStream_flushMethodID;
169
170class SkJavaOutputStream : public SkWStream {
171public:
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
215private:
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
222SkWStream* 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
234static jclass findClassCheck(JNIEnv* env, const char classname[]) {
235    jclass clazz = env->FindClass(classname);
236    SkASSERT(!env->ExceptionCheck());
237    return clazz;
238}
239
240static 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
247int 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