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