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