blob: 589b255f25b231a870aa6be88bac7e08d25f5ce5 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/**
2 ** Copyright 2007, The Android Open Source Project
3 **
Jack Palevich106006c2009-08-31 17:42:50 -07004 ** Licensed under the Apache License, Version 2.0 (the "License");
5 ** you may not use this file except in compliance with the License.
6 ** You may obtain a copy of the License at
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007 **
Jack Palevich106006c2009-08-31 17:42:50 -07008 ** http://www.apache.org/licenses/LICENSE-2.0
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08009 **
Jack Palevich106006c2009-08-31 17:42:50 -070010 ** Unless required by applicable law or agreed to in writing, software
11 ** distributed under the License is distributed on an "AS IS" BASIS,
12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 ** See the License for the specific language governing permissions and
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080014 ** limitations under the License.
15 */
16
17#include <nativehelper/jni.h>
18#include <math.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <assert.h>
23#include <dlfcn.h>
24
25#include <GLES/gl.h>
Jack Palevicha6276fd2009-12-28 19:31:43 +080026#include <ETC1/etc1.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027
28#include <core/SkBitmap.h>
29
30#include "android_runtime/AndroidRuntime.h"
31
32#undef LOG_TAG
33#define LOG_TAG "OpenGLUtil"
34#include <utils/Log.h>
35#include "utils/misc.h"
36
37#include "poly.h"
38
39namespace android {
40
41static jclass gIAEClass;
42static jclass gUOEClass;
Jack Palevicha6276fd2009-12-28 19:31:43 +080043static jclass gAIOOBEClass;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044
45static inline
46void mx4transform(float x, float y, float z, float w, const float* pM, float* pDest) {
47 pDest[0] = pM[0 + 4 * 0] * x + pM[0 + 4 * 1] * y + pM[0 + 4 * 2] * z + pM[0 + 4 * 3] * w;
48 pDest[1] = pM[1 + 4 * 0] * x + pM[1 + 4 * 1] * y + pM[1 + 4 * 2] * z + pM[1 + 4 * 3] * w;
49 pDest[2] = pM[2 + 4 * 0] * x + pM[2 + 4 * 1] * y + pM[2 + 4 * 2] * z + pM[2 + 4 * 3] * w;
50 pDest[3] = pM[3 + 4 * 0] * x + pM[3 + 4 * 1] * y + pM[3 + 4 * 2] * z + pM[3 + 4 * 3] * w;
51}
52
53class MallocHelper {
54public:
55 MallocHelper() {
56 mData = 0;
57 }
Jack Palevich106006c2009-08-31 17:42:50 -070058
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059 ~MallocHelper() {
60 if (mData != 0) {
61 free(mData);
62 }
63 }
Jack Palevich106006c2009-08-31 17:42:50 -070064
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065 void* alloc(size_t size) {
66 mData = malloc(size);
67 return mData;
68 }
Jack Palevich106006c2009-08-31 17:42:50 -070069
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070private:
71 void* mData;
72};
73
74#if 0
75static
76void
77print_poly(const char* label, Poly* pPoly) {
78 LOGI("%s: %d verts", label, pPoly->n);
79 for(int i = 0; i < pPoly->n; i++) {
80 Poly_vert* pV = & pPoly->vert[i];
81 LOGI("[%d] %g, %g, %g %g", i, pV->sx, pV->sy, pV->sz, pV->sw);
82 }
83}
84#endif
85
86static
87int visibilityTest(float* pWS, float* pPositions, int positionsLength,
88 unsigned short* pIndices, int indexCount) {
89 MallocHelper mallocHelper;
90 int result = POLY_CLIP_OUT;
91 float* pTransformed = 0;
92 int transformedIndexCount = 0;
Jack Palevich106006c2009-08-31 17:42:50 -070093
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 if ( indexCount < 3 ) {
95 return POLY_CLIP_OUT;
96 }
Jack Palevich106006c2009-08-31 17:42:50 -070097
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 // Find out how many vertices we need to transform
99 // We transform every vertex between the min and max indices, inclusive.
100 // This is OK for the data sets we expect to use with this function, but
101 // for other loads it might be better to use a more sophisticated vertex
102 // cache of some sort.
Jack Palevich106006c2009-08-31 17:42:50 -0700103
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 int minIndex = 65536;
105 int maxIndex = -1;
106 for(int i = 0; i < indexCount; i++) {
107 int index = pIndices[i];
108 if ( index < minIndex ) {
109 minIndex = index;
110 }
111 if ( index > maxIndex ) {
112 maxIndex = index;
113 }
114 }
Jack Palevich106006c2009-08-31 17:42:50 -0700115
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 if ( maxIndex * 3 > positionsLength) {
117 return -1;
118 }
Jack Palevich106006c2009-08-31 17:42:50 -0700119
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 transformedIndexCount = maxIndex - minIndex + 1;
121 pTransformed = (float*) mallocHelper.alloc(transformedIndexCount * 4 * sizeof(float));
Jack Palevich106006c2009-08-31 17:42:50 -0700122
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 if (pTransformed == 0 ) {
124 return -2;
125 }
Jack Palevich106006c2009-08-31 17:42:50 -0700126
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 // Transform the vertices
128 {
129 const float* pSrc = pPositions + 3 * minIndex;
130 float* pDst = pTransformed;
131 for (int i = 0; i < transformedIndexCount; i++, pSrc += 3, pDst += 4) {
132 mx4transform(pSrc[0], pSrc[1], pSrc[2], 1.0f, pWS, pDst);
133 }
134 }
Jack Palevich106006c2009-08-31 17:42:50 -0700135
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 // Clip the triangles
Jack Palevich106006c2009-08-31 17:42:50 -0700137
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 Poly poly;
139 float* pDest = & poly.vert[0].sx;
140 for (int i = 0; i < indexCount; i += 3) {
141 poly.n = 3;
142 memcpy(pDest , pTransformed + 4 * (pIndices[i ] - minIndex), 4 * sizeof(float));
143 memcpy(pDest + 4, pTransformed + 4 * (pIndices[i + 1] - minIndex), 4 * sizeof(float));
144 memcpy(pDest + 8, pTransformed + 4 * (pIndices[i + 2] - minIndex), 4 * sizeof(float));
145 result = poly_clip_to_frustum(&poly);
146 if ( result != POLY_CLIP_OUT) {
147 return result;
148 }
149 }
150
151 return result;
152}
153
154template<class JArray, class T>
155class ArrayHelper {
156public:
157 ArrayHelper(JNIEnv* env, JArray ref, jint offset, jint minSize) {
158 mEnv = env;
159 mRef = ref;
160 mOffset = offset;
161 mMinSize = minSize;
162 mBase = 0;
163 mReleaseParam = JNI_ABORT;
164 }
165
166 ~ArrayHelper() {
167 if (mBase) {
168 mEnv->ReleasePrimitiveArrayCritical(mRef, mBase, mReleaseParam);
169 }
170 }
Jack Palevich106006c2009-08-31 17:42:50 -0700171
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 // We seperate the bounds check from the initialization because we want to
173 // be able to bounds-check multiple arrays, and we can't throw an exception
174 // after we've called GetPrimitiveArrayCritical.
Jack Palevich106006c2009-08-31 17:42:50 -0700175
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 // Return true if the bounds check succeeded
177 // Else instruct the runtime to throw an exception
Jack Palevich106006c2009-08-31 17:42:50 -0700178
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 bool check() {
180 if ( ! mRef) {
181 mEnv->ThrowNew(gIAEClass, "array == null");
182 return false;
183 }
184 if ( mOffset < 0) {
185 mEnv->ThrowNew(gIAEClass, "offset < 0");
186 return false;
187 }
188 mLength = mEnv->GetArrayLength(mRef) - mOffset;
189 if (mLength < mMinSize ) {
190 mEnv->ThrowNew(gIAEClass, "length - offset < n");
191 return false;
192 }
193 return true;
194 }
Jack Palevich106006c2009-08-31 17:42:50 -0700195
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 // Bind the array.
Jack Palevich106006c2009-08-31 17:42:50 -0700197
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 void bind() {
199 mBase = (T*) mEnv->GetPrimitiveArrayCritical(mRef, (jboolean *) 0);
200 mData = mBase + mOffset;
201 }
202
203 void commitChanges() {
204 mReleaseParam = 0;
205 }
Jack Palevich106006c2009-08-31 17:42:50 -0700206
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 T* mData;
208 int mLength;
Jack Palevich106006c2009-08-31 17:42:50 -0700209
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210private:
211 T* mBase;
212 JNIEnv* mEnv;
213 JArray mRef;
214 jint mOffset;
215 jint mMinSize;
216 int mReleaseParam;
217};
218
219typedef ArrayHelper<jfloatArray, float> FloatArrayHelper;
220typedef ArrayHelper<jcharArray, unsigned short> UnsignedShortArrayHelper;
221typedef ArrayHelper<jintArray, int> IntArrayHelper;
222typedef ArrayHelper<jbyteArray, unsigned char> ByteArrayHelper;
223
224inline float distance2(float x, float y, float z) {
225 return x * x + y * y + z * z;
226}
227
228inline float distance(float x, float y, float z) {
229 return sqrtf(distance2(x, y, z));
230}
Jack Palevich106006c2009-08-31 17:42:50 -0700231
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232static
233void util_computeBoundingSphere(JNIEnv *env, jclass clazz,
234 jfloatArray positions_ref, jint positionsOffset, jint positionsCount,
235 jfloatArray sphere_ref, jint sphereOffset) {
236 FloatArrayHelper positions(env, positions_ref, positionsOffset, 0);
237 FloatArrayHelper sphere(env, sphere_ref, sphereOffset, 4);
Jack Palevich106006c2009-08-31 17:42:50 -0700238
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239 bool checkOK = positions.check() && sphere.check();
240 if (! checkOK) {
241 return;
242 }
Jack Palevich106006c2009-08-31 17:42:50 -0700243
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 positions.bind();
245 sphere.bind();
Jack Palevich106006c2009-08-31 17:42:50 -0700246
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 if ( positionsCount < 1 ) {
248 env->ThrowNew(gIAEClass, "positionsCount < 1");
249 return;
250 }
Jack Palevich106006c2009-08-31 17:42:50 -0700251
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 const float* pSrc = positions.mData;
Jack Palevich106006c2009-08-31 17:42:50 -0700253
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 // find bounding box
255 float x0 = *pSrc++;
256 float x1 = x0;
257 float y0 = *pSrc++;
258 float y1 = y0;
259 float z0 = *pSrc++;
260 float z1 = z0;
Jack Palevich106006c2009-08-31 17:42:50 -0700261
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 for(int i = 1; i < positionsCount; i++) {
263 {
264 float x = *pSrc++;
265 if (x < x0) {
266 x0 = x;
267 }
268 else if (x > x1) {
269 x1 = x;
270 }
271 }
272 {
273 float y = *pSrc++;
274 if (y < y0) {
275 y0 = y;
276 }
277 else if (y > y1) {
278 y1 = y;
279 }
280 }
281 {
282 float z = *pSrc++;
283 if (z < z0) {
284 z0 = z;
285 }
286 else if (z > z1) {
287 z1 = z;
288 }
289 }
290 }
Jack Palevich106006c2009-08-31 17:42:50 -0700291
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 // Because we know our input meshes fit pretty well into bounding boxes,
293 // just take the diagonal of the box as defining our sphere.
294 float* pSphere = sphere.mData;
295 float dx = x1 - x0;
296 float dy = y1 - y0;
297 float dz = z1 - z0;
298 *pSphere++ = x0 + dx * 0.5f;
299 *pSphere++ = y0 + dy * 0.5f;
300 *pSphere++ = z0 + dz * 0.5f;
301 *pSphere++ = distance(dx, dy, dz) * 0.5f;
Jack Palevich106006c2009-08-31 17:42:50 -0700302
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 sphere.commitChanges();
304}
305
306static void normalizePlane(float* p) {
307 float rdist = 1.0f / distance(p[0], p[1], p[2]);
308 for(int i = 0; i < 4; i++) {
309 p[i] *= rdist;
310 }
311}
312
313static inline float dot3(float x0, float y0, float z0, float x1, float y1, float z1) {
314 return x0 * x1 + y0 * y1 + z0 * z1;
315}
316
317static inline float signedDistance(const float* pPlane, float x, float y, float z) {
318 return dot3(pPlane[0], pPlane[1], pPlane[2], x, y, z) + pPlane[3];
319}
320
321// Return true if the sphere intersects or is inside the frustum
322
323static bool sphereHitsFrustum(const float* pFrustum, const float* pSphere) {
324 float x = pSphere[0];
325 float y = pSphere[1];
326 float z = pSphere[2];
327 float negRadius = -pSphere[3];
328 for (int i = 0; i < 6; i++, pFrustum += 4) {
329 if (signedDistance(pFrustum, x, y, z) <= negRadius) {
330 return false;
331 }
332 }
333 return true;
334}
335
336static void computeFrustum(const float* m, float* f) {
337 float m3 = m[3];
338 float m7 = m[7];
339 float m11 = m[11];
340 float m15 = m[15];
341 // right
342 f[0] = m3 - m[0];
343 f[1] = m7 - m[4];
344 f[2] = m11 - m[8];
345 f[3] = m15 - m[12];
346 normalizePlane(f);
347 f+= 4;
348
Jack Palevich106006c2009-08-31 17:42:50 -0700349 // left
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 f[0] = m3 + m[0];
351 f[1] = m7 + m[4];
352 f[2] = m11 + m[8];
353 f[3] = m15 + m[12];
354 normalizePlane(f);
355 f+= 4;
356
357 // top
358 f[0] = m3 - m[1];
359 f[1] = m7 - m[5];
360 f[2] = m11 - m[9];
361 f[3] = m15 - m[13];
362 normalizePlane(f);
363 f+= 4;
364
365 // bottom
366 f[0] = m3 + m[1];
367 f[1] = m7 + m[5];
368 f[2] = m11 + m[9];
369 f[3] = m15 + m[13];
370 normalizePlane(f);
371 f+= 4;
372
373 // far
374 f[0] = m3 - m[2];
375 f[1] = m7 - m[6];
376 f[2] = m11 - m[10];
377 f[3] = m15 - m[14];
378 normalizePlane(f);
379 f+= 4;
380
381 // near
382 f[0] = m3 + m[2];
383 f[1] = m7 + m[6];
384 f[2] = m11 + m[10];
385 f[3] = m15 + m[14];
386 normalizePlane(f);
387}
388
389static
390int util_frustumCullSpheres(JNIEnv *env, jclass clazz,
391 jfloatArray mvp_ref, jint mvpOffset,
392 jfloatArray spheres_ref, jint spheresOffset, jint spheresCount,
393 jintArray results_ref, jint resultsOffset, jint resultsCapacity) {
394 float frustum[6*4];
395 int outputCount;
396 int* pResults;
397 float* pSphere;
398 FloatArrayHelper mvp(env, mvp_ref, mvpOffset, 16);
399 FloatArrayHelper spheres(env, spheres_ref, spheresOffset, spheresCount * 4);
400 IntArrayHelper results(env, results_ref, resultsOffset, resultsCapacity);
Jack Palevich106006c2009-08-31 17:42:50 -0700401
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 bool initializedOK = mvp.check() && spheres.check() && results.check();
403 if (! initializedOK) {
404 return -1;
405 }
Jack Palevich106006c2009-08-31 17:42:50 -0700406
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 mvp.bind();
408 spheres.bind();
409 results.bind();
Jack Palevich106006c2009-08-31 17:42:50 -0700410
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 computeFrustum(mvp.mData, frustum);
Jack Palevich106006c2009-08-31 17:42:50 -0700412
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 // Cull the spheres
Jack Palevich106006c2009-08-31 17:42:50 -0700414
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 pSphere = spheres.mData;
416 pResults = results.mData;
417 outputCount = 0;
418 for(int i = 0; i < spheresCount; i++, pSphere += 4) {
419 if (sphereHitsFrustum(frustum, pSphere)) {
420 if (outputCount < resultsCapacity) {
421 *pResults++ = i;
422 }
423 outputCount++;
424 }
425 }
426 results.commitChanges();
427 return outputCount;
428}
429
430/*
431 public native int visibilityTest(float[] ws, int wsOffset,
432 float[] positions, int positionsOffset,
433 char[] indices, int indicesOffset, int indexCount);
434 */
435
436static
437int util_visibilityTest(JNIEnv *env, jclass clazz,
438 jfloatArray ws_ref, jint wsOffset,
439 jfloatArray positions_ref, jint positionsOffset,
440 jcharArray indices_ref, jint indicesOffset, jint indexCount) {
Jack Palevich106006c2009-08-31 17:42:50 -0700441
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 FloatArrayHelper ws(env, ws_ref, wsOffset, 16);
443 FloatArrayHelper positions(env, positions_ref, positionsOffset, 0);
444 UnsignedShortArrayHelper indices(env, indices_ref, indicesOffset, 0);
Jack Palevich106006c2009-08-31 17:42:50 -0700445
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 bool checkOK = ws.check() && positions.check() && indices.check();
447 if (! checkOK) {
448 // Return value will be ignored, because an exception has been thrown.
449 return -1;
450 }
Jack Palevich106006c2009-08-31 17:42:50 -0700451
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 if (indices.mLength < indexCount) {
453 env->ThrowNew(gIAEClass, "length < offset + indexCount");
454 // Return value will be ignored, because an exception has been thrown.
455 return -1;
456 }
Jack Palevich106006c2009-08-31 17:42:50 -0700457
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458 ws.bind();
459 positions.bind();
460 indices.bind();
Jack Palevich106006c2009-08-31 17:42:50 -0700461
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 return visibilityTest(ws.mData,
463 positions.mData, positions.mLength,
464 indices.mData, indexCount);
465}
466
467#define I(_i, _j) ((_j)+ 4*(_i))
468
469static
470void multiplyMM(float* r, const float* lhs, const float* rhs)
471{
472 for (int i=0 ; i<4 ; i++) {
473 register const float rhs_i0 = rhs[ I(i,0) ];
474 register float ri0 = lhs[ I(0,0) ] * rhs_i0;
475 register float ri1 = lhs[ I(0,1) ] * rhs_i0;
476 register float ri2 = lhs[ I(0,2) ] * rhs_i0;
477 register float ri3 = lhs[ I(0,3) ] * rhs_i0;
478 for (int j=1 ; j<4 ; j++) {
479 register const float rhs_ij = rhs[ I(i,j) ];
480 ri0 += lhs[ I(j,0) ] * rhs_ij;
481 ri1 += lhs[ I(j,1) ] * rhs_ij;
482 ri2 += lhs[ I(j,2) ] * rhs_ij;
483 ri3 += lhs[ I(j,3) ] * rhs_ij;
484 }
485 r[ I(i,0) ] = ri0;
486 r[ I(i,1) ] = ri1;
487 r[ I(i,2) ] = ri2;
488 r[ I(i,3) ] = ri3;
489 }
490}
491
492static
493void util_multiplyMM(JNIEnv *env, jclass clazz,
494 jfloatArray result_ref, jint resultOffset,
495 jfloatArray lhs_ref, jint lhsOffset,
496 jfloatArray rhs_ref, jint rhsOffset) {
497
498 FloatArrayHelper resultMat(env, result_ref, resultOffset, 16);
499 FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16);
500 FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 16);
Jack Palevich106006c2009-08-31 17:42:50 -0700501
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 bool checkOK = resultMat.check() && lhs.check() && rhs.check();
Jack Palevich106006c2009-08-31 17:42:50 -0700503
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 if ( !checkOK ) {
505 return;
506 }
Jack Palevich106006c2009-08-31 17:42:50 -0700507
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 resultMat.bind();
509 lhs.bind();
510 rhs.bind();
Jack Palevich106006c2009-08-31 17:42:50 -0700511
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512 multiplyMM(resultMat.mData, lhs.mData, rhs.mData);
Jack Palevich106006c2009-08-31 17:42:50 -0700513
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 resultMat.commitChanges();
515}
516
517static
518void multiplyMV(float* r, const float* lhs, const float* rhs)
519{
520 mx4transform(rhs[0], rhs[1], rhs[2], rhs[3], lhs, r);
521}
522
523static
524void util_multiplyMV(JNIEnv *env, jclass clazz,
525 jfloatArray result_ref, jint resultOffset,
526 jfloatArray lhs_ref, jint lhsOffset,
527 jfloatArray rhs_ref, jint rhsOffset) {
528
529 FloatArrayHelper resultV(env, result_ref, resultOffset, 4);
530 FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16);
531 FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 4);
Jack Palevich106006c2009-08-31 17:42:50 -0700532
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 bool checkOK = resultV.check() && lhs.check() && rhs.check();
Jack Palevich106006c2009-08-31 17:42:50 -0700534
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 if ( !checkOK ) {
536 return;
537 }
Jack Palevich106006c2009-08-31 17:42:50 -0700538
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 resultV.bind();
540 lhs.bind();
541 rhs.bind();
Jack Palevich106006c2009-08-31 17:42:50 -0700542
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 multiplyMV(resultV.mData, lhs.mData, rhs.mData);
Jack Palevich106006c2009-08-31 17:42:50 -0700544
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545 resultV.commitChanges();
546}
547
548// ---------------------------------------------------------------------------
549
550static jfieldID nativeBitmapID = 0;
551
552void nativeUtilsClassInit(JNIEnv *env, jclass clazz)
553{
554 jclass bitmapClass = env->FindClass("android/graphics/Bitmap");
Jack Palevich106006c2009-08-31 17:42:50 -0700555 nativeBitmapID = env->GetFieldID(bitmapClass, "mNativeBitmap", "I");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800556}
557
558static int checkFormat(SkBitmap::Config config, int format, int type)
559{
560 switch(config) {
561 case SkBitmap::kIndex8_Config:
562 if (format == GL_PALETTE8_RGBA8_OES)
563 return 0;
564 case SkBitmap::kARGB_8888_Config:
565 case SkBitmap::kA8_Config:
566 if (type == GL_UNSIGNED_BYTE)
567 return 0;
568 case SkBitmap::kARGB_4444_Config:
569 case SkBitmap::kRGB_565_Config:
570 switch (type) {
571 case GL_UNSIGNED_SHORT_4_4_4_4:
572 case GL_UNSIGNED_SHORT_5_6_5:
573 case GL_UNSIGNED_SHORT_5_5_5_1:
574 return 0;
575 case GL_UNSIGNED_BYTE:
576 if (format == GL_LUMINANCE_ALPHA)
577 return 0;
578 }
579 break;
580 default:
581 break;
582 }
583 return -1;
584}
585
586static int getInternalFormat(SkBitmap::Config config)
587{
588 switch(config) {
589 case SkBitmap::kA8_Config:
590 return GL_ALPHA;
591 case SkBitmap::kARGB_4444_Config:
592 return GL_RGBA;
593 case SkBitmap::kARGB_8888_Config:
594 return GL_RGBA;
595 case SkBitmap::kIndex8_Config:
596 return GL_PALETTE8_RGBA8_OES;
597 case SkBitmap::kRGB_565_Config:
598 return GL_RGB;
599 default:
600 return -1;
601 }
602}
603
Jack Palevich708c17b2009-03-24 21:05:22 -0700604static int getType(SkBitmap::Config config)
605{
606 switch(config) {
607 case SkBitmap::kA8_Config:
608 return GL_UNSIGNED_BYTE;
609 case SkBitmap::kARGB_4444_Config:
610 return GL_UNSIGNED_SHORT_4_4_4_4;
611 case SkBitmap::kARGB_8888_Config:
612 return GL_UNSIGNED_BYTE;
613 case SkBitmap::kIndex8_Config:
614 return -1; // No type for compressed data.
615 case SkBitmap::kRGB_565_Config:
616 return GL_UNSIGNED_SHORT_5_6_5;
617 default:
618 return -1;
619 }
620}
621
622static jint util_getInternalFormat(JNIEnv *env, jclass clazz,
623 jobject jbitmap)
624{
625 SkBitmap const * nativeBitmap =
626 (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID);
627 const SkBitmap& bitmap(*nativeBitmap);
628 SkBitmap::Config config = bitmap.getConfig();
629 return getInternalFormat(config);
630}
631
632static jint util_getType(JNIEnv *env, jclass clazz,
633 jobject jbitmap)
634{
635 SkBitmap const * nativeBitmap =
636 (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID);
637 const SkBitmap& bitmap(*nativeBitmap);
638 SkBitmap::Config config = bitmap.getConfig();
639 return getType(config);
640}
641
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800642static jint util_texImage2D(JNIEnv *env, jclass clazz,
643 jint target, jint level, jint internalformat,
644 jobject jbitmap, jint type, jint border)
645{
646 SkBitmap const * nativeBitmap =
647 (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID);
648 const SkBitmap& bitmap(*nativeBitmap);
649 SkBitmap::Config config = bitmap.getConfig();
650 if (internalformat < 0) {
651 internalformat = getInternalFormat(config);
652 }
Jack Palevich708c17b2009-03-24 21:05:22 -0700653 if (type < 0) {
654 type = getType(config);
655 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800656 int err = checkFormat(config, internalformat, type);
657 if (err)
Jack Palevich106006c2009-08-31 17:42:50 -0700658 return err;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659 bitmap.lockPixels();
660 const int w = bitmap.width();
661 const int h = bitmap.height();
662 const void* p = bitmap.getPixels();
663 if (internalformat == GL_PALETTE8_RGBA8_OES) {
664 if (sizeof(SkPMColor) != sizeof(uint32_t)) {
665 err = -1;
666 goto error;
667 }
668 const size_t size = bitmap.getSize();
669 const size_t palette_size = 256*sizeof(SkPMColor);
Jack Palevich106006c2009-08-31 17:42:50 -0700670 const size_t imageSize = size + palette_size;
671 void* const data = malloc(imageSize);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800672 if (data) {
673 void* const pixels = (char*)data + palette_size;
674 SkColorTable* ctable = bitmap.getColorTable();
675 memcpy(data, ctable->lockColors(), ctable->count() * sizeof(SkPMColor));
676 memcpy(pixels, p, size);
677 ctable->unlockColors(false);
Jack Palevich106006c2009-08-31 17:42:50 -0700678 glCompressedTexImage2D(target, level, internalformat, w, h, border, imageSize, data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 free(data);
680 } else {
681 err = -1;
682 }
683 } else {
684 glTexImage2D(target, level, internalformat, w, h, border, internalformat, type, p);
685 }
686error:
687 bitmap.unlockPixels();
688 return err;
689}
690
691static jint util_texSubImage2D(JNIEnv *env, jclass clazz,
692 jint target, jint level, jint xoffset, jint yoffset,
693 jobject jbitmap, jint format, jint type)
694{
695 SkBitmap const * nativeBitmap =
696 (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID);
697 const SkBitmap& bitmap(*nativeBitmap);
698 SkBitmap::Config config = bitmap.getConfig();
699 if (format < 0) {
700 format = getInternalFormat(config);
701 if (format == GL_PALETTE8_RGBA8_OES)
702 return -1; // glCompressedTexSubImage2D() not supported
703 }
704 int err = checkFormat(config, format, type);
705 if (err)
Jack Palevich106006c2009-08-31 17:42:50 -0700706 return err;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707 bitmap.lockPixels();
708 const int w = bitmap.width();
709 const int h = bitmap.height();
710 const void* p = bitmap.getPixels();
711 glTexSubImage2D(target, level, xoffset, yoffset, w, h, format, type, p);
712 bitmap.unlockPixels();
713 return 0;
714}
715
716/*
Jack Palevicha6276fd2009-12-28 19:31:43 +0800717 * ETC1 methods.
718 */
719
720static jclass nioAccessClass;
721static jclass bufferClass;
722static jmethodID getBasePointerID;
723static jmethodID getBaseArrayID;
724static jmethodID getBaseArrayOffsetID;
725static jfieldID positionID;
726static jfieldID limitID;
727static jfieldID elementSizeShiftID;
728
729/* Cache method IDs each time the class is loaded. */
730
731static void
732nativeClassInitBuffer(JNIEnv *_env)
733{
734 jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
735 nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
736
737 jclass bufferClassLocal = _env->FindClass("java/nio/Buffer");
738 bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal);
739
740 getBasePointerID = _env->GetStaticMethodID(nioAccessClass,
741 "getBasePointer", "(Ljava/nio/Buffer;)J");
742 getBaseArrayID = _env->GetStaticMethodID(nioAccessClass,
743 "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;");
744 getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass,
745 "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
746 positionID = _env->GetFieldID(bufferClass, "position", "I");
747 limitID = _env->GetFieldID(bufferClass, "limit", "I");
748 elementSizeShiftID =
749 _env->GetFieldID(bufferClass, "_elementSizeShift", "I");
750}
751
752static void *
753getPointer(JNIEnv *_env, jobject buffer, jint *remaining)
754{
755 jint position;
756 jint limit;
757 jint elementSizeShift;
758 jlong pointer;
759 jint offset;
760 void *data;
761
762 position = _env->GetIntField(buffer, positionID);
763 limit = _env->GetIntField(buffer, limitID);
764 elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
765 *remaining = (limit - position) << elementSizeShift;
766 pointer = _env->CallStaticLongMethod(nioAccessClass,
767 getBasePointerID, buffer);
768 if (pointer != 0L) {
769 return (void *) (jint) pointer;
770 }
771 return NULL;
772}
773
774class BufferHelper {
775public:
776 BufferHelper(JNIEnv *env, jobject buffer) {
777 mEnv = env;
778 mBuffer = buffer;
779 mData = NULL;
780 mRemaining = 0;
781 }
782
783 bool checkPointer(const char* errorMessage) {
784 if (mBuffer) {
785 mData = getPointer(mEnv, mBuffer, &mRemaining);
786 if (mData == NULL) {
787 mEnv->ThrowNew(gIAEClass, errorMessage);
788 }
789 return mData != NULL;
790 } else {
791 mEnv->ThrowNew(gIAEClass, errorMessage);
792 return false;
793 }
794 }
795
796 inline void* getData() {
797 return mData;
798 }
799
800 inline jint remaining() {
801 return mRemaining;
802 }
803
804private:
805 JNIEnv* mEnv;
806 jobject mBuffer;
807 void* mData;
808 jint mRemaining;
809};
810
811/**
812 * Encode a block of pixels.
813 *
814 * @param in a pointer to a ETC1_DECODED_BLOCK_SIZE array of bytes that represent a
815 * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
816 * value of pixel (x, y).
817 *
818 * @param validPixelMask is a 16-bit mask where bit (1 << (x + y * 4)) indicates whether
819 * the corresponding (x,y) pixel is valid. Invalid pixel color values are ignored when compressing.
820 *
821 * @param out an ETC1 compressed version of the data.
822 *
823 */
824static void etc1_encodeBlock(JNIEnv *env, jclass clazz,
825 jobject in, jint validPixelMask, jobject out) {
826 if (validPixelMask < 0 || validPixelMask > 15) {
827 env->ThrowNew(gIAEClass, "validPixelMask");
828 return;
829 }
830 BufferHelper inB(env, in);
831 BufferHelper outB(env, out);
832 if (inB.checkPointer("in") && outB.checkPointer("out")) {
833 if (inB.remaining() < ETC1_DECODED_BLOCK_SIZE) {
834 env->ThrowNew(gIAEClass, "in's remaining data < DECODED_BLOCK_SIZE");
835 } else if (outB.remaining() < ETC1_ENCODED_BLOCK_SIZE) {
836 env->ThrowNew(gIAEClass, "out's remaining data < ENCODED_BLOCK_SIZE");
837 } else {
838 etc1_encode_block((etc1_byte*) inB.getData(), validPixelMask,
839 (etc1_byte*) outB.getData());
840 }
841 }
842}
843
844/**
845 * Decode a block of pixels.
846 *
847 * @param in an ETC1 compressed version of the data.
848 *
849 * @param out a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a
850 * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
851 * value of pixel (x, y).
852 */
853static void etc1_decodeBlock(JNIEnv *env, jclass clazz,
854 jobject in, jobject out){
855 BufferHelper inB(env, in);
856 BufferHelper outB(env, out);
857 if (inB.checkPointer("in") && outB.checkPointer("out")) {
858 if (inB.remaining() < ETC1_ENCODED_BLOCK_SIZE) {
859 env->ThrowNew(gIAEClass, "in's remaining data < ENCODED_BLOCK_SIZE");
860 } else if (outB.remaining() < ETC1_DECODED_BLOCK_SIZE) {
861 env->ThrowNew(gIAEClass, "out's remaining data < DECODED_BLOCK_SIZE");
862 } else {
863 etc1_decode_block((etc1_byte*) inB.getData(),
864 (etc1_byte*) outB.getData());
865 }
866 }
867}
868
869/**
870 * Return the size of the encoded image data (does not include size of PKM header).
871 */
872static jint etc1_getEncodedDataSize(JNIEnv *env, jclass clazz,
873 jint width, jint height) {
874 return etc1_get_encoded_data_size(width, height);
875}
876
877/**
878 * Encode an entire image.
879 * @param in pointer to the image data. Formatted such that
880 * pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset;
881 * @param out pointer to encoded data. Must be large enough to store entire encoded image.
882 */
883static void etc1_encodeImage(JNIEnv *env, jclass clazz,
884 jobject in, jint width, jint height,
885 jint pixelSize, jint stride, jobject out) {
886 if (pixelSize < 2 || pixelSize > 3) {
887 env->ThrowNew(gIAEClass, "pixelSize must be 2 or 3");
888 return;
889 }
890 BufferHelper inB(env, in);
891 BufferHelper outB(env, out);
892 if (inB.checkPointer("in") && outB.checkPointer("out")) {
893 jint imageSize = stride * height;
894 jint encodedImageSize = etc1_get_encoded_data_size(width, height);
895 if (inB.remaining() < imageSize) {
896 env->ThrowNew(gIAEClass, "in's remaining data < image size");
897 } else if (outB.remaining() < encodedImageSize) {
898 env->ThrowNew(gIAEClass, "out's remaining data < encoded image size");
899 } else {
900 int result = etc1_encode_image((etc1_byte*) inB.getData(),
901 width, height, pixelSize,
902 stride,
903 (etc1_byte*) outB.getData());
904 }
905 }
906}
907
908/**
909 * Decode an entire image.
910 * @param in the encoded data.
911 * @param out pointer to the image data. Will be written such that
912 * pixel (x,y) is at pIn + pixelSize * x + stride * y. Must be
913 * large enough to store entire image.
914 */
915static void etc1_decodeImage(JNIEnv *env, jclass clazz,
916 jobject in, jobject out,
917 jint width, jint height,
918 jint pixelSize, jint stride) {
919 if (pixelSize < 2 || pixelSize > 3) {
920 env->ThrowNew(gIAEClass, "pixelSize must be 2 or 3");
921 return;
922 }
923 BufferHelper inB(env, in);
924 BufferHelper outB(env, out);
925 if (inB.checkPointer("in") && outB.checkPointer("out")) {
926 jint imageSize = stride * height;
927 jint encodedImageSize = etc1_get_encoded_data_size(width, height);
928 if (inB.remaining() < encodedImageSize) {
929 env->ThrowNew(gIAEClass, "in's remaining data < encoded image size");
930 } else if (outB.remaining() < imageSize) {
931 env->ThrowNew(gIAEClass, "out's remaining data < image size");
932 } else {
933 int result = etc1_decode_image((etc1_byte*) inB.getData(),
934 (etc1_byte*) outB.getData(),
935 width, height, pixelSize,
936 stride);
937 }
938 }
939}
940
941/**
942 * Format a PKM header
943 */
944static void etc1_formatHeader(JNIEnv *env, jclass clazz,
945 jobject header, jint width, jint height) {
946 BufferHelper headerB(env, header);
947 if (headerB.checkPointer("header") ){
948 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
949 env->ThrowNew(gIAEClass, "header's remaining data < ETC_PKM_HEADER_SIZE");
950 } else {
951 etc1_pkm_format_header((etc1_byte*) headerB.getData(), width, height);
952 }
953 }
954}
955
956/**
957 * Check if a PKM header is correctly formatted.
958 */
959static jboolean etc1_isValid(JNIEnv *env, jclass clazz,
960 jobject header) {
961 jboolean result = false;
962 BufferHelper headerB(env, header);
963 if (headerB.checkPointer("header") ){
964 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
965 env->ThrowNew(gIAEClass, "header's remaining data < ETC_PKM_HEADER_SIZE");
966 } else {
967 result = etc1_pkm_is_valid((etc1_byte*) headerB.getData());
968 }
969 }
970 return result;
971}
972
973/**
974 * Read the image width from a PKM header
975 */
976static jint etc1_getWidth(JNIEnv *env, jclass clazz,
977 jobject header) {
978 jint result = 0;
979 BufferHelper headerB(env, header);
980 if (headerB.checkPointer("header") ){
981 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
982 env->ThrowNew(gIAEClass, "header's remaining data < ETC_PKM_HEADER_SIZE");
983 } else {
984 result = etc1_pkm_get_width((etc1_byte*) headerB.getData());
985 }
986 }
987 return result;
988}
989
990/**
991 * Read the image height from a PKM header
992 */
993static int etc1_getHeight(JNIEnv *env, jclass clazz,
994 jobject header) {
995 jint result = 0;
996 BufferHelper headerB(env, header);
997 if (headerB.checkPointer("header") ){
998 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
999 env->ThrowNew(gIAEClass, "header's remaining data < ETC_PKM_HEADER_SIZE");
1000 } else {
1001 result = etc1_pkm_get_height((etc1_byte*) headerB.getData());
1002 }
1003 }
1004 return result;
1005}
1006
1007/*
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001008 * JNI registration
1009 */
1010
1011static void
1012lookupClasses(JNIEnv* env) {
1013 gIAEClass = (jclass) env->NewGlobalRef(
1014 env->FindClass("java/lang/IllegalArgumentException"));
1015 gUOEClass = (jclass) env->NewGlobalRef(
1016 env->FindClass("java/lang/UnsupportedOperationException"));
Jack Palevicha6276fd2009-12-28 19:31:43 +08001017 gAIOOBEClass = (jclass) env->NewGlobalRef(
1018 env->FindClass("java/lang/ArrayIndexOutOfBoundsException"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019}
1020
1021static JNINativeMethod gMatrixMethods[] = {
Jack Palevich106006c2009-08-31 17:42:50 -07001022 { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM },
1023 { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001024};
1025
1026static JNINativeMethod gVisiblityMethods[] = {
Jack Palevich106006c2009-08-31 17:42:50 -07001027 { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001028 { "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres },
Jack Palevich106006c2009-08-31 17:42:50 -07001029 { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001030};
1031
1032static JNINativeMethod gUtilsMethods[] = {
1033 {"nativeClassInit", "()V", (void*)nativeUtilsClassInit },
Jack Palevich106006c2009-08-31 17:42:50 -07001034 { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat },
1035 { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType },
1036 { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D },
1037 { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001038};
1039
Jack Palevicha6276fd2009-12-28 19:31:43 +08001040static JNINativeMethod gEtc1Methods[] = {
1041 { "encodeBlock", "(Ljava/nio/Buffer;ILjava/nio/Buffer;)V", (void*) etc1_encodeBlock },
1042 { "decodeBlock", "(Ljava/nio/Buffer;Ljava/nio/Buffer;)V", (void*) etc1_decodeBlock },
1043 { "getEncodedDataSize", "(II)I", (void*) etc1_getEncodedDataSize },
1044 { "encodeImage", "(Ljava/nio/Buffer;IIIILjava/nio/Buffer;)V", (void*) etc1_encodeImage },
1045 { "decodeImage", "(Ljava/nio/Buffer;Ljava/nio/Buffer;IIII)V", (void*) etc1_decodeImage },
1046 { "formatHeader", "(Ljava/nio/Buffer;II)V", (void*) etc1_formatHeader },
1047 { "isValid", "(Ljava/nio/Buffer;)Z", (void*) etc1_isValid },
1048 { "getWidth", "(Ljava/nio/Buffer;)I", (void*) etc1_getWidth },
1049 { "getHeight", "(Ljava/nio/Buffer;)I", (void*) etc1_getHeight },
1050};
1051
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052typedef struct _ClassRegistrationInfo {
1053 const char* classPath;
1054 JNINativeMethod* methods;
1055 size_t methodCount;
1056} ClassRegistrationInfo;
1057
1058static ClassRegistrationInfo gClasses[] = {
1059 {"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)},
1060 {"android/opengl/Visibility", gVisiblityMethods, NELEM(gVisiblityMethods)},
1061 {"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)},
Jack Palevicha6276fd2009-12-28 19:31:43 +08001062 {"android/opengl/ETC1", gEtc1Methods, NELEM(gEtc1Methods)},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063};
1064
1065int register_android_opengl_classes(JNIEnv* env)
1066{
1067 lookupClasses(env);
Jack Palevicha6276fd2009-12-28 19:31:43 +08001068 nativeClassInitBuffer(env);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001069 int result = 0;
1070 for (int i = 0; i < NELEM(gClasses); i++) {
1071 ClassRegistrationInfo* cri = &gClasses[i];
1072 result = AndroidRuntime::registerNativeMethods(env,
1073 cri->classPath, cri->methods, cri->methodCount);
1074 if (result < 0) {
1075 LOGE("Failed to register %s: %d", cri->classPath, result);
1076 break;
1077 }
1078 }
1079 return result;
1080}
1081
1082} // namespace android
1083