OSDN Git Service

Frequently used OpenGL ES methods whitelisted for fast JNI
[android-x86/frameworks-base.git] / core / jni / android / opengl / util.cpp
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
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:
54     MallocHelper() {
55         mData = 0;
56     }
57
58     ~MallocHelper() {
59         if (mData != 0) {
60             free(mData);
61         }
62     }
63
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
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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
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
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
295     void bind() {
296         mBase = (T*) ArrayGetter::Get(mEnv, mRef, (jboolean *) 0);
297         mData = mBase + mOffset;
298     }
299
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
321 inline float distance2(float x, float y, float z) {
322     return x * x + y * y + z * z;
323 }
324
325 inline float distance(float x, float y, float z) {
326     return sqrtf(distance2(x, y, z));
327 }
328
329 static
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
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
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
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
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
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
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
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
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
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
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
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
646 extern void setGLDebugLevel(int level);
647 void setTracingLevel(JNIEnv *env, jclass clazz, jint level)
648 {
649     setGLDebugLevel(level);
650 }
651
652 static int checkFormat(SkColorType colorType, int format, int type)
653 {
654     switch(colorType) {
655         case kIndex_8_SkColorType:
656             if (format == GL_PALETTE8_RGBA8_OES)
657                 return 0;
658         case kN32_SkColorType:
659         case kAlpha_8_SkColorType:
660             if (type == GL_UNSIGNED_BYTE)
661                 return 0;
662         case kARGB_4444_SkColorType:
663         case kRGB_565_SkColorType:
664             switch (type) {
665                 case GL_UNSIGNED_SHORT_4_4_4_4:
666                 case GL_UNSIGNED_SHORT_5_6_5:
667                 case GL_UNSIGNED_SHORT_5_5_5_1:
668                     return 0;
669                 case GL_UNSIGNED_BYTE:
670                     if (format == GL_LUMINANCE_ALPHA)
671                         return 0;
672             }
673             break;
674         default:
675             break;
676     }
677     return -1;
678 }
679
680 static int getInternalFormat(SkColorType colorType)
681 {
682     switch(colorType) {
683         case kAlpha_8_SkColorType:
684             return GL_ALPHA;
685         case kARGB_4444_SkColorType:
686             return GL_RGBA;
687         case kN32_SkColorType:
688             return GL_RGBA;
689         case kIndex_8_SkColorType:
690             return GL_PALETTE8_RGBA8_OES;
691         case kRGB_565_SkColorType:
692             return GL_RGB;
693         default:
694             return -1;
695     }
696 }
697
698 static int getType(SkColorType colorType)
699 {
700     switch(colorType) {
701         case kAlpha_8_SkColorType:
702             return GL_UNSIGNED_BYTE;
703         case kARGB_4444_SkColorType:
704             return GL_UNSIGNED_SHORT_4_4_4_4;
705         case kN32_SkColorType:
706             return GL_UNSIGNED_BYTE;
707         case kIndex_8_SkColorType:
708             return -1; // No type for compressed data.
709         case kRGB_565_SkColorType:
710             return GL_UNSIGNED_SHORT_5_6_5;
711         default:
712             return -1;
713     }
714 }
715
716 static jint util_getInternalFormat(JNIEnv *env, jclass clazz,
717         jobject jbitmap)
718 {
719     SkBitmap nativeBitmap;
720     GraphicsJNI::getSkBitmap(env, jbitmap, &nativeBitmap);
721     return getInternalFormat(nativeBitmap.colorType());
722 }
723
724 static jint util_getType(JNIEnv *env, jclass clazz,
725         jobject jbitmap)
726 {
727     SkBitmap nativeBitmap;
728     GraphicsJNI::getSkBitmap(env, jbitmap, &nativeBitmap);
729     return getType(nativeBitmap.colorType());
730 }
731
732 static jint util_texImage2D(JNIEnv *env, jclass clazz,
733         jint target, jint level, jint internalformat,
734         jobject jbitmap, jint type, jint border)
735 {
736     SkBitmap bitmap;
737     GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
738     SkColorType colorType = bitmap.colorType();
739     if (internalformat < 0) {
740         internalformat = getInternalFormat(colorType);
741     }
742     if (type < 0) {
743         type = getType(colorType);
744     }
745     int err = checkFormat(colorType, internalformat, type);
746     if (err)
747         return err;
748     bitmap.lockPixels();
749     const int w = bitmap.width();
750     const int h = bitmap.height();
751     const void* p = bitmap.getPixels();
752     if (internalformat == GL_PALETTE8_RGBA8_OES) {
753         if (sizeof(SkPMColor) != sizeof(uint32_t)) {
754             err = -1;
755             goto error;
756         }
757         const size_t size = bitmap.getSize();
758         const size_t palette_size = 256*sizeof(SkPMColor);
759         const size_t imageSize = size + palette_size;
760         void* const data = malloc(imageSize);
761         if (data) {
762             void* const pixels = (char*)data + palette_size;
763             SkColorTable* ctable = bitmap.getColorTable();
764             memcpy(data, ctable->readColors(), ctable->count() * sizeof(SkPMColor));
765             memcpy(pixels, p, size);
766             glCompressedTexImage2D(target, level, internalformat, w, h, border, imageSize, data);
767             free(data);
768         } else {
769             err = -1;
770         }
771     } else {
772         glTexImage2D(target, level, internalformat, w, h, border, internalformat, type, p);
773     }
774 error:
775     bitmap.unlockPixels();
776     return err;
777 }
778
779 static jint util_texSubImage2D(JNIEnv *env, jclass clazz,
780         jint target, jint level, jint xoffset, jint yoffset,
781         jobject jbitmap, jint format, jint type)
782 {
783     SkBitmap bitmap;
784     GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
785     SkColorType colorType = bitmap.colorType();
786     if (format < 0) {
787         format = getInternalFormat(colorType);
788         if (format == GL_PALETTE8_RGBA8_OES)
789             return -1; // glCompressedTexSubImage2D() not supported
790     }
791     int err = checkFormat(colorType, format, type);
792     if (err)
793         return err;
794     bitmap.lockPixels();
795     const int w = bitmap.width();
796     const int h = bitmap.height();
797     const void* p = bitmap.getPixels();
798     glTexSubImage2D(target, level, xoffset, yoffset, w, h, format, type, p);
799     bitmap.unlockPixels();
800     return 0;
801 }
802
803 /*
804  * ETC1 methods.
805  */
806
807 static jclass nioAccessClass;
808 static jclass bufferClass;
809 static jmethodID getBasePointerID;
810 static jmethodID getBaseArrayID;
811 static jmethodID getBaseArrayOffsetID;
812 static jfieldID positionID;
813 static jfieldID limitID;
814 static jfieldID elementSizeShiftID;
815
816 /* Cache method IDs each time the class is loaded. */
817
818 static void
819 nativeClassInitBuffer(JNIEnv *env)
820 {
821     jclass nioAccessClassLocal = FindClassOrDie(env, "java/nio/NIOAccess");
822     nioAccessClass = MakeGlobalRefOrDie(env, nioAccessClassLocal);
823     getBasePointerID = GetStaticMethodIDOrDie(env, nioAccessClass,
824             "getBasePointer", "(Ljava/nio/Buffer;)J");
825     getBaseArrayID = GetStaticMethodIDOrDie(env, nioAccessClass,
826             "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;");
827     getBaseArrayOffsetID = GetStaticMethodIDOrDie(env, nioAccessClass,
828             "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
829
830     jclass bufferClassLocal = FindClassOrDie(env, "java/nio/Buffer");
831     bufferClass = MakeGlobalRefOrDie(env, bufferClassLocal);
832     positionID = GetFieldIDOrDie(env, bufferClass, "position", "I");
833     limitID = GetFieldIDOrDie(env, bufferClass, "limit", "I");
834     elementSizeShiftID = GetFieldIDOrDie(env, bufferClass, "_elementSizeShift", "I");
835 }
836
837 static void *
838 getPointer(JNIEnv *_env, jobject buffer, jint *remaining)
839 {
840     jint position;
841     jint limit;
842     jint elementSizeShift;
843     jlong pointer;
844
845     position = _env->GetIntField(buffer, positionID);
846     limit = _env->GetIntField(buffer, limitID);
847     elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
848     *remaining = (limit - position) << elementSizeShift;
849     pointer = _env->CallStaticLongMethod(nioAccessClass,
850             getBasePointerID, buffer);
851     if (pointer != 0L) {
852         return reinterpret_cast<void *>(pointer);
853     }
854     return NULL;
855 }
856
857 class BufferHelper {
858 public:
859     BufferHelper(JNIEnv *env, jobject buffer) {
860         mEnv = env;
861         mBuffer = buffer;
862         mData = NULL;
863         mRemaining = 0;
864     }
865
866     bool checkPointer(const char* errorMessage) {
867         if (mBuffer) {
868             mData = getPointer(mEnv, mBuffer, &mRemaining);
869             if (mData == NULL) {
870                 doThrowIAE(mEnv, errorMessage);
871             }
872             return mData != NULL;
873         } else {
874             doThrowIAE(mEnv, errorMessage);
875             return false;
876         }
877     }
878
879     inline void* getData() {
880         return mData;
881     }
882
883     inline jint remaining() {
884         return mRemaining;
885     }
886
887 private:
888     JNIEnv* mEnv;
889     jobject mBuffer;
890     void* mData;
891     jint mRemaining;
892 };
893
894 /**
895  * Encode a block of pixels.
896  *
897  * @param in a pointer to a ETC1_DECODED_BLOCK_SIZE array of bytes that represent a
898  * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
899  * value of pixel (x, y).
900  *
901  * @param validPixelMask is a 16-bit mask where bit (1 << (x + y * 4)) indicates whether
902  * the corresponding (x,y) pixel is valid. Invalid pixel color values are ignored when compressing.
903  *
904  * @param out an ETC1 compressed version of the data.
905  *
906  */
907 static void etc1_encodeBlock(JNIEnv *env, jclass clazz,
908         jobject in, jint validPixelMask, jobject out) {
909     if (validPixelMask < 0 || validPixelMask > 15) {
910         doThrowIAE(env, "validPixelMask");
911         return;
912     }
913     BufferHelper inB(env, in);
914     BufferHelper outB(env, out);
915     if (inB.checkPointer("in") && outB.checkPointer("out")) {
916         if (inB.remaining() < ETC1_DECODED_BLOCK_SIZE) {
917             doThrowIAE(env, "in's remaining data < DECODED_BLOCK_SIZE");
918         } else if (outB.remaining() < ETC1_ENCODED_BLOCK_SIZE) {
919             doThrowIAE(env, "out's remaining data < ENCODED_BLOCK_SIZE");
920         } else {
921             etc1_encode_block((etc1_byte*) inB.getData(), validPixelMask,
922                     (etc1_byte*) outB.getData());
923         }
924     }
925 }
926
927 /**
928  * Decode a block of pixels.
929  *
930  * @param in an ETC1 compressed version of the data.
931  *
932  * @param out a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a
933  * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
934  * value of pixel (x, y).
935  */
936 static void etc1_decodeBlock(JNIEnv *env, jclass clazz,
937         jobject in, jobject out){
938     BufferHelper inB(env, in);
939     BufferHelper outB(env, out);
940     if (inB.checkPointer("in") && outB.checkPointer("out")) {
941         if (inB.remaining() < ETC1_ENCODED_BLOCK_SIZE) {
942             doThrowIAE(env, "in's remaining data < ENCODED_BLOCK_SIZE");
943         } else if (outB.remaining() < ETC1_DECODED_BLOCK_SIZE) {
944             doThrowIAE(env, "out's remaining data < DECODED_BLOCK_SIZE");
945         } else {
946             etc1_decode_block((etc1_byte*) inB.getData(),
947                     (etc1_byte*) outB.getData());
948         }
949     }
950 }
951
952 /**
953  * Return the size of the encoded image data (does not include size of PKM header).
954  */
955 static jint etc1_getEncodedDataSize(JNIEnv *env, jclass clazz,
956         jint width, jint height) {
957     return etc1_get_encoded_data_size(width, height);
958 }
959
960 /**
961  * Encode an entire image.
962  * @param in pointer to the image data. Formatted such that
963  *           pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset;
964  * @param out pointer to encoded data. Must be large enough to store entire encoded image.
965  */
966 static void etc1_encodeImage(JNIEnv *env, jclass clazz,
967         jobject in, jint width, jint height,
968         jint pixelSize, jint stride, jobject out) {
969     if (pixelSize < 2 || pixelSize > 3) {
970         doThrowIAE(env, "pixelSize must be 2 or 3");
971         return;
972     }
973     BufferHelper inB(env, in);
974     BufferHelper outB(env, out);
975     if (inB.checkPointer("in") && outB.checkPointer("out")) {
976         jint imageSize = stride * height;
977         jint encodedImageSize = etc1_get_encoded_data_size(width, height);
978         if (inB.remaining() < imageSize) {
979             doThrowIAE(env, "in's remaining data < image size");
980         } else if (outB.remaining() < encodedImageSize) {
981             doThrowIAE(env, "out's remaining data < encoded image size");
982         } else {
983             etc1_encode_image((etc1_byte*) inB.getData(), width, height, pixelSize, stride,
984                               (etc1_byte*) outB.getData());
985         }
986     }
987 }
988
989 /**
990  * Decode an entire image.
991  * @param in the encoded data.
992  * @param out pointer to the image data. Will be written such that
993  *            pixel (x,y) is at pIn + pixelSize * x + stride * y. Must be
994  *            large enough to store entire image.
995  */
996 static void etc1_decodeImage(JNIEnv *env, jclass clazz,
997         jobject  in, jobject out,
998         jint width, jint height,
999         jint pixelSize, jint stride) {
1000     if (pixelSize < 2 || pixelSize > 3) {
1001         doThrowIAE(env, "pixelSize must be 2 or 3");
1002         return;
1003     }
1004     BufferHelper inB(env, in);
1005     BufferHelper outB(env, out);
1006     if (inB.checkPointer("in") && outB.checkPointer("out")) {
1007         jint imageSize = stride * height;
1008         jint encodedImageSize = etc1_get_encoded_data_size(width, height);
1009         if (inB.remaining() < encodedImageSize) {
1010             doThrowIAE(env, "in's remaining data < encoded image size");
1011         } else if (outB.remaining() < imageSize) {
1012             doThrowIAE(env, "out's remaining data < image size");
1013         } else {
1014             etc1_decode_image((etc1_byte*) inB.getData(), (etc1_byte*) outB.getData(),
1015                               width, height, pixelSize, stride);
1016         }
1017     }
1018 }
1019
1020 /**
1021  * Format a PKM header
1022  */
1023 static void etc1_formatHeader(JNIEnv *env, jclass clazz,
1024         jobject header, jint width, jint height) {
1025     BufferHelper headerB(env, header);
1026     if (headerB.checkPointer("header") ){
1027         if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
1028             doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1029         } else {
1030             etc1_pkm_format_header((etc1_byte*) headerB.getData(), width, height);
1031         }
1032     }
1033 }
1034
1035 /**
1036  * Check if a PKM header is correctly formatted.
1037  */
1038 static jboolean etc1_isValid(JNIEnv *env, jclass clazz,
1039         jobject header) {
1040     jboolean result = false;
1041     BufferHelper headerB(env, header);
1042     if (headerB.checkPointer("header") ){
1043         if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
1044             doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1045         } else {
1046             result = etc1_pkm_is_valid((etc1_byte*) headerB.getData());
1047         }
1048     }
1049     return result ? JNI_TRUE : JNI_FALSE;
1050 }
1051
1052 /**
1053  * Read the image width from a PKM header
1054  */
1055 static jint etc1_getWidth(JNIEnv *env, jclass clazz,
1056         jobject header) {
1057     jint result = 0;
1058     BufferHelper headerB(env, header);
1059     if (headerB.checkPointer("header") ){
1060         if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
1061             doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1062         } else {
1063             result = etc1_pkm_get_width((etc1_byte*) headerB.getData());
1064         }
1065     }
1066     return result;
1067 }
1068
1069 /**
1070  * Read the image height from a PKM header
1071  */
1072 static jint etc1_getHeight(JNIEnv *env, jclass clazz,
1073         jobject header) {
1074     jint result = 0;
1075     BufferHelper headerB(env, header);
1076     if (headerB.checkPointer("header") ){
1077         if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
1078             doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1079         } else {
1080             result = etc1_pkm_get_height((etc1_byte*) headerB.getData());
1081         }
1082     }
1083     return result;
1084 }
1085
1086 /*
1087  * JNI registration
1088  */
1089
1090 static JNINativeMethod gMatrixMethods[] = {
1091     { "multiplyMM", "!([FI[FI[FI)V", (void*)util_multiplyMM },
1092     { "multiplyMV", "!([FI[FI[FI)V", (void*)util_multiplyMV },
1093 };
1094
1095 static JNINativeMethod gVisibilityMethods[] = {
1096     { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere },
1097     { "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres },
1098     { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest },
1099 };
1100
1101 static JNINativeMethod gUtilsMethods[] = {
1102     { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat },
1103     { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType },
1104     { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D },
1105     { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D },
1106     { "setTracingLevel", "(I)V",                        (void*)setTracingLevel },
1107 };
1108
1109 static JNINativeMethod gEtc1Methods[] = {
1110     { "encodeBlock", "(Ljava/nio/Buffer;ILjava/nio/Buffer;)V", (void*) etc1_encodeBlock },
1111     { "decodeBlock", "(Ljava/nio/Buffer;Ljava/nio/Buffer;)V", (void*) etc1_decodeBlock },
1112     { "getEncodedDataSize", "(II)I", (void*) etc1_getEncodedDataSize },
1113     { "encodeImage", "(Ljava/nio/Buffer;IIIILjava/nio/Buffer;)V", (void*) etc1_encodeImage },
1114     { "decodeImage", "(Ljava/nio/Buffer;Ljava/nio/Buffer;IIII)V", (void*) etc1_decodeImage },
1115     { "formatHeader", "(Ljava/nio/Buffer;II)V", (void*) etc1_formatHeader },
1116     { "isValid", "(Ljava/nio/Buffer;)Z", (void*) etc1_isValid },
1117     { "getWidth", "(Ljava/nio/Buffer;)I", (void*) etc1_getWidth },
1118     { "getHeight", "(Ljava/nio/Buffer;)I", (void*) etc1_getHeight },
1119 };
1120
1121 typedef struct _ClassRegistrationInfo {
1122     const char* classPath;
1123     JNINativeMethod* methods;
1124     size_t methodCount;
1125 } ClassRegistrationInfo;
1126
1127 static ClassRegistrationInfo gClasses[] = {
1128     {"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)},
1129     {"android/opengl/Visibility", gVisibilityMethods, NELEM(gVisibilityMethods)},
1130     {"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)},
1131     {"android/opengl/ETC1", gEtc1Methods, NELEM(gEtc1Methods)},
1132 };
1133
1134 int register_android_opengl_classes(JNIEnv* env)
1135 {
1136     nativeClassInitBuffer(env);
1137     int result = 0;
1138     for (int i = 0; i < NELEM(gClasses); i++) {
1139         ClassRegistrationInfo* cri = &gClasses[i];
1140         result = RegisterMethodsOrDie(env, cri->classPath, cri->methods, cri->methodCount);
1141     }
1142     return result;
1143 }
1144
1145 } // namespace android