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