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