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     LOGI("%s: %d verts", label, pPoly->n);
     77     for(int i = 0; i < pPoly->n; i++) {
     78         Poly_vert* pV = & pPoly->vert[i];
     79         LOGI("[%d] %g, %g, %g %g", i, pV->sx, pV->sy, pV->sz, pV->sw);
     80     }
     81 }
     82 #endif
     83 
     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 static int checkFormat(SkBitmap::Config config, int format, int type)
    560 {
    561     switch(config) {
    562         case SkBitmap::kIndex8_Config:
    563             if (format == GL_PALETTE8_RGBA8_OES)
    564                 return 0;
    565         case SkBitmap::kARGB_8888_Config:
    566         case SkBitmap::kA8_Config:
    567             if (type == GL_UNSIGNED_BYTE)
    568                 return 0;
    569         case SkBitmap::kARGB_4444_Config:
    570         case SkBitmap::kRGB_565_Config:
    571             switch (type) {
    572                 case GL_UNSIGNED_SHORT_4_4_4_4:
    573                 case GL_UNSIGNED_SHORT_5_6_5:
    574                 case GL_UNSIGNED_SHORT_5_5_5_1:
    575                     return 0;
    576                 case GL_UNSIGNED_BYTE:
    577                     if (format == GL_LUMINANCE_ALPHA)
    578                         return 0;
    579             }
    580             break;
    581         default:
    582             break;
    583     }
    584     return -1;
    585 }
    586 
    587 static int getInternalFormat(SkBitmap::Config config)
    588 {
    589     switch(config) {
    590         case SkBitmap::kA8_Config:
    591             return GL_ALPHA;
    592         case SkBitmap::kARGB_4444_Config:
    593             return GL_RGBA;
    594         case SkBitmap::kARGB_8888_Config:
    595             return GL_RGBA;
    596         case SkBitmap::kIndex8_Config:
    597             return GL_PALETTE8_RGBA8_OES;
    598         case SkBitmap::kRGB_565_Config:
    599             return GL_RGB;
    600         default:
    601             return -1;
    602     }
    603 }
    604 
    605 static int getType(SkBitmap::Config config)
    606 {
    607     switch(config) {
    608         case SkBitmap::kA8_Config:
    609             return GL_UNSIGNED_BYTE;
    610         case SkBitmap::kARGB_4444_Config:
    611             return GL_UNSIGNED_SHORT_4_4_4_4;
    612         case SkBitmap::kARGB_8888_Config:
    613             return GL_UNSIGNED_BYTE;
    614         case SkBitmap::kIndex8_Config:
    615             return -1; // No type for compressed data.
    616         case SkBitmap::kRGB_565_Config:
    617             return GL_UNSIGNED_SHORT_5_6_5;
    618         default:
    619             return -1;
    620     }
    621 }
    622 
    623 static jint util_getInternalFormat(JNIEnv *env, jclass clazz,
    624         jobject jbitmap)
    625 {
    626     SkBitmap const * nativeBitmap =
    627             (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID);
    628     const SkBitmap& bitmap(*nativeBitmap);
    629     SkBitmap::Config config = bitmap.getConfig();
    630     return getInternalFormat(config);
    631 }
    632 
    633 static jint util_getType(JNIEnv *env, jclass clazz,
    634         jobject jbitmap)
    635 {
    636     SkBitmap const * nativeBitmap =
    637             (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID);
    638     const SkBitmap& bitmap(*nativeBitmap);
    639     SkBitmap::Config config = bitmap.getConfig();
    640     return getType(config);
    641 }
    642 
    643 static jint util_texImage2D(JNIEnv *env, jclass clazz,
    644         jint target, jint level, jint internalformat,
    645         jobject jbitmap, jint type, jint border)
    646 {
    647     SkBitmap const * nativeBitmap =
    648             (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID);
    649     const SkBitmap& bitmap(*nativeBitmap);
    650     SkBitmap::Config config = bitmap.getConfig();
    651     if (internalformat < 0) {
    652         internalformat = getInternalFormat(config);
    653     }
    654     if (type < 0) {
    655         type = getType(config);
    656     }
    657     int err = checkFormat(config, internalformat, type);
    658     if (err)
    659         return err;
    660     bitmap.lockPixels();
    661     const int w = bitmap.width();
    662     const int h = bitmap.height();
    663     const void* p = bitmap.getPixels();
    664     if (internalformat == GL_PALETTE8_RGBA8_OES) {
    665         if (sizeof(SkPMColor) != sizeof(uint32_t)) {
    666             err = -1;
    667             goto error;
    668         }
    669         const size_t size = bitmap.getSize();
    670         const size_t palette_size = 256*sizeof(SkPMColor);
    671         const size_t imageSize = size + palette_size;
    672         void* const data = malloc(imageSize);
    673         if (data) {
    674             void* const pixels = (char*)data + palette_size;
    675             SkColorTable* ctable = bitmap.getColorTable();
    676             memcpy(data, ctable->lockColors(), ctable->count() * sizeof(SkPMColor));
    677             memcpy(pixels, p, size);
    678             ctable->unlockColors(false);
    679             glCompressedTexImage2D(target, level, internalformat, w, h, border, imageSize, data);
    680             free(data);
    681         } else {
    682             err = -1;
    683         }
    684     } else {
    685         glTexImage2D(target, level, internalformat, w, h, border, internalformat, type, p);
    686     }
    687 error:
    688     bitmap.unlockPixels();
    689     return err;
    690 }
    691 
    692 static jint util_texSubImage2D(JNIEnv *env, jclass clazz,
    693         jint target, jint level, jint xoffset, jint yoffset,
    694         jobject jbitmap, jint format, jint type)
    695 {
    696     SkBitmap const * nativeBitmap =
    697             (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID);
    698     const SkBitmap& bitmap(*nativeBitmap);
    699     SkBitmap::Config config = bitmap.getConfig();
    700     if (format < 0) {
    701         format = getInternalFormat(config);
    702         if (format == GL_PALETTE8_RGBA8_OES)
    703             return -1; // glCompressedTexSubImage2D() not supported
    704     }
    705     int err = checkFormat(config, format, type);
    706     if (err)
    707         return err;
    708     bitmap.lockPixels();
    709     const int w = bitmap.width();
    710     const int h = bitmap.height();
    711     const void* p = bitmap.getPixels();
    712     glTexSubImage2D(target, level, xoffset, yoffset, w, h, format, type, p);
    713     bitmap.unlockPixels();
    714     return 0;
    715 }
    716 
    717 /*
    718  * ETC1 methods.
    719  */
    720 
    721 static jclass nioAccessClass;
    722 static jclass bufferClass;
    723 static jmethodID getBasePointerID;
    724 static jmethodID getBaseArrayID;
    725 static jmethodID getBaseArrayOffsetID;
    726 static jfieldID positionID;
    727 static jfieldID limitID;
    728 static jfieldID elementSizeShiftID;
    729 
    730 /* Cache method IDs each time the class is loaded. */
    731 
    732 static void
    733 nativeClassInitBuffer(JNIEnv *_env)
    734 {
    735     jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
    736     nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
    737 
    738     jclass bufferClassLocal = _env->FindClass("java/nio/Buffer");
    739     bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal);
    740 
    741     getBasePointerID = _env->GetStaticMethodID(nioAccessClass,
    742             "getBasePointer", "(Ljava/nio/Buffer;)J");
    743     getBaseArrayID = _env->GetStaticMethodID(nioAccessClass,
    744             "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;");
    745     getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass,
    746             "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
    747     positionID = _env->GetFieldID(bufferClass, "position", "I");
    748     limitID = _env->GetFieldID(bufferClass, "limit", "I");
    749     elementSizeShiftID =
    750         _env->GetFieldID(bufferClass, "_elementSizeShift", "I");
    751 }
    752 
    753 static void *
    754 getPointer(JNIEnv *_env, jobject buffer, jint *remaining)
    755 {
    756     jint position;
    757     jint limit;
    758     jint elementSizeShift;
    759     jlong pointer;
    760     jint offset;
    761     void *data;
    762 
    763     position = _env->GetIntField(buffer, positionID);
    764     limit = _env->GetIntField(buffer, limitID);
    765     elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
    766     *remaining = (limit - position) << elementSizeShift;
    767     pointer = _env->CallStaticLongMethod(nioAccessClass,
    768             getBasePointerID, buffer);
    769     if (pointer != 0L) {
    770         return (void *) (jint) pointer;
    771     }
    772     return NULL;
    773 }
    774 
    775 class BufferHelper {
    776 public:
    777     BufferHelper(JNIEnv *env, jobject buffer) {
    778         mEnv = env;
    779         mBuffer = buffer;
    780         mData = NULL;
    781         mRemaining = 0;
    782     }
    783 
    784     bool checkPointer(const char* errorMessage) {
    785         if (mBuffer) {
    786             mData = getPointer(mEnv, mBuffer, &mRemaining);
    787             if (mData == NULL) {
    788                 doThrowIAE(mEnv, errorMessage);
    789             }
    790             return mData != NULL;
    791         } else {
    792             doThrowIAE(mEnv, errorMessage);
    793             return false;
    794         }
    795     }
    796 
    797     inline void* getData() {
    798         return mData;
    799     }
    800 
    801     inline jint remaining() {
    802         return mRemaining;
    803     }
    804 
    805 private:
    806     JNIEnv* mEnv;
    807     jobject mBuffer;
    808     void* mData;
    809     jint mRemaining;
    810 };
    811 
    812 /**
    813  * Encode a block of pixels.
    814  *
    815  * @param in a pointer to a ETC1_DECODED_BLOCK_SIZE array of bytes that represent a
    816  * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
    817  * value of pixel (x, y).
    818  *
    819  * @param validPixelMask is a 16-bit mask where bit (1 << (x + y * 4)) indicates whether
    820  * the corresponding (x,y) pixel is valid. Invalid pixel color values are ignored when compressing.
    821  *
    822  * @param out an ETC1 compressed version of the data.
    823  *
    824  */
    825 static void etc1_encodeBlock(JNIEnv *env, jclass clazz,
    826         jobject in, jint validPixelMask, jobject out) {
    827     if (validPixelMask < 0 || validPixelMask > 15) {
    828         doThrowIAE(env, "validPixelMask");
    829         return;
    830     }
    831     BufferHelper inB(env, in);
    832     BufferHelper outB(env, out);
    833     if (inB.checkPointer("in") && outB.checkPointer("out")) {
    834         if (inB.remaining() < ETC1_DECODED_BLOCK_SIZE) {
    835             doThrowIAE(env, "in's remaining data < DECODED_BLOCK_SIZE");
    836         } else if (outB.remaining() < ETC1_ENCODED_BLOCK_SIZE) {
    837             doThrowIAE(env, "out's remaining data < ENCODED_BLOCK_SIZE");
    838         } else {
    839             etc1_encode_block((etc1_byte*) inB.getData(), validPixelMask,
    840                     (etc1_byte*) outB.getData());
    841         }
    842     }
    843 }
    844 
    845 /**
    846  * Decode a block of pixels.
    847  *
    848  * @param in an ETC1 compressed version of the data.
    849  *
    850  * @param out a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a
    851  * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
    852  * value of pixel (x, y).
    853  */
    854 static void etc1_decodeBlock(JNIEnv *env, jclass clazz,
    855         jobject in, jobject out){
    856     BufferHelper inB(env, in);
    857     BufferHelper outB(env, out);
    858     if (inB.checkPointer("in") && outB.checkPointer("out")) {
    859         if (inB.remaining() < ETC1_ENCODED_BLOCK_SIZE) {
    860             doThrowIAE(env, "in's remaining data < ENCODED_BLOCK_SIZE");
    861         } else if (outB.remaining() < ETC1_DECODED_BLOCK_SIZE) {
    862             doThrowIAE(env, "out's remaining data < DECODED_BLOCK_SIZE");
    863         } else {
    864             etc1_decode_block((etc1_byte*) inB.getData(),
    865                     (etc1_byte*) outB.getData());
    866         }
    867     }
    868 }
    869 
    870 /**
    871  * Return the size of the encoded image data (does not include size of PKM header).
    872  */
    873 static jint etc1_getEncodedDataSize(JNIEnv *env, jclass clazz,
    874         jint width, jint height) {
    875     return etc1_get_encoded_data_size(width, height);
    876 }
    877 
    878 /**
    879  * Encode an entire image.
    880  * @param in pointer to the image data. Formatted such that
    881  *           pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset;
    882  * @param out pointer to encoded data. Must be large enough to store entire encoded image.
    883  */
    884 static void etc1_encodeImage(JNIEnv *env, jclass clazz,
    885         jobject in, jint width, jint height,
    886         jint pixelSize, jint stride, jobject out) {
    887     if (pixelSize < 2 || pixelSize > 3) {
    888         doThrowIAE(env, "pixelSize must be 2 or 3");
    889         return;
    890     }
    891     BufferHelper inB(env, in);
    892     BufferHelper outB(env, out);
    893     if (inB.checkPointer("in") && outB.checkPointer("out")) {
    894         jint imageSize = stride * height;
    895         jint encodedImageSize = etc1_get_encoded_data_size(width, height);
    896         if (inB.remaining() < imageSize) {
    897             doThrowIAE(env, "in's remaining data < image size");
    898         } else if (outB.remaining() < encodedImageSize) {
    899             doThrowIAE(env, "out's remaining data < encoded image size");
    900         } else {
    901             int result = etc1_encode_image((etc1_byte*) inB.getData(),
    902                     width, height, pixelSize,
    903                     stride,
    904                     (etc1_byte*) outB.getData());
    905         }
    906     }
    907 }
    908 
    909 /**
    910  * Decode an entire image.
    911  * @param in the encoded data.
    912  * @param out pointer to the image data. Will be written such that
    913  *            pixel (x,y) is at pIn + pixelSize * x + stride * y. Must be
    914  *            large enough to store entire image.
    915  */
    916 static void etc1_decodeImage(JNIEnv *env, jclass clazz,
    917         jobject  in, jobject out,
    918         jint width, jint height,
    919         jint pixelSize, jint stride) {
    920     if (pixelSize < 2 || pixelSize > 3) {
    921         doThrowIAE(env, "pixelSize must be 2 or 3");
    922         return;
    923     }
    924     BufferHelper inB(env, in);
    925     BufferHelper outB(env, out);
    926     if (inB.checkPointer("in") && outB.checkPointer("out")) {
    927         jint imageSize = stride * height;
    928         jint encodedImageSize = etc1_get_encoded_data_size(width, height);
    929         if (inB.remaining() < encodedImageSize) {
    930             doThrowIAE(env, "in's remaining data < encoded image size");
    931         } else if (outB.remaining() < imageSize) {
    932             doThrowIAE(env, "out's remaining data < image size");
    933         } else {
    934             int result = etc1_decode_image((etc1_byte*) inB.getData(),
    935                     (etc1_byte*) outB.getData(),
    936                     width, height, pixelSize,
    937                     stride);
    938         }
    939     }
    940 }
    941 
    942 /**
    943  * Format a PKM header
    944  */
    945 static void etc1_formatHeader(JNIEnv *env, jclass clazz,
    946         jobject header, jint width, jint height) {
    947     BufferHelper headerB(env, header);
    948     if (headerB.checkPointer("header") ){
    949         if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
    950             doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
    951         } else {
    952             etc1_pkm_format_header((etc1_byte*) headerB.getData(), width, height);
    953         }
    954     }
    955 }
    956 
    957 /**
    958  * Check if a PKM header is correctly formatted.
    959  */
    960 static jboolean etc1_isValid(JNIEnv *env, jclass clazz,
    961         jobject header) {
    962     jboolean result = false;
    963     BufferHelper headerB(env, header);
    964     if (headerB.checkPointer("header") ){
    965         if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
    966             doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
    967         } else {
    968             result = etc1_pkm_is_valid((etc1_byte*) headerB.getData());
    969         }
    970     }
    971     return result;
    972 }
    973 
    974 /**
    975  * Read the image width from a PKM header
    976  */
    977 static jint etc1_getWidth(JNIEnv *env, jclass clazz,
    978         jobject header) {
    979     jint result = 0;
    980     BufferHelper headerB(env, header);
    981     if (headerB.checkPointer("header") ){
    982         if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
    983             doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
    984         } else {
    985             result = etc1_pkm_get_width((etc1_byte*) headerB.getData());
    986         }
    987     }
    988     return result;
    989 }
    990 
    991 /**
    992  * Read the image height from a PKM header
    993  */
    994 static int etc1_getHeight(JNIEnv *env, jclass clazz,
    995         jobject header) {
    996     jint result = 0;
    997     BufferHelper headerB(env, header);
    998     if (headerB.checkPointer("header") ){
    999         if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
   1000             doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
   1001         } else {
   1002             result = etc1_pkm_get_height((etc1_byte*) headerB.getData());
   1003         }
   1004     }
   1005     return result;
   1006 }
   1007 
   1008 /*
   1009  * JNI registration
   1010  */
   1011 
   1012 static JNINativeMethod gMatrixMethods[] = {
   1013     { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM },
   1014     { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV },
   1015 };
   1016 
   1017 static JNINativeMethod gVisibilityMethods[] = {
   1018     { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere },
   1019     { "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres },
   1020     { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest },
   1021 };
   1022 
   1023 static JNINativeMethod gUtilsMethods[] = {
   1024     {"nativeClassInit", "()V",                          (void*)nativeUtilsClassInit },
   1025     { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat },
   1026     { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType },
   1027     { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D },
   1028     { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D },
   1029 };
   1030 
   1031 static JNINativeMethod gEtc1Methods[] = {
   1032     { "encodeBlock", "(Ljava/nio/Buffer;ILjava/nio/Buffer;)V", (void*) etc1_encodeBlock },
   1033     { "decodeBlock", "(Ljava/nio/Buffer;Ljava/nio/Buffer;)V", (void*) etc1_decodeBlock },
   1034     { "getEncodedDataSize", "(II)I", (void*) etc1_getEncodedDataSize },
   1035     { "encodeImage", "(Ljava/nio/Buffer;IIIILjava/nio/Buffer;)V", (void*) etc1_encodeImage },
   1036     { "decodeImage", "(Ljava/nio/Buffer;Ljava/nio/Buffer;IIII)V", (void*) etc1_decodeImage },
   1037     { "formatHeader", "(Ljava/nio/Buffer;II)V", (void*) etc1_formatHeader },
   1038     { "isValid", "(Ljava/nio/Buffer;)Z", (void*) etc1_isValid },
   1039     { "getWidth", "(Ljava/nio/Buffer;)I", (void*) etc1_getWidth },
   1040     { "getHeight", "(Ljava/nio/Buffer;)I", (void*) etc1_getHeight },
   1041 };
   1042 
   1043 typedef struct _ClassRegistrationInfo {
   1044     const char* classPath;
   1045     JNINativeMethod* methods;
   1046     size_t methodCount;
   1047 } ClassRegistrationInfo;
   1048 
   1049 static ClassRegistrationInfo gClasses[] = {
   1050     {"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)},
   1051     {"android/opengl/Visibility", gVisibilityMethods, NELEM(gVisibilityMethods)},
   1052     {"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)},
   1053     {"android/opengl/ETC1", gEtc1Methods, NELEM(gEtc1Methods)},
   1054 };
   1055 
   1056 int register_android_opengl_classes(JNIEnv* env)
   1057 {
   1058     nativeClassInitBuffer(env);
   1059     int result = 0;
   1060     for (int i = 0; i < NELEM(gClasses); i++) {
   1061         ClassRegistrationInfo* cri = &gClasses[i];
   1062         result = AndroidRuntime::registerNativeMethods(env,
   1063                 cri->classPath, cri->methods, cri->methodCount);
   1064         if (result < 0) {
   1065             LOGE("Failed to register %s: %d", cri->classPath, result);
   1066             break;
   1067         }
   1068     }
   1069     return result;
   1070 }
   1071 
   1072 } // namespace android
   1073