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