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