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
mx4transform(float x,float y,float z,float w,const float * pM,float * pDest)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
visibilityTest(float * pWS,float * pPositions,int positionsLength,unsigned short * pIndices,int indexCount)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:
Get(JNIEnv * _env,jbyteArray array,jboolean * is_copy)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:
Get(JNIEnv * _env,jbooleanArray array,jboolean * is_copy)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:
Get(JNIEnv * _env,jcharArray array,jboolean * is_copy)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:
Get(JNIEnv * _env,jshortArray array,jboolean * is_copy)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:
Get(JNIEnv * _env,jintArray array,jboolean * is_copy)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:
Get(JNIEnv * _env,jlongArray array,jboolean * is_copy)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:
Get(JNIEnv * _env,jfloatArray array,jboolean * is_copy)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:
Get(JNIEnv * _env,jdoubleArray array,jboolean * is_copy)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:
Release(JNIEnv * _env,jbyteArray array,jbyte * data,jint mode)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:
Release(JNIEnv * _env,jbooleanArray array,jboolean * data,jint mode)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:
Release(JNIEnv * _env,jcharArray array,jchar * data,jint mode)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:
Release(JNIEnv * _env,jshortArray array,jshort * data,jint mode)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:
Release(JNIEnv * _env,jintArray array,jint * data,jint mode)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:
Release(JNIEnv * _env,jlongArray array,jlong * data,jint mode)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:
Release(JNIEnv * _env,jfloatArray array,jfloat * data,jint mode)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:
Release(JNIEnv * _env,jdoubleArray array,jdouble * data,jint mode)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:
ArrayHelper(JNIEnv * env,JArray ref,jint offset,jint minSize)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 
~ArrayHelper()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 
check()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 
bind()274     void bind() {
275         mBase = (T*) ArrayGetter::Get(mEnv, mRef, (jboolean *) 0);
276         mData = mBase + mOffset;
277     }
278 
commitChanges()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 
distance2(float x,float y,float z)300 inline float distance2(float x, float y, float z) {
301     return x * x + y * y + z * z;
302 }
303 
distance(float x,float y,float z)304 inline float distance(float x, float y, float z) {
305     return sqrtf(distance2(x, y, z));
306 }
307 
308 static
util_computeBoundingSphere(JNIEnv * env,jclass clazz,jfloatArray positions_ref,jint positionsOffset,jint positionsCount,jfloatArray sphere_ref,jint sphereOffset)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 
normalizePlane(float * p)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 
dot3(float x0,float y0,float z0,float x1,float y1,float z1)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 
signedDistance(const float * pPlane,float x,float y,float z)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 
sphereHitsFrustum(const float * pFrustum,const float * pSphere)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 
computeFrustum(const float * m,float * f)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
util_frustumCullSpheres(JNIEnv * env,jclass clazz,jfloatArray mvp_ref,jint mvpOffset,jfloatArray spheres_ref,jint spheresOffset,jint spheresCount,jintArray results_ref,jint resultsOffset,jint resultsCapacity)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
util_visibilityTest(JNIEnv * env,jclass clazz,jfloatArray ws_ref,jint wsOffset,jfloatArray positions_ref,jint positionsOffset,jcharArray indices_ref,jint indicesOffset,jint indexCount)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
multiplyMM(float * r,const float * lhs,const float * rhs)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
util_multiplyMM(JNIEnv * env,jclass clazz,jfloatArray result_ref,jint resultOffset,jfloatArray lhs_ref,jint lhsOffset,jfloatArray rhs_ref,jint rhsOffset)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
multiplyMV(float * r,const float * lhs,const float * rhs)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
util_multiplyMV(JNIEnv * env,jclass clazz,jfloatArray result_ref,jint resultOffset,jfloatArray lhs_ref,jint lhsOffset,jfloatArray rhs_ref,jint rhsOffset)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 
checkInternalFormat(SkColorType colorType,int internalformat,int type)625 static int checkInternalFormat(SkColorType colorType, int internalformat,
626     int type)
627 {
628     switch(colorType) {
629         case kN32_SkColorType:
630             return (type == GL_UNSIGNED_BYTE &&
631                 internalformat == GL_RGBA) ? 0 : -1;
632         case kAlpha_8_SkColorType:
633             return (type == GL_UNSIGNED_BYTE &&
634                 internalformat == GL_ALPHA) ? 0 : -1;
635         case kARGB_4444_SkColorType:
636             return (type == GL_UNSIGNED_SHORT_4_4_4_4 &&
637                 internalformat == GL_RGBA) ? 0 : -1;
638         case kRGB_565_SkColorType:
639             return (type == GL_UNSIGNED_SHORT_5_6_5 &&
640                 internalformat == GL_RGB) ? 0 : -1;
641         case kRGBA_F16_SkColorType:
642             return (type == GL_HALF_FLOAT &&
643                 internalformat == GL_RGBA16F) ? 0 : -1;
644         default:
645             break;
646     }
647     return -1;
648 }
649 
650 // The internal format is no longer the same as pixel format, per Table 2 in
651 // https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glTexImage2D.xhtml
getPixelFormatFromInternalFormat(uint32_t internalFormat)652 static int getPixelFormatFromInternalFormat(uint32_t internalFormat) {
653     switch (internalFormat) {
654         // For sized internal format.
655         case GL_RGBA16F:
656             return GL_RGBA;
657         // Base internal formats and pixel formats are still the same, see Table 1 in
658         // https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glTexImage2D.xhtml
659         default:
660             return internalFormat;
661     }
662 }
663 
getInternalFormat(SkColorType colorType)664 static int getInternalFormat(SkColorType colorType)
665 {
666     switch(colorType) {
667         case kAlpha_8_SkColorType:
668             return GL_ALPHA;
669         case kARGB_4444_SkColorType:
670             return GL_RGBA;
671         case kN32_SkColorType:
672             return GL_RGBA;
673         case kRGB_565_SkColorType:
674             return GL_RGB;
675         case kRGBA_F16_SkColorType:
676             return GL_RGBA16F;
677         default:
678             return -1;
679     }
680 }
681 
getType(SkColorType colorType)682 static int getType(SkColorType colorType)
683 {
684     switch(colorType) {
685         case kAlpha_8_SkColorType:
686             return GL_UNSIGNED_BYTE;
687         case kARGB_4444_SkColorType:
688             return GL_UNSIGNED_SHORT_4_4_4_4;
689         case kN32_SkColorType:
690             return GL_UNSIGNED_BYTE;
691         case kRGB_565_SkColorType:
692             return GL_UNSIGNED_SHORT_5_6_5;
693         case kRGBA_F16_SkColorType:
694             return GL_HALF_FLOAT;
695         default:
696             return -1;
697     }
698 }
699 
util_getInternalFormat(JNIEnv * env,jclass clazz,jobject jbitmap)700 static jint util_getInternalFormat(JNIEnv *env, jclass clazz,
701         jobject jbitmap)
702 {
703     SkBitmap nativeBitmap;
704     GraphicsJNI::getSkBitmap(env, jbitmap, &nativeBitmap);
705     return getInternalFormat(nativeBitmap.colorType());
706 }
707 
util_getType(JNIEnv * env,jclass clazz,jobject jbitmap)708 static jint util_getType(JNIEnv *env, jclass clazz,
709         jobject jbitmap)
710 {
711     SkBitmap nativeBitmap;
712     GraphicsJNI::getSkBitmap(env, jbitmap, &nativeBitmap);
713     return getType(nativeBitmap.colorType());
714 }
715 
util_texImage2D(JNIEnv * env,jclass clazz,jint target,jint level,jint internalformat,jobject jbitmap,jint type,jint border)716 static jint util_texImage2D(JNIEnv *env, jclass clazz,
717         jint target, jint level, jint internalformat,
718         jobject jbitmap, jint type, jint border)
719 {
720     SkBitmap bitmap;
721     GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
722     SkColorType colorType = bitmap.colorType();
723     if (internalformat < 0) {
724         internalformat = getInternalFormat(colorType);
725     }
726     if (type < 0) {
727         type = getType(colorType);
728     }
729     int err = checkInternalFormat(colorType, internalformat, type);
730     if (err)
731         return err;
732     const int w = bitmap.width();
733     const int h = bitmap.height();
734     const void* p = bitmap.getPixels();
735     if (internalformat == GL_PALETTE8_RGBA8_OES) {
736         err = -1;
737     } else {
738         glTexImage2D(target, level, internalformat, w, h, border,
739                      getPixelFormatFromInternalFormat(internalformat), type, p);
740     }
741     return err;
742 }
743 
util_texSubImage2D(JNIEnv * env,jclass clazz,jint target,jint level,jint xoffset,jint yoffset,jobject jbitmap,jint format,jint type)744 static jint util_texSubImage2D(JNIEnv *env, jclass clazz,
745         jint target, jint level, jint xoffset, jint yoffset,
746         jobject jbitmap, jint format, jint type)
747 {
748     SkBitmap bitmap;
749     GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
750     SkColorType colorType = bitmap.colorType();
751     int internalFormat = getInternalFormat(colorType);
752     if (format < 0) {
753         format = getPixelFormatFromInternalFormat(internalFormat);
754         if (format == GL_PALETTE8_RGBA8_OES)
755             return -1; // glCompressedTexSubImage2D() not supported
756     }
757     int err = checkInternalFormat(colorType, internalFormat, type);
758     if (err)
759         return err;
760     const int w = bitmap.width();
761     const int h = bitmap.height();
762     const void* p = bitmap.getPixels();
763     glTexSubImage2D(target, level, xoffset, yoffset, w, h, format, type, p);
764     return 0;
765 }
766 
767 /*
768  * ETC1 methods.
769  */
770 
771 static jclass nioAccessClass;
772 static jclass bufferClass;
773 static jmethodID getBasePointerID;
774 static jmethodID getBaseArrayID;
775 static jmethodID getBaseArrayOffsetID;
776 static jfieldID positionID;
777 static jfieldID limitID;
778 static jfieldID elementSizeShiftID;
779 
780 /* Cache method IDs each time the class is loaded. */
781 
782 static void
nativeClassInitBuffer(JNIEnv * env)783 nativeClassInitBuffer(JNIEnv *env)
784 {
785     jclass nioAccessClassLocal = FindClassOrDie(env, "java/nio/NIOAccess");
786     nioAccessClass = MakeGlobalRefOrDie(env, nioAccessClassLocal);
787     getBasePointerID = GetStaticMethodIDOrDie(env, nioAccessClass,
788             "getBasePointer", "(Ljava/nio/Buffer;)J");
789     getBaseArrayID = GetStaticMethodIDOrDie(env, nioAccessClass,
790             "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;");
791     getBaseArrayOffsetID = GetStaticMethodIDOrDie(env, nioAccessClass,
792             "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
793 
794     jclass bufferClassLocal = FindClassOrDie(env, "java/nio/Buffer");
795     bufferClass = MakeGlobalRefOrDie(env, bufferClassLocal);
796     positionID = GetFieldIDOrDie(env, bufferClass, "position", "I");
797     limitID = GetFieldIDOrDie(env, bufferClass, "limit", "I");
798     elementSizeShiftID = GetFieldIDOrDie(env, bufferClass, "_elementSizeShift", "I");
799 }
800 
801 static void *
getPointer(JNIEnv * _env,jobject buffer,jint * remaining)802 getPointer(JNIEnv *_env, jobject buffer, jint *remaining)
803 {
804     jint position;
805     jint limit;
806     jint elementSizeShift;
807     jlong pointer;
808 
809     position = _env->GetIntField(buffer, positionID);
810     limit = _env->GetIntField(buffer, limitID);
811     elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
812     *remaining = (limit - position) << elementSizeShift;
813     pointer = _env->CallStaticLongMethod(nioAccessClass,
814             getBasePointerID, buffer);
815     if (pointer != 0L) {
816         return reinterpret_cast<void *>(pointer);
817     }
818     return NULL;
819 }
820 
821 class BufferHelper {
822 public:
BufferHelper(JNIEnv * env,jobject buffer)823     BufferHelper(JNIEnv *env, jobject buffer) {
824         mEnv = env;
825         mBuffer = buffer;
826         mData = NULL;
827         mRemaining = 0;
828     }
829 
checkPointer(const char * errorMessage)830     bool checkPointer(const char* errorMessage) {
831         if (mBuffer) {
832             mData = getPointer(mEnv, mBuffer, &mRemaining);
833             if (mData == NULL) {
834                 doThrowIAE(mEnv, errorMessage);
835             }
836             return mData != NULL;
837         } else {
838             doThrowIAE(mEnv, errorMessage);
839             return false;
840         }
841     }
842 
getData()843     inline void* getData() {
844         return mData;
845     }
846 
remaining()847     inline jint remaining() {
848         return mRemaining;
849     }
850 
851 private:
852     JNIEnv* mEnv;
853     jobject mBuffer;
854     void* mData;
855     jint mRemaining;
856 };
857 
858 /**
859  * Encode a block of pixels.
860  *
861  * @param in a pointer to a ETC1_DECODED_BLOCK_SIZE array of bytes that represent a
862  * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
863  * value of pixel (x, y).
864  *
865  * @param validPixelMask is a 16-bit mask where bit (1 << (x + y * 4)) indicates whether
866  * the corresponding (x,y) pixel is valid. Invalid pixel color values are ignored when compressing.
867  *
868  * @param out an ETC1 compressed version of the data.
869  *
870  */
etc1_encodeBlock(JNIEnv * env,jclass clazz,jobject in,jint validPixelMask,jobject out)871 static void etc1_encodeBlock(JNIEnv *env, jclass clazz,
872         jobject in, jint validPixelMask, jobject out) {
873     if (validPixelMask < 0 || validPixelMask > 15) {
874         doThrowIAE(env, "validPixelMask");
875         return;
876     }
877     BufferHelper inB(env, in);
878     BufferHelper outB(env, out);
879     if (inB.checkPointer("in") && outB.checkPointer("out")) {
880         if (inB.remaining() < ETC1_DECODED_BLOCK_SIZE) {
881             doThrowIAE(env, "in's remaining data < DECODED_BLOCK_SIZE");
882         } else if (outB.remaining() < ETC1_ENCODED_BLOCK_SIZE) {
883             doThrowIAE(env, "out's remaining data < ENCODED_BLOCK_SIZE");
884         } else {
885             etc1_encode_block((etc1_byte*) inB.getData(), validPixelMask,
886                     (etc1_byte*) outB.getData());
887         }
888     }
889 }
890 
891 /**
892  * Decode a block of pixels.
893  *
894  * @param in an ETC1 compressed version of the data.
895  *
896  * @param out a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a
897  * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
898  * value of pixel (x, y).
899  */
etc1_decodeBlock(JNIEnv * env,jclass clazz,jobject in,jobject out)900 static void etc1_decodeBlock(JNIEnv *env, jclass clazz,
901         jobject in, jobject out){
902     BufferHelper inB(env, in);
903     BufferHelper outB(env, out);
904     if (inB.checkPointer("in") && outB.checkPointer("out")) {
905         if (inB.remaining() < ETC1_ENCODED_BLOCK_SIZE) {
906             doThrowIAE(env, "in's remaining data < ENCODED_BLOCK_SIZE");
907         } else if (outB.remaining() < ETC1_DECODED_BLOCK_SIZE) {
908             doThrowIAE(env, "out's remaining data < DECODED_BLOCK_SIZE");
909         } else {
910             etc1_decode_block((etc1_byte*) inB.getData(),
911                     (etc1_byte*) outB.getData());
912         }
913     }
914 }
915 
916 /**
917  * Return the size of the encoded image data (does not include size of PKM header).
918  */
etc1_getEncodedDataSize(JNIEnv * env,jclass clazz,jint width,jint height)919 static jint etc1_getEncodedDataSize(JNIEnv *env, jclass clazz,
920         jint width, jint height) {
921     return etc1_get_encoded_data_size(width, height);
922 }
923 
924 /**
925  * Encode an entire image.
926  * @param in pointer to the image data. Formatted such that
927  *           pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset;
928  * @param out pointer to encoded data. Must be large enough to store entire encoded image.
929  */
etc1_encodeImage(JNIEnv * env,jclass clazz,jobject in,jint width,jint height,jint pixelSize,jint stride,jobject out)930 static void etc1_encodeImage(JNIEnv *env, jclass clazz,
931         jobject in, jint width, jint height,
932         jint pixelSize, jint stride, jobject out) {
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() < imageSize) {
943             doThrowIAE(env, "in's remaining data < image size");
944         } else if (outB.remaining() < encodedImageSize) {
945             doThrowIAE(env, "out's remaining data < encoded image size");
946         } else {
947             etc1_encode_image((etc1_byte*) inB.getData(), width, height, pixelSize, stride,
948                               (etc1_byte*) outB.getData());
949         }
950     }
951 }
952 
953 /**
954  * Decode an entire image.
955  * @param in the encoded data.
956  * @param out pointer to the image data. Will be written such that
957  *            pixel (x,y) is at pIn + pixelSize * x + stride * y. Must be
958  *            large enough to store entire image.
959  */
etc1_decodeImage(JNIEnv * env,jclass clazz,jobject in,jobject out,jint width,jint height,jint pixelSize,jint stride)960 static void etc1_decodeImage(JNIEnv *env, jclass clazz,
961         jobject  in, jobject out,
962         jint width, jint height,
963         jint pixelSize, jint stride) {
964     if (pixelSize < 2 || pixelSize > 3) {
965         doThrowIAE(env, "pixelSize must be 2 or 3");
966         return;
967     }
968     BufferHelper inB(env, in);
969     BufferHelper outB(env, out);
970     if (inB.checkPointer("in") && outB.checkPointer("out")) {
971         jint imageSize = stride * height;
972         jint encodedImageSize = etc1_get_encoded_data_size(width, height);
973         if (inB.remaining() < encodedImageSize) {
974             doThrowIAE(env, "in's remaining data < encoded image size");
975         } else if (outB.remaining() < imageSize) {
976             doThrowIAE(env, "out's remaining data < image size");
977         } else {
978             etc1_decode_image((etc1_byte*) inB.getData(), (etc1_byte*) outB.getData(),
979                               width, height, pixelSize, stride);
980         }
981     }
982 }
983 
984 /**
985  * Format a PKM header
986  */
etc1_formatHeader(JNIEnv * env,jclass clazz,jobject header,jint width,jint height)987 static void etc1_formatHeader(JNIEnv *env, jclass clazz,
988         jobject header, jint width, jint height) {
989     BufferHelper headerB(env, header);
990     if (headerB.checkPointer("header") ){
991         if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
992             doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
993         } else {
994             etc1_pkm_format_header((etc1_byte*) headerB.getData(), width, height);
995         }
996     }
997 }
998 
999 /**
1000  * Check if a PKM header is correctly formatted.
1001  */
etc1_isValid(JNIEnv * env,jclass clazz,jobject header)1002 static jboolean etc1_isValid(JNIEnv *env, jclass clazz,
1003         jobject header) {
1004     jboolean result = false;
1005     BufferHelper headerB(env, header);
1006     if (headerB.checkPointer("header") ){
1007         if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
1008             doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1009         } else {
1010             result = etc1_pkm_is_valid((etc1_byte*) headerB.getData());
1011         }
1012     }
1013     return result ? JNI_TRUE : JNI_FALSE;
1014 }
1015 
1016 /**
1017  * Read the image width from a PKM header
1018  */
etc1_getWidth(JNIEnv * env,jclass clazz,jobject header)1019 static jint etc1_getWidth(JNIEnv *env, jclass clazz,
1020         jobject header) {
1021     jint result = 0;
1022     BufferHelper headerB(env, header);
1023     if (headerB.checkPointer("header") ){
1024         if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
1025             doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1026         } else {
1027             result = etc1_pkm_get_width((etc1_byte*) headerB.getData());
1028         }
1029     }
1030     return result;
1031 }
1032 
1033 /**
1034  * Read the image height from a PKM header
1035  */
etc1_getHeight(JNIEnv * env,jclass clazz,jobject header)1036 static jint etc1_getHeight(JNIEnv *env, jclass clazz,
1037         jobject header) {
1038     jint result = 0;
1039     BufferHelper headerB(env, header);
1040     if (headerB.checkPointer("header") ){
1041         if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
1042             doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1043         } else {
1044             result = etc1_pkm_get_height((etc1_byte*) headerB.getData());
1045         }
1046     }
1047     return result;
1048 }
1049 
1050 /*
1051  * JNI registration
1052  */
1053 
1054 static const JNINativeMethod gMatrixMethods[] = {
1055     { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM },
1056     { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV },
1057 };
1058 
1059 static const JNINativeMethod gVisibilityMethods[] = {
1060     { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere },
1061     { "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres },
1062     { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest },
1063 };
1064 
1065 static const JNINativeMethod gUtilsMethods[] = {
1066     { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat },
1067     { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType },
1068     { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D },
1069     { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D },
1070 };
1071 
1072 static const JNINativeMethod gEtc1Methods[] = {
1073     { "encodeBlock", "(Ljava/nio/Buffer;ILjava/nio/Buffer;)V", (void*) etc1_encodeBlock },
1074     { "decodeBlock", "(Ljava/nio/Buffer;Ljava/nio/Buffer;)V", (void*) etc1_decodeBlock },
1075     { "getEncodedDataSize", "(II)I", (void*) etc1_getEncodedDataSize },
1076     { "encodeImage", "(Ljava/nio/Buffer;IIIILjava/nio/Buffer;)V", (void*) etc1_encodeImage },
1077     { "decodeImage", "(Ljava/nio/Buffer;Ljava/nio/Buffer;IIII)V", (void*) etc1_decodeImage },
1078     { "formatHeader", "(Ljava/nio/Buffer;II)V", (void*) etc1_formatHeader },
1079     { "isValid", "(Ljava/nio/Buffer;)Z", (void*) etc1_isValid },
1080     { "getWidth", "(Ljava/nio/Buffer;)I", (void*) etc1_getWidth },
1081     { "getHeight", "(Ljava/nio/Buffer;)I", (void*) etc1_getHeight },
1082 };
1083 
1084 typedef struct _ClassRegistrationInfo {
1085     const char* classPath;
1086     const JNINativeMethod* methods;
1087     size_t methodCount;
1088 } ClassRegistrationInfo;
1089 
1090 static const ClassRegistrationInfo gClasses[] = {
1091     {"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)},
1092     {"android/opengl/Visibility", gVisibilityMethods, NELEM(gVisibilityMethods)},
1093     {"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)},
1094     {"android/opengl/ETC1", gEtc1Methods, NELEM(gEtc1Methods)},
1095 };
1096 
register_android_opengl_classes(JNIEnv * env)1097 int register_android_opengl_classes(JNIEnv* env)
1098 {
1099     nativeClassInitBuffer(env);
1100     int result = 0;
1101     for (int i = 0; i < NELEM(gClasses); i++) {
1102         const ClassRegistrationInfo* cri = &gClasses[i];
1103         result = RegisterMethodsOrDie(env, cri->classPath, cri->methods, cri->methodCount);
1104     }
1105     return result;
1106 }
1107 
1108 } // namespace android
1109