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