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
103static void
104releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit)
105{
106    _env->ReleasePrimitiveArrayCritical(array, data,
107                       commit ? 0 : JNI_ABORT);
108}
109
110static void *
111getDirectBufferPointer(JNIEnv *_env, jobject buffer) {
112    char* buf = (char*) _env->GetDirectBufferAddress(buffer);
113    if (buf) {
114        jint position = _env->GetIntField(buffer, positionID);
115        jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
116        buf += position << elementSizeShift;
117    } else {
118        jniThrowException(_env, "java/lang/IllegalArgumentException",
119                          "Must use a native order direct Buffer");
120    }
121    return (void*) buf;
122}
123
124// --------------------------------------------------------------------------
125
126/*
127 * returns the number of values glGet returns for a given pname.
128 *
129 * The code below is written such that pnames requiring only one values
130 * are the default (and are not explicitely tested for). This makes the
131 * checking code much shorter/readable/efficient.
132 *
133 * This means that unknown pnames (e.g.: extensions) will default to 1. If
134 * that unknown pname needs more than 1 value, then the validation check
135 * is incomplete and the app may crash if it passed the wrong number params.
136 */
137static int getNeededCount(GLint pname) {
138    int needed = 1;
139#ifdef GL_ES_VERSION_2_0
140    // GLES 2.x pnames
141    switch (pname) {
142        case GL_ALIASED_LINE_WIDTH_RANGE:
143        case GL_ALIASED_POINT_SIZE_RANGE:
144            needed = 2;
145            break;
146
147        case GL_BLEND_COLOR:
148        case GL_COLOR_CLEAR_VALUE:
149        case GL_COLOR_WRITEMASK:
150        case GL_SCISSOR_BOX:
151        case GL_VIEWPORT:
152            needed = 4;
153            break;
154
155        case GL_COMPRESSED_TEXTURE_FORMATS:
156            glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &needed);
157            break;
158
159        case GL_SHADER_BINARY_FORMATS:
160            glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &needed);
161            break;
162    }
163#endif
164
165#ifdef GL_VERSION_ES_CM_1_1
166    // GLES 1.x pnames
167    switch (pname) {
168        case GL_ALIASED_LINE_WIDTH_RANGE:
169        case GL_ALIASED_POINT_SIZE_RANGE:
170        case GL_DEPTH_RANGE:
171        case GL_SMOOTH_LINE_WIDTH_RANGE:
172        case GL_SMOOTH_POINT_SIZE_RANGE:
173            needed = 2;
174            break;
175
176        case GL_CURRENT_NORMAL:
177        case GL_POINT_DISTANCE_ATTENUATION:
178            needed = 3;
179            break;
180
181        case GL_COLOR_CLEAR_VALUE:
182        case GL_COLOR_WRITEMASK:
183        case GL_CURRENT_COLOR:
184        case GL_CURRENT_TEXTURE_COORDS:
185        case GL_FOG_COLOR:
186        case GL_LIGHT_MODEL_AMBIENT:
187        case GL_SCISSOR_BOX:
188        case GL_VIEWPORT:
189            needed = 4;
190            break;
191
192        case GL_MODELVIEW_MATRIX:
193        case GL_PROJECTION_MATRIX:
194        case GL_TEXTURE_MATRIX:
195            needed = 16;
196            break;
197
198        case GL_COMPRESSED_TEXTURE_FORMATS:
199            glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &needed);
200            break;
201    }
202#endif
203    return needed;
204}
205
206template <typename JTYPEARRAY, typename CTYPE, void GET(GLenum, CTYPE*)>
207static void
208get
209  (JNIEnv *_env, jobject _this, jint pname, JTYPEARRAY params_ref, jint offset) {
210    jint _exception = 0;
211    const char * _exceptionType;
212    const char * _exceptionMessage;
213    CTYPE *params_base = (CTYPE *) 0;
214    jint _remaining;
215    CTYPE *params = (CTYPE *) 0;
216    int _needed = 0;
217
218    if (!params_ref) {
219        _exception = 1;
220        _exceptionType = "java/lang/IllegalArgumentException";
221        _exceptionMessage = "params == null";
222        goto exit;
223    }
224    if (offset < 0) {
225        _exception = 1;
226        _exceptionType = "java/lang/IllegalArgumentException";
227        _exceptionMessage = "offset < 0";
228        goto exit;
229    }
230    _remaining = _env->GetArrayLength(params_ref) - offset;
231    _needed = getNeededCount(pname);
232    // if we didn't find this pname, we just assume the user passed
233    // an array of the right size -- this might happen with extensions
234    // or if we forget an enum here.
235    if (_remaining < _needed) {
236        _exception = 1;
237        _exceptionType = "java/lang/IllegalArgumentException";
238        _exceptionMessage = "length - offset < needed";
239        goto exit;
240    }
241    params_base = (CTYPE *)
242        _env->GetPrimitiveArrayCritical(params_ref, (jboolean *)0);
243    params = params_base + offset;
244
245    GET(
246        (GLenum)pname,
247        (CTYPE *)params
248    );
249
250exit:
251    if (params_base) {
252        _env->ReleasePrimitiveArrayCritical(params_ref, params_base,
253            _exception ? JNI_ABORT: 0);
254    }
255    if (_exception) {
256        jniThrowException(_env, _exceptionType, _exceptionMessage);
257    }
258}
259
260
261template <typename CTYPE, void GET(GLenum, CTYPE*)>
262static void
263getarray
264  (JNIEnv *_env, jobject _this, jint pname, jobject params_buf) {
265    jint _exception = 0;
266    const char * _exceptionType;
267    const char * _exceptionMessage;
268    jarray _array = (jarray) 0;
269    jint _bufferOffset = (jint) 0;
270    jint _remaining;
271    CTYPE *params = (CTYPE *) 0;
272    int _needed = 0;
273
274    params = (CTYPE *)getPointer(_env, params_buf, &_array, &_remaining, &_bufferOffset);
275    _remaining /= sizeof(CTYPE);    // convert from bytes to item count
276    _needed = getNeededCount(pname);
277    // if we didn't find this pname, we just assume the user passed
278    // an array of the right size -- this might happen with extensions
279    // or if we forget an enum here.
280    if (_needed>0 && _remaining < _needed) {
281        _exception = 1;
282        _exceptionType = "java/lang/IllegalArgumentException";
283        _exceptionMessage = "remaining() < needed";
284        goto exit;
285    }
286    if (params == NULL) {
287        char * _paramsBase = (char *)_env->GetPrimitiveArrayCritical(_array, (jboolean *) 0);
288        params = (CTYPE *) (_paramsBase + _bufferOffset);
289    }
290    GET(
291        (GLenum)pname,
292        (CTYPE *)params
293    );
294
295exit:
296    if (_array) {
297        releasePointer(_env, _array, params, _exception ? JNI_FALSE : JNI_TRUE);
298    }
299    if (_exception) {
300        jniThrowException(_env, _exceptionType, _exceptionMessage);
301    }
302}
303
304// --------------------------------------------------------------------------
305