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