OSDN Git Service

DO NOT MERGE Back-port fixes for b/62196835 am: 24fdc53cfe -s ours am: ff115bffb1...
[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 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
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
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
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
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
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     const int w = bitmap.width();
743     const int h = bitmap.height();
744     const void* p = bitmap.getPixels();
745     if (internalformat == GL_PALETTE8_RGBA8_OES) {
746         if (sizeof(SkPMColor) != sizeof(uint32_t)) {
747             err = -1;
748             goto error;
749         }
750         const size_t size = bitmap.getSize();
751         const size_t palette_size = 256*sizeof(SkPMColor);
752         const size_t imageSize = size + palette_size;
753         void* const data = malloc(imageSize);
754         if (data) {
755             void* const pixels = (char*)data + palette_size;
756             SkColorTable* ctable = bitmap.getColorTable();
757             memcpy(data, ctable->readColors(), ctable->count() * sizeof(SkPMColor));
758             memcpy(pixels, p, size);
759             glCompressedTexImage2D(target, level, internalformat, w, h, border, imageSize, data);
760             free(data);
761         } else {
762             err = -1;
763         }
764     } else {
765         glTexImage2D(target, level, internalformat, w, h, border, internalformat, type, p);
766     }
767 error:
768     return err;
769 }
770
771 static jint util_texSubImage2D(JNIEnv *env, jclass clazz,
772         jint target, jint level, jint xoffset, jint yoffset,
773         jobject jbitmap, jint format, jint type)
774 {
775     SkBitmap bitmap;
776     GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
777     SkColorType colorType = bitmap.colorType();
778     if (format < 0) {
779         format = getInternalFormat(colorType);
780         if (format == GL_PALETTE8_RGBA8_OES)
781             return -1; // glCompressedTexSubImage2D() not supported
782     }
783     int err = checkFormat(colorType, format, type);
784     if (err)
785         return err;
786     const int w = bitmap.width();
787     const int h = bitmap.height();
788     const void* p = bitmap.getPixels();
789     glTexSubImage2D(target, level, xoffset, yoffset, w, h, format, type, p);
790     return 0;
791 }
792
793 /*
794  * ETC1 methods.
795  */
796
797 static jclass nioAccessClass;
798 static jclass bufferClass;
799 static jmethodID getBasePointerID;
800 static jmethodID getBaseArrayID;
801 static jmethodID getBaseArrayOffsetID;
802 static jfieldID positionID;
803 static jfieldID limitID;
804 static jfieldID elementSizeShiftID;
805
806 /* Cache method IDs each time the class is loaded. */
807
808 static void
809 nativeClassInitBuffer(JNIEnv *env)
810 {
811     jclass nioAccessClassLocal = FindClassOrDie(env, "java/nio/NIOAccess");
812     nioAccessClass = MakeGlobalRefOrDie(env, nioAccessClassLocal);
813     getBasePointerID = GetStaticMethodIDOrDie(env, nioAccessClass,
814             "getBasePointer", "(Ljava/nio/Buffer;)J");
815     getBaseArrayID = GetStaticMethodIDOrDie(env, nioAccessClass,
816             "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;");
817     getBaseArrayOffsetID = GetStaticMethodIDOrDie(env, nioAccessClass,
818             "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
819
820     jclass bufferClassLocal = FindClassOrDie(env, "java/nio/Buffer");
821     bufferClass = MakeGlobalRefOrDie(env, bufferClassLocal);
822     positionID = GetFieldIDOrDie(env, bufferClass, "position", "I");
823     limitID = GetFieldIDOrDie(env, bufferClass, "limit", "I");
824     elementSizeShiftID = GetFieldIDOrDie(env, bufferClass, "_elementSizeShift", "I");
825 }
826
827 static void *
828 getPointer(JNIEnv *_env, jobject buffer, jint *remaining)
829 {
830     jint position;
831     jint limit;
832     jint elementSizeShift;
833     jlong pointer;
834
835     position = _env->GetIntField(buffer, positionID);
836     limit = _env->GetIntField(buffer, limitID);
837     elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
838     *remaining = (limit - position) << elementSizeShift;
839     pointer = _env->CallStaticLongMethod(nioAccessClass,
840             getBasePointerID, buffer);
841     if (pointer != 0L) {
842         return reinterpret_cast<void *>(pointer);
843     }
844     return NULL;
845 }
846
847 class BufferHelper {
848 public:
849     BufferHelper(JNIEnv *env, jobject buffer) {
850         mEnv = env;
851         mBuffer = buffer;
852         mData = NULL;
853         mRemaining = 0;
854     }
855
856     bool checkPointer(const char* errorMessage) {
857         if (mBuffer) {
858             mData = getPointer(mEnv, mBuffer, &mRemaining);
859             if (mData == NULL) {
860                 doThrowIAE(mEnv, errorMessage);
861             }
862             return mData != NULL;
863         } else {
864             doThrowIAE(mEnv, errorMessage);
865             return false;
866         }
867     }
868
869     inline void* getData() {
870         return mData;
871     }
872
873     inline jint remaining() {
874         return mRemaining;
875     }
876
877 private:
878     JNIEnv* mEnv;
879     jobject mBuffer;
880     void* mData;
881     jint mRemaining;
882 };
883
884 /**
885  * Encode a block of pixels.
886  *
887  * @param in a pointer to a ETC1_DECODED_BLOCK_SIZE array of bytes that represent a
888  * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
889  * value of pixel (x, y).
890  *
891  * @param validPixelMask is a 16-bit mask where bit (1 << (x + y * 4)) indicates whether
892  * the corresponding (x,y) pixel is valid. Invalid pixel color values are ignored when compressing.
893  *
894  * @param out an ETC1 compressed version of the data.
895  *
896  */
897 static void etc1_encodeBlock(JNIEnv *env, jclass clazz,
898         jobject in, jint validPixelMask, jobject out) {
899     if (validPixelMask < 0 || validPixelMask > 15) {
900         doThrowIAE(env, "validPixelMask");
901         return;
902     }
903     BufferHelper inB(env, in);
904     BufferHelper outB(env, out);
905     if (inB.checkPointer("in") && outB.checkPointer("out")) {
906         if (inB.remaining() < ETC1_DECODED_BLOCK_SIZE) {
907             doThrowIAE(env, "in's remaining data < DECODED_BLOCK_SIZE");
908         } else if (outB.remaining() < ETC1_ENCODED_BLOCK_SIZE) {
909             doThrowIAE(env, "out's remaining data < ENCODED_BLOCK_SIZE");
910         } else {
911             etc1_encode_block((etc1_byte*) inB.getData(), validPixelMask,
912                     (etc1_byte*) outB.getData());
913         }
914     }
915 }
916
917 /**
918  * Decode a block of pixels.
919  *
920  * @param in an ETC1 compressed version of the data.
921  *
922  * @param out a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a
923  * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
924  * value of pixel (x, y).
925  */
926 static void etc1_decodeBlock(JNIEnv *env, jclass clazz,
927         jobject in, jobject out){
928     BufferHelper inB(env, in);
929     BufferHelper outB(env, out);
930     if (inB.checkPointer("in") && outB.checkPointer("out")) {
931         if (inB.remaining() < ETC1_ENCODED_BLOCK_SIZE) {
932             doThrowIAE(env, "in's remaining data < ENCODED_BLOCK_SIZE");
933         } else if (outB.remaining() < ETC1_DECODED_BLOCK_SIZE) {
934             doThrowIAE(env, "out's remaining data < DECODED_BLOCK_SIZE");
935         } else {
936             etc1_decode_block((etc1_byte*) inB.getData(),
937                     (etc1_byte*) outB.getData());
938         }
939     }
940 }
941
942 /**
943  * Return the size of the encoded image data (does not include size of PKM header).
944  */
945 static jint etc1_getEncodedDataSize(JNIEnv *env, jclass clazz,
946         jint width, jint height) {
947     return etc1_get_encoded_data_size(width, height);
948 }
949
950 /**
951  * Encode an entire image.
952  * @param in pointer to the image data. Formatted such that
953  *           pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset;
954  * @param out pointer to encoded data. Must be large enough to store entire encoded image.
955  */
956 static void etc1_encodeImage(JNIEnv *env, jclass clazz,
957         jobject in, jint width, jint height,
958         jint pixelSize, jint stride, jobject out) {
959     if (pixelSize < 2 || pixelSize > 3) {
960         doThrowIAE(env, "pixelSize must be 2 or 3");
961         return;
962     }
963     BufferHelper inB(env, in);
964     BufferHelper outB(env, out);
965     if (inB.checkPointer("in") && outB.checkPointer("out")) {
966         jint imageSize = stride * height;
967         jint encodedImageSize = etc1_get_encoded_data_size(width, height);
968         if (inB.remaining() < imageSize) {
969             doThrowIAE(env, "in's remaining data < image size");
970         } else if (outB.remaining() < encodedImageSize) {
971             doThrowIAE(env, "out's remaining data < encoded image size");
972         } else {
973             etc1_encode_image((etc1_byte*) inB.getData(), width, height, pixelSize, stride,
974                               (etc1_byte*) outB.getData());
975         }
976     }
977 }
978
979 /**
980  * Decode an entire image.
981  * @param in the encoded data.
982  * @param out pointer to the image data. Will be written such that
983  *            pixel (x,y) is at pIn + pixelSize * x + stride * y. Must be
984  *            large enough to store entire image.
985  */
986 static void etc1_decodeImage(JNIEnv *env, jclass clazz,
987         jobject  in, jobject out,
988         jint width, jint height,
989         jint pixelSize, jint stride) {
990     if (pixelSize < 2 || pixelSize > 3) {
991         doThrowIAE(env, "pixelSize must be 2 or 3");
992         return;
993     }
994     BufferHelper inB(env, in);
995     BufferHelper outB(env, out);
996     if (inB.checkPointer("in") && outB.checkPointer("out")) {
997         jint imageSize = stride * height;
998         jint encodedImageSize = etc1_get_encoded_data_size(width, height);
999         if (inB.remaining() < encodedImageSize) {
1000             doThrowIAE(env, "in's remaining data < encoded image size");
1001         } else if (outB.remaining() < imageSize) {
1002             doThrowIAE(env, "out's remaining data < image size");
1003         } else {
1004             etc1_decode_image((etc1_byte*) inB.getData(), (etc1_byte*) outB.getData(),
1005                               width, height, pixelSize, stride);
1006         }
1007     }
1008 }
1009
1010 /**
1011  * Format a PKM header
1012  */
1013 static void etc1_formatHeader(JNIEnv *env, jclass clazz,
1014         jobject header, jint width, jint height) {
1015     BufferHelper headerB(env, header);
1016     if (headerB.checkPointer("header") ){
1017         if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
1018             doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1019         } else {
1020             etc1_pkm_format_header((etc1_byte*) headerB.getData(), width, height);
1021         }
1022     }
1023 }
1024
1025 /**
1026  * Check if a PKM header is correctly formatted.
1027  */
1028 static jboolean etc1_isValid(JNIEnv *env, jclass clazz,
1029         jobject header) {
1030     jboolean result = false;
1031     BufferHelper headerB(env, header);
1032     if (headerB.checkPointer("header") ){
1033         if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
1034             doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1035         } else {
1036             result = etc1_pkm_is_valid((etc1_byte*) headerB.getData());
1037         }
1038     }
1039     return result ? JNI_TRUE : JNI_FALSE;
1040 }
1041
1042 /**
1043  * Read the image width from a PKM header
1044  */
1045 static jint etc1_getWidth(JNIEnv *env, jclass clazz,
1046         jobject header) {
1047     jint result = 0;
1048     BufferHelper headerB(env, header);
1049     if (headerB.checkPointer("header") ){
1050         if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
1051             doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1052         } else {
1053             result = etc1_pkm_get_width((etc1_byte*) headerB.getData());
1054         }
1055     }
1056     return result;
1057 }
1058
1059 /**
1060  * Read the image height from a PKM header
1061  */
1062 static jint etc1_getHeight(JNIEnv *env, jclass clazz,
1063         jobject header) {
1064     jint result = 0;
1065     BufferHelper headerB(env, header);
1066     if (headerB.checkPointer("header") ){
1067         if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
1068             doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1069         } else {
1070             result = etc1_pkm_get_height((etc1_byte*) headerB.getData());
1071         }
1072     }
1073     return result;
1074 }
1075
1076 /*
1077  * JNI registration
1078  */
1079
1080 static const JNINativeMethod gMatrixMethods[] = {
1081     { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM },
1082     { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV },
1083 };
1084
1085 static const JNINativeMethod gVisibilityMethods[] = {
1086     { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere },
1087     { "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres },
1088     { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest },
1089 };
1090
1091 static const JNINativeMethod gUtilsMethods[] = {
1092     { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat },
1093     { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType },
1094     { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D },
1095     { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D },
1096 };
1097
1098 static const JNINativeMethod gEtc1Methods[] = {
1099     { "encodeBlock", "(Ljava/nio/Buffer;ILjava/nio/Buffer;)V", (void*) etc1_encodeBlock },
1100     { "decodeBlock", "(Ljava/nio/Buffer;Ljava/nio/Buffer;)V", (void*) etc1_decodeBlock },
1101     { "getEncodedDataSize", "(II)I", (void*) etc1_getEncodedDataSize },
1102     { "encodeImage", "(Ljava/nio/Buffer;IIIILjava/nio/Buffer;)V", (void*) etc1_encodeImage },
1103     { "decodeImage", "(Ljava/nio/Buffer;Ljava/nio/Buffer;IIII)V", (void*) etc1_decodeImage },
1104     { "formatHeader", "(Ljava/nio/Buffer;II)V", (void*) etc1_formatHeader },
1105     { "isValid", "(Ljava/nio/Buffer;)Z", (void*) etc1_isValid },
1106     { "getWidth", "(Ljava/nio/Buffer;)I", (void*) etc1_getWidth },
1107     { "getHeight", "(Ljava/nio/Buffer;)I", (void*) etc1_getHeight },
1108 };
1109
1110 typedef struct _ClassRegistrationInfo {
1111     const char* classPath;
1112     const JNINativeMethod* methods;
1113     size_t methodCount;
1114 } ClassRegistrationInfo;
1115
1116 static const ClassRegistrationInfo gClasses[] = {
1117     {"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)},
1118     {"android/opengl/Visibility", gVisibilityMethods, NELEM(gVisibilityMethods)},
1119     {"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)},
1120     {"android/opengl/ETC1", gEtc1Methods, NELEM(gEtc1Methods)},
1121 };
1122
1123 int register_android_opengl_classes(JNIEnv* env)
1124 {
1125     nativeClassInitBuffer(env);
1126     int result = 0;
1127     for (int i = 0; i < NELEM(gClasses); i++) {
1128         const ClassRegistrationInfo* cri = &gClasses[i];
1129         result = RegisterMethodsOrDie(env, cri->classPath, cri->methods, cri->methodCount);
1130     }
1131     return result;
1132 }
1133
1134 } // namespace android