OSDN Git Service

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