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