util.cpp revision 106006cbdedc79ce8746ca5449610c69a2f69655
1/**
2 ** Copyright 2007, The Android Open Source Project
3 **
4 ** 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
7 **
8 **     http://www.apache.org/licenses/LICENSE-2.0
9 **
10 ** 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
14 ** 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>
26
27#include <core/SkBitmap.h>
28
29#include "android_runtime/AndroidRuntime.h"
30
31#undef LOG_TAG
32#define LOG_TAG "OpenGLUtil"
33#include <utils/Log.h>
34#include "utils/misc.h"
35
36#include "poly.h"
37
38namespace android {
39
40static jclass gIAEClass;
41static jclass gUOEClass;
42
43static inline
44void mx4transform(float x, float y, float z, float w, const float* pM, float* pDest) {
45    pDest[0] = pM[0 + 4 * 0] * x + pM[0 + 4 * 1] * y + pM[0 + 4 * 2] * z + pM[0 + 4 * 3] * w;
46    pDest[1] = pM[1 + 4 * 0] * x + pM[1 + 4 * 1] * y + pM[1 + 4 * 2] * z + pM[1 + 4 * 3] * w;
47    pDest[2] = pM[2 + 4 * 0] * x + pM[2 + 4 * 1] * y + pM[2 + 4 * 2] * z + pM[2 + 4 * 3] * w;
48    pDest[3] = pM[3 + 4 * 0] * x + pM[3 + 4 * 1] * y + pM[3 + 4 * 2] * z + pM[3 + 4 * 3] * w;
49}
50
51class MallocHelper {
52public:
53    MallocHelper() {
54        mData = 0;
55    }
56
57    ~MallocHelper() {
58        if (mData != 0) {
59            free(mData);
60        }
61    }
62
63    void* alloc(size_t size) {
64        mData = malloc(size);
65        return mData;
66    }
67
68private:
69    void* mData;
70};
71
72#if 0
73static
74void
75print_poly(const char* label, Poly* pPoly) {
76    LOGI("%s: %d verts", label, pPoly->n);
77    for(int i = 0; i < pPoly->n; i++) {
78        Poly_vert* pV = & pPoly->vert[i];
79        LOGI("[%d] %g, %g, %g %g", i, pV->sx, pV->sy, pV->sz, pV->sw);
80    }
81}
82#endif
83
84static
85int visibilityTest(float* pWS, float* pPositions, int positionsLength,
86        unsigned short* pIndices, int indexCount) {
87    MallocHelper mallocHelper;
88    int result = POLY_CLIP_OUT;
89    float* pTransformed = 0;
90    int transformedIndexCount = 0;
91
92    if ( indexCount < 3 ) {
93        return POLY_CLIP_OUT;
94    }
95
96    // Find out how many vertices we need to transform
97    // We transform every vertex between the min and max indices, inclusive.
98    // This is OK for the data sets we expect to use with this function, but
99    // for other loads it might be better to use a more sophisticated vertex
100    // cache of some sort.
101
102    int minIndex = 65536;
103    int maxIndex = -1;
104    for(int i = 0; i < indexCount; i++) {
105        int index = pIndices[i];
106        if ( index < minIndex ) {
107            minIndex = index;
108        }
109        if ( index > maxIndex ) {
110            maxIndex = index;
111        }
112    }
113
114    if ( maxIndex * 3 > positionsLength) {
115        return -1;
116    }
117
118    transformedIndexCount = maxIndex - minIndex + 1;
119    pTransformed = (float*) mallocHelper.alloc(transformedIndexCount * 4 * sizeof(float));
120
121    if (pTransformed == 0 ) {
122        return -2;
123    }
124
125    // Transform the vertices
126    {
127        const float* pSrc = pPositions + 3 * minIndex;
128        float* pDst = pTransformed;
129        for (int i = 0; i < transformedIndexCount; i++, pSrc += 3, pDst += 4) {
130            mx4transform(pSrc[0], pSrc[1], pSrc[2], 1.0f, pWS,  pDst);
131        }
132    }
133
134    // Clip the triangles
135
136    Poly poly;
137    float* pDest = & poly.vert[0].sx;
138    for (int i = 0; i < indexCount; i += 3) {
139        poly.n = 3;
140        memcpy(pDest    , pTransformed + 4 * (pIndices[i    ] - minIndex), 4 * sizeof(float));
141        memcpy(pDest + 4, pTransformed + 4 * (pIndices[i + 1] - minIndex), 4 * sizeof(float));
142        memcpy(pDest + 8, pTransformed + 4 * (pIndices[i + 2] - minIndex), 4 * sizeof(float));
143        result = poly_clip_to_frustum(&poly);
144        if ( result != POLY_CLIP_OUT) {
145            return result;
146        }
147    }
148
149    return result;
150}
151
152template<class JArray, class T>
153class ArrayHelper {
154public:
155    ArrayHelper(JNIEnv* env, JArray ref, jint offset, jint minSize) {
156        mEnv = env;
157        mRef = ref;
158        mOffset = offset;
159        mMinSize = minSize;
160        mBase = 0;
161        mReleaseParam = JNI_ABORT;
162    }
163
164    ~ArrayHelper() {
165        if (mBase) {
166            mEnv->ReleasePrimitiveArrayCritical(mRef, mBase, mReleaseParam);
167        }
168    }
169
170    // We seperate the bounds check from the initialization because we want to
171    // be able to bounds-check multiple arrays, and we can't throw an exception
172    // after we've called GetPrimitiveArrayCritical.
173
174    // Return true if the bounds check succeeded
175    // Else instruct the runtime to throw an exception
176
177    bool check() {
178        if ( ! mRef) {
179            mEnv->ThrowNew(gIAEClass, "array == null");
180            return false;
181        }
182        if ( mOffset < 0) {
183            mEnv->ThrowNew(gIAEClass, "offset < 0");
184            return false;
185        }
186        mLength = mEnv->GetArrayLength(mRef) - mOffset;
187        if (mLength < mMinSize ) {
188            mEnv->ThrowNew(gIAEClass, "length - offset < n");
189            return false;
190        }
191        return true;
192    }
193
194    // Bind the array.
195
196    void bind() {
197        mBase = (T*) mEnv->GetPrimitiveArrayCritical(mRef, (jboolean *) 0);
198        mData = mBase + mOffset;
199    }
200
201    void commitChanges() {
202        mReleaseParam = 0;
203    }
204
205    T* mData;
206    int mLength;
207
208private:
209    T* mBase;
210    JNIEnv* mEnv;
211    JArray mRef;
212    jint mOffset;
213    jint mMinSize;
214    int mReleaseParam;
215};
216
217typedef ArrayHelper<jfloatArray, float> FloatArrayHelper;
218typedef ArrayHelper<jcharArray, unsigned short> UnsignedShortArrayHelper;
219typedef ArrayHelper<jintArray, int> IntArrayHelper;
220typedef ArrayHelper<jbyteArray, unsigned char> ByteArrayHelper;
221
222inline float distance2(float x, float y, float z) {
223    return x * x + y * y + z * z;
224}
225
226inline float distance(float x, float y, float z) {
227    return sqrtf(distance2(x, y, z));
228}
229
230static
231void util_computeBoundingSphere(JNIEnv *env, jclass clazz,
232        jfloatArray positions_ref, jint positionsOffset, jint positionsCount,
233        jfloatArray sphere_ref, jint sphereOffset) {
234    FloatArrayHelper positions(env, positions_ref, positionsOffset, 0);
235    FloatArrayHelper sphere(env, sphere_ref, sphereOffset, 4);
236
237    bool checkOK = positions.check() && sphere.check();
238        if (! checkOK) {
239        return;
240    }
241
242    positions.bind();
243    sphere.bind();
244
245    if ( positionsCount < 1 ) {
246        env->ThrowNew(gIAEClass, "positionsCount < 1");
247        return;
248    }
249
250    const float* pSrc = positions.mData;
251
252    // find bounding box
253    float x0 = *pSrc++;
254    float x1 = x0;
255    float y0 = *pSrc++;
256    float y1 = y0;
257    float z0 = *pSrc++;
258    float z1 = z0;
259
260    for(int i = 1; i < positionsCount; i++) {
261        {
262            float x = *pSrc++;
263            if (x < x0) {
264                x0 = x;
265            }
266            else if (x > x1) {
267                x1 = x;
268            }
269        }
270        {
271            float y = *pSrc++;
272            if (y < y0) {
273                y0 = y;
274            }
275            else if (y > y1) {
276                y1 = y;
277            }
278        }
279        {
280            float z = *pSrc++;
281            if (z < z0) {
282                z0 = z;
283            }
284            else if (z > z1) {
285                z1 = z;
286            }
287        }
288    }
289
290    // Because we know our input meshes fit pretty well into bounding boxes,
291    // just take the diagonal of the box as defining our sphere.
292    float* pSphere = sphere.mData;
293    float dx = x1 - x0;
294    float dy = y1 - y0;
295    float dz = z1 - z0;
296    *pSphere++ = x0 + dx * 0.5f;
297    *pSphere++ = y0 + dy * 0.5f;
298    *pSphere++ = z0 + dz * 0.5f;
299    *pSphere++ = distance(dx, dy, dz) * 0.5f;
300
301    sphere.commitChanges();
302}
303
304static void normalizePlane(float* p) {
305    float rdist = 1.0f / distance(p[0], p[1], p[2]);
306    for(int i = 0; i < 4; i++) {
307        p[i] *= rdist;
308    }
309}
310
311static inline float dot3(float x0, float y0, float z0, float x1, float y1, float z1) {
312    return x0 * x1 + y0 * y1 + z0 * z1;
313}
314
315static inline float signedDistance(const float* pPlane, float x, float y, float z) {
316    return dot3(pPlane[0], pPlane[1], pPlane[2], x, y, z) + pPlane[3];
317}
318
319// Return true if the sphere intersects or is inside the frustum
320
321static bool sphereHitsFrustum(const float* pFrustum, const float* pSphere) {
322    float x = pSphere[0];
323    float y = pSphere[1];
324    float z = pSphere[2];
325    float negRadius = -pSphere[3];
326    for (int i = 0; i < 6; i++, pFrustum += 4) {
327        if (signedDistance(pFrustum, x, y, z) <= negRadius) {
328            return false;
329        }
330    }
331    return true;
332}
333
334static void computeFrustum(const float* m, float* f) {
335    float m3 = m[3];
336    float m7 = m[7];
337    float m11 = m[11];
338    float m15 = m[15];
339    // right
340    f[0] = m3  - m[0];
341    f[1] = m7  - m[4];
342    f[2] = m11 - m[8];
343    f[3] = m15 - m[12];
344    normalizePlane(f);
345    f+= 4;
346
347    // left
348    f[0] = m3  + m[0];
349    f[1] = m7  + m[4];
350    f[2] = m11 + m[8];
351    f[3] = m15 + m[12];
352    normalizePlane(f);
353    f+= 4;
354
355    // top
356    f[0] = m3  - m[1];
357    f[1] = m7  - m[5];
358    f[2] = m11 - m[9];
359    f[3] = m15 - m[13];
360    normalizePlane(f);
361    f+= 4;
362
363    // bottom
364    f[0] = m3  + m[1];
365    f[1] = m7  + m[5];
366    f[2] = m11 + m[9];
367    f[3] = m15 + m[13];
368    normalizePlane(f);
369    f+= 4;
370
371    // far
372    f[0] = m3  - m[2];
373    f[1] = m7  - m[6];
374    f[2] = m11 - m[10];
375    f[3] = m15 - m[14];
376    normalizePlane(f);
377    f+= 4;
378
379    // near
380    f[0] = m3  + m[2];
381    f[1] = m7  + m[6];
382    f[2] = m11 + m[10];
383    f[3] = m15 + m[14];
384    normalizePlane(f);
385}
386
387static
388int util_frustumCullSpheres(JNIEnv *env, jclass clazz,
389        jfloatArray mvp_ref, jint mvpOffset,
390        jfloatArray spheres_ref, jint spheresOffset, jint spheresCount,
391        jintArray results_ref, jint resultsOffset, jint resultsCapacity) {
392    float frustum[6*4];
393    int outputCount;
394    int* pResults;
395    float* pSphere;
396    FloatArrayHelper mvp(env, mvp_ref, mvpOffset, 16);
397    FloatArrayHelper spheres(env, spheres_ref, spheresOffset, spheresCount * 4);
398    IntArrayHelper results(env, results_ref, resultsOffset, resultsCapacity);
399
400    bool initializedOK = mvp.check() && spheres.check() && results.check();
401        if (! initializedOK) {
402        return -1;
403    }
404
405    mvp.bind();
406    spheres.bind();
407    results.bind();
408
409    computeFrustum(mvp.mData, frustum);
410
411    // Cull the spheres
412
413    pSphere = spheres.mData;
414    pResults = results.mData;
415    outputCount = 0;
416    for(int i = 0; i < spheresCount; i++, pSphere += 4) {
417        if (sphereHitsFrustum(frustum, pSphere)) {
418            if (outputCount < resultsCapacity) {
419                *pResults++ = i;
420            }
421            outputCount++;
422        }
423    }
424    results.commitChanges();
425    return outputCount;
426}
427
428/*
429 public native int visibilityTest(float[] ws, int wsOffset,
430 float[] positions, int positionsOffset,
431 char[] indices, int indicesOffset, int indexCount);
432 */
433
434static
435int util_visibilityTest(JNIEnv *env, jclass clazz,
436        jfloatArray ws_ref, jint wsOffset,
437        jfloatArray positions_ref, jint positionsOffset,
438        jcharArray indices_ref, jint indicesOffset, jint indexCount) {
439
440    FloatArrayHelper ws(env, ws_ref, wsOffset, 16);
441    FloatArrayHelper positions(env, positions_ref, positionsOffset, 0);
442    UnsignedShortArrayHelper indices(env, indices_ref, indicesOffset, 0);
443
444    bool checkOK = ws.check() && positions.check() && indices.check();
445    if (! checkOK) {
446        // Return value will be ignored, because an exception has been thrown.
447        return -1;
448    }
449
450    if (indices.mLength < indexCount) {
451        env->ThrowNew(gIAEClass, "length < offset + indexCount");
452        // Return value will be ignored, because an exception has been thrown.
453        return -1;
454    }
455
456    ws.bind();
457    positions.bind();
458    indices.bind();
459
460    return visibilityTest(ws.mData,
461            positions.mData, positions.mLength,
462            indices.mData, indexCount);
463}
464
465#define I(_i, _j) ((_j)+ 4*(_i))
466
467static
468void multiplyMM(float* r, const float* lhs, const float* rhs)
469{
470    for (int i=0 ; i<4 ; i++) {
471        register const float rhs_i0 = rhs[ I(i,0) ];
472        register float ri0 = lhs[ I(0,0) ] * rhs_i0;
473        register float ri1 = lhs[ I(0,1) ] * rhs_i0;
474        register float ri2 = lhs[ I(0,2) ] * rhs_i0;
475        register float ri3 = lhs[ I(0,3) ] * rhs_i0;
476        for (int j=1 ; j<4 ; j++) {
477            register const float rhs_ij = rhs[ I(i,j) ];
478            ri0 += lhs[ I(j,0) ] * rhs_ij;
479            ri1 += lhs[ I(j,1) ] * rhs_ij;
480            ri2 += lhs[ I(j,2) ] * rhs_ij;
481            ri3 += lhs[ I(j,3) ] * rhs_ij;
482        }
483        r[ I(i,0) ] = ri0;
484        r[ I(i,1) ] = ri1;
485        r[ I(i,2) ] = ri2;
486        r[ I(i,3) ] = ri3;
487    }
488}
489
490static
491void util_multiplyMM(JNIEnv *env, jclass clazz,
492    jfloatArray result_ref, jint resultOffset,
493    jfloatArray lhs_ref, jint lhsOffset,
494    jfloatArray rhs_ref, jint rhsOffset) {
495
496    FloatArrayHelper resultMat(env, result_ref, resultOffset, 16);
497    FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16);
498    FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 16);
499
500    bool checkOK = resultMat.check() && lhs.check() && rhs.check();
501
502    if ( !checkOK ) {
503        return;
504    }
505
506    resultMat.bind();
507    lhs.bind();
508    rhs.bind();
509
510    multiplyMM(resultMat.mData, lhs.mData, rhs.mData);
511
512    resultMat.commitChanges();
513}
514
515static
516void multiplyMV(float* r, const float* lhs, const float* rhs)
517{
518    mx4transform(rhs[0], rhs[1], rhs[2], rhs[3], lhs, r);
519}
520
521static
522void util_multiplyMV(JNIEnv *env, jclass clazz,
523    jfloatArray result_ref, jint resultOffset,
524    jfloatArray lhs_ref, jint lhsOffset,
525    jfloatArray rhs_ref, jint rhsOffset) {
526
527    FloatArrayHelper resultV(env, result_ref, resultOffset, 4);
528    FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16);
529    FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 4);
530
531    bool checkOK = resultV.check() && lhs.check() && rhs.check();
532
533    if ( !checkOK ) {
534        return;
535    }
536
537    resultV.bind();
538    lhs.bind();
539    rhs.bind();
540
541    multiplyMV(resultV.mData, lhs.mData, rhs.mData);
542
543    resultV.commitChanges();
544}
545
546// ---------------------------------------------------------------------------
547
548static jfieldID nativeBitmapID = 0;
549
550void nativeUtilsClassInit(JNIEnv *env, jclass clazz)
551{
552    jclass bitmapClass = env->FindClass("android/graphics/Bitmap");
553    nativeBitmapID = env->GetFieldID(bitmapClass, "mNativeBitmap", "I");
554}
555
556static int checkFormat(SkBitmap::Config config, int format, int type)
557{
558    switch(config) {
559        case SkBitmap::kIndex8_Config:
560            if (format == GL_PALETTE8_RGBA8_OES)
561                return 0;
562        case SkBitmap::kARGB_8888_Config:
563        case SkBitmap::kA8_Config:
564            if (type == GL_UNSIGNED_BYTE)
565                return 0;
566        case SkBitmap::kARGB_4444_Config:
567        case SkBitmap::kRGB_565_Config:
568            switch (type) {
569                case GL_UNSIGNED_SHORT_4_4_4_4:
570                case GL_UNSIGNED_SHORT_5_6_5:
571                case GL_UNSIGNED_SHORT_5_5_5_1:
572                    return 0;
573                case GL_UNSIGNED_BYTE:
574                    if (format == GL_LUMINANCE_ALPHA)
575                        return 0;
576            }
577            break;
578        default:
579            break;
580    }
581    return -1;
582}
583
584static int getInternalFormat(SkBitmap::Config config)
585{
586    switch(config) {
587        case SkBitmap::kA8_Config:
588            return GL_ALPHA;
589        case SkBitmap::kARGB_4444_Config:
590            return GL_RGBA;
591        case SkBitmap::kARGB_8888_Config:
592            return GL_RGBA;
593        case SkBitmap::kIndex8_Config:
594            return GL_PALETTE8_RGBA8_OES;
595        case SkBitmap::kRGB_565_Config:
596            return GL_RGB;
597        default:
598            return -1;
599    }
600}
601
602static int getType(SkBitmap::Config config)
603{
604    switch(config) {
605        case SkBitmap::kA8_Config:
606            return GL_UNSIGNED_BYTE;
607        case SkBitmap::kARGB_4444_Config:
608            return GL_UNSIGNED_SHORT_4_4_4_4;
609        case SkBitmap::kARGB_8888_Config:
610            return GL_UNSIGNED_BYTE;
611        case SkBitmap::kIndex8_Config:
612            return -1; // No type for compressed data.
613        case SkBitmap::kRGB_565_Config:
614            return GL_UNSIGNED_SHORT_5_6_5;
615        default:
616            return -1;
617    }
618}
619
620static jint util_getInternalFormat(JNIEnv *env, jclass clazz,
621        jobject jbitmap)
622{
623    SkBitmap const * nativeBitmap =
624            (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID);
625    const SkBitmap& bitmap(*nativeBitmap);
626    SkBitmap::Config config = bitmap.getConfig();
627    return getInternalFormat(config);
628}
629
630static jint util_getType(JNIEnv *env, jclass clazz,
631        jobject jbitmap)
632{
633    SkBitmap const * nativeBitmap =
634            (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID);
635    const SkBitmap& bitmap(*nativeBitmap);
636    SkBitmap::Config config = bitmap.getConfig();
637    return getType(config);
638}
639
640static jint util_texImage2D(JNIEnv *env, jclass clazz,
641        jint target, jint level, jint internalformat,
642        jobject jbitmap, jint type, jint border)
643{
644    SkBitmap const * nativeBitmap =
645            (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID);
646    const SkBitmap& bitmap(*nativeBitmap);
647    SkBitmap::Config config = bitmap.getConfig();
648    if (internalformat < 0) {
649        internalformat = getInternalFormat(config);
650    }
651    if (type < 0) {
652        type = getType(config);
653    }
654    int err = checkFormat(config, internalformat, type);
655    if (err)
656        return err;
657    bitmap.lockPixels();
658    const int w = bitmap.width();
659    const int h = bitmap.height();
660    const void* p = bitmap.getPixels();
661    if (internalformat == GL_PALETTE8_RGBA8_OES) {
662        if (sizeof(SkPMColor) != sizeof(uint32_t)) {
663            err = -1;
664            goto error;
665        }
666        const size_t size = bitmap.getSize();
667        const size_t palette_size = 256*sizeof(SkPMColor);
668        const size_t imageSize = size + palette_size;
669        void* const data = malloc(imageSize);
670        if (data) {
671            void* const pixels = (char*)data + palette_size;
672            SkColorTable* ctable = bitmap.getColorTable();
673            memcpy(data, ctable->lockColors(), ctable->count() * sizeof(SkPMColor));
674            memcpy(pixels, p, size);
675            ctable->unlockColors(false);
676            glCompressedTexImage2D(target, level, internalformat, w, h, border, imageSize, data);
677            free(data);
678        } else {
679            err = -1;
680        }
681    } else {
682        glTexImage2D(target, level, internalformat, w, h, border, internalformat, type, p);
683    }
684error:
685    bitmap.unlockPixels();
686    return err;
687}
688
689static jint util_texSubImage2D(JNIEnv *env, jclass clazz,
690        jint target, jint level, jint xoffset, jint yoffset,
691        jobject jbitmap, jint format, jint type)
692{
693    SkBitmap const * nativeBitmap =
694            (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID);
695    const SkBitmap& bitmap(*nativeBitmap);
696    SkBitmap::Config config = bitmap.getConfig();
697    if (format < 0) {
698        format = getInternalFormat(config);
699        if (format == GL_PALETTE8_RGBA8_OES)
700            return -1; // glCompressedTexSubImage2D() not supported
701    }
702    int err = checkFormat(config, format, type);
703    if (err)
704        return err;
705    bitmap.lockPixels();
706    const int w = bitmap.width();
707    const int h = bitmap.height();
708    const void* p = bitmap.getPixels();
709    glTexSubImage2D(target, level, xoffset, yoffset, w, h, format, type, p);
710    bitmap.unlockPixels();
711    return 0;
712}
713
714/*
715 * JNI registration
716 */
717
718static void
719lookupClasses(JNIEnv* env) {
720    gIAEClass = (jclass) env->NewGlobalRef(
721            env->FindClass("java/lang/IllegalArgumentException"));
722    gUOEClass = (jclass) env->NewGlobalRef(
723            env->FindClass("java/lang/UnsupportedOperationException"));
724}
725
726static JNINativeMethod gMatrixMethods[] = {
727    { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM },
728    { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV },
729};
730
731static JNINativeMethod gVisiblityMethods[] = {
732    { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere },
733    { "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres },
734    { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest },
735};
736
737static JNINativeMethod gUtilsMethods[] = {
738    {"nativeClassInit", "()V",                          (void*)nativeUtilsClassInit },
739    { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat },
740    { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType },
741    { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D },
742    { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D },
743};
744
745typedef struct _ClassRegistrationInfo {
746    const char* classPath;
747    JNINativeMethod* methods;
748    size_t methodCount;
749} ClassRegistrationInfo;
750
751static ClassRegistrationInfo gClasses[] = {
752        {"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)},
753        {"android/opengl/Visibility", gVisiblityMethods, NELEM(gVisiblityMethods)},
754        {"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)},
755};
756
757int register_android_opengl_classes(JNIEnv* env)
758{
759    lookupClasses(env);
760    int result = 0;
761    for (int i = 0; i < NELEM(gClasses); i++) {
762        ClassRegistrationInfo* cri = &gClasses[i];
763        result = AndroidRuntime::registerNativeMethods(env,
764                cri->classPath, cri->methods, cri->methodCount);
765        if (result < 0) {
766            LOGE("Failed to register %s: %d", cri->classPath, result);
767            break;
768        }
769    }
770    return result;
771}
772
773} // namespace android
774
775