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