blob: 067489a547c0285c1eeaa54f3b2fe63d612b2f04 [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 "SkData.h"
3#include "SkRefCnt.h"
4#include "SkStream.h"
5#include "SkTypes.h"
6#include "Utils.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007
Ben Wagner60126ef2015-08-07 12:13:48 -04008#include <JNIHelp.h>
9#include <memory>
10
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080011static jmethodID gInputStream_readMethodID;
12static jmethodID gInputStream_skipMethodID;
13
Leon Scroggins IIIca320212013-08-20 17:59:39 -040014/**
Leon Scroggins III7315f1b2013-09-10 20:26:05 -040015 * Wrapper for a Java InputStream.
Leon Scroggins IIIca320212013-08-20 17:59:39 -040016 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080017class JavaInputStreamAdaptor : public SkStream {
18public:
19 JavaInputStreamAdaptor(JNIEnv* env, jobject js, jbyteArray ar)
20 : fEnv(env), fJavaInputStream(js), fJavaByteArray(ar) {
21 SkASSERT(ar);
Leon Scroggins IIIca320212013-08-20 17:59:39 -040022 fCapacity = env->GetArrayLength(ar);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023 SkASSERT(fCapacity > 0);
Leon Scroggins IIIca320212013-08-20 17:59:39 -040024 fBytesRead = 0;
25 fIsAtEnd = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -070027
Leon Scroggins IIIca320212013-08-20 17:59:39 -040028 virtual size_t read(void* buffer, size_t size) {
Leon Scroggins IIIca320212013-08-20 17:59:39 -040029 if (NULL == buffer) {
30 if (0 == size) {
31 return 0;
32 } else {
33 /* InputStream.skip(n) can return <=0 but still not be at EOF
34 If we see that value, we need to call read(), which will
35 block if waiting for more data, or return -1 at EOF
36 */
37 size_t amountSkipped = 0;
38 do {
39 size_t amount = this->doSkip(size - amountSkipped);
40 if (0 == amount) {
41 char tmp;
42 amount = this->doRead(&tmp, 1);
43 if (0 == amount) {
44 // if read returned 0, we're at EOF
45 fIsAtEnd = true;
46 break;
47 }
48 }
49 amountSkipped += amount;
50 } while (amountSkipped < size);
51 return amountSkipped;
52 }
53 }
54 return this->doRead(buffer, size);
55 }
56
57 virtual bool isAtEnd() const {
58 return fIsAtEnd;
59 }
60
61private:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062 size_t doRead(void* buffer, size_t size) {
63 JNIEnv* env = fEnv;
64 size_t bytesRead = 0;
65 // read the bytes
66 do {
Ashok Bhat2bb39d72014-03-05 12:40:53 +000067 jint requested = 0;
68 if (size > static_cast<size_t>(fCapacity)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069 requested = fCapacity;
Ashok Bhat2bb39d72014-03-05 12:40:53 +000070 } else {
71 // This is safe because requested is clamped to (jint)
72 // fCapacity.
73 requested = static_cast<jint>(size);
74 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -070075
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 jint n = env->CallIntMethod(fJavaInputStream,
77 gInputStream_readMethodID, fJavaByteArray, 0, requested);
78 if (env->ExceptionCheck()) {
79 env->ExceptionDescribe();
80 env->ExceptionClear();
81 SkDebugf("---- read threw an exception\n");
Leon Scroggins III91332e72014-10-16 16:50:41 -040082 // Consider the stream to be at the end, since there was an error.
83 fIsAtEnd = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 return 0;
85 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -070086
Gilles Debunne8cd48572010-07-15 18:06:36 -070087 if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications.
Leon Scroggins IIIca320212013-08-20 17:59:39 -040088 fIsAtEnd = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 break; // eof
90 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -070091
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092 env->GetByteArrayRegion(fJavaByteArray, 0, n,
93 reinterpret_cast<jbyte*>(buffer));
94 if (env->ExceptionCheck()) {
95 env->ExceptionDescribe();
96 env->ExceptionClear();
97 SkDebugf("---- read:GetByteArrayRegion threw an exception\n");
Leon Scroggins III91332e72014-10-16 16:50:41 -040098 // The error was not with the stream itself, but consider it to be at the
99 // end, since we do not have a way to recover.
100 fIsAtEnd = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 return 0;
102 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700103
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 buffer = (void*)((char*)buffer + n);
105 bytesRead += n;
106 size -= n;
107 fBytesRead += n;
108 } while (size != 0);
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 return bytesRead;
111 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700112
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 size_t doSkip(size_t size) {
114 JNIEnv* env = fEnv;
Gilles Debunne8cd48572010-07-15 18:06:36 -0700115
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 jlong skipped = env->CallLongMethod(fJavaInputStream,
117 gInputStream_skipMethodID, (jlong)size);
118 if (env->ExceptionCheck()) {
119 env->ExceptionDescribe();
120 env->ExceptionClear();
Gilles Debunne8cd48572010-07-15 18:06:36 -0700121 SkDebugf("------- skip threw an exception\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 return 0;
123 }
124 if (skipped < 0) {
125 skipped = 0;
126 }
Gilles Debunne8cd48572010-07-15 18:06:36 -0700127
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 return (size_t)skipped;
129 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700130
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 JNIEnv* fEnv;
132 jobject fJavaInputStream; // the caller owns this object
133 jbyteArray fJavaByteArray; // the caller owns this object
Ashok Bhat2bb39d72014-03-05 12:40:53 +0000134 jint fCapacity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 size_t fBytesRead;
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400136 bool fIsAtEnd;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137};
138
Leon Scroggins IIIc7797522013-09-03 11:35:00 -0400139SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
140 jbyteArray storage) {
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400141 return new JavaInputStreamAdaptor(env, stream, storage);
142}
143
Leon Scroggins IIIc7797522013-09-03 11:35:00 -0400144
Leon Scroggins III37b82e32013-09-12 20:00:46 -0400145static SkMemoryStream* adaptor_to_mem_stream(SkStream* stream) {
146 SkASSERT(stream != NULL);
147 size_t bufferSize = 4096;
148 size_t streamLen = 0;
149 size_t len;
150 char* data = (char*)sk_malloc_throw(bufferSize);
151
152 while ((len = stream->read(data + streamLen,
153 bufferSize - streamLen)) != 0) {
154 streamLen += len;
155 if (streamLen == bufferSize) {
156 bufferSize *= 2;
157 data = (char*)sk_realloc_throw(data, bufferSize);
158 }
159 }
160 data = (char*)sk_realloc_throw(data, streamLen);
161
162 SkMemoryStream* streamMem = new SkMemoryStream();
163 streamMem->setMemoryOwned(data, streamLen);
164 return streamMem;
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400165}
166
Leon Scroggins IIIc7797522013-09-03 11:35:00 -0400167SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream,
168 jbyteArray storage) {
Ben Wagner60126ef2015-08-07 12:13:48 -0400169 std::unique_ptr<SkStream> adaptor(CreateJavaInputStreamAdaptor(env, stream, storage));
Leon Scroggins IIIca320212013-08-20 17:59:39 -0400170 if (NULL == adaptor.get()) {
171 return NULL;
172 }
173 return adaptor_to_mem_stream(adaptor.get());
174}
175
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176///////////////////////////////////////////////////////////////////////////////
177
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178static jmethodID gOutputStream_writeMethodID;
179static jmethodID gOutputStream_flushMethodID;
180
181class SkJavaOutputStream : public SkWStream {
182public:
183 SkJavaOutputStream(JNIEnv* env, jobject stream, jbyteArray storage)
Leon Scrogginscc11f152014-03-31 16:52:13 -0400184 : fEnv(env), fJavaOutputStream(stream), fJavaByteArray(storage), fBytesWritten(0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 fCapacity = env->GetArrayLength(storage);
186 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700187
Leon Scrogginscc11f152014-03-31 16:52:13 -0400188 virtual size_t bytesWritten() const {
189 return fBytesWritten;
190 }
191
Ashok Bhat2bb39d72014-03-05 12:40:53 +0000192 virtual bool write(const void* buffer, size_t size) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 JNIEnv* env = fEnv;
194 jbyteArray storage = fJavaByteArray;
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700195
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 while (size > 0) {
Ashok Bhat2bb39d72014-03-05 12:40:53 +0000197 jint requested = 0;
198 if (size > static_cast<size_t>(fCapacity)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 requested = fCapacity;
Ashok Bhat2bb39d72014-03-05 12:40:53 +0000200 } else {
201 // This is safe because requested is clamped to (jint)
202 // fCapacity.
203 requested = static_cast<jint>(size);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 }
205
206 env->SetByteArrayRegion(storage, 0, requested,
207 reinterpret_cast<const jbyte*>(buffer));
208 if (env->ExceptionCheck()) {
209 env->ExceptionDescribe();
210 env->ExceptionClear();
211 SkDebugf("--- write:SetByteArrayElements threw an exception\n");
212 return false;
213 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700214
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_writeMethodID,
216 storage, 0, requested);
217 if (env->ExceptionCheck()) {
218 env->ExceptionDescribe();
219 env->ExceptionClear();
220 SkDebugf("------- write threw an exception\n");
221 return false;
222 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700223
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 buffer = (void*)((char*)buffer + requested);
225 size -= requested;
Leon Scrogginscc11f152014-03-31 16:52:13 -0400226 fBytesWritten += requested;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 }
228 return true;
229 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700230
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 virtual void flush() {
232 fEnv->CallVoidMethod(fJavaOutputStream, gOutputStream_flushMethodID);
233 }
Elliott Hughesdd66bcb2011-04-12 11:28:59 -0700234
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235private:
236 JNIEnv* fEnv;
237 jobject fJavaOutputStream; // the caller owns this object
238 jbyteArray fJavaByteArray; // the caller owns this object
Ashok Bhat2bb39d72014-03-05 12:40:53 +0000239 jint fCapacity;
Leon Scrogginscc11f152014-03-31 16:52:13 -0400240 size_t fBytesWritten;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241};
242
243SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream,
244 jbyteArray storage) {
245 static bool gInited;
246
247 if (!gInited) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248
249 gInited = true;
250 }
251
252 return new SkJavaOutputStream(env, stream, storage);
253}
Leon Scroggins IIId0d7eaf2013-09-06 16:46:57 -0400254
255static jclass findClassCheck(JNIEnv* env, const char classname[]) {
256 jclass clazz = env->FindClass(classname);
257 SkASSERT(!env->ExceptionCheck());
258 return clazz;
259}
260
Leon Scroggins IIId0d7eaf2013-09-06 16:46:57 -0400261static jmethodID getMethodIDCheck(JNIEnv* env, jclass clazz,
262 const char methodname[], const char type[]) {
263 jmethodID id = env->GetMethodID(clazz, methodname, type);
264 SkASSERT(!env->ExceptionCheck());
265 return id;
266}
267
268int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env) {
269 jclass inputStream_Clazz = findClassCheck(env, "java/io/InputStream");
Leon Scroggins IIId0d7eaf2013-09-06 16:46:57 -0400270 gInputStream_readMethodID = getMethodIDCheck(env, inputStream_Clazz, "read", "([BII)I");
271 gInputStream_skipMethodID = getMethodIDCheck(env, inputStream_Clazz, "skip", "(J)J");
272
Leon Scroggins IIId0d7eaf2013-09-06 16:46:57 -0400273 jclass outputStream_Clazz = findClassCheck(env, "java/io/OutputStream");
274 gOutputStream_writeMethodID = getMethodIDCheck(env, outputStream_Clazz, "write", "([BII)V");
275 gOutputStream_flushMethodID = getMethodIDCheck(env, outputStream_Clazz, "flush", "()V");
276
277 return 0;
278}