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