#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_3_0 // GLES 3.x pnames switch (pname) { case GL_MAX_VIEWPORT_DIMS: needed = 2; break; case GL_PROGRAM_BINARY_FORMATS: glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &needed); break; } #endif #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); } } // --------------------------------------------------------------------------