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