• 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 #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 
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
checkInternalFormat(SkColorType colorType,int internalformat,int type)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
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(SkColorType colorType)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 
getType(SkColorType colorType)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 
util_getInternalFormat(JNIEnv * env,jclass clazz,jlong bitmapPtr)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 
util_getType(JNIEnv * env,jclass clazz,jlong bitmapPtr)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 
util_texImage2D(JNIEnv * env,jclass clazz,jint target,jint level,jint internalformat,jlong bitmapPtr,jint type,jint border)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 
util_texSubImage2D(JNIEnv * env,jclass clazz,jint target,jint level,jint xoffset,jint yoffset,jlong bitmapPtr,jint format,jint type)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 *
getPointer(JNIEnv * _env,jobject buffer,jint * remaining)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:
BufferHelper(JNIEnv * env,jobject buffer)792     BufferHelper(JNIEnv *env, jobject buffer) {
793         mEnv = env;
794         mBuffer = buffer;
795         mData = NULL;
796         mRemaining = 0;
797     }
798 
checkPointer(const char * errorMessage)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 
getData()812     inline void* getData() {
813         return mData;
814     }
815 
remaining()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  */
etc1_encodeBlock(JNIEnv * env,jclass clazz,jobject in,jint validPixelMask,jobject out)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  */
etc1_decodeBlock(JNIEnv * env,jclass clazz,jobject in,jobject out)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  */
etc1_getEncodedDataSize(JNIEnv * env,jclass clazz,jint width,jint height)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  */
etc1_encodeImage(JNIEnv * env,jclass clazz,jobject in,jint width,jint height,jint pixelSize,jint stride,jobject out)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  */
etc1_decodeImage(JNIEnv * env,jclass clazz,jobject in,jobject out,jint width,jint height,jint pixelSize,jint stride)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  */
etc1_formatHeader(JNIEnv * env,jclass clazz,jobject header,jint width,jint height)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  */
etc1_isValid(JNIEnv * env,jclass clazz,jobject header)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  */
etc1_getWidth(JNIEnv * env,jclass clazz,jobject header)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  */
etc1_getHeight(JNIEnv * env,jclass clazz,jobject header)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 
register_android_opengl_classes(JNIEnv * env)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