1 #include "ByteBufferStreamAdaptor.h"
2 #include "GraphicsJNI.h"
3 #include "Utils.h"
4
5 #include <SkData.h>
6 #include <SkStream.h>
7
8 using namespace android;
9
10 static jmethodID gByteBuffer_getMethodID;
11 static jmethodID gByteBuffer_setPositionMethodID;
12
13 class ByteBufferStream : public SkStreamAsset {
14 private:
ByteBufferStream(JavaVM * jvm,jobject jbyteBuffer,size_t initialPosition,size_t length,jbyteArray storage)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
24 public:
Create(JavaVM * jvm,JNIEnv * env,jobject jbyteBuffer,size_t position,size_t length)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
~ByteBufferStream()49 ~ByteBufferStream() override {
50 auto* env = requireEnv(mJvm);
51 env->DeleteGlobalRef(mByteBuffer);
52 env->DeleteGlobalRef(mStorage);
53 }
54
read(void * buffer,size_t size)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) {
64 return this->setPosition(mPosition + size) ? size : 0;
65 }
66
67 auto* env = requireEnv(mJvm);
68 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
isAtEnd() const98 bool isAtEnd() const override { return mLength == mPosition; }
99
100 // SkStreamRewindable overrides
rewind()101 bool rewind() override { return this->setPosition(0); }
102
onDuplicate() const103 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
getPosition() const112 size_t getPosition() const override { return mPosition; }
113
seek(size_t position)114 bool seek(size_t position) override {
115 return this->setPosition(position > mLength ? mLength : position);
116 }
117
move(long offset)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
onFork() const126 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
getLength() const135 size_t getLength() const override { return mLength; }
136
137 private:
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.
setPosition(size_t newPosition)149 bool setPosition(size_t newPosition) {
150 auto* env = requireEnv(mJvm);
151 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
172 class ByteArrayStream : public SkStreamAsset {
173 private:
ByteArrayStream(JavaVM * jvm,jbyteArray jarray,size_t offset,size_t length)174 ByteArrayStream(JavaVM* jvm, jbyteArray jarray, size_t offset, size_t length)
175 : mJvm(jvm), mByteArray(jarray), mOffset(offset), mPosition(0), mLength(length) {}
176
177 public:
Create(JavaVM * jvm,JNIEnv * env,jbyteArray jarray,size_t offset,size_t length)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
~ByteArrayStream()188 ~ByteArrayStream() override {
189 auto* env = requireEnv(mJvm);
190 env->DeleteGlobalRef(mByteArray);
191 }
192
read(void * buffer,size_t size)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
201 auto* env = requireEnv(mJvm);
202 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
isAtEnd() const218 bool isAtEnd() const override { return mLength == mPosition; }
219
220 // SkStreamRewindable overrides
rewind()221 bool rewind() override {
222 mPosition = 0;
223 return true;
224 }
onDuplicate() const225 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
getPosition() const233 size_t getPosition() const override { return mPosition; }
234
seek(size_t position)235 bool seek(size_t position) override {
236 mPosition = (position > mLength) ? mLength : position;
237 return true;
238 }
239
move(long offset)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
onFork() const248 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
getLength() const256 size_t getLength() const override { return mLength; }
257
258 private:
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
268 struct release_proc_context {
269 JavaVM* jvm;
270 jobject jbyteBuffer;
271 };
272
CreateByteBufferStreamAdaptor(JNIEnv * env,jobject jbyteBuffer,size_t position,size_t limit)273 std::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);
290 JNIEnv* env = requireEnv(c->jvm);
291 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
CreateByteArrayStreamAdaptor(JNIEnv * env,jbyteArray array,size_t offset,size_t length)304 std::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
register_android_graphics_ByteBufferStreamAdaptor(JNIEnv * env)312 int 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 }
318