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