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 "jni.h"
18#include "JNIHelp.h"
19#include "GraphicsJNI.h"
20
21#include <math.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <assert.h>
26#include <dlfcn.h>
27
28#include <GLES/gl.h>
29#include <ETC1/etc1.h>
30
31#include <SkBitmap.h>
32
33#include "core_jni_helpers.h"
34
35#undef LOG_TAG
36#define LOG_TAG "OpenGLUtil"
37#include <utils/Log.h>
38#include "utils/misc.h"
39
40#include "poly.h"
41
42namespace android {
43
44static inline
45void mx4transform(float x, float y, float z, float w, const float* pM, float* pDest) {
46    pDest[0] = pM[0 + 4 * 0] * x + pM[0 + 4 * 1] * y + pM[0 + 4 * 2] * z + pM[0 + 4 * 3] * w;
47    pDest[1] = pM[1 + 4 * 0] * x + pM[1 + 4 * 1] * y + pM[1 + 4 * 2] * z + pM[1 + 4 * 3] * w;
48    pDest[2] = pM[2 + 4 * 0] * x + pM[2 + 4 * 1] * y + pM[2 + 4 * 2] * z + pM[2 + 4 * 3] * w;
49    pDest[3] = pM[3 + 4 * 0] * x + pM[3 + 4 * 1] * y + pM[3 + 4 * 2] * z + pM[3 + 4 * 3] * w;
50}
51
52class MallocHelper {
53public:
54    MallocHelper() {
55        mData = 0;
56    }
57
58    ~MallocHelper() {
59        if (mData != 0) {
60            free(mData);
61        }
62    }
63
64    void* alloc(size_t size) {
65        mData = malloc(size);
66        return mData;
67    }
68
69private:
70    void* mData;
71};
72
73#if 0
74static
75void
76print_poly(const char* label, Poly* pPoly) {
77    ALOGI("%s: %d verts", label, pPoly->n);
78    for(int i = 0; i < pPoly->n; i++) {
79        Poly_vert* pV = & pPoly->vert[i];
80        ALOGI("[%d] %g, %g, %g %g", i, pV->sx, pV->sy, pV->sz, pV->sw);
81    }
82}
83#endif
84
85static
86int visibilityTest(float* pWS, float* pPositions, int positionsLength,
87        unsigned short* pIndices, int indexCount) {
88    MallocHelper mallocHelper;
89    int result = POLY_CLIP_OUT;
90    float* pTransformed = 0;
91    int transformedIndexCount = 0;
92
93    if ( indexCount < 3 ) {
94        return POLY_CLIP_OUT;
95    }
96
97    // Find out how many vertices we need to transform
98    // We transform every vertex between the min and max indices, inclusive.
99    // This is OK for the data sets we expect to use with this function, but
100    // for other loads it might be better to use a more sophisticated vertex
101    // cache of some sort.
102
103    int minIndex = 65536;
104    int maxIndex = -1;
105    for(int i = 0; i < indexCount; i++) {
106        int index = pIndices[i];
107        if ( index < minIndex ) {
108            minIndex = index;
109        }
110        if ( index > maxIndex ) {
111            maxIndex = index;
112        }
113    }
114
115    if ( maxIndex * 3 > positionsLength) {
116        return -1;
117    }
118
119    transformedIndexCount = maxIndex - minIndex + 1;
120    pTransformed = (float*) mallocHelper.alloc(transformedIndexCount * 4 * sizeof(float));
121
122    if (pTransformed == 0 ) {
123        return -2;
124    }
125
126    // Transform the vertices
127    {
128        const float* pSrc = pPositions + 3 * minIndex;
129        float* pDst = pTransformed;
130        for (int i = 0; i < transformedIndexCount; i++, pSrc += 3, pDst += 4) {
131            mx4transform(pSrc[0], pSrc[1], pSrc[2], 1.0f, pWS,  pDst);
132        }
133    }
134
135    // Clip the triangles
136
137    Poly poly;
138    float* pDest = & poly.vert[0].sx;
139    for (int i = 0; i < indexCount; i += 3) {
140        poly.n = 3;
141        memcpy(pDest    , pTransformed + 4 * (pIndices[i    ] - minIndex), 4 * sizeof(float));
142        memcpy(pDest + 4, pTransformed + 4 * (pIndices[i + 1] - minIndex), 4 * sizeof(float));
143        memcpy(pDest + 8, pTransformed + 4 * (pIndices[i + 2] - minIndex), 4 * sizeof(float));
144        result = poly_clip_to_frustum(&poly);
145        if ( result != POLY_CLIP_OUT) {
146            return result;
147        }
148    }
149
150    return result;
151}
152
153class ByteArrayGetter {
154public:
155    static void* Get(JNIEnv* _env, jbyteArray array, jboolean* is_copy) {
156        return _env->GetByteArrayElements(array, is_copy);
157    }
158};
159class BooleanArrayGetter {
160public:
161    static void* Get(JNIEnv* _env, jbooleanArray array, jboolean* is_copy) {
162        return _env->GetBooleanArrayElements(array, is_copy);
163    }
164};
165class CharArrayGetter {
166public:
167    static void* Get(JNIEnv* _env, jcharArray array, jboolean* is_copy) {
168        return _env->GetCharArrayElements(array, is_copy);
169    }
170};
171class ShortArrayGetter {
172public:
173    static void* Get(JNIEnv* _env, jshortArray array, jboolean* is_copy) {
174        return _env->GetShortArrayElements(array, is_copy);
175    }
176};
177class IntArrayGetter {
178public:
179    static void* Get(JNIEnv* _env, jintArray array, jboolean* is_copy) {
180        return _env->GetIntArrayElements(array, is_copy);
181    }
182};
183class LongArrayGetter {
184public:
185    static void* Get(JNIEnv* _env, jlongArray array, jboolean* is_copy) {
186        return _env->GetLongArrayElements(array, is_copy);
187    }
188};
189class FloatArrayGetter {
190public:
191    static void* Get(JNIEnv* _env, jfloatArray array, jboolean* is_copy) {
192        return _env->GetFloatArrayElements(array, is_copy);
193    }
194};
195class DoubleArrayGetter {
196public:
197    static void* Get(JNIEnv* _env, jdoubleArray array, jboolean* is_copy) {
198        return _env->GetDoubleArrayElements(array, is_copy);
199    }
200};
201
202class ByteArrayReleaser {
203public:
204    static void Release(JNIEnv* _env, jbyteArray array, jbyte* data, jint mode) {
205        _env->ReleaseByteArrayElements(array, data, mode);
206    }
207};
208class BooleanArrayReleaser {
209public:
210    static void Release(JNIEnv* _env, jbooleanArray array, jboolean* data, jint mode) {
211        _env->ReleaseBooleanArrayElements(array, data, mode);
212    }
213};
214class CharArrayReleaser {
215public:
216    static void Release(JNIEnv* _env, jcharArray array, jchar* data, jint mode) {
217        _env->ReleaseCharArrayElements(array, data, mode);
218    }
219};
220class ShortArrayReleaser {
221public:
222    static void Release(JNIEnv* _env, jshortArray array, jshort* data, jint mode) {
223        _env->ReleaseShortArrayElements(array, data, mode);
224    }
225};
226class IntArrayReleaser {
227public:
228    static void Release(JNIEnv* _env, jintArray array, jint* data, jint mode) {
229        _env->ReleaseIntArrayElements(array, data, mode);
230    }
231};
232class LongArrayReleaser {
233public:
234    static void Release(JNIEnv* _env, jlongArray array, jlong* data, jint mode) {
235        _env->ReleaseLongArrayElements(array, data, mode);
236    }
237};
238class FloatArrayReleaser {
239public:
240    static void Release(JNIEnv* _env, jfloatArray array, jfloat* data, jint mode) {
241        _env->ReleaseFloatArrayElements(array, data, mode);
242    }
243};
244class DoubleArrayReleaser {
245public:
246    static void Release(JNIEnv* _env, jdoubleArray array, jdouble* data, jint mode) {
247        _env->ReleaseDoubleArrayElements(array, data, mode);
248    }
249};
250
251template<class JArray, class T, class ArrayGetter, class ArrayReleaser>
252class ArrayHelper {
253public:
254    ArrayHelper(JNIEnv* env, JArray ref, jint offset, jint minSize) {
255        mEnv = env;
256        mRef = ref;
257        mOffset = offset;
258        mMinSize = minSize;
259        mBase = 0;
260        mReleaseParam = JNI_ABORT;
261    }
262
263    ~ArrayHelper() {
264        if (mBase) {
265            ArrayReleaser::Release(mEnv, mRef, mBase, mReleaseParam);
266        }
267    }
268
269    // We seperate the bounds check from the initialization because we want to
270    // be able to bounds-check multiple arrays, and we can't throw an exception
271    // after we've called GetPrimitiveArrayCritical.
272
273    // Return true if the bounds check succeeded
274    // Else instruct the runtime to throw an exception
275
276    bool check() {
277        if ( ! mRef) {
278            doThrowIAE(mEnv, "array == null");
279            return false;
280        }
281        if ( mOffset < 0) {
282            doThrowIAE(mEnv, "offset < 0");
283            return false;
284        }
285        mLength = mEnv->GetArrayLength(mRef) - mOffset;
286        if (mLength < mMinSize ) {
287            doThrowIAE(mEnv, "length - offset < n");
288            return false;
289        }
290        return true;
291    }
292
293    // Bind the array.
294
295    void bind() {
296        mBase = (T*) ArrayGetter::Get(mEnv, mRef, (jboolean *) 0);
297        mData = mBase + mOffset;
298    }
299
300    void commitChanges() {
301        mReleaseParam = 0;
302    }
303
304    T* mData;
305    int mLength;
306
307private:
308    T* mBase;
309    JNIEnv* mEnv;
310    JArray mRef;
311    jint mOffset;
312    jint mMinSize;
313    int mReleaseParam;
314};
315
316typedef ArrayHelper<jfloatArray, float, FloatArrayGetter, FloatArrayReleaser> FloatArrayHelper;
317typedef ArrayHelper<jcharArray, unsigned short, CharArrayGetter, CharArrayReleaser> UnsignedShortArrayHelper;
318typedef ArrayHelper<jintArray, int, IntArrayGetter, IntArrayReleaser> IntArrayHelper;
319typedef ArrayHelper<jbyteArray, unsigned char, ByteArrayGetter, ByteArrayReleaser> ByteArrayHelper;
320
321inline float distance2(float x, float y, float z) {
322    return x * x + y * y + z * z;
323}
324
325inline float distance(float x, float y, float z) {
326    return sqrtf(distance2(x, y, z));
327}
328
329static
330void util_computeBoundingSphere(JNIEnv *env, jclass clazz,
331        jfloatArray positions_ref, jint positionsOffset, jint positionsCount,
332        jfloatArray sphere_ref, jint sphereOffset) {
333    FloatArrayHelper positions(env, positions_ref, positionsOffset, 0);
334    FloatArrayHelper sphere(env, sphere_ref, sphereOffset, 4);
335
336    bool checkOK = positions.check() && sphere.check();
337        if (! checkOK) {
338        return;
339    }
340
341    positions.bind();
342    sphere.bind();
343
344    if ( positionsCount < 1 ) {
345        doThrowIAE(env, "positionsCount < 1");
346        return;
347    }
348
349    const float* pSrc = positions.mData;
350
351    // find bounding box
352    float x0 = *pSrc++;
353    float x1 = x0;
354    float y0 = *pSrc++;
355    float y1 = y0;
356    float z0 = *pSrc++;
357    float z1 = z0;
358
359    for(int i = 1; i < positionsCount; i++) {
360        {
361            float x = *pSrc++;
362            if (x < x0) {
363                x0 = x;
364            }
365            else if (x > x1) {
366                x1 = x;
367            }
368        }
369        {
370            float y = *pSrc++;
371            if (y < y0) {
372                y0 = y;
373            }
374            else if (y > y1) {
375                y1 = y;
376            }
377        }
378        {
379            float z = *pSrc++;
380            if (z < z0) {
381                z0 = z;
382            }
383            else if (z > z1) {
384                z1 = z;
385            }
386        }
387    }
388
389    // Because we know our input meshes fit pretty well into bounding boxes,
390    // just take the diagonal of the box as defining our sphere.
391    float* pSphere = sphere.mData;
392    float dx = x1 - x0;
393    float dy = y1 - y0;
394    float dz = z1 - z0;
395    *pSphere++ = x0 + dx * 0.5f;
396    *pSphere++ = y0 + dy * 0.5f;
397    *pSphere++ = z0 + dz * 0.5f;
398    *pSphere++ = distance(dx, dy, dz) * 0.5f;
399
400    sphere.commitChanges();
401}
402
403static void normalizePlane(float* p) {
404    float rdist = 1.0f / distance(p[0], p[1], p[2]);
405    for(int i = 0; i < 4; i++) {
406        p[i] *= rdist;
407    }
408}
409
410static inline float dot3(float x0, float y0, float z0, float x1, float y1, float z1) {
411    return x0 * x1 + y0 * y1 + z0 * z1;
412}
413
414static inline float signedDistance(const float* pPlane, float x, float y, float z) {
415    return dot3(pPlane[0], pPlane[1], pPlane[2], x, y, z) + pPlane[3];
416}
417
418// Return true if the sphere intersects or is inside the frustum
419
420static bool sphereHitsFrustum(const float* pFrustum, const float* pSphere) {
421    float x = pSphere[0];
422    float y = pSphere[1];
423    float z = pSphere[2];
424    float negRadius = -pSphere[3];
425    for (int i = 0; i < 6; i++, pFrustum += 4) {
426        if (signedDistance(pFrustum, x, y, z) <= negRadius) {
427            return false;
428        }
429    }
430    return true;
431}
432
433static void computeFrustum(const float* m, float* f) {
434    float m3 = m[3];
435    float m7 = m[7];
436    float m11 = m[11];
437    float m15 = m[15];
438    // right
439    f[0] = m3  - m[0];
440    f[1] = m7  - m[4];
441    f[2] = m11 - m[8];
442    f[3] = m15 - m[12];
443    normalizePlane(f);
444    f+= 4;
445
446    // left
447    f[0] = m3  + m[0];
448    f[1] = m7  + m[4];
449    f[2] = m11 + m[8];
450    f[3] = m15 + m[12];
451    normalizePlane(f);
452    f+= 4;
453
454    // top
455    f[0] = m3  - m[1];
456    f[1] = m7  - m[5];
457    f[2] = m11 - m[9];
458    f[3] = m15 - m[13];
459    normalizePlane(f);
460    f+= 4;
461
462    // bottom
463    f[0] = m3  + m[1];
464    f[1] = m7  + m[5];
465    f[2] = m11 + m[9];
466    f[3] = m15 + m[13];
467    normalizePlane(f);
468    f+= 4;
469
470    // far
471    f[0] = m3  - m[2];
472    f[1] = m7  - m[6];
473    f[2] = m11 - m[10];
474    f[3] = m15 - m[14];
475    normalizePlane(f);
476    f+= 4;
477
478    // near
479    f[0] = m3  + m[2];
480    f[1] = m7  + m[6];
481    f[2] = m11 + m[10];
482    f[3] = m15 + m[14];
483    normalizePlane(f);
484}
485
486static
487jint util_frustumCullSpheres(JNIEnv *env, jclass clazz,
488        jfloatArray mvp_ref, jint mvpOffset,
489        jfloatArray spheres_ref, jint spheresOffset, jint spheresCount,
490        jintArray results_ref, jint resultsOffset, jint resultsCapacity) {
491    float frustum[6*4];
492    int outputCount;
493    int* pResults;
494    float* pSphere;
495    FloatArrayHelper mvp(env, mvp_ref, mvpOffset, 16);
496    FloatArrayHelper spheres(env, spheres_ref, spheresOffset, spheresCount * 4);
497    IntArrayHelper results(env, results_ref, resultsOffset, resultsCapacity);
498
499    bool initializedOK = mvp.check() && spheres.check() && results.check();
500        if (! initializedOK) {
501        return -1;
502    }
503
504    mvp.bind();
505    spheres.bind();
506    results.bind();
507
508    computeFrustum(mvp.mData, frustum);
509
510    // Cull the spheres
511
512    pSphere = spheres.mData;
513    pResults = results.mData;
514    outputCount = 0;
515    for(int i = 0; i < spheresCount; i++, pSphere += 4) {
516        if (sphereHitsFrustum(frustum, pSphere)) {
517            if (outputCount < resultsCapacity) {
518                *pResults++ = i;
519            }
520            outputCount++;
521        }
522    }
523    results.commitChanges();
524    return outputCount;
525}
526
527/*
528 public native int visibilityTest(float[] ws, int wsOffset,
529 float[] positions, int positionsOffset,
530 char[] indices, int indicesOffset, int indexCount);
531 */
532
533static
534jint util_visibilityTest(JNIEnv *env, jclass clazz,
535        jfloatArray ws_ref, jint wsOffset,
536        jfloatArray positions_ref, jint positionsOffset,
537        jcharArray indices_ref, jint indicesOffset, jint indexCount) {
538
539    FloatArrayHelper ws(env, ws_ref, wsOffset, 16);
540    FloatArrayHelper positions(env, positions_ref, positionsOffset, 0);
541    UnsignedShortArrayHelper indices(env, indices_ref, indicesOffset, 0);
542
543    bool checkOK = ws.check() && positions.check() && indices.check();
544    if (! checkOK) {
545        // Return value will be ignored, because an exception has been thrown.
546        return -1;
547    }
548
549    if (indices.mLength < indexCount) {
550        doThrowIAE(env, "length < offset + indexCount");
551        return -1;
552    }
553
554    ws.bind();
555    positions.bind();
556    indices.bind();
557
558    return visibilityTest(ws.mData,
559            positions.mData, positions.mLength,
560            indices.mData, indexCount);
561}
562
563#define I(_i, _j) ((_j)+ 4*(_i))
564
565static
566void multiplyMM(float* r, const float* lhs, const float* rhs)
567{
568    for (int i=0 ; i<4 ; i++) {
569        const float rhs_i0 = rhs[ I(i,0) ];
570        float ri0 = lhs[ I(0,0) ] * rhs_i0;
571        float ri1 = lhs[ I(0,1) ] * rhs_i0;
572        float ri2 = lhs[ I(0,2) ] * rhs_i0;
573        float ri3 = lhs[ I(0,3) ] * rhs_i0;
574        for (int j=1 ; j<4 ; j++) {
575            const float rhs_ij = rhs[ I(i,j) ];
576            ri0 += lhs[ I(j,0) ] * rhs_ij;
577            ri1 += lhs[ I(j,1) ] * rhs_ij;
578            ri2 += lhs[ I(j,2) ] * rhs_ij;
579            ri3 += lhs[ I(j,3) ] * rhs_ij;
580        }
581        r[ I(i,0) ] = ri0;
582        r[ I(i,1) ] = ri1;
583        r[ I(i,2) ] = ri2;
584        r[ I(i,3) ] = ri3;
585    }
586}
587
588static
589void util_multiplyMM(JNIEnv *env, jclass clazz,
590    jfloatArray result_ref, jint resultOffset,
591    jfloatArray lhs_ref, jint lhsOffset,
592    jfloatArray rhs_ref, jint rhsOffset) {
593
594    FloatArrayHelper resultMat(env, result_ref, resultOffset, 16);
595    FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16);
596    FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 16);
597
598    bool checkOK = resultMat.check() && lhs.check() && rhs.check();
599
600    if ( !checkOK ) {
601        return;
602    }
603
604    resultMat.bind();
605    lhs.bind();
606    rhs.bind();
607
608    multiplyMM(resultMat.mData, lhs.mData, rhs.mData);
609
610    resultMat.commitChanges();
611}
612
613static
614void multiplyMV(float* r, const float* lhs, const float* rhs)
615{
616    mx4transform(rhs[0], rhs[1], rhs[2], rhs[3], lhs, r);
617}
618
619static
620void util_multiplyMV(JNIEnv *env, jclass clazz,
621    jfloatArray result_ref, jint resultOffset,
622    jfloatArray lhs_ref, jint lhsOffset,
623    jfloatArray rhs_ref, jint rhsOffset) {
624
625    FloatArrayHelper resultV(env, result_ref, resultOffset, 4);
626    FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16);
627    FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 4);
628
629    bool checkOK = resultV.check() && lhs.check() && rhs.check();
630
631    if ( !checkOK ) {
632        return;
633    }
634
635    resultV.bind();
636    lhs.bind();
637    rhs.bind();
638
639    multiplyMV(resultV.mData, lhs.mData, rhs.mData);
640
641    resultV.commitChanges();
642}
643
644// ---------------------------------------------------------------------------
645
646static int checkFormat(SkColorType colorType, int format, int type)
647{
648    switch(colorType) {
649        case kIndex_8_SkColorType:
650            if (format == GL_PALETTE8_RGBA8_OES)
651                return 0;
652        case kN32_SkColorType:
653        case kAlpha_8_SkColorType:
654            if (type == GL_UNSIGNED_BYTE)
655                return 0;
656        case kARGB_4444_SkColorType:
657        case kRGB_565_SkColorType:
658            switch (type) {
659                case GL_UNSIGNED_SHORT_4_4_4_4:
660                case GL_UNSIGNED_SHORT_5_6_5:
661                case GL_UNSIGNED_SHORT_5_5_5_1:
662                    return 0;
663                case GL_UNSIGNED_BYTE:
664                    if (format == GL_LUMINANCE_ALPHA)
665                        return 0;
666            }
667            break;
668        default:
669            break;
670    }
671    return -1;
672}
673
674static int getInternalFormat(SkColorType colorType)
675{
676    switch(colorType) {
677        case kAlpha_8_SkColorType:
678            return GL_ALPHA;
679        case kARGB_4444_SkColorType:
680            return GL_RGBA;
681        case kN32_SkColorType:
682            return GL_RGBA;
683        case kIndex_8_SkColorType:
684            return GL_PALETTE8_RGBA8_OES;
685        case kRGB_565_SkColorType:
686            return GL_RGB;
687        default:
688            return -1;
689    }
690}
691
692static int getType(SkColorType colorType)
693{
694    switch(colorType) {
695        case kAlpha_8_SkColorType:
696            return GL_UNSIGNED_BYTE;
697        case kARGB_4444_SkColorType:
698            return GL_UNSIGNED_SHORT_4_4_4_4;
699        case kN32_SkColorType:
700            return GL_UNSIGNED_BYTE;
701        case kIndex_8_SkColorType:
702            return -1; // No type for compressed data.
703        case kRGB_565_SkColorType:
704            return GL_UNSIGNED_SHORT_5_6_5;
705        default:
706            return -1;
707    }
708}
709
710static jint util_getInternalFormat(JNIEnv *env, jclass clazz,
711        jobject jbitmap)
712{
713    SkBitmap nativeBitmap;
714    GraphicsJNI::getSkBitmap(env, jbitmap, &nativeBitmap);
715    return getInternalFormat(nativeBitmap.colorType());
716}
717
718static jint util_getType(JNIEnv *env, jclass clazz,
719        jobject jbitmap)
720{
721    SkBitmap nativeBitmap;
722    GraphicsJNI::getSkBitmap(env, jbitmap, &nativeBitmap);
723    return getType(nativeBitmap.colorType());
724}
725
726static jint util_texImage2D(JNIEnv *env, jclass clazz,
727        jint target, jint level, jint internalformat,
728        jobject jbitmap, jint type, jint border)
729{
730    SkBitmap bitmap;
731    GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
732    SkColorType colorType = bitmap.colorType();
733    if (internalformat < 0) {
734        internalformat = getInternalFormat(colorType);
735    }
736    if (type < 0) {
737        type = getType(colorType);
738    }
739    int err = checkFormat(colorType, internalformat, type);
740    if (err)
741        return err;
742    bitmap.lockPixels();
743    const int w = bitmap.width();
744    const int h = bitmap.height();
745    const void* p = bitmap.getPixels();
746    if (internalformat == GL_PALETTE8_RGBA8_OES) {
747        if (sizeof(SkPMColor) != sizeof(uint32_t)) {
748            err = -1;
749            goto error;
750        }
751        const size_t size = bitmap.getSize();
752        const size_t palette_size = 256*sizeof(SkPMColor);
753        const size_t imageSize = size + palette_size;
754        void* const data = malloc(imageSize);
755        if (data) {
756            void* const pixels = (char*)data + palette_size;
757            SkColorTable* ctable = bitmap.getColorTable();
758            memcpy(data, ctable->readColors(), ctable->count() * sizeof(SkPMColor));
759            memcpy(pixels, p, size);
760            glCompressedTexImage2D(target, level, internalformat, w, h, border, imageSize, data);
761            free(data);
762        } else {
763            err = -1;
764        }
765    } else {
766        glTexImage2D(target, level, internalformat, w, h, border, internalformat, type, p);
767    }
768error:
769    bitmap.unlockPixels();
770    return err;
771}
772
773static jint util_texSubImage2D(JNIEnv *env, jclass clazz,
774        jint target, jint level, jint xoffset, jint yoffset,
775        jobject jbitmap, jint format, jint type)
776{
777    SkBitmap bitmap;
778    GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
779    SkColorType colorType = bitmap.colorType();
780    if (format < 0) {
781        format = getInternalFormat(colorType);
782        if (format == GL_PALETTE8_RGBA8_OES)
783            return -1; // glCompressedTexSubImage2D() not supported
784    }
785    int err = checkFormat(colorType, format, type);
786    if (err)
787        return err;
788    bitmap.lockPixels();
789    const int w = bitmap.width();
790    const int h = bitmap.height();
791    const void* p = bitmap.getPixels();
792    glTexSubImage2D(target, level, xoffset, yoffset, w, h, format, type, p);
793    bitmap.unlockPixels();
794    return 0;
795}
796
797/*
798 * ETC1 methods.
799 */
800
801static jclass nioAccessClass;
802static jclass bufferClass;
803static jmethodID getBasePointerID;
804static jmethodID getBaseArrayID;
805static jmethodID getBaseArrayOffsetID;
806static jfieldID positionID;
807static jfieldID limitID;
808static jfieldID elementSizeShiftID;
809
810/* Cache method IDs each time the class is loaded. */
811
812static void
813nativeClassInitBuffer(JNIEnv *env)
814{
815    jclass nioAccessClassLocal = FindClassOrDie(env, "java/nio/NIOAccess");
816    nioAccessClass = MakeGlobalRefOrDie(env, nioAccessClassLocal);
817    getBasePointerID = GetStaticMethodIDOrDie(env, nioAccessClass,
818            "getBasePointer", "(Ljava/nio/Buffer;)J");
819    getBaseArrayID = GetStaticMethodIDOrDie(env, nioAccessClass,
820            "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;");
821    getBaseArrayOffsetID = GetStaticMethodIDOrDie(env, nioAccessClass,
822            "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
823
824    jclass bufferClassLocal = FindClassOrDie(env, "java/nio/Buffer");
825    bufferClass = MakeGlobalRefOrDie(env, bufferClassLocal);
826    positionID = GetFieldIDOrDie(env, bufferClass, "position", "I");
827    limitID = GetFieldIDOrDie(env, bufferClass, "limit", "I");
828    elementSizeShiftID = GetFieldIDOrDie(env, bufferClass, "_elementSizeShift", "I");
829}
830
831static void *
832getPointer(JNIEnv *_env, jobject buffer, jint *remaining)
833{
834    jint position;
835    jint limit;
836    jint elementSizeShift;
837    jlong pointer;
838
839    position = _env->GetIntField(buffer, positionID);
840    limit = _env->GetIntField(buffer, limitID);
841    elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
842    *remaining = (limit - position) << elementSizeShift;
843    pointer = _env->CallStaticLongMethod(nioAccessClass,
844            getBasePointerID, buffer);
845    if (pointer != 0L) {
846        return reinterpret_cast<void *>(pointer);
847    }
848    return NULL;
849}
850
851class BufferHelper {
852public:
853    BufferHelper(JNIEnv *env, jobject buffer) {
854        mEnv = env;
855        mBuffer = buffer;
856        mData = NULL;
857        mRemaining = 0;
858    }
859
860    bool checkPointer(const char* errorMessage) {
861        if (mBuffer) {
862            mData = getPointer(mEnv, mBuffer, &mRemaining);
863            if (mData == NULL) {
864                doThrowIAE(mEnv, errorMessage);
865            }
866            return mData != NULL;
867        } else {
868            doThrowIAE(mEnv, errorMessage);
869            return false;
870        }
871    }
872
873    inline void* getData() {
874        return mData;
875    }
876
877    inline jint remaining() {
878        return mRemaining;
879    }
880
881private:
882    JNIEnv* mEnv;
883    jobject mBuffer;
884    void* mData;
885    jint mRemaining;
886};
887
888/**
889 * Encode a block of pixels.
890 *
891 * @param in a pointer to a ETC1_DECODED_BLOCK_SIZE array of bytes that represent a
892 * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
893 * value of pixel (x, y).
894 *
895 * @param validPixelMask is a 16-bit mask where bit (1 << (x + y * 4)) indicates whether
896 * the corresponding (x,y) pixel is valid. Invalid pixel color values are ignored when compressing.
897 *
898 * @param out an ETC1 compressed version of the data.
899 *
900 */
901static void etc1_encodeBlock(JNIEnv *env, jclass clazz,
902        jobject in, jint validPixelMask, jobject out) {
903    if (validPixelMask < 0 || validPixelMask > 15) {
904        doThrowIAE(env, "validPixelMask");
905        return;
906    }
907    BufferHelper inB(env, in);
908    BufferHelper outB(env, out);
909    if (inB.checkPointer("in") && outB.checkPointer("out")) {
910        if (inB.remaining() < ETC1_DECODED_BLOCK_SIZE) {
911            doThrowIAE(env, "in's remaining data < DECODED_BLOCK_SIZE");
912        } else if (outB.remaining() < ETC1_ENCODED_BLOCK_SIZE) {
913            doThrowIAE(env, "out's remaining data < ENCODED_BLOCK_SIZE");
914        } else {
915            etc1_encode_block((etc1_byte*) inB.getData(), validPixelMask,
916                    (etc1_byte*) outB.getData());
917        }
918    }
919}
920
921/**
922 * Decode a block of pixels.
923 *
924 * @param in an ETC1 compressed version of the data.
925 *
926 * @param out a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a
927 * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
928 * value of pixel (x, y).
929 */
930static void etc1_decodeBlock(JNIEnv *env, jclass clazz,
931        jobject in, jobject out){
932    BufferHelper inB(env, in);
933    BufferHelper outB(env, out);
934    if (inB.checkPointer("in") && outB.checkPointer("out")) {
935        if (inB.remaining() < ETC1_ENCODED_BLOCK_SIZE) {
936            doThrowIAE(env, "in's remaining data < ENCODED_BLOCK_SIZE");
937        } else if (outB.remaining() < ETC1_DECODED_BLOCK_SIZE) {
938            doThrowIAE(env, "out's remaining data < DECODED_BLOCK_SIZE");
939        } else {
940            etc1_decode_block((etc1_byte*) inB.getData(),
941                    (etc1_byte*) outB.getData());
942        }
943    }
944}
945
946/**
947 * Return the size of the encoded image data (does not include size of PKM header).
948 */
949static jint etc1_getEncodedDataSize(JNIEnv *env, jclass clazz,
950        jint width, jint height) {
951    return etc1_get_encoded_data_size(width, height);
952}
953
954/**
955 * Encode an entire image.
956 * @param in pointer to the image data. Formatted such that
957 *           pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset;
958 * @param out pointer to encoded data. Must be large enough to store entire encoded image.
959 */
960static void etc1_encodeImage(JNIEnv *env, jclass clazz,
961        jobject in, jint width, jint height,
962        jint pixelSize, jint stride, jobject out) {
963    if (pixelSize < 2 || pixelSize > 3) {
964        doThrowIAE(env, "pixelSize must be 2 or 3");
965        return;
966    }
967    BufferHelper inB(env, in);
968    BufferHelper outB(env, out);
969    if (inB.checkPointer("in") && outB.checkPointer("out")) {
970        jint imageSize = stride * height;
971        jint encodedImageSize = etc1_get_encoded_data_size(width, height);
972        if (inB.remaining() < imageSize) {
973            doThrowIAE(env, "in's remaining data < image size");
974        } else if (outB.remaining() < encodedImageSize) {
975            doThrowIAE(env, "out's remaining data < encoded image size");
976        } else {
977            etc1_encode_image((etc1_byte*) inB.getData(), width, height, pixelSize, stride,
978                              (etc1_byte*) outB.getData());
979        }
980    }
981}
982
983/**
984 * Decode an entire image.
985 * @param in the encoded data.
986 * @param out pointer to the image data. Will be written such that
987 *            pixel (x,y) is at pIn + pixelSize * x + stride * y. Must be
988 *            large enough to store entire image.
989 */
990static void etc1_decodeImage(JNIEnv *env, jclass clazz,
991        jobject  in, jobject out,
992        jint width, jint height,
993        jint pixelSize, jint stride) {
994    if (pixelSize < 2 || pixelSize > 3) {
995        doThrowIAE(env, "pixelSize must be 2 or 3");
996        return;
997    }
998    BufferHelper inB(env, in);
999    BufferHelper outB(env, out);
1000    if (inB.checkPointer("in") && outB.checkPointer("out")) {
1001        jint imageSize = stride * height;
1002        jint encodedImageSize = etc1_get_encoded_data_size(width, height);
1003        if (inB.remaining() < encodedImageSize) {
1004            doThrowIAE(env, "in's remaining data < encoded image size");
1005        } else if (outB.remaining() < imageSize) {
1006            doThrowIAE(env, "out's remaining data < image size");
1007        } else {
1008            etc1_decode_image((etc1_byte*) inB.getData(), (etc1_byte*) outB.getData(),
1009                              width, height, pixelSize, stride);
1010        }
1011    }
1012}
1013
1014/**
1015 * Format a PKM header
1016 */
1017static void etc1_formatHeader(JNIEnv *env, jclass clazz,
1018        jobject header, jint width, jint height) {
1019    BufferHelper headerB(env, header);
1020    if (headerB.checkPointer("header") ){
1021        if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
1022            doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1023        } else {
1024            etc1_pkm_format_header((etc1_byte*) headerB.getData(), width, height);
1025        }
1026    }
1027}
1028
1029/**
1030 * Check if a PKM header is correctly formatted.
1031 */
1032static jboolean etc1_isValid(JNIEnv *env, jclass clazz,
1033        jobject header) {
1034    jboolean result = false;
1035    BufferHelper headerB(env, header);
1036    if (headerB.checkPointer("header") ){
1037        if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
1038            doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1039        } else {
1040            result = etc1_pkm_is_valid((etc1_byte*) headerB.getData());
1041        }
1042    }
1043    return result ? JNI_TRUE : JNI_FALSE;
1044}
1045
1046/**
1047 * Read the image width from a PKM header
1048 */
1049static jint etc1_getWidth(JNIEnv *env, jclass clazz,
1050        jobject header) {
1051    jint result = 0;
1052    BufferHelper headerB(env, header);
1053    if (headerB.checkPointer("header") ){
1054        if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
1055            doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1056        } else {
1057            result = etc1_pkm_get_width((etc1_byte*) headerB.getData());
1058        }
1059    }
1060    return result;
1061}
1062
1063/**
1064 * Read the image height from a PKM header
1065 */
1066static jint etc1_getHeight(JNIEnv *env, jclass clazz,
1067        jobject header) {
1068    jint result = 0;
1069    BufferHelper headerB(env, header);
1070    if (headerB.checkPointer("header") ){
1071        if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
1072            doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1073        } else {
1074            result = etc1_pkm_get_height((etc1_byte*) headerB.getData());
1075        }
1076    }
1077    return result;
1078}
1079
1080/*
1081 * JNI registration
1082 */
1083
1084static const JNINativeMethod gMatrixMethods[] = {
1085    { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM },
1086    { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV },
1087};
1088
1089static const JNINativeMethod gVisibilityMethods[] = {
1090    { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere },
1091    { "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres },
1092    { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest },
1093};
1094
1095static const JNINativeMethod gUtilsMethods[] = {
1096    { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat },
1097    { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType },
1098    { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D },
1099    { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D },
1100};
1101
1102static const JNINativeMethod gEtc1Methods[] = {
1103    { "encodeBlock", "(Ljava/nio/Buffer;ILjava/nio/Buffer;)V", (void*) etc1_encodeBlock },
1104    { "decodeBlock", "(Ljava/nio/Buffer;Ljava/nio/Buffer;)V", (void*) etc1_decodeBlock },
1105    { "getEncodedDataSize", "(II)I", (void*) etc1_getEncodedDataSize },
1106    { "encodeImage", "(Ljava/nio/Buffer;IIIILjava/nio/Buffer;)V", (void*) etc1_encodeImage },
1107    { "decodeImage", "(Ljava/nio/Buffer;Ljava/nio/Buffer;IIII)V", (void*) etc1_decodeImage },
1108    { "formatHeader", "(Ljava/nio/Buffer;II)V", (void*) etc1_formatHeader },
1109    { "isValid", "(Ljava/nio/Buffer;)Z", (void*) etc1_isValid },
1110    { "getWidth", "(Ljava/nio/Buffer;)I", (void*) etc1_getWidth },
1111    { "getHeight", "(Ljava/nio/Buffer;)I", (void*) etc1_getHeight },
1112};
1113
1114typedef struct _ClassRegistrationInfo {
1115    const char* classPath;
1116    const JNINativeMethod* methods;
1117    size_t methodCount;
1118} ClassRegistrationInfo;
1119
1120static const ClassRegistrationInfo gClasses[] = {
1121    {"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)},
1122    {"android/opengl/Visibility", gVisibilityMethods, NELEM(gVisibilityMethods)},
1123    {"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)},
1124    {"android/opengl/ETC1", gEtc1Methods, NELEM(gEtc1Methods)},
1125};
1126
1127int register_android_opengl_classes(JNIEnv* env)
1128{
1129    nativeClassInitBuffer(env);
1130    int result = 0;
1131    for (int i = 0; i < NELEM(gClasses); i++) {
1132        const ClassRegistrationInfo* cri = &gClasses[i];
1133        result = RegisterMethodsOrDie(env, cri->classPath, cri->methods, cri->methodCount);
1134    }
1135    return result;
1136}
1137
1138} // namespace android
1139