1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "BufferUtils.h"
17
18 #include "graphics_jni_helpers.h"
19
copyToVector(std::vector<uint8_t> & dst,const void * src,size_t srcSize)20 static void copyToVector(std::vector<uint8_t>& dst, const void* src, size_t srcSize) {
21 if (src) {
22 dst.resize(srcSize);
23 memcpy(dst.data(), src, srcSize);
24 }
25 }
26
27 /**
28 * This code is taken and modified from com_google_android_gles_jni_GLImpl.cpp to extract data
29 * from a java.nio.Buffer.
30 */
getDirectBufferPointer(JNIEnv * env,jobject buffer)31 static void* getDirectBufferPointer(JNIEnv* env, jobject buffer) {
32 if (buffer == nullptr) {
33 return nullptr;
34 }
35
36 jint position;
37 jint limit;
38 jint elementSizeShift;
39 jlong pointer;
40 pointer = jniGetNioBufferFields(env, buffer, &position, &limit, &elementSizeShift);
41 if (pointer == 0) {
42 jniThrowException(env, "java/lang/IllegalArgumentException",
43 "Must use a native order direct Buffer");
44 return nullptr;
45 }
46 pointer += position << elementSizeShift;
47 return reinterpret_cast<void*>(pointer);
48 }
49
releasePointer(JNIEnv * env,jarray array,void * data,jboolean commit)50 static void releasePointer(JNIEnv* env, jarray array, void* data, jboolean commit) {
51 env->ReleasePrimitiveArrayCritical(array, data, commit ? 0 : JNI_ABORT);
52 }
53
getPointer(JNIEnv * env,jobject buffer,jarray * array,jint * remaining,jint * offset)54 static void* getPointer(JNIEnv* env, jobject buffer, jarray* array, jint* remaining, jint* offset) {
55 jint position;
56 jint limit;
57 jint elementSizeShift;
58
59 jlong pointer;
60 pointer = jniGetNioBufferFields(env, buffer, &position, &limit, &elementSizeShift);
61 *remaining = (limit - position) << elementSizeShift;
62 if (pointer != 0L) {
63 *array = nullptr;
64 pointer += position << elementSizeShift;
65 return reinterpret_cast<void*>(pointer);
66 }
67
68 *array = jniGetNioBufferBaseArray(env, buffer);
69 *offset = jniGetNioBufferBaseArrayOffset(env, buffer);
70 return nullptr;
71 }
72
73 /**
74 * This is a copy of
75 * static void android_glBufferData__IILjava_nio_Buffer_2I
76 * from com_google_android_gles_jni_GLImpl.cpp
77 */
setIndirectData(JNIEnv * env,size_t size,jobject data_buf,std::vector<uint8_t> & result)78 static void setIndirectData(JNIEnv* env, size_t size, jobject data_buf,
79 std::vector<uint8_t>& result) {
80 jint exception = 0;
81 const char* exceptionType = nullptr;
82 const char* exceptionMessage = nullptr;
83 jarray array = nullptr;
84 jint bufferOffset = 0;
85 jint remaining;
86 void* data = 0;
87 char* dataBase = nullptr;
88
89 if (data_buf) {
90 data = getPointer(env, data_buf, (jarray*)&array, &remaining, &bufferOffset);
91 if (remaining < size) {
92 exception = 1;
93 exceptionType = "java/lang/IllegalArgumentException";
94 exceptionMessage = "remaining() < size < needed";
95 goto exit;
96 }
97 }
98 if (data_buf && data == nullptr) {
99 dataBase = (char*)env->GetPrimitiveArrayCritical(array, (jboolean*)0);
100 data = (void*)(dataBase + bufferOffset);
101 }
102
103 copyToVector(result, data, size);
104
105 exit:
106 if (array) {
107 releasePointer(env, array, (void*)dataBase, JNI_FALSE);
108 }
109 if (exception) {
110 jniThrowException(env, exceptionType, exceptionMessage);
111 }
112 }
113
copyJavaNioBufferToVector(JNIEnv * env,jobject buffer,size_t size,jboolean isDirect)114 std::vector<uint8_t> copyJavaNioBufferToVector(JNIEnv* env, jobject buffer, size_t size,
115 jboolean isDirect) {
116 std::vector<uint8_t> data;
117 if (buffer == nullptr) {
118 jniThrowNullPointerException(env);
119 } else {
120 if (isDirect) {
121 void* directBufferPtr = getDirectBufferPointer(env, buffer);
122 if (directBufferPtr) {
123 copyToVector(data, directBufferPtr, size);
124 }
125 } else {
126 setIndirectData(env, size, buffer, data);
127 }
128 }
129 return data;
130 }
131