1#include <jni.h>
2#include <JNIHelp.h>
3#include <android_runtime/AndroidRuntime.h>
4#include <utils/misc.h>
5#include <assert.h>
6
7static int initialized = 0;
8
9static jclass nioAccessClass;
10static jclass bufferClass;
11static jmethodID getBasePointerID;
12static jmethodID getBaseArrayID;
13static jmethodID getBaseArrayOffsetID;
14static jfieldID positionID;
15static jfieldID limitID;
16static jfieldID elementSizeShiftID;
17
18
19/* special calls implemented in Android's GLES wrapper used to more
20 * efficiently bound-check passed arrays */
21extern "C" {
22#ifdef GL_VERSION_ES_CM_1_1
23GL_API void GL_APIENTRY glColorPointerBounds(GLint size, GLenum type, GLsizei stride,
24        const GLvoid *ptr, GLsizei count);
25GL_API void GL_APIENTRY glNormalPointerBounds(GLenum type, GLsizei stride,
26        const GLvoid *pointer, GLsizei count);
27GL_API void GL_APIENTRY glTexCoordPointerBounds(GLint size, GLenum type,
28        GLsizei stride, const GLvoid *pointer, GLsizei count);
29GL_API void GL_APIENTRY glVertexPointerBounds(GLint size, GLenum type,
30        GLsizei stride, const GLvoid *pointer, GLsizei count);
31GL_API void GL_APIENTRY glPointSizePointerOESBounds(GLenum type,
32        GLsizei stride, const GLvoid *pointer, GLsizei count);
33GL_API void GL_APIENTRY glMatrixIndexPointerOESBounds(GLint size, GLenum type,
34        GLsizei stride, const GLvoid *pointer, GLsizei count);
35GL_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
39static 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
45static 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
54static void
55nativeClassInit(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
76static void *
77getPointer(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
103class ByteArrayGetter {
104public:
105    static void* Get(JNIEnv* _env, jbyteArray array, jboolean* is_copy) {
106        return _env->GetByteArrayElements(array, is_copy);
107    }
108};
109class BooleanArrayGetter {
110public:
111    static void* Get(JNIEnv* _env, jbooleanArray array, jboolean* is_copy) {
112        return _env->GetBooleanArrayElements(array, is_copy);
113    }
114};
115class CharArrayGetter {
116public:
117    static void* Get(JNIEnv* _env, jcharArray array, jboolean* is_copy) {
118        return _env->GetCharArrayElements(array, is_copy);
119    }
120};
121class ShortArrayGetter {
122public:
123    static void* Get(JNIEnv* _env, jshortArray array, jboolean* is_copy) {
124        return _env->GetShortArrayElements(array, is_copy);
125    }
126};
127class IntArrayGetter {
128public:
129    static void* Get(JNIEnv* _env, jintArray array, jboolean* is_copy) {
130        return _env->GetIntArrayElements(array, is_copy);
131    }
132};
133class LongArrayGetter {
134public:
135    static void* Get(JNIEnv* _env, jlongArray array, jboolean* is_copy) {
136        return _env->GetLongArrayElements(array, is_copy);
137    }
138};
139class FloatArrayGetter {
140public:
141    static void* Get(JNIEnv* _env, jfloatArray array, jboolean* is_copy) {
142        return _env->GetFloatArrayElements(array, is_copy);
143    }
144};
145class DoubleArrayGetter {
146public:
147    static void* Get(JNIEnv* _env, jdoubleArray array, jboolean* is_copy) {
148        return _env->GetDoubleArrayElements(array, is_copy);
149    }
150};
151
152template<typename JTYPEARRAY, typename ARRAYGETTER>
153static void*
154getArrayPointer(JNIEnv *_env, JTYPEARRAY array, jboolean* is_copy) {
155    return ARRAYGETTER::Get(_env, array, is_copy);
156}
157
158class ByteArrayReleaser {
159public:
160    static void Release(JNIEnv* _env, jbyteArray array, jbyte* data, jboolean commit) {
161        _env->ReleaseByteArrayElements(array, data, commit ? 0 : JNI_ABORT);
162    }
163};
164class BooleanArrayReleaser {
165public:
166    static void Release(JNIEnv* _env, jbooleanArray array, jboolean* data, jboolean commit) {
167        _env->ReleaseBooleanArrayElements(array, data, commit ? 0 : JNI_ABORT);
168    }
169};
170class CharArrayReleaser {
171public:
172    static void Release(JNIEnv* _env, jcharArray array, jchar* data, jboolean commit) {
173        _env->ReleaseCharArrayElements(array, data, commit ? 0 : JNI_ABORT);
174    }
175};
176class ShortArrayReleaser {
177public:
178    static void Release(JNIEnv* _env, jshortArray array, jshort* data, jboolean commit) {
179        _env->ReleaseShortArrayElements(array, data, commit ? 0 : JNI_ABORT);
180    }
181};
182class IntArrayReleaser {
183public:
184    static void Release(JNIEnv* _env, jintArray array, jint* data, jboolean commit) {
185        _env->ReleaseIntArrayElements(array, data, commit ? 0 : JNI_ABORT);
186    }
187};
188class LongArrayReleaser {
189public:
190    static void Release(JNIEnv* _env, jlongArray array, jlong* data, jboolean commit) {
191        _env->ReleaseLongArrayElements(array, data, commit ? 0 : JNI_ABORT);
192    }
193};
194class FloatArrayReleaser {
195public:
196    static void Release(JNIEnv* _env, jfloatArray array, jfloat* data, jboolean commit) {
197        _env->ReleaseFloatArrayElements(array, data, commit ? 0 : JNI_ABORT);
198    }
199};
200class DoubleArrayReleaser {
201public:
202    static void Release(JNIEnv* _env, jdoubleArray array, jdouble* data, jboolean commit) {
203        _env->ReleaseDoubleArrayElements(array, data, commit ? 0 : JNI_ABORT);
204    }
205};
206
207template<typename JTYPEARRAY, typename NTYPEARRAY, typename ARRAYRELEASER>
208static void
209releaseArrayPointer(JNIEnv *_env, JTYPEARRAY array, NTYPEARRAY data, jboolean commit) {
210    ARRAYRELEASER::Release(_env, array, data, commit);
211}
212
213static void
214releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit)
215{
216    _env->ReleasePrimitiveArrayCritical(array, data,
217                       commit ? 0 : JNI_ABORT);
218}
219
220static void *
221getDirectBufferPointer(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 */
247static int getNeededCount(GLint pname) {
248    int needed = 1;
249#ifdef GL_ES_VERSION_2_0
250    // GLES 2.x pnames
251    switch (pname) {
252        case GL_ALIASED_LINE_WIDTH_RANGE:
253        case GL_ALIASED_POINT_SIZE_RANGE:
254            needed = 2;
255            break;
256
257        case GL_BLEND_COLOR:
258        case GL_COLOR_CLEAR_VALUE:
259        case GL_COLOR_WRITEMASK:
260        case GL_SCISSOR_BOX:
261        case GL_VIEWPORT:
262            needed = 4;
263            break;
264
265        case GL_COMPRESSED_TEXTURE_FORMATS:
266            glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &needed);
267            break;
268
269        case GL_SHADER_BINARY_FORMATS:
270            glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &needed);
271            break;
272    }
273#endif
274
275#ifdef GL_VERSION_ES_CM_1_1
276    // GLES 1.x pnames
277    switch (pname) {
278        case GL_ALIASED_LINE_WIDTH_RANGE:
279        case GL_ALIASED_POINT_SIZE_RANGE:
280        case GL_DEPTH_RANGE:
281        case GL_SMOOTH_LINE_WIDTH_RANGE:
282        case GL_SMOOTH_POINT_SIZE_RANGE:
283            needed = 2;
284            break;
285
286        case GL_CURRENT_NORMAL:
287        case GL_POINT_DISTANCE_ATTENUATION:
288            needed = 3;
289            break;
290
291        case GL_COLOR_CLEAR_VALUE:
292        case GL_COLOR_WRITEMASK:
293        case GL_CURRENT_COLOR:
294        case GL_CURRENT_TEXTURE_COORDS:
295        case GL_FOG_COLOR:
296        case GL_LIGHT_MODEL_AMBIENT:
297        case GL_SCISSOR_BOX:
298        case GL_VIEWPORT:
299            needed = 4;
300            break;
301
302        case GL_MODELVIEW_MATRIX:
303        case GL_PROJECTION_MATRIX:
304        case GL_TEXTURE_MATRIX:
305            needed = 16;
306            break;
307
308        case GL_COMPRESSED_TEXTURE_FORMATS:
309            glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &needed);
310            break;
311    }
312#endif
313    return needed;
314}
315
316template <typename JTYPEARRAY, typename ARRAYGETTER, typename NTYPEARRAY,
317          typename ARRAYRELEASER, typename CTYPE, void GET(GLenum, CTYPE*)>
318static void
319get
320  (JNIEnv *_env, jobject _this, jint pname, JTYPEARRAY params_ref, jint offset) {
321    jint _exception = 0;
322    const char * _exceptionType;
323    const char * _exceptionMessage;
324    CTYPE *params_base = (CTYPE *) 0;
325    jint _remaining;
326    CTYPE *params = (CTYPE *) 0;
327    int _needed = 0;
328
329    if (!params_ref) {
330        _exception = 1;
331        _exceptionType = "java/lang/IllegalArgumentException";
332        _exceptionMessage = "params == null";
333        goto exit;
334    }
335    if (offset < 0) {
336        _exception = 1;
337        _exceptionType = "java/lang/IllegalArgumentException";
338        _exceptionMessage = "offset < 0";
339        goto exit;
340    }
341    _remaining = _env->GetArrayLength(params_ref) - offset;
342    _needed = getNeededCount(pname);
343    // if we didn't find this pname, we just assume the user passed
344    // an array of the right size -- this might happen with extensions
345    // or if we forget an enum here.
346    if (_remaining < _needed) {
347        _exception = 1;
348        _exceptionType = "java/lang/IllegalArgumentException";
349        _exceptionMessage = "length - offset < needed";
350        goto exit;
351    }
352    params_base = (CTYPE *) getArrayPointer<JTYPEARRAY, ARRAYGETTER>(
353        _env, params_ref, (jboolean *)0);
354    params = params_base + offset;
355
356    GET(
357        (GLenum)pname,
358        (CTYPE *)params
359    );
360
361exit:
362    if (params_base) {
363        releaseArrayPointer<JTYPEARRAY, NTYPEARRAY, ARRAYRELEASER>(
364            _env, params_ref, params_base, !_exception);
365    }
366    if (_exception) {
367        jniThrowException(_env, _exceptionType, _exceptionMessage);
368    }
369}
370
371
372template <typename CTYPE, typename JTYPEARRAY, typename ARRAYGETTER, typename NTYPEARRAY,
373          typename ARRAYRELEASER, void GET(GLenum, CTYPE*)>
374static void
375getarray
376  (JNIEnv *_env, jobject _this, jint pname, jobject params_buf) {
377    jint _exception = 0;
378    const char * _exceptionType;
379    const char * _exceptionMessage;
380    JTYPEARRAY _array = (JTYPEARRAY) 0;
381    jint _bufferOffset = (jint) 0;
382    jint _remaining;
383    CTYPE *params = (CTYPE *) 0;
384    int _needed = 0;
385
386    params = (CTYPE *)getPointer(_env, params_buf, (jarray*)&_array, &_remaining, &_bufferOffset);
387    _remaining /= sizeof(CTYPE);    // convert from bytes to item count
388    _needed = getNeededCount(pname);
389    // if we didn't find this pname, we just assume the user passed
390    // an array of the right size -- this might happen with extensions
391    // or if we forget an enum here.
392    if (_needed>0 && _remaining < _needed) {
393        _exception = 1;
394        _exceptionType = "java/lang/IllegalArgumentException";
395        _exceptionMessage = "remaining() < needed";
396        goto exit;
397    }
398    if (params == NULL) {
399        char * _paramsBase = (char *) getArrayPointer<JTYPEARRAY, ARRAYGETTER>(
400            _env, _array, (jboolean *) 0);
401        params = (CTYPE *) (_paramsBase + _bufferOffset);
402    }
403    GET(
404        (GLenum)pname,
405        (CTYPE *)params
406    );
407
408exit:
409    if (_array) {
410        releaseArrayPointer<JTYPEARRAY, NTYPEARRAY, ARRAYRELEASER>(
411            _env, _array, (NTYPEARRAY)params, _exception ? JNI_FALSE : JNI_TRUE);
412    }
413    if (_exception) {
414        jniThrowException(_env, _exceptionType, _exceptionMessage);
415    }
416}
417
418// --------------------------------------------------------------------------
419