blob: 26523f89ec4f9e22e2554d04cf97678d93856472 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001#include "CreateJavaOutputStreamAdaptor.h"
Leon Scroggins IIIca320212013-08-20 17:59:39 -04002#include "JNIHelp.h"
3#include "SkData.h"
4#include "SkRefCnt.h"
5#include "SkStream.h"
6#include "SkTypes.h"
7#include "Utils.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08009static jmethodID gInputStream_readMethodID;
10static jmethodID gInputStream_skipMethodID;
11
Leon Scroggins IIIca320212013-08-20 17:59:39 -040012/**
Leon Scroggins III7315f1b2013-09-10 20:26:05 -040013 * Wrapper for a Java InputStream.
Leon Scroggins IIIca320212013-08-20 17:59:39 -040014 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080015class JavaInputStreamAdaptor : public SkStream {
16public:
17 JavaInputStreamAdaptor(JNIEnv* env, jobject js, jbyteArray ar)
18 : fEnv(env), fJavaInputStream(js), fJavaByteArray(ar) {
19 SkASSERT(ar);
Leon Scroggins IIIca320212013-08-20 17:59:39 -040020 fCapacity = env->GetArrayLength(ar);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021 SkASSERT(fCapacity > 0);
Leon Scroggins IIIca320212013-08-20 17:59:39 -040022 fBytesRead = 0;
23 fIsAtEnd = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -070025
Leon Scroggins IIIca320212013-08-20 17:59:39 -040026 virtual size_t read(void* buffer, size_t size) {
Leon Scroggins IIIca320212013-08-20 17:59:39 -040027 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:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 size_t doRead(void* buffer, size_t size) {
61 JNIEnv* env = fEnv;
62 size_t bytesRead = 0;
63 // read the bytes
64 do {
Ashok Bhat2bb39d72014-03-05 12:40:53 +000065 jint requested = 0;
66 if (size > static_cast<size_t>(fCapacity)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067 requested = fCapacity;
Ashok Bhat2bb39d72014-03-05 12:40:53 +000068 } else {
69 // This is safe because requested is clamped to (jint)
70 // fCapacity.
71 requested = static_cast<jint>(size);
72 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -070073
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 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");
Leon Scroggins III91332e72014-10-16 16:50:41 -040080 // Consider the stream to be at the end, since there was an error.
81 fIsAtEnd = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 return 0;
83 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -070084
Gilles Debunne8cd48572010-07-15 18:06:36 -070085 if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications.
Leon Scroggins IIIca320212013-08-20 17:59:39 -040086 fIsAtEnd = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 break; // eof
88 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -070089
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 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");
Leon Scroggins III91332e72014-10-16 16:50:41 -040096 // 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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 return 0;
100 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700101
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 buffer = (void*)((char*)buffer + n);
103 bytesRead += n;
104 size -= n;
105 fBytesRead += n;
106 } while (size != 0);
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700107
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 return bytesRead;
109 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700110
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 size_t doSkip(size_t size) {
112 JNIEnv* env = fEnv;
Gilles Debunne8cd48572010-07-15 18:06:36 -0700113
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 jlong skipped = env->CallLongMethod(fJavaInputStream,
115 gInputStream_skipMethodID, (jlong)size);
116 if (env->ExceptionCheck()) {
117 env->ExceptionDescribe();
118 env->ExceptionClear();
Gilles Debunne8cd48572010-07-15 18:06:36 -0700119 SkDebugf("------- skip threw an exception\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 return 0;
121 }
122 if (skipped < 0) {
123 skipped = 0;
124 }
Gilles Debunne8cd48572010-07-15 18:06:36 -0700125
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 return (size_t)skipped;
127 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700128
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 JNIEnv* fEnv;
130 jobject fJavaInputStream; // the caller owns this object
131 jbyteArray fJavaByteArray; // the caller owns this object
Ashok Bhat2bb39d72014-03-05 12:40:53 +0000132 jint fCapacity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 size_t fBytesRead;
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400134 bool fIsAtEnd;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135};
136
Leon Scroggins IIIc7797522013-09-03 11:35:00 -0400137SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
138 jbyteArray storage) {
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400139 return new JavaInputStreamAdaptor(env, stream, storage);
140}
141
Leon Scroggins IIIc7797522013-09-03 11:35:00 -0400142
Leon Scroggins III37b82e32013-09-12 20:00:46 -0400143static 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;
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400163}
164
Leon Scroggins IIIc7797522013-09-03 11:35:00 -0400165SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream,
166 jbyteArray storage) {
Leon Scroggins III34497892015-01-20 15:52:43 -0500167 SkAutoTDelete<SkStream> adaptor(CreateJavaInputStreamAdaptor(env, stream, storage));
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400168 if (NULL == adaptor.get()) {
169 return NULL;
170 }
171 return adaptor_to_mem_stream(adaptor.get());
172}
173
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174///////////////////////////////////////////////////////////////////////////////
175
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176static jmethodID gOutputStream_writeMethodID;
177static jmethodID gOutputStream_flushMethodID;
178
179class SkJavaOutputStream : public SkWStream {
180public:
181 SkJavaOutputStream(JNIEnv* env, jobject stream, jbyteArray storage)
Leon Scrogginscc11f152014-03-31 16:52:13 -0400182 : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage), fBytesWritten(0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 fCapacity = env->GetArrayLength(storage);
184 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700185
Leon Scrogginscc11f152014-03-31 16:52:13 -0400186 virtual size_t bytesWritten() const {
187 return fBytesWritten;
188 }
189
Ashok Bhat2bb39d72014-03-05 12:40:53 +0000190 virtual bool write(const void* buffer, size_t size) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 JNIEnv* env = fEnv;
192 jbyteArray storage = fJavaByteArray;
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700193
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 while (size > 0) {
Ashok Bhat2bb39d72014-03-05 12:40:53 +0000195 jint requested = 0;
196 if (size > static_cast<size_t>(fCapacity)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 requested = fCapacity;
Ashok Bhat2bb39d72014-03-05 12:40:53 +0000198 } else {
199 // This is safe because requested is clamped to (jint)
200 // fCapacity.
201 requested = static_cast<jint>(size);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 }
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 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700212
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 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 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700221
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 buffer = (void*)((char*)buffer + requested);
223 size -= requested;
Leon Scrogginscc11f152014-03-31 16:52:13 -0400224 fBytesWritten += requested;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 }
226 return true;
227 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700228
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 virtual void flush() {
230 fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_flushMethodID);
231 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700232
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233private:
234 JNIEnv* fEnv;
235 jobject fJavaOutputStream; // the caller owns this object
236 jbyteArray fJavaByteArray; // the caller owns this object
Ashok Bhat2bb39d72014-03-05 12:40:53 +0000237 jint fCapacity;
Leon Scrogginscc11f152014-03-31 16:52:13 -0400238 size_t fBytesWritten;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239};
240
241SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream,
242 jbyteArray storage) {
243 static bool gInited;
244
245 if (!gInited) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800246
247 gInited = true;
248 }
249
250 return new SkJavaOutputStream(env, stream, storage);
251}
Leon Scroggins IIId0d7eaf2013-09-06 16:46:57 -0400252
253static jclass findClassCheck(JNIEnv* env, const char classname[]) {
254 jclass clazz = env->FindClass(classname);
255 SkASSERT(!env->ExceptionCheck());
256 return clazz;
257}
258
Leon Scroggins IIId0d7eaf2013-09-06 16:46:57 -0400259static 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
266int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env) {
267 jclass inputStream_Clazz = findClassCheck(env, "java/io/InputStream");
Leon Scroggins IIId0d7eaf2013-09-06 16:46:57 -0400268 gInputStream_readMethodID = getMethodIDCheck(env, inputStream_Clazz, "read", "([BII)I");
269 gInputStream_skipMethodID = getMethodIDCheck(env, inputStream_Clazz, "skip", "(J)J");
270
Leon Scroggins IIId0d7eaf2013-09-06 16:46:57 -0400271 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}