blob: d443fd8cdf14cb63dbb99022a27f9926ebcd9792 [file] [log] [blame]
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04001#include "ByteBufferStreamAdaptor.h"
2#include "core_jni_helpers.h"
Leon Scroggins III127d31a2018-01-19 12:29:47 -05003#include "Utils.h"
Leon Scroggins IIIa7ec12f2019-09-11 12:33:00 -04004#include <jni.h>
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -04005
6#include <SkStream.h>
7
8using namespace android;
9
10static jmethodID gByteBuffer_getMethodID;
11static jmethodID gByteBuffer_setPositionMethodID;
12
Leon Scroggins IIIa7ec12f2019-09-11 12:33:00 -040013/**
14 * Helper method for accessing the JNI interface pointer.
15 *
16 * Image decoding (which this supports) is started on a thread that is already
17 * attached to the Java VM. But an AnimatedImageDrawable continues decoding on
18 * the AnimatedImageThread, which is not attached. This will attach if
19 * necessary.
20 */
21static JNIEnv* requireEnv(JavaVM* jvm) {
22 JNIEnv* env;
23 if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
24 if (jvm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
25 LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
26 }
27 }
28 return env;
29}
30
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -040031class ByteBufferStream : public SkStreamAsset {
32private:
33 ByteBufferStream(JavaVM* jvm, jobject jbyteBuffer, size_t initialPosition, size_t length,
34 jbyteArray storage)
35 : mJvm(jvm)
36 , mByteBuffer(jbyteBuffer)
37 , mPosition(0)
38 , mInitialPosition(initialPosition)
39 , mLength(length)
40 , mStorage(storage) {}
41
42public:
43 static ByteBufferStream* Create(JavaVM* jvm, JNIEnv* env, jobject jbyteBuffer,
44 size_t position, size_t length) {
45 // This object outlives its native method call.
46 jbyteBuffer = env->NewGlobalRef(jbyteBuffer);
47 if (!jbyteBuffer) {
48 return nullptr;
49 }
50
51 jbyteArray storage = env->NewByteArray(kStorageSize);
52 if (!storage) {
53 env->DeleteGlobalRef(jbyteBuffer);
54 return nullptr;
55 }
56
57 // This object outlives its native method call.
58 storage = static_cast<jbyteArray>(env->NewGlobalRef(storage));
59 if (!storage) {
60 env->DeleteGlobalRef(jbyteBuffer);
61 return nullptr;
62 }
63
64 return new ByteBufferStream(jvm, jbyteBuffer, position, length, storage);
65 }
66
67 ~ByteBufferStream() override {
Leon Scroggins IIIa7ec12f2019-09-11 12:33:00 -040068 auto* env = requireEnv(mJvm);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -040069 env->DeleteGlobalRef(mByteBuffer);
70 env->DeleteGlobalRef(mStorage);
71 }
72
73 size_t read(void* buffer, size_t size) override {
74 if (size > mLength - mPosition) {
75 size = mLength - mPosition;
76 }
77 if (!size) {
78 return 0;
79 }
80
81 if (!buffer) {
Leon Scroggins IIIe5de9aa2018-01-10 20:56:51 -050082 return this->setPosition(mPosition + size) ? size : 0;
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -040083 }
84
Leon Scroggins IIIa7ec12f2019-09-11 12:33:00 -040085 auto* env = requireEnv(mJvm);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -040086 size_t bytesRead = 0;
87 do {
88 const size_t requested = (size > kStorageSize) ? kStorageSize : size;
89 const jint jrequested = static_cast<jint>(requested);
90 env->CallObjectMethod(mByteBuffer, gByteBuffer_getMethodID, mStorage, 0, jrequested);
91 if (env->ExceptionCheck()) {
92 ALOGE("Error in ByteBufferStream::read - was the ByteBuffer modified externally?");
93 env->ExceptionDescribe();
94 env->ExceptionClear();
95 mPosition = mLength;
96 return bytesRead;
97 }
98
99 env->GetByteArrayRegion(mStorage, 0, requested, reinterpret_cast<jbyte*>(buffer));
100 if (env->ExceptionCheck()) {
101 ALOGE("Internal error in ByteBufferStream::read");
102 env->ExceptionDescribe();
103 env->ExceptionClear();
104 mPosition = mLength;
105 return bytesRead;
106 }
107
108 mPosition += requested;
109 buffer = reinterpret_cast<void*>(reinterpret_cast<char*>(buffer) + requested);
110 bytesRead += requested;
111 size -= requested;
112 } while (size);
113 return bytesRead;
114 }
115
116 bool isAtEnd() const override { return mLength == mPosition; }
117
118 // SkStreamRewindable overrides
119 bool rewind() override { return this->setPosition(0); }
120
121 SkStreamAsset* onDuplicate() const override {
122 // SkStreamRewindable requires overriding this, but it is not called by
123 // decoders, so does not need a true implementation. A proper
124 // implementation would require duplicating the ByteBuffer, which has
125 // its own internal position state.
126 return nullptr;
127 }
128
129 // SkStreamSeekable overrides
130 size_t getPosition() const override { return mPosition; }
131
132 bool seek(size_t position) override {
133 return this->setPosition(position > mLength ? mLength : position);
134 }
135
136 bool move(long offset) override {
137 long newPosition = mPosition + offset;
138 if (newPosition < 0) {
139 return this->setPosition(0);
140 }
141 return this->seek(static_cast<size_t>(newPosition));
142 }
143
144 SkStreamAsset* onFork() const override {
145 // SkStreamSeekable requires overriding this, but it is not called by
146 // decoders, so does not need a true implementation. A proper
147 // implementation would require duplicating the ByteBuffer, which has
148 // its own internal position state.
149 return nullptr;
150 }
151
152 // SkStreamAsset overrides
153 size_t getLength() const override { return mLength; }
154
155private:
156 JavaVM* mJvm;
157 jobject mByteBuffer;
158 // Logical position of the SkStream, between 0 and mLength.
159 size_t mPosition;
160 // Initial position of mByteBuffer, treated as mPosition 0.
161 const size_t mInitialPosition;
162 // Logical length of the SkStream, from mInitialPosition to
163 // mByteBuffer.limit().
164 const size_t mLength;
165
166 // Range has already been checked by the caller.
167 bool setPosition(size_t newPosition) {
Leon Scroggins IIIa7ec12f2019-09-11 12:33:00 -0400168 auto* env = requireEnv(mJvm);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400169 env->CallObjectMethod(mByteBuffer, gByteBuffer_setPositionMethodID,
170 newPosition + mInitialPosition);
171 if (env->ExceptionCheck()) {
172 ALOGE("Internal error in ByteBufferStream::setPosition");
173 env->ExceptionDescribe();
174 env->ExceptionClear();
175 mPosition = mLength;
176 return false;
177 }
178 mPosition = newPosition;
179 return true;
180 }
181
182 // FIXME: This is an arbitrary storage size, which should be plenty for
183 // some formats (png, gif, many bmps). But for jpeg, the more we can supply
184 // in one call the better, and webp really wants all of the data. How to
185 // best choose the amount of storage used?
186 static constexpr size_t kStorageSize = 4096;
187 jbyteArray mStorage;
188};
189
190class ByteArrayStream : public SkStreamAsset {
191private:
192 ByteArrayStream(JavaVM* jvm, jbyteArray jarray, size_t offset, size_t length)
193 : mJvm(jvm), mByteArray(jarray), mOffset(offset), mPosition(0), mLength(length) {}
194
195public:
196 static ByteArrayStream* Create(JavaVM* jvm, JNIEnv* env, jbyteArray jarray, size_t offset,
197 size_t length) {
198 // This object outlives its native method call.
199 jarray = static_cast<jbyteArray>(env->NewGlobalRef(jarray));
200 if (!jarray) {
201 return nullptr;
202 }
203 return new ByteArrayStream(jvm, jarray, offset, length);
204 }
205
206 ~ByteArrayStream() override {
Leon Scroggins IIIa7ec12f2019-09-11 12:33:00 -0400207 auto* env = requireEnv(mJvm);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400208 env->DeleteGlobalRef(mByteArray);
209 }
210
211 size_t read(void* buffer, size_t size) override {
212 if (size > mLength - mPosition) {
213 size = mLength - mPosition;
214 }
215 if (!size) {
216 return 0;
217 }
218
Leon Scroggins IIIa7ec12f2019-09-11 12:33:00 -0400219 auto* env = requireEnv(mJvm);
Leon Scroggins III0c01dbf2017-10-20 14:08:11 -0400220 if (buffer) {
221 env->GetByteArrayRegion(mByteArray, mPosition + mOffset, size,
222 reinterpret_cast<jbyte*>(buffer));
223 if (env->ExceptionCheck()) {
224 ALOGE("Internal error in ByteArrayStream::read");
225 env->ExceptionDescribe();
226 env->ExceptionClear();
227 mPosition = mLength;
228 return 0;
229 }
230 }
231
232 mPosition += size;
233 return size;
234 }
235
236 bool isAtEnd() const override { return mLength == mPosition; }
237
238 // SkStreamRewindable overrides
239 bool rewind() override {
240 mPosition = 0;
241 return true;
242 }
243 SkStreamAsset* onDuplicate() const override {
244 // SkStreamRewindable requires overriding this, but it is not called by
245 // decoders, so does not need a true implementation. Note that a proper
246 // implementation is fairly straightforward
247 return nullptr;
248 }
249
250 // SkStreamSeekable overrides
251 size_t getPosition() const override { return mPosition; }
252
253 bool seek(size_t position) override {
254 mPosition = (position > mLength) ? mLength : position;
255 return true;
256 }
257
258 bool move(long offset) override {
259 long newPosition = mPosition + offset;
260 if (newPosition < 0) {
261 return this->seek(0);
262 }
263 return this->seek(static_cast<size_t>(newPosition));
264 }
265
266 SkStreamAsset* onFork() const override {
267 // SkStreamSeekable requires overriding this, but it is not called by
268 // decoders, so does not need a true implementation. Note that a proper
269 // implementation is fairly straightforward
270 return nullptr;
271 }
272
273 // SkStreamAsset overrides
274 size_t getLength() const override { return mLength; }
275
276private:
277 JavaVM* mJvm;
278 jbyteArray mByteArray;
279 // Offset in mByteArray. Only used when communicating with Java.
280 const size_t mOffset;
281 // Logical position of the SkStream, between 0 and mLength.
282 size_t mPosition;
283 const size_t mLength;
284};
285
286struct release_proc_context {
287 JavaVM* jvm;
288 jobject jbyteBuffer;
289};
290
291std::unique_ptr<SkStream> CreateByteBufferStreamAdaptor(JNIEnv* env, jobject jbyteBuffer,
292 size_t position, size_t limit) {
293 JavaVM* jvm;
294 LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK);
295
296 const size_t length = limit - position;
297 void* addr = env->GetDirectBufferAddress(jbyteBuffer);
298 if (addr) {
299 addr = reinterpret_cast<void*>(reinterpret_cast<char*>(addr) + position);
300 jbyteBuffer = env->NewGlobalRef(jbyteBuffer);
301 if (!jbyteBuffer) {
302 return nullptr;
303 }
304
305 auto* context = new release_proc_context{jvm, jbyteBuffer};
306 auto releaseProc = [](const void*, void* context) {
307 auto* c = reinterpret_cast<release_proc_context*>(context);
308 JNIEnv* env = get_env_or_die(c->jvm);
309 env->DeleteGlobalRef(c->jbyteBuffer);
310 delete c;
311 };
312 auto data = SkData::MakeWithProc(addr, length, releaseProc, context);
313 // The new SkMemoryStream will read directly from addr.
314 return std::unique_ptr<SkStream>(new SkMemoryStream(std::move(data)));
315 }
316
317 // Non-direct, or direct access is not supported.
318 return std::unique_ptr<SkStream>(ByteBufferStream::Create(jvm, env, jbyteBuffer, position,
319 length));
320}
321
322std::unique_ptr<SkStream> CreateByteArrayStreamAdaptor(JNIEnv* env, jbyteArray array, size_t offset,
323 size_t length) {
324 JavaVM* jvm;
325 LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK);
326
327 return std::unique_ptr<SkStream>(ByteArrayStream::Create(jvm, env, array, offset, length));
328}
329
330int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env) {
331 jclass byteBuffer_class = FindClassOrDie(env, "java/nio/ByteBuffer");
332 gByteBuffer_getMethodID = GetMethodIDOrDie(env, byteBuffer_class, "get", "([BII)Ljava/nio/ByteBuffer;");
333 gByteBuffer_setPositionMethodID = GetMethodIDOrDie(env, byteBuffer_class, "position", "(I)Ljava/nio/Buffer;");
334 return true;
335}