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