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