#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);
}
}
// --------------------------------------------------------------------------