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