1 #include <jni.h>
2 #include <nativehelper/JNIHelp.h>
3 #include <android_runtime/AndroidRuntime.h>
4 #include <utils/misc.h>
5 #include <assert.h>
6 
7 static int initialized = 0;
8 
9 static jclass nioAccessClass;
10 static jclass bufferClass;
11 static jmethodID getBasePointerID;
12 static jmethodID getBaseArrayID;
13 static jmethodID getBaseArrayOffsetID;
14 static jfieldID positionID;
15 static jfieldID limitID;
16 static jfieldID elementSizeShiftID;
17 
18 
19 /* special calls implemented in Android's GLES wrapper used to more
20  * efficiently bound-check passed arrays */
21 extern "C" {
22 #ifdef GL_VERSION_ES_CM_1_1
23 GL_API void GL_APIENTRY glColorPointerBounds(GLint size, GLenum type, GLsizei stride,
24         const GLvoid *ptr, GLsizei count);
25 GL_API void GL_APIENTRY glNormalPointerBounds(GLenum type, GLsizei stride,
26         const GLvoid *pointer, GLsizei count);
27 GL_API void GL_APIENTRY glTexCoordPointerBounds(GLint size, GLenum type,
28         GLsizei stride, const GLvoid *pointer, GLsizei count);
29 GL_API void GL_APIENTRY glVertexPointerBounds(GLint size, GLenum type,
30         GLsizei stride, const GLvoid *pointer, GLsizei count);
31 GL_API void GL_APIENTRY glPointSizePointerOESBounds(GLenum type,
32         GLsizei stride, const GLvoid *pointer, GLsizei count);
33 GL_API void GL_APIENTRY glMatrixIndexPointerOESBounds(GLint size, GLenum type,
34         GLsizei stride, const GLvoid *pointer, GLsizei count);
35 GL_API void GL_APIENTRY glWeightPointerOESBounds(GLint size, GLenum type,
36         GLsizei stride, const GLvoid *pointer, GLsizei count);
37 #endif
38 #ifdef GL_ES_VERSION_2_0
glVertexAttribPointerBounds(GLuint indx,GLint size,GLenum type,GLboolean normalized,GLsizei stride,const GLvoid * pointer,GLsizei count)39 static void glVertexAttribPointerBounds(GLuint indx, GLint size, GLenum type,
40         GLboolean normalized, GLsizei stride, const GLvoid *pointer, GLsizei count) {
41     glVertexAttribPointer(indx, size, type, normalized, stride, pointer);
42 }
43 #endif
44 #ifdef GL_ES_VERSION_3_0
glVertexAttribIPointerBounds(GLuint indx,GLint size,GLenum type,GLsizei stride,const GLvoid * pointer,GLsizei count)45 static void glVertexAttribIPointerBounds(GLuint indx, GLint size, GLenum type,
46         GLsizei stride, const GLvoid *pointer, GLsizei count) {
47     glVertexAttribIPointer(indx, size, type, stride, pointer);
48 }
49 #endif
50 }
51 
52 /* Cache method IDs each time the class is loaded. */
53 
54 static void
nativeClassInit(JNIEnv * _env,jclass glImplClass)55 nativeClassInit(JNIEnv *_env, jclass glImplClass)
56 {
57     jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
58     nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
59 
60     jclass bufferClassLocal = _env->FindClass("java/nio/Buffer");
61     bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal);
62 
63     getBasePointerID = _env->GetStaticMethodID(nioAccessClass,
64             "getBasePointer", "(Ljava/nio/Buffer;)J");
65     getBaseArrayID = _env->GetStaticMethodID(nioAccessClass,
66             "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;");
67     getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass,
68             "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
69 
70     positionID = _env->GetFieldID(bufferClass, "position", "I");
71     limitID = _env->GetFieldID(bufferClass, "limit", "I");
72     elementSizeShiftID =
73         _env->GetFieldID(bufferClass, "_elementSizeShift", "I");
74 }
75 
76 static void *
getPointer(JNIEnv * _env,jobject buffer,jarray * array,jint * remaining,jint * offset)77 getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining, jint *offset)
78 {
79     jint position;
80     jint limit;
81     jint elementSizeShift;
82     jlong pointer;
83 
84     position = _env->GetIntField(buffer, positionID);
85     limit = _env->GetIntField(buffer, limitID);
86     elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
87     *remaining = (limit - position) << elementSizeShift;
88     pointer = _env->CallStaticLongMethod(nioAccessClass,
89             getBasePointerID, buffer);
90     if (pointer != 0L) {
91         *array = NULL;
92         return reinterpret_cast<void*>(pointer);
93     }
94 
95     *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
96             getBaseArrayID, buffer);
97     *offset = _env->CallStaticIntMethod(nioAccessClass,
98             getBaseArrayOffsetID, buffer);
99 
100     return NULL;
101 }
102 
103 class ByteArrayGetter {
104 public:
Get(JNIEnv * _env,jbyteArray array,jboolean * is_copy)105     static void* Get(JNIEnv* _env, jbyteArray array, jboolean* is_copy) {
106         return _env->GetByteArrayElements(array, is_copy);
107     }
108 };
109 class BooleanArrayGetter {
110 public:
Get(JNIEnv * _env,jbooleanArray array,jboolean * is_copy)111     static void* Get(JNIEnv* _env, jbooleanArray array, jboolean* is_copy) {
112         return _env->GetBooleanArrayElements(array, is_copy);
113     }
114 };
115 class CharArrayGetter {
116 public:
Get(JNIEnv * _env,jcharArray array,jboolean * is_copy)117     static void* Get(JNIEnv* _env, jcharArray array, jboolean* is_copy) {
118         return _env->GetCharArrayElements(array, is_copy);
119     }
120 };
121 class ShortArrayGetter {
122 public:
Get(JNIEnv * _env,jshortArray array,jboolean * is_copy)123     static void* Get(JNIEnv* _env, jshortArray array, jboolean* is_copy) {
124         return _env->GetShortArrayElements(array, is_copy);
125     }
126 };
127 class IntArrayGetter {
128 public:
Get(JNIEnv * _env,jintArray array,jboolean * is_copy)129     static void* Get(JNIEnv* _env, jintArray array, jboolean* is_copy) {
130         return _env->GetIntArrayElements(array, is_copy);
131     }
132 };
133 class LongArrayGetter {
134 public:
Get(JNIEnv * _env,jlongArray array,jboolean * is_copy)135     static void* Get(JNIEnv* _env, jlongArray array, jboolean* is_copy) {
136         return _env->GetLongArrayElements(array, is_copy);
137     }
138 };
139 class FloatArrayGetter {
140 public:
Get(JNIEnv * _env,jfloatArray array,jboolean * is_copy)141     static void* Get(JNIEnv* _env, jfloatArray array, jboolean* is_copy) {
142         return _env->GetFloatArrayElements(array, is_copy);
143     }
144 };
145 class DoubleArrayGetter {
146 public:
Get(JNIEnv * _env,jdoubleArray array,jboolean * is_copy)147     static void* Get(JNIEnv* _env, jdoubleArray array, jboolean* is_copy) {
148         return _env->GetDoubleArrayElements(array, is_copy);
149     }
150 };
151 
152 template<typename JTYPEARRAY, typename ARRAYGETTER>
153 static void*
getArrayPointer(JNIEnv * _env,JTYPEARRAY array,jboolean * is_copy)154 getArrayPointer(JNIEnv *_env, JTYPEARRAY array, jboolean* is_copy) {
155     return ARRAYGETTER::Get(_env, array, is_copy);
156 }
157 
158 class ByteArrayReleaser {
159 public:
Release(JNIEnv * _env,jbyteArray array,jbyte * data,jboolean commit)160     static void Release(JNIEnv* _env, jbyteArray array, jbyte* data, jboolean commit) {
161         _env->ReleaseByteArrayElements(array, data, commit ? 0 : JNI_ABORT);
162     }
163 };
164 class BooleanArrayReleaser {
165 public:
Release(JNIEnv * _env,jbooleanArray array,jboolean * data,jboolean commit)166     static void Release(JNIEnv* _env, jbooleanArray array, jboolean* data, jboolean commit) {
167         _env->ReleaseBooleanArrayElements(array, data, commit ? 0 : JNI_ABORT);
168     }
169 };
170 class CharArrayReleaser {
171 public:
Release(JNIEnv * _env,jcharArray array,jchar * data,jboolean commit)172     static void Release(JNIEnv* _env, jcharArray array, jchar* data, jboolean commit) {
173         _env->ReleaseCharArrayElements(array, data, commit ? 0 : JNI_ABORT);
174     }
175 };
176 class ShortArrayReleaser {
177 public:
Release(JNIEnv * _env,jshortArray array,jshort * data,jboolean commit)178     static void Release(JNIEnv* _env, jshortArray array, jshort* data, jboolean commit) {
179         _env->ReleaseShortArrayElements(array, data, commit ? 0 : JNI_ABORT);
180     }
181 };
182 class IntArrayReleaser {
183 public:
Release(JNIEnv * _env,jintArray array,jint * data,jboolean commit)184     static void Release(JNIEnv* _env, jintArray array, jint* data, jboolean commit) {
185         _env->ReleaseIntArrayElements(array, data, commit ? 0 : JNI_ABORT);
186     }
187 };
188 class LongArrayReleaser {
189 public:
Release(JNIEnv * _env,jlongArray array,jlong * data,jboolean commit)190     static void Release(JNIEnv* _env, jlongArray array, jlong* data, jboolean commit) {
191         _env->ReleaseLongArrayElements(array, data, commit ? 0 : JNI_ABORT);
192     }
193 };
194 class FloatArrayReleaser {
195 public:
Release(JNIEnv * _env,jfloatArray array,jfloat * data,jboolean commit)196     static void Release(JNIEnv* _env, jfloatArray array, jfloat* data, jboolean commit) {
197         _env->ReleaseFloatArrayElements(array, data, commit ? 0 : JNI_ABORT);
198     }
199 };
200 class DoubleArrayReleaser {
201 public:
Release(JNIEnv * _env,jdoubleArray array,jdouble * data,jboolean commit)202     static void Release(JNIEnv* _env, jdoubleArray array, jdouble* data, jboolean commit) {
203         _env->ReleaseDoubleArrayElements(array, data, commit ? 0 : JNI_ABORT);
204     }
205 };
206 
207 template<typename JTYPEARRAY, typename NTYPEARRAY, typename ARRAYRELEASER>
208 static void
releaseArrayPointer(JNIEnv * _env,JTYPEARRAY array,NTYPEARRAY data,jboolean commit)209 releaseArrayPointer(JNIEnv *_env, JTYPEARRAY array, NTYPEARRAY data, jboolean commit) {
210     ARRAYRELEASER::Release(_env, array, data, commit);
211 }
212 
213 static void
releasePointer(JNIEnv * _env,jarray array,void * data,jboolean commit)214 releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit)
215 {
216     _env->ReleasePrimitiveArrayCritical(array, data,
217                        commit ? 0 : JNI_ABORT);
218 }
219 
220 static void *
getDirectBufferPointer(JNIEnv * _env,jobject buffer)221 getDirectBufferPointer(JNIEnv *_env, jobject buffer) {
222     char* buf = (char*) _env->GetDirectBufferAddress(buffer);
223     if (buf) {
224         jint position = _env->GetIntField(buffer, positionID);
225         jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
226         buf += position << elementSizeShift;
227     } else {
228         jniThrowException(_env, "java/lang/IllegalArgumentException",
229                           "Must use a native order direct Buffer");
230     }
231     return (void*) buf;
232 }
233 
234 // --------------------------------------------------------------------------
235 
236 /*
237  * returns the number of values glGet returns for a given pname.
238  *
239  * The code below is written such that pnames requiring only one values
240  * are the default (and are not explicitely tested for). This makes the
241  * checking code much shorter/readable/efficient.
242  *
243  * This means that unknown pnames (e.g.: extensions) will default to 1. If
244  * that unknown pname needs more than 1 value, then the validation check
245  * is incomplete and the app may crash if it passed the wrong number params.
246  */
getNeededCount(GLint pname)247 static int getNeededCount(GLint pname) {
248     int needed = 1;
249 #ifdef GL_ES_VERSION_3_0
250     // GLES 3.x pnames
251     switch (pname) {
252         case GL_MAX_VIEWPORT_DIMS:
253             needed = 2;
254             break;
255 
256         case GL_PROGRAM_BINARY_FORMATS:
257             glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &needed);
258             break;
259     }
260 #endif
261 
262 #ifdef GL_ES_VERSION_2_0
263     // GLES 2.x pnames
264     switch (pname) {
265         case GL_ALIASED_LINE_WIDTH_RANGE:
266         case GL_ALIASED_POINT_SIZE_RANGE:
267             needed = 2;
268             break;
269 
270         case GL_BLEND_COLOR:
271         case GL_COLOR_CLEAR_VALUE:
272         case GL_COLOR_WRITEMASK:
273         case GL_SCISSOR_BOX:
274         case GL_VIEWPORT:
275             needed = 4;
276             break;
277 
278         case GL_COMPRESSED_TEXTURE_FORMATS:
279             glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &needed);
280             break;
281 
282         case GL_SHADER_BINARY_FORMATS:
283             glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &needed);
284             break;
285     }
286 #endif
287 
288 #ifdef GL_VERSION_ES_CM_1_1
289     // GLES 1.x pnames
290     switch (pname) {
291         case GL_ALIASED_LINE_WIDTH_RANGE:
292         case GL_ALIASED_POINT_SIZE_RANGE:
293         case GL_DEPTH_RANGE:
294         case GL_SMOOTH_LINE_WIDTH_RANGE:
295         case GL_SMOOTH_POINT_SIZE_RANGE:
296             needed = 2;
297             break;
298 
299         case GL_CURRENT_NORMAL:
300         case GL_POINT_DISTANCE_ATTENUATION:
301             needed = 3;
302             break;
303 
304         case GL_COLOR_CLEAR_VALUE:
305         case GL_COLOR_WRITEMASK:
306         case GL_CURRENT_COLOR:
307         case GL_CURRENT_TEXTURE_COORDS:
308         case GL_FOG_COLOR:
309         case GL_LIGHT_MODEL_AMBIENT:
310         case GL_SCISSOR_BOX:
311         case GL_VIEWPORT:
312             needed = 4;
313             break;
314 
315         case GL_MODELVIEW_MATRIX:
316         case GL_PROJECTION_MATRIX:
317         case GL_TEXTURE_MATRIX:
318             needed = 16;
319             break;
320 
321         case GL_COMPRESSED_TEXTURE_FORMATS:
322             glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &needed);
323             break;
324     }
325 #endif
326     return needed;
327 }
328 
329 template <typename JTYPEARRAY, typename ARRAYGETTER, typename NTYPEARRAY,
330           typename ARRAYRELEASER, typename CTYPE, void GET(GLenum, CTYPE*)>
331 static void
get(JNIEnv * _env,jobject _this,jint pname,JTYPEARRAY params_ref,jint offset)332 get
333   (JNIEnv *_env, jobject _this, jint pname, JTYPEARRAY params_ref, jint offset) {
334     jint _exception = 0;
335     const char * _exceptionType;
336     const char * _exceptionMessage;
337     CTYPE *params_base = (CTYPE *) 0;
338     jint _remaining;
339     CTYPE *params = (CTYPE *) 0;
340     int _needed = 0;
341 
342     if (!params_ref) {
343         _exception = 1;
344         _exceptionType = "java/lang/IllegalArgumentException";
345         _exceptionMessage = "params == null";
346         goto exit;
347     }
348     if (offset < 0) {
349         _exception = 1;
350         _exceptionType = "java/lang/IllegalArgumentException";
351         _exceptionMessage = "offset < 0";
352         goto exit;
353     }
354     _remaining = _env->GetArrayLength(params_ref) - offset;
355     _needed = getNeededCount(pname);
356     // if we didn't find this pname, we just assume the user passed
357     // an array of the right size -- this might happen with extensions
358     // or if we forget an enum here.
359     if (_remaining < _needed) {
360         _exception = 1;
361         _exceptionType = "java/lang/IllegalArgumentException";
362         _exceptionMessage = "length - offset < needed";
363         goto exit;
364     }
365     params_base = (CTYPE *) getArrayPointer<JTYPEARRAY, ARRAYGETTER>(
366         _env, params_ref, (jboolean *)0);
367     params = params_base + offset;
368 
369     GET(
370         (GLenum)pname,
371         (CTYPE *)params
372     );
373 
374 exit:
375     if (params_base) {
376         releaseArrayPointer<JTYPEARRAY, NTYPEARRAY, ARRAYRELEASER>(
377             _env, params_ref, params_base, !_exception);
378     }
379     if (_exception) {
380         jniThrowException(_env, _exceptionType, _exceptionMessage);
381     }
382 }
383 
384 
385 template <typename CTYPE, typename JTYPEARRAY, typename ARRAYGETTER, typename NTYPEARRAY,
386           typename ARRAYRELEASER, void GET(GLenum, CTYPE*)>
387 static void
getarray(JNIEnv * _env,jobject _this,jint pname,jobject params_buf)388 getarray
389   (JNIEnv *_env, jobject _this, jint pname, jobject params_buf) {
390     jint _exception = 0;
391     const char * _exceptionType;
392     const char * _exceptionMessage;
393     JTYPEARRAY _array = (JTYPEARRAY) 0;
394     jint _bufferOffset = (jint) 0;
395     jint _remaining;
396     CTYPE *params = (CTYPE *) 0;
397     int _needed = 0;
398 
399     params = (CTYPE *)getPointer(_env, params_buf, (jarray*)&_array, &_remaining, &_bufferOffset);
400     _remaining /= sizeof(CTYPE);    // convert from bytes to item count
401     _needed = getNeededCount(pname);
402     // if we didn't find this pname, we just assume the user passed
403     // an array of the right size -- this might happen with extensions
404     // or if we forget an enum here.
405     if (_needed>0 && _remaining < _needed) {
406         _exception = 1;
407         _exceptionType = "java/lang/IllegalArgumentException";
408         _exceptionMessage = "remaining() < needed";
409         goto exit;
410     }
411     if (params == NULL) {
412         char * _paramsBase = (char *) getArrayPointer<JTYPEARRAY, ARRAYGETTER>(
413             _env, _array, (jboolean *) 0);
414         params = (CTYPE *) (_paramsBase + _bufferOffset);
415     }
416     GET(
417         (GLenum)pname,
418         (CTYPE *)params
419     );
420 
421 exit:
422     if (_array) {
423         releaseArrayPointer<JTYPEARRAY, NTYPEARRAY, ARRAYRELEASER>(
424             _env, _array, (NTYPEARRAY)params, _exception ? JNI_FALSE : JNI_TRUE);
425     }
426     if (_exception) {
427         jniThrowException(_env, _exceptionType, _exceptionMessage);
428     }
429 }
430 
431 // --------------------------------------------------------------------------
432