summaryrefslogtreecommitdiffstats
path: root/core/jni/android/opengl
diff options
context:
space:
mode:
Diffstat (limited to 'core/jni/android/opengl')
-rw-r--r--core/jni/android/opengl/poly.h51
-rw-r--r--core/jni/android/opengl/poly_clip.cpp155
-rw-r--r--core/jni/android/opengl/util.cpp730
3 files changed, 936 insertions, 0 deletions
diff --git a/core/jni/android/opengl/poly.h b/core/jni/android/opengl/poly.h
new file mode 100644
index 0000000..85b44e3
--- /dev/null
+++ b/core/jni/android/opengl/poly.h
@@ -0,0 +1,51 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+/* Based on the public domain code:
+ * Generic Convex Polygon Scan Conversion and Clipping
+ * by Paul Heckbert
+ * from "Graphics Gems", Academic Press, 1990
+ */
+
+
+#ifndef POLY_HDR
+#define POLY_HDR
+
+namespace android {
+
+#define POLY_NMAX 10 /* max #sides to a polygon; change if needed */
+/* note that poly_clip, given an n-gon as input, might output an (n+6)gon */
+/* POLY_NMAX=10 is thus appropriate if input polygons are triangles or quads */
+
+typedef struct { /* A POLYGON VERTEX */
+ float sx, sy, sz, sw; /* screen space position (sometimes homo.) */
+} Poly_vert;
+
+typedef struct { /* A POLYGON */
+ int n; /* number of sides */
+ Poly_vert vert[POLY_NMAX]; /* vertices */
+} Poly;
+
+#define POLY_CLIP_OUT 0 /* polygon entirely outside box */
+#define POLY_CLIP_PARTIAL 1 /* polygon partially inside */
+#define POLY_CLIP_IN 2 /* polygon entirely inside box */
+
+int poly_clip_to_frustum(Poly *p1);
+
+} // namespace android
+
+#endif
diff --git a/core/jni/android/opengl/poly_clip.cpp b/core/jni/android/opengl/poly_clip.cpp
new file mode 100644
index 0000000..04e4b17
--- /dev/null
+++ b/core/jni/android/opengl/poly_clip.cpp
@@ -0,0 +1,155 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+/*
+ * Generic Convex Polygon Scan Conversion and Clipping
+ * by Paul Heckbert
+ * from "Graphics Gems", Academic Press, 1990
+ */
+
+/* Based on the public domain code:
+ * poly_clip.c: homogeneous 3-D convex polygon clipper
+ *
+ * Paul Heckbert 1985, Dec 1989
+ */
+
+#include "poly.h"
+#include "string.h"
+
+#define LOG_TAG "StreetView"
+#include <utils/Log.h>
+
+namespace android {
+
+#define SWAP(a, b, temp) {temp = a; a = b; b = temp;}
+#define COORD(vert, i) ((float *)(vert))[i]
+
+#define CLIP_AND_SWAP(elem, sign, k, p, q, r) { \
+ poly_clip_to_halfspace(p, q, &v->elem-(float *)v, sign, sign*k); \
+ if (q->n==0) {p1->n = 0; return POLY_CLIP_OUT;} \
+ SWAP(p, q, r); \
+}
+
+/*
+ * poly_clip_to_halfspace: clip convex polygon p against a plane,
+ * copying the portion satisfying sign*s[index] < k*sw into q,
+ * where s is a Poly_vert* cast as a float*.
+ * index is an index into the array of floats at each vertex, such that
+ * s[index] is sx, sy, or sz (screen space x, y, or z).
+ * Thus, to clip against xmin, use
+ * poly_clip_to_halfspace(p, q, XINDEX, -1., -xmin);
+ * and to clip against xmax, use
+ * poly_clip_to_halfspace(p, q, XINDEX, 1., xmax);
+ */
+
+void poly_clip_to_halfspace(Poly* p, Poly* q, int index, float sign, float k)
+{
+ unsigned long m;
+ float *up, *vp, *wp;
+ Poly_vert *v;
+ int i;
+ Poly_vert *u;
+ float t, tu, tv;
+
+ q->n = 0;
+
+ /* start with u=vert[n-1], v=vert[0] */
+ u = &p->vert[p->n-1];
+ tu = sign*COORD(u, index) - u->sw*k;
+ for (v= &p->vert[0], i=p->n; i>0; i--, u=v, tu=tv, v++) {
+ /* on old polygon (p), u is previous vertex, v is current vertex */
+ /* tv is negative if vertex v is in */
+ tv = sign*COORD(v, index) - v->sw*k;
+ if ((tu <= 0.0f) ^ (tv <= 0.0f)) {
+ /* edge crosses plane; add intersection point to q */
+ t = tu/(tu-tv);
+ up = (float *)u;
+ vp = (float *)v;
+ wp = (float *)&q->vert[q->n].sx;
+ for(int i = 0; i < 4; i++, wp++, up++, vp++) {
+ *wp = *up+t*(*vp-*up);
+ }
+ q->n++;
+ }
+ if (tv<=0.0f) /* vertex v is in, copy it to q */
+ q->vert[q->n++] = *v;
+ }
+}
+
+/*
+ * poly_clip_to_frustum: Clip the convex polygon p1 to the screen space frustum
+ * using the homogeneous screen coordinates (sx, sy, sz, sw) of each vertex,
+ * testing if v->sx/v->sw > box->x0 and v->sx/v->sw < box->x1,
+ * and similar tests for y and z, for each vertex v of the polygon.
+ * If polygon is entirely inside box, then POLY_CLIP_IN is returned.
+ * If polygon is entirely outside box, then POLY_CLIP_OUT is returned.
+ * Otherwise, if the polygon is cut by the box, p1 is modified and
+ * POLY_CLIP_PARTIAL is returned.
+ *
+ * Given an n-gon as input, clipping against 6 planes could generate an
+ * (n+6)gon, so POLY_NMAX in poly.h must be big enough to allow that.
+ */
+
+int poly_clip_to_frustum(Poly *p1)
+{
+ int x0out = 0, x1out = 0, y0out = 0, y1out = 0, z0out = 0, z1out = 0;
+ int i;
+ Poly_vert *v;
+ Poly p2, *p, *q, *r;
+
+ /* count vertices "outside" with respect to each of the six planes */
+ for (v=p1->vert, i=p1->n; i>0; i--, v++) {
+ float sw = v->sw;
+ if (v->sx < -sw) x0out++; /* out on left */
+ if (v->sx > sw) x1out++; /* out on right */
+ if (v->sy < -sw) y0out++; /* out on top */
+ if (v->sy > sw) y1out++; /* out on bottom */
+ if (v->sz < -sw) z0out++; /* out on near */
+ if (v->sz > sw) z1out++; /* out on far */
+ }
+
+ /* check if all vertices inside */
+ if (x0out+x1out+y0out+y1out+z0out+z1out == 0)
+ return POLY_CLIP_IN;
+
+ /* check if all vertices are "outside" any of the six planes */
+ if (x0out==p1->n || x1out==p1->n || y0out==p1->n ||
+ y1out==p1->n || z0out==p1->n || z1out==p1->n) {
+ p1->n = 0;
+ return POLY_CLIP_OUT;
+ }
+
+ /*
+ * now clip against each of the planes that might cut the polygon,
+ * at each step toggling between polygons p1 and p2
+ */
+ p = p1;
+ q = &p2;
+ if (x0out) CLIP_AND_SWAP(sx, -1.0f, -1.0f, p, q, r);
+ if (x1out) CLIP_AND_SWAP(sx, 1.0f, 1.0f, p, q, r);
+ if (y0out) CLIP_AND_SWAP(sy, -1.0f, -1.0f, p, q, r);
+ if (y1out) CLIP_AND_SWAP(sy, 1.0f, 1.0f, p, q, r);
+ if (z0out) CLIP_AND_SWAP(sz, -1.0f, -1.0f, p, q, r);
+ if (z1out) CLIP_AND_SWAP(sz, 1.0f, 1.0f, p, q, r);
+
+ /* if result ended up in p2 then copy it to p1 */
+ if (p==&p2)
+ memcpy(p1, &p2, sizeof(Poly)-(POLY_NMAX-p2.n)*sizeof(Poly_vert));
+ return POLY_CLIP_PARTIAL;
+}
+
+} // namespace android
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
new file mode 100644
index 0000000..5cd2ceb
--- /dev/null
+++ b/core/jni/android/opengl/util.cpp
@@ -0,0 +1,730 @@
+/**
+ ** Copyright 2007, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <nativehelper/jni.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <dlfcn.h>
+
+#include <GLES/gl.h>
+
+#include <graphics/SkBitmap.h>
+
+#include "android_runtime/AndroidRuntime.h"
+
+#undef LOG_TAG
+#define LOG_TAG "OpenGLUtil"
+#include <utils/Log.h>
+#include "utils/misc.h"
+
+#include "poly.h"
+
+namespace android {
+
+static jclass gIAEClass;
+static jclass gUOEClass;
+
+static inline
+void mx4transform(float x, float y, float z, float w, const float* pM, float* pDest) {
+ pDest[0] = pM[0 + 4 * 0] * x + pM[0 + 4 * 1] * y + pM[0 + 4 * 2] * z + pM[0 + 4 * 3] * w;
+ pDest[1] = pM[1 + 4 * 0] * x + pM[1 + 4 * 1] * y + pM[1 + 4 * 2] * z + pM[1 + 4 * 3] * w;
+ pDest[2] = pM[2 + 4 * 0] * x + pM[2 + 4 * 1] * y + pM[2 + 4 * 2] * z + pM[2 + 4 * 3] * w;
+ pDest[3] = pM[3 + 4 * 0] * x + pM[3 + 4 * 1] * y + pM[3 + 4 * 2] * z + pM[3 + 4 * 3] * w;
+}
+
+class MallocHelper {
+public:
+ MallocHelper() {
+ mData = 0;
+ }
+
+ ~MallocHelper() {
+ if (mData != 0) {
+ free(mData);
+ }
+ }
+
+ void* alloc(size_t size) {
+ mData = malloc(size);
+ return mData;
+ }
+
+private:
+ void* mData;
+};
+
+#if 0
+static
+void
+print_poly(const char* label, Poly* pPoly) {
+ LOGI("%s: %d verts", label, pPoly->n);
+ for(int i = 0; i < pPoly->n; i++) {
+ Poly_vert* pV = & pPoly->vert[i];
+ LOGI("[%d] %g, %g, %g %g", i, pV->sx, pV->sy, pV->sz, pV->sw);
+ }
+}
+#endif
+
+static
+int visibilityTest(float* pWS, float* pPositions, int positionsLength,
+ unsigned short* pIndices, int indexCount) {
+ MallocHelper mallocHelper;
+ int result = POLY_CLIP_OUT;
+ float* pTransformed = 0;
+ int transformedIndexCount = 0;
+
+ if ( indexCount < 3 ) {
+ return POLY_CLIP_OUT;
+ }
+
+ // Find out how many vertices we need to transform
+ // We transform every vertex between the min and max indices, inclusive.
+ // This is OK for the data sets we expect to use with this function, but
+ // for other loads it might be better to use a more sophisticated vertex
+ // cache of some sort.
+
+ int minIndex = 65536;
+ int maxIndex = -1;
+ for(int i = 0; i < indexCount; i++) {
+ int index = pIndices[i];
+ if ( index < minIndex ) {
+ minIndex = index;
+ }
+ if ( index > maxIndex ) {
+ maxIndex = index;
+ }
+ }
+
+ if ( maxIndex * 3 > positionsLength) {
+ return -1;
+ }
+
+ transformedIndexCount = maxIndex - minIndex + 1;
+ pTransformed = (float*) mallocHelper.alloc(transformedIndexCount * 4 * sizeof(float));
+
+ if (pTransformed == 0 ) {
+ return -2;
+ }
+
+ // Transform the vertices
+ {
+ const float* pSrc = pPositions + 3 * minIndex;
+ float* pDst = pTransformed;
+ for (int i = 0; i < transformedIndexCount; i++, pSrc += 3, pDst += 4) {
+ mx4transform(pSrc[0], pSrc[1], pSrc[2], 1.0f, pWS, pDst);
+ }
+ }
+
+ // Clip the triangles
+
+ Poly poly;
+ float* pDest = & poly.vert[0].sx;
+ for (int i = 0; i < indexCount; i += 3) {
+ poly.n = 3;
+ memcpy(pDest , pTransformed + 4 * (pIndices[i ] - minIndex), 4 * sizeof(float));
+ memcpy(pDest + 4, pTransformed + 4 * (pIndices[i + 1] - minIndex), 4 * sizeof(float));
+ memcpy(pDest + 8, pTransformed + 4 * (pIndices[i + 2] - minIndex), 4 * sizeof(float));
+ result = poly_clip_to_frustum(&poly);
+ if ( result != POLY_CLIP_OUT) {
+ return result;
+ }
+ }
+
+ return result;
+}
+
+template<class JArray, class T>
+class ArrayHelper {
+public:
+ ArrayHelper(JNIEnv* env, JArray ref, jint offset, jint minSize) {
+ mEnv = env;
+ mRef = ref;
+ mOffset = offset;
+ mMinSize = minSize;
+ mBase = 0;
+ mReleaseParam = JNI_ABORT;
+ }
+
+ ~ArrayHelper() {
+ if (mBase) {
+ mEnv->ReleasePrimitiveArrayCritical(mRef, mBase, mReleaseParam);
+ }
+ }
+
+ // We seperate the bounds check from the initialization because we want to
+ // be able to bounds-check multiple arrays, and we can't throw an exception
+ // after we've called GetPrimitiveArrayCritical.
+
+ // Return true if the bounds check succeeded
+ // Else instruct the runtime to throw an exception
+
+ bool check() {
+ if ( ! mRef) {
+ mEnv->ThrowNew(gIAEClass, "array == null");
+ return false;
+ }
+ if ( mOffset < 0) {
+ mEnv->ThrowNew(gIAEClass, "offset < 0");
+ return false;
+ }
+ mLength = mEnv->GetArrayLength(mRef) - mOffset;
+ if (mLength < mMinSize ) {
+ mEnv->ThrowNew(gIAEClass, "length - offset < n");
+ return false;
+ }
+ return true;
+ }
+
+ // Bind the array.
+
+ void bind() {
+ mBase = (T*) mEnv->GetPrimitiveArrayCritical(mRef, (jboolean *) 0);
+ mData = mBase + mOffset;
+ }
+
+ void commitChanges() {
+ mReleaseParam = 0;
+ }
+
+ T* mData;
+ int mLength;
+
+private:
+ T* mBase;
+ JNIEnv* mEnv;
+ JArray mRef;
+ jint mOffset;
+ jint mMinSize;
+ int mReleaseParam;
+};
+
+typedef ArrayHelper<jfloatArray, float> FloatArrayHelper;
+typedef ArrayHelper<jcharArray, unsigned short> UnsignedShortArrayHelper;
+typedef ArrayHelper<jintArray, int> IntArrayHelper;
+typedef ArrayHelper<jbyteArray, unsigned char> ByteArrayHelper;
+
+inline float distance2(float x, float y, float z) {
+ return x * x + y * y + z * z;
+}
+
+inline float distance(float x, float y, float z) {
+ return sqrtf(distance2(x, y, z));
+}
+
+static
+void util_computeBoundingSphere(JNIEnv *env, jclass clazz,
+ jfloatArray positions_ref, jint positionsOffset, jint positionsCount,
+ jfloatArray sphere_ref, jint sphereOffset) {
+ FloatArrayHelper positions(env, positions_ref, positionsOffset, 0);
+ FloatArrayHelper sphere(env, sphere_ref, sphereOffset, 4);
+
+ bool checkOK = positions.check() && sphere.check();
+ if (! checkOK) {
+ return;
+ }
+
+ positions.bind();
+ sphere.bind();
+
+ if ( positionsCount < 1 ) {
+ env->ThrowNew(gIAEClass, "positionsCount < 1");
+ return;
+ }
+
+ const float* pSrc = positions.mData;
+
+ // find bounding box
+ float x0 = *pSrc++;
+ float x1 = x0;
+ float y0 = *pSrc++;
+ float y1 = y0;
+ float z0 = *pSrc++;
+ float z1 = z0;
+
+ for(int i = 1; i < positionsCount; i++) {
+ {
+ float x = *pSrc++;
+ if (x < x0) {
+ x0 = x;
+ }
+ else if (x > x1) {
+ x1 = x;
+ }
+ }
+ {
+ float y = *pSrc++;
+ if (y < y0) {
+ y0 = y;
+ }
+ else if (y > y1) {
+ y1 = y;
+ }
+ }
+ {
+ float z = *pSrc++;
+ if (z < z0) {
+ z0 = z;
+ }
+ else if (z > z1) {
+ z1 = z;
+ }
+ }
+ }
+
+ // Because we know our input meshes fit pretty well into bounding boxes,
+ // just take the diagonal of the box as defining our sphere.
+ float* pSphere = sphere.mData;
+ float dx = x1 - x0;
+ float dy = y1 - y0;
+ float dz = z1 - z0;
+ *pSphere++ = x0 + dx * 0.5f;
+ *pSphere++ = y0 + dy * 0.5f;
+ *pSphere++ = z0 + dz * 0.5f;
+ *pSphere++ = distance(dx, dy, dz) * 0.5f;
+
+ sphere.commitChanges();
+}
+
+static void normalizePlane(float* p) {
+ float rdist = 1.0f / distance(p[0], p[1], p[2]);
+ for(int i = 0; i < 4; i++) {
+ p[i] *= rdist;
+ }
+}
+
+static inline float dot3(float x0, float y0, float z0, float x1, float y1, float z1) {
+ return x0 * x1 + y0 * y1 + z0 * z1;
+}
+
+static inline float signedDistance(const float* pPlane, float x, float y, float z) {
+ return dot3(pPlane[0], pPlane[1], pPlane[2], x, y, z) + pPlane[3];
+}
+
+// Return true if the sphere intersects or is inside the frustum
+
+static bool sphereHitsFrustum(const float* pFrustum, const float* pSphere) {
+ float x = pSphere[0];
+ float y = pSphere[1];
+ float z = pSphere[2];
+ float negRadius = -pSphere[3];
+ for (int i = 0; i < 6; i++, pFrustum += 4) {
+ if (signedDistance(pFrustum, x, y, z) <= negRadius) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static void computeFrustum(const float* m, float* f) {
+ float m3 = m[3];
+ float m7 = m[7];
+ float m11 = m[11];
+ float m15 = m[15];
+ // right
+ f[0] = m3 - m[0];
+ f[1] = m7 - m[4];
+ f[2] = m11 - m[8];
+ f[3] = m15 - m[12];
+ normalizePlane(f);
+ f+= 4;
+
+ // left
+ f[0] = m3 + m[0];
+ f[1] = m7 + m[4];
+ f[2] = m11 + m[8];
+ f[3] = m15 + m[12];
+ normalizePlane(f);
+ f+= 4;
+
+ // top
+ f[0] = m3 - m[1];
+ f[1] = m7 - m[5];
+ f[2] = m11 - m[9];
+ f[3] = m15 - m[13];
+ normalizePlane(f);
+ f+= 4;
+
+ // bottom
+ f[0] = m3 + m[1];
+ f[1] = m7 + m[5];
+ f[2] = m11 + m[9];
+ f[3] = m15 + m[13];
+ normalizePlane(f);
+ f+= 4;
+
+ // far
+ f[0] = m3 - m[2];
+ f[1] = m7 - m[6];
+ f[2] = m11 - m[10];
+ f[3] = m15 - m[14];
+ normalizePlane(f);
+ f+= 4;
+
+ // near
+ f[0] = m3 + m[2];
+ f[1] = m7 + m[6];
+ f[2] = m11 + m[10];
+ f[3] = m15 + m[14];
+ normalizePlane(f);
+}
+
+static
+int util_frustumCullSpheres(JNIEnv *env, jclass clazz,
+ jfloatArray mvp_ref, jint mvpOffset,
+ jfloatArray spheres_ref, jint spheresOffset, jint spheresCount,
+ jintArray results_ref, jint resultsOffset, jint resultsCapacity) {
+ float frustum[6*4];
+ int outputCount;
+ int* pResults;
+ float* pSphere;
+ FloatArrayHelper mvp(env, mvp_ref, mvpOffset, 16);
+ FloatArrayHelper spheres(env, spheres_ref, spheresOffset, spheresCount * 4);
+ IntArrayHelper results(env, results_ref, resultsOffset, resultsCapacity);
+
+ bool initializedOK = mvp.check() && spheres.check() && results.check();
+ if (! initializedOK) {
+ return -1;
+ }
+
+ mvp.bind();
+ spheres.bind();
+ results.bind();
+
+ computeFrustum(mvp.mData, frustum);
+
+ // Cull the spheres
+
+ pSphere = spheres.mData;
+ pResults = results.mData;
+ outputCount = 0;
+ for(int i = 0; i < spheresCount; i++, pSphere += 4) {
+ if (sphereHitsFrustum(frustum, pSphere)) {
+ if (outputCount < resultsCapacity) {
+ *pResults++ = i;
+ }
+ outputCount++;
+ }
+ }
+ results.commitChanges();
+ return outputCount;
+}
+
+/*
+ public native int visibilityTest(float[] ws, int wsOffset,
+ float[] positions, int positionsOffset,
+ char[] indices, int indicesOffset, int indexCount);
+ */
+
+static
+int util_visibilityTest(JNIEnv *env, jclass clazz,
+ jfloatArray ws_ref, jint wsOffset,
+ jfloatArray positions_ref, jint positionsOffset,
+ jcharArray indices_ref, jint indicesOffset, jint indexCount) {
+
+ FloatArrayHelper ws(env, ws_ref, wsOffset, 16);
+ FloatArrayHelper positions(env, positions_ref, positionsOffset, 0);
+ UnsignedShortArrayHelper indices(env, indices_ref, indicesOffset, 0);
+
+ bool checkOK = ws.check() && positions.check() && indices.check();
+ if (! checkOK) {
+ // Return value will be ignored, because an exception has been thrown.
+ return -1;
+ }
+
+ if (indices.mLength < indexCount) {
+ env->ThrowNew(gIAEClass, "length < offset + indexCount");
+ // Return value will be ignored, because an exception has been thrown.
+ return -1;
+ }
+
+ ws.bind();
+ positions.bind();
+ indices.bind();
+
+ return visibilityTest(ws.mData,
+ positions.mData, positions.mLength,
+ indices.mData, indexCount);
+}
+
+#define I(_i, _j) ((_j)+ 4*(_i))
+
+static
+void multiplyMM(float* r, const float* lhs, const float* rhs)
+{
+ for (int i=0 ; i<4 ; i++) {
+ register const float rhs_i0 = rhs[ I(i,0) ];
+ register float ri0 = lhs[ I(0,0) ] * rhs_i0;
+ register float ri1 = lhs[ I(0,1) ] * rhs_i0;
+ register float ri2 = lhs[ I(0,2) ] * rhs_i0;
+ register float ri3 = lhs[ I(0,3) ] * rhs_i0;
+ for (int j=1 ; j<4 ; j++) {
+ register const float rhs_ij = rhs[ I(i,j) ];
+ ri0 += lhs[ I(j,0) ] * rhs_ij;
+ ri1 += lhs[ I(j,1) ] * rhs_ij;
+ ri2 += lhs[ I(j,2) ] * rhs_ij;
+ ri3 += lhs[ I(j,3) ] * rhs_ij;
+ }
+ r[ I(i,0) ] = ri0;
+ r[ I(i,1) ] = ri1;
+ r[ I(i,2) ] = ri2;
+ r[ I(i,3) ] = ri3;
+ }
+}
+
+static
+void util_multiplyMM(JNIEnv *env, jclass clazz,
+ jfloatArray result_ref, jint resultOffset,
+ jfloatArray lhs_ref, jint lhsOffset,
+ jfloatArray rhs_ref, jint rhsOffset) {
+
+ FloatArrayHelper resultMat(env, result_ref, resultOffset, 16);
+ FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16);
+ FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 16);
+
+ bool checkOK = resultMat.check() && lhs.check() && rhs.check();
+
+ if ( !checkOK ) {
+ return;
+ }
+
+ resultMat.bind();
+ lhs.bind();
+ rhs.bind();
+
+ multiplyMM(resultMat.mData, lhs.mData, rhs.mData);
+
+ resultMat.commitChanges();
+}
+
+static
+void multiplyMV(float* r, const float* lhs, const float* rhs)
+{
+ mx4transform(rhs[0], rhs[1], rhs[2], rhs[3], lhs, r);
+}
+
+static
+void util_multiplyMV(JNIEnv *env, jclass clazz,
+ jfloatArray result_ref, jint resultOffset,
+ jfloatArray lhs_ref, jint lhsOffset,
+ jfloatArray rhs_ref, jint rhsOffset) {
+
+ FloatArrayHelper resultV(env, result_ref, resultOffset, 4);
+ FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16);
+ FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 4);
+
+ bool checkOK = resultV.check() && lhs.check() && rhs.check();
+
+ if ( !checkOK ) {
+ return;
+ }
+
+ resultV.bind();
+ lhs.bind();
+ rhs.bind();
+
+ multiplyMV(resultV.mData, lhs.mData, rhs.mData);
+
+ resultV.commitChanges();
+}
+
+// ---------------------------------------------------------------------------
+
+static jfieldID nativeBitmapID = 0;
+
+void nativeUtilsClassInit(JNIEnv *env, jclass clazz)
+{
+ jclass bitmapClass = env->FindClass("android/graphics/Bitmap");
+ nativeBitmapID = env->GetFieldID(bitmapClass, "mNativeBitmap", "I");
+}
+
+static int checkFormat(SkBitmap::Config config, int format, int type)
+{
+ switch(config) {
+ case SkBitmap::kIndex8_Config:
+ if (format == GL_PALETTE8_RGBA8_OES)
+ return 0;
+ case SkBitmap::kARGB_8888_Config:
+ case SkBitmap::kA8_Config:
+ if (type == GL_UNSIGNED_BYTE)
+ return 0;
+ case SkBitmap::kARGB_4444_Config:
+ case SkBitmap::kRGB_565_Config:
+ switch (type) {
+ case GL_UNSIGNED_SHORT_4_4_4_4:
+ case GL_UNSIGNED_SHORT_5_6_5:
+ case GL_UNSIGNED_SHORT_5_5_5_1:
+ return 0;
+ case GL_UNSIGNED_BYTE:
+ if (format == GL_LUMINANCE_ALPHA)
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+ return -1;
+}
+
+static int getInternalFormat(SkBitmap::Config config)
+{
+ switch(config) {
+ case SkBitmap::kA8_Config:
+ return GL_ALPHA;
+ case SkBitmap::kARGB_4444_Config:
+ return GL_RGBA;
+ case SkBitmap::kARGB_8888_Config:
+ return GL_RGBA;
+ case SkBitmap::kIndex8_Config:
+ return GL_PALETTE8_RGBA8_OES;
+ case SkBitmap::kRGB_565_Config:
+ return GL_RGB;
+ default:
+ return -1;
+ }
+}
+
+static jint util_texImage2D(JNIEnv *env, jclass clazz,
+ jint target, jint level, jint internalformat,
+ jobject jbitmap, jint type, jint border)
+{
+ SkBitmap const * nativeBitmap =
+ (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID);
+ const SkBitmap& bitmap(*nativeBitmap);
+ SkBitmap::Config config = bitmap.getConfig();
+ if (internalformat < 0) {
+ internalformat = getInternalFormat(config);
+ }
+ int err = checkFormat(config, internalformat, type);
+ if (err)
+ return err;
+ bitmap.lockPixels();
+ const int w = bitmap.width();
+ const int h = bitmap.height();
+ const void* p = bitmap.getPixels();
+ if (internalformat == GL_PALETTE8_RGBA8_OES) {
+ if (sizeof(SkPMColor) != sizeof(uint32_t)) {
+ err = -1;
+ goto error;
+ }
+ const size_t size = bitmap.getSize();
+ const size_t palette_size = 256*sizeof(SkPMColor);
+ void* const data = malloc(size + palette_size);
+ if (data) {
+ void* const pixels = (char*)data + palette_size;
+ SkColorTable* ctable = bitmap.getColorTable();
+ memcpy(data, ctable->lockColors(), ctable->count() * sizeof(SkPMColor));
+ memcpy(pixels, p, size);
+ ctable->unlockColors(false);
+ glCompressedTexImage2D(target, level, internalformat, w, h, border, 0, data);
+ free(data);
+ } else {
+ err = -1;
+ }
+ } else {
+ glTexImage2D(target, level, internalformat, w, h, border, internalformat, type, p);
+ }
+error:
+ bitmap.unlockPixels();
+ return err;
+}
+
+static jint util_texSubImage2D(JNIEnv *env, jclass clazz,
+ jint target, jint level, jint xoffset, jint yoffset,
+ jobject jbitmap, jint format, jint type)
+{
+ SkBitmap const * nativeBitmap =
+ (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID);
+ const SkBitmap& bitmap(*nativeBitmap);
+ SkBitmap::Config config = bitmap.getConfig();
+ if (format < 0) {
+ format = getInternalFormat(config);
+ if (format == GL_PALETTE8_RGBA8_OES)
+ return -1; // glCompressedTexSubImage2D() not supported
+ }
+ int err = checkFormat(config, format, type);
+ if (err)
+ return err;
+ bitmap.lockPixels();
+ const int w = bitmap.width();
+ const int h = bitmap.height();
+ const void* p = bitmap.getPixels();
+ glTexSubImage2D(target, level, xoffset, yoffset, w, h, format, type, p);
+ bitmap.unlockPixels();
+ return 0;
+}
+
+/*
+ * JNI registration
+ */
+
+static void
+lookupClasses(JNIEnv* env) {
+ gIAEClass = (jclass) env->NewGlobalRef(
+ env->FindClass("java/lang/IllegalArgumentException"));
+ gUOEClass = (jclass) env->NewGlobalRef(
+ env->FindClass("java/lang/UnsupportedOperationException"));
+}
+
+static JNINativeMethod gMatrixMethods[] = {
+ { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM },
+ { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV },
+};
+
+static JNINativeMethod gVisiblityMethods[] = {
+ { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere },
+ { "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres },
+ { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest },
+};
+
+static JNINativeMethod gUtilsMethods[] = {
+ {"nativeClassInit", "()V", (void*)nativeUtilsClassInit },
+ { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D },
+ { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D },
+};
+
+typedef struct _ClassRegistrationInfo {
+ const char* classPath;
+ JNINativeMethod* methods;
+ size_t methodCount;
+} ClassRegistrationInfo;
+
+static ClassRegistrationInfo gClasses[] = {
+ {"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)},
+ {"android/opengl/Visibility", gVisiblityMethods, NELEM(gVisiblityMethods)},
+ {"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)},
+};
+
+int register_android_opengl_classes(JNIEnv* env)
+{
+ lookupClasses(env);
+ int result = 0;
+ for (int i = 0; i < NELEM(gClasses); i++) {
+ ClassRegistrationInfo* cri = &gClasses[i];
+ result = AndroidRuntime::registerNativeMethods(env,
+ cri->classPath, cri->methods, cri->methodCount);
+ if (result < 0) {
+ LOGE("Failed to register %s: %d", cri->classPath, result);
+ break;
+ }
+ }
+ return result;
+}
+
+} // namespace android
+