|  | #include <jni.h> | 
|  | #include <JNIHelp.h> | 
|  | #include <android_runtime/AndroidRuntime.h> | 
|  | #include <utils/misc.h> | 
|  | #include <assert.h> | 
|  |  | 
|  | static int initialized = 0; | 
|  |  | 
|  | static jclass nioAccessClass; | 
|  | static jclass bufferClass; | 
|  | static jmethodID getBasePointerID; | 
|  | static jmethodID getBaseArrayID; | 
|  | static jmethodID getBaseArrayOffsetID; | 
|  | static jfieldID positionID; | 
|  | static jfieldID limitID; | 
|  | static jfieldID elementSizeShiftID; | 
|  |  | 
|  |  | 
|  | /* special calls implemented in Android's GLES wrapper used to more | 
|  | * efficiently bound-check passed arrays */ | 
|  | extern "C" { | 
|  | #ifdef GL_VERSION_ES_CM_1_1 | 
|  | GL_API void GL_APIENTRY glColorPointerBounds(GLint size, GLenum type, GLsizei stride, | 
|  | const GLvoid *ptr, GLsizei count); | 
|  | GL_API void GL_APIENTRY glNormalPointerBounds(GLenum type, GLsizei stride, | 
|  | const GLvoid *pointer, GLsizei count); | 
|  | GL_API void GL_APIENTRY glTexCoordPointerBounds(GLint size, GLenum type, | 
|  | GLsizei stride, const GLvoid *pointer, GLsizei count); | 
|  | GL_API void GL_APIENTRY glVertexPointerBounds(GLint size, GLenum type, | 
|  | GLsizei stride, const GLvoid *pointer, GLsizei count); | 
|  | GL_API void GL_APIENTRY glPointSizePointerOESBounds(GLenum type, | 
|  | GLsizei stride, const GLvoid *pointer, GLsizei count); | 
|  | GL_API void GL_APIENTRY glMatrixIndexPointerOESBounds(GLint size, GLenum type, | 
|  | GLsizei stride, const GLvoid *pointer, GLsizei count); | 
|  | GL_API void GL_APIENTRY glWeightPointerOESBounds(GLint size, GLenum type, | 
|  | GLsizei stride, const GLvoid *pointer, GLsizei count); | 
|  | #endif | 
|  | #ifdef GL_ES_VERSION_2_0 | 
|  | static void glVertexAttribPointerBounds(GLuint indx, GLint size, GLenum type, | 
|  | GLboolean normalized, GLsizei stride, const GLvoid *pointer, GLsizei count) { | 
|  | glVertexAttribPointer(indx, size, type, normalized, stride, pointer); | 
|  | } | 
|  | #endif | 
|  | #ifdef GL_ES_VERSION_3_0 | 
|  | static void glVertexAttribIPointerBounds(GLuint indx, GLint size, GLenum type, | 
|  | GLsizei stride, const GLvoid *pointer, GLsizei count) { | 
|  | glVertexAttribIPointer(indx, size, type, stride, pointer); | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | /* Cache method IDs each time the class is loaded. */ | 
|  |  | 
|  | static void | 
|  | nativeClassInit(JNIEnv *_env, jclass glImplClass) | 
|  | { | 
|  | jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess"); | 
|  | nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal); | 
|  |  | 
|  | jclass bufferClassLocal = _env->FindClass("java/nio/Buffer"); | 
|  | bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal); | 
|  |  | 
|  | getBasePointerID = _env->GetStaticMethodID(nioAccessClass, | 
|  | "getBasePointer", "(Ljava/nio/Buffer;)J"); | 
|  | getBaseArrayID = _env->GetStaticMethodID(nioAccessClass, | 
|  | "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;"); | 
|  | getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass, | 
|  | "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); | 
|  |  | 
|  | positionID = _env->GetFieldID(bufferClass, "position", "I"); | 
|  | limitID = _env->GetFieldID(bufferClass, "limit", "I"); | 
|  | elementSizeShiftID = | 
|  | _env->GetFieldID(bufferClass, "_elementSizeShift", "I"); | 
|  | } | 
|  |  | 
|  | static void * | 
|  | getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining, jint *offset) | 
|  | { | 
|  | jint position; | 
|  | jint limit; | 
|  | jint elementSizeShift; | 
|  | jlong pointer; | 
|  |  | 
|  | position = _env->GetIntField(buffer, positionID); | 
|  | limit = _env->GetIntField(buffer, limitID); | 
|  | elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); | 
|  | *remaining = (limit - position) << elementSizeShift; | 
|  | pointer = _env->CallStaticLongMethod(nioAccessClass, | 
|  | getBasePointerID, buffer); | 
|  | if (pointer != 0L) { | 
|  | *array = NULL; | 
|  | return reinterpret_cast<void*>(pointer); | 
|  | } | 
|  |  | 
|  | *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass, | 
|  | getBaseArrayID, buffer); | 
|  | *offset = _env->CallStaticIntMethod(nioAccessClass, | 
|  | getBaseArrayOffsetID, buffer); | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | class ByteArrayGetter { | 
|  | public: | 
|  | static void* Get(JNIEnv* _env, jbyteArray array, jboolean* is_copy) { | 
|  | return _env->GetByteArrayElements(array, is_copy); | 
|  | } | 
|  | }; | 
|  | class BooleanArrayGetter { | 
|  | public: | 
|  | static void* Get(JNIEnv* _env, jbooleanArray array, jboolean* is_copy) { | 
|  | return _env->GetBooleanArrayElements(array, is_copy); | 
|  | } | 
|  | }; | 
|  | class CharArrayGetter { | 
|  | public: | 
|  | static void* Get(JNIEnv* _env, jcharArray array, jboolean* is_copy) { | 
|  | return _env->GetCharArrayElements(array, is_copy); | 
|  | } | 
|  | }; | 
|  | class ShortArrayGetter { | 
|  | public: | 
|  | static void* Get(JNIEnv* _env, jshortArray array, jboolean* is_copy) { | 
|  | return _env->GetShortArrayElements(array, is_copy); | 
|  | } | 
|  | }; | 
|  | class IntArrayGetter { | 
|  | public: | 
|  | static void* Get(JNIEnv* _env, jintArray array, jboolean* is_copy) { | 
|  | return _env->GetIntArrayElements(array, is_copy); | 
|  | } | 
|  | }; | 
|  | class LongArrayGetter { | 
|  | public: | 
|  | static void* Get(JNIEnv* _env, jlongArray array, jboolean* is_copy) { | 
|  | return _env->GetLongArrayElements(array, is_copy); | 
|  | } | 
|  | }; | 
|  | class FloatArrayGetter { | 
|  | public: | 
|  | static void* Get(JNIEnv* _env, jfloatArray array, jboolean* is_copy) { | 
|  | return _env->GetFloatArrayElements(array, is_copy); | 
|  | } | 
|  | }; | 
|  | class DoubleArrayGetter { | 
|  | public: | 
|  | static void* Get(JNIEnv* _env, jdoubleArray array, jboolean* is_copy) { | 
|  | return _env->GetDoubleArrayElements(array, is_copy); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template<typename JTYPEARRAY, typename ARRAYGETTER> | 
|  | static void* | 
|  | getArrayPointer(JNIEnv *_env, JTYPEARRAY array, jboolean* is_copy) { | 
|  | return ARRAYGETTER::Get(_env, array, is_copy); | 
|  | } | 
|  |  | 
|  | class ByteArrayReleaser { | 
|  | public: | 
|  | static void Release(JNIEnv* _env, jbyteArray array, jbyte* data, jboolean commit) { | 
|  | _env->ReleaseByteArrayElements(array, data, commit ? 0 : JNI_ABORT); | 
|  | } | 
|  | }; | 
|  | class BooleanArrayReleaser { | 
|  | public: | 
|  | static void Release(JNIEnv* _env, jbooleanArray array, jboolean* data, jboolean commit) { | 
|  | _env->ReleaseBooleanArrayElements(array, data, commit ? 0 : JNI_ABORT); | 
|  | } | 
|  | }; | 
|  | class CharArrayReleaser { | 
|  | public: | 
|  | static void Release(JNIEnv* _env, jcharArray array, jchar* data, jboolean commit) { | 
|  | _env->ReleaseCharArrayElements(array, data, commit ? 0 : JNI_ABORT); | 
|  | } | 
|  | }; | 
|  | class ShortArrayReleaser { | 
|  | public: | 
|  | static void Release(JNIEnv* _env, jshortArray array, jshort* data, jboolean commit) { | 
|  | _env->ReleaseShortArrayElements(array, data, commit ? 0 : JNI_ABORT); | 
|  | } | 
|  | }; | 
|  | class IntArrayReleaser { | 
|  | public: | 
|  | static void Release(JNIEnv* _env, jintArray array, jint* data, jboolean commit) { | 
|  | _env->ReleaseIntArrayElements(array, data, commit ? 0 : JNI_ABORT); | 
|  | } | 
|  | }; | 
|  | class LongArrayReleaser { | 
|  | public: | 
|  | static void Release(JNIEnv* _env, jlongArray array, jlong* data, jboolean commit) { | 
|  | _env->ReleaseLongArrayElements(array, data, commit ? 0 : JNI_ABORT); | 
|  | } | 
|  | }; | 
|  | class FloatArrayReleaser { | 
|  | public: | 
|  | static void Release(JNIEnv* _env, jfloatArray array, jfloat* data, jboolean commit) { | 
|  | _env->ReleaseFloatArrayElements(array, data, commit ? 0 : JNI_ABORT); | 
|  | } | 
|  | }; | 
|  | class DoubleArrayReleaser { | 
|  | public: | 
|  | static void Release(JNIEnv* _env, jdoubleArray array, jdouble* data, jboolean commit) { | 
|  | _env->ReleaseDoubleArrayElements(array, data, commit ? 0 : JNI_ABORT); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template<typename JTYPEARRAY, typename NTYPEARRAY, typename ARRAYRELEASER> | 
|  | static void | 
|  | releaseArrayPointer(JNIEnv *_env, JTYPEARRAY array, NTYPEARRAY data, jboolean commit) { | 
|  | ARRAYRELEASER::Release(_env, array, data, commit); | 
|  | } | 
|  |  | 
|  | static void | 
|  | releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) | 
|  | { | 
|  | _env->ReleasePrimitiveArrayCritical(array, data, | 
|  | commit ? 0 : JNI_ABORT); | 
|  | } | 
|  |  | 
|  | static void * | 
|  | getDirectBufferPointer(JNIEnv *_env, jobject buffer) { | 
|  | char* buf = (char*) _env->GetDirectBufferAddress(buffer); | 
|  | if (buf) { | 
|  | jint position = _env->GetIntField(buffer, positionID); | 
|  | jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); | 
|  | buf += position << elementSizeShift; | 
|  | } else { | 
|  | jniThrowException(_env, "java/lang/IllegalArgumentException", | 
|  | "Must use a native order direct Buffer"); | 
|  | } | 
|  | return (void*) buf; | 
|  | } | 
|  |  | 
|  | // -------------------------------------------------------------------------- | 
|  |  | 
|  | /* | 
|  | * returns the number of values glGet returns for a given pname. | 
|  | * | 
|  | * The code below is written such that pnames requiring only one values | 
|  | * are the default (and are not explicitely tested for). This makes the | 
|  | * checking code much shorter/readable/efficient. | 
|  | * | 
|  | * This means that unknown pnames (e.g.: extensions) will default to 1. If | 
|  | * that unknown pname needs more than 1 value, then the validation check | 
|  | * is incomplete and the app may crash if it passed the wrong number params. | 
|  | */ | 
|  | static int getNeededCount(GLint pname) { | 
|  | int needed = 1; | 
|  | #ifdef GL_ES_VERSION_2_0 | 
|  | // GLES 2.x pnames | 
|  | switch (pname) { | 
|  | case GL_ALIASED_LINE_WIDTH_RANGE: | 
|  | case GL_ALIASED_POINT_SIZE_RANGE: | 
|  | needed = 2; | 
|  | break; | 
|  |  | 
|  | case GL_BLEND_COLOR: | 
|  | case GL_COLOR_CLEAR_VALUE: | 
|  | case GL_COLOR_WRITEMASK: | 
|  | case GL_SCISSOR_BOX: | 
|  | case GL_VIEWPORT: | 
|  | needed = 4; | 
|  | break; | 
|  |  | 
|  | case GL_COMPRESSED_TEXTURE_FORMATS: | 
|  | glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &needed); | 
|  | break; | 
|  |  | 
|  | case GL_SHADER_BINARY_FORMATS: | 
|  | glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &needed); | 
|  | break; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #ifdef GL_VERSION_ES_CM_1_1 | 
|  | // GLES 1.x pnames | 
|  | switch (pname) { | 
|  | case GL_ALIASED_LINE_WIDTH_RANGE: | 
|  | case GL_ALIASED_POINT_SIZE_RANGE: | 
|  | case GL_DEPTH_RANGE: | 
|  | case GL_SMOOTH_LINE_WIDTH_RANGE: | 
|  | case GL_SMOOTH_POINT_SIZE_RANGE: | 
|  | needed = 2; | 
|  | break; | 
|  |  | 
|  | case GL_CURRENT_NORMAL: | 
|  | case GL_POINT_DISTANCE_ATTENUATION: | 
|  | needed = 3; | 
|  | break; | 
|  |  | 
|  | case GL_COLOR_CLEAR_VALUE: | 
|  | case GL_COLOR_WRITEMASK: | 
|  | case GL_CURRENT_COLOR: | 
|  | case GL_CURRENT_TEXTURE_COORDS: | 
|  | case GL_FOG_COLOR: | 
|  | case GL_LIGHT_MODEL_AMBIENT: | 
|  | case GL_SCISSOR_BOX: | 
|  | case GL_VIEWPORT: | 
|  | needed = 4; | 
|  | break; | 
|  |  | 
|  | case GL_MODELVIEW_MATRIX: | 
|  | case GL_PROJECTION_MATRIX: | 
|  | case GL_TEXTURE_MATRIX: | 
|  | needed = 16; | 
|  | break; | 
|  |  | 
|  | case GL_COMPRESSED_TEXTURE_FORMATS: | 
|  | glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &needed); | 
|  | break; | 
|  | } | 
|  | #endif | 
|  | return needed; | 
|  | } | 
|  |  | 
|  | template <typename JTYPEARRAY, typename ARRAYGETTER, typename NTYPEARRAY, | 
|  | typename ARRAYRELEASER, typename CTYPE, void GET(GLenum, CTYPE*)> | 
|  | static void | 
|  | get | 
|  | (JNIEnv *_env, jobject _this, jint pname, JTYPEARRAY params_ref, jint offset) { | 
|  | jint _exception = 0; | 
|  | const char * _exceptionType; | 
|  | const char * _exceptionMessage; | 
|  | CTYPE *params_base = (CTYPE *) 0; | 
|  | jint _remaining; | 
|  | CTYPE *params = (CTYPE *) 0; | 
|  | int _needed = 0; | 
|  |  | 
|  | if (!params_ref) { | 
|  | _exception = 1; | 
|  | _exceptionType = "java/lang/IllegalArgumentException"; | 
|  | _exceptionMessage = "params == null"; | 
|  | goto exit; | 
|  | } | 
|  | if (offset < 0) { | 
|  | _exception = 1; | 
|  | _exceptionType = "java/lang/IllegalArgumentException"; | 
|  | _exceptionMessage = "offset < 0"; | 
|  | goto exit; | 
|  | } | 
|  | _remaining = _env->GetArrayLength(params_ref) - offset; | 
|  | _needed = getNeededCount(pname); | 
|  | // if we didn't find this pname, we just assume the user passed | 
|  | // an array of the right size -- this might happen with extensions | 
|  | // or if we forget an enum here. | 
|  | if (_remaining < _needed) { | 
|  | _exception = 1; | 
|  | _exceptionType = "java/lang/IllegalArgumentException"; | 
|  | _exceptionMessage = "length - offset < needed"; | 
|  | goto exit; | 
|  | } | 
|  | params_base = (CTYPE *) getArrayPointer<JTYPEARRAY, ARRAYGETTER>( | 
|  | _env, params_ref, (jboolean *)0); | 
|  | params = params_base + offset; | 
|  |  | 
|  | GET( | 
|  | (GLenum)pname, | 
|  | (CTYPE *)params | 
|  | ); | 
|  |  | 
|  | exit: | 
|  | if (params_base) { | 
|  | releaseArrayPointer<JTYPEARRAY, NTYPEARRAY, ARRAYRELEASER>( | 
|  | _env, params_ref, params_base, !_exception); | 
|  | } | 
|  | if (_exception) { | 
|  | jniThrowException(_env, _exceptionType, _exceptionMessage); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | template <typename CTYPE, typename JTYPEARRAY, typename ARRAYGETTER, typename NTYPEARRAY, | 
|  | typename ARRAYRELEASER, void GET(GLenum, CTYPE*)> | 
|  | static void | 
|  | getarray | 
|  | (JNIEnv *_env, jobject _this, jint pname, jobject params_buf) { | 
|  | jint _exception = 0; | 
|  | const char * _exceptionType; | 
|  | const char * _exceptionMessage; | 
|  | JTYPEARRAY _array = (JTYPEARRAY) 0; | 
|  | jint _bufferOffset = (jint) 0; | 
|  | jint _remaining; | 
|  | CTYPE *params = (CTYPE *) 0; | 
|  | int _needed = 0; | 
|  |  | 
|  | params = (CTYPE *)getPointer(_env, params_buf, (jarray*)&_array, &_remaining, &_bufferOffset); | 
|  | _remaining /= sizeof(CTYPE);    // convert from bytes to item count | 
|  | _needed = getNeededCount(pname); | 
|  | // if we didn't find this pname, we just assume the user passed | 
|  | // an array of the right size -- this might happen with extensions | 
|  | // or if we forget an enum here. | 
|  | if (_needed>0 && _remaining < _needed) { | 
|  | _exception = 1; | 
|  | _exceptionType = "java/lang/IllegalArgumentException"; | 
|  | _exceptionMessage = "remaining() < needed"; | 
|  | goto exit; | 
|  | } | 
|  | if (params == NULL) { | 
|  | char * _paramsBase = (char *) getArrayPointer<JTYPEARRAY, ARRAYGETTER>( | 
|  | _env, _array, (jboolean *) 0); | 
|  | params = (CTYPE *) (_paramsBase + _bufferOffset); | 
|  | } | 
|  | GET( | 
|  | (GLenum)pname, | 
|  | (CTYPE *)params | 
|  | ); | 
|  |  | 
|  | exit: | 
|  | if (_array) { | 
|  | releaseArrayPointer<JTYPEARRAY, NTYPEARRAY, ARRAYRELEASER>( | 
|  | _env, _array, (NTYPEARRAY)params, _exception ? JNI_FALSE : JNI_TRUE); | 
|  | } | 
|  | if (_exception) { | 
|  | jniThrowException(_env, _exceptionType, _exceptionMessage); | 
|  | } | 
|  | } | 
|  |  | 
|  | // -------------------------------------------------------------------------- |