util.cpp revision 106006cbdedc79ce8746ca5449610c69a2f69655
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 <nativehelper/jni.h> 18#include <math.h> 19#include <stdio.h> 20#include <stdlib.h> 21#include <string.h> 22#include <assert.h> 23#include <dlfcn.h> 24 25#include <GLES/gl.h> 26 27#include <core/SkBitmap.h> 28 29#include "android_runtime/AndroidRuntime.h" 30 31#undef LOG_TAG 32#define LOG_TAG "OpenGLUtil" 33#include <utils/Log.h> 34#include "utils/misc.h" 35 36#include "poly.h" 37 38namespace android { 39 40static jclass gIAEClass; 41static jclass gUOEClass; 42 43static inline 44void mx4transform(float x, float y, float z, float w, const float* pM, float* pDest) { 45 pDest[0] = pM[0 + 4 * 0] * x + pM[0 + 4 * 1] * y + pM[0 + 4 * 2] * z + pM[0 + 4 * 3] * w; 46 pDest[1] = pM[1 + 4 * 0] * x + pM[1 + 4 * 1] * y + pM[1 + 4 * 2] * z + pM[1 + 4 * 3] * w; 47 pDest[2] = pM[2 + 4 * 0] * x + pM[2 + 4 * 1] * y + pM[2 + 4 * 2] * z + pM[2 + 4 * 3] * w; 48 pDest[3] = pM[3 + 4 * 0] * x + pM[3 + 4 * 1] * y + pM[3 + 4 * 2] * z + pM[3 + 4 * 3] * w; 49} 50 51class MallocHelper { 52public: 53 MallocHelper() { 54 mData = 0; 55 } 56 57 ~MallocHelper() { 58 if (mData != 0) { 59 free(mData); 60 } 61 } 62 63 void* alloc(size_t size) { 64 mData = malloc(size); 65 return mData; 66 } 67 68private: 69 void* mData; 70}; 71 72#if 0 73static 74void 75print_poly(const char* label, Poly* pPoly) { 76 LOGI("%s: %d verts", label, pPoly->n); 77 for(int i = 0; i < pPoly->n; i++) { 78 Poly_vert* pV = & pPoly->vert[i]; 79 LOGI("[%d] %g, %g, %g %g", i, pV->sx, pV->sy, pV->sz, pV->sw); 80 } 81} 82#endif 83 84static 85int visibilityTest(float* pWS, float* pPositions, int positionsLength, 86 unsigned short* pIndices, int indexCount) { 87 MallocHelper mallocHelper; 88 int result = POLY_CLIP_OUT; 89 float* pTransformed = 0; 90 int transformedIndexCount = 0; 91 92 if ( indexCount < 3 ) { 93 return POLY_CLIP_OUT; 94 } 95 96 // Find out how many vertices we need to transform 97 // We transform every vertex between the min and max indices, inclusive. 98 // This is OK for the data sets we expect to use with this function, but 99 // for other loads it might be better to use a more sophisticated vertex 100 // cache of some sort. 101 102 int minIndex = 65536; 103 int maxIndex = -1; 104 for(int i = 0; i < indexCount; i++) { 105 int index = pIndices[i]; 106 if ( index < minIndex ) { 107 minIndex = index; 108 } 109 if ( index > maxIndex ) { 110 maxIndex = index; 111 } 112 } 113 114 if ( maxIndex * 3 > positionsLength) { 115 return -1; 116 } 117 118 transformedIndexCount = maxIndex - minIndex + 1; 119 pTransformed = (float*) mallocHelper.alloc(transformedIndexCount * 4 * sizeof(float)); 120 121 if (pTransformed == 0 ) { 122 return -2; 123 } 124 125 // Transform the vertices 126 { 127 const float* pSrc = pPositions + 3 * minIndex; 128 float* pDst = pTransformed; 129 for (int i = 0; i < transformedIndexCount; i++, pSrc += 3, pDst += 4) { 130 mx4transform(pSrc[0], pSrc[1], pSrc[2], 1.0f, pWS, pDst); 131 } 132 } 133 134 // Clip the triangles 135 136 Poly poly; 137 float* pDest = & poly.vert[0].sx; 138 for (int i = 0; i < indexCount; i += 3) { 139 poly.n = 3; 140 memcpy(pDest , pTransformed + 4 * (pIndices[i ] - minIndex), 4 * sizeof(float)); 141 memcpy(pDest + 4, pTransformed + 4 * (pIndices[i + 1] - minIndex), 4 * sizeof(float)); 142 memcpy(pDest + 8, pTransformed + 4 * (pIndices[i + 2] - minIndex), 4 * sizeof(float)); 143 result = poly_clip_to_frustum(&poly); 144 if ( result != POLY_CLIP_OUT) { 145 return result; 146 } 147 } 148 149 return result; 150} 151 152template<class JArray, class T> 153class ArrayHelper { 154public: 155 ArrayHelper(JNIEnv* env, JArray ref, jint offset, jint minSize) { 156 mEnv = env; 157 mRef = ref; 158 mOffset = offset; 159 mMinSize = minSize; 160 mBase = 0; 161 mReleaseParam = JNI_ABORT; 162 } 163 164 ~ArrayHelper() { 165 if (mBase) { 166 mEnv->ReleasePrimitiveArrayCritical(mRef, mBase, mReleaseParam); 167 } 168 } 169 170 // We seperate the bounds check from the initialization because we want to 171 // be able to bounds-check multiple arrays, and we can't throw an exception 172 // after we've called GetPrimitiveArrayCritical. 173 174 // Return true if the bounds check succeeded 175 // Else instruct the runtime to throw an exception 176 177 bool check() { 178 if ( ! mRef) { 179 mEnv->ThrowNew(gIAEClass, "array == null"); 180 return false; 181 } 182 if ( mOffset < 0) { 183 mEnv->ThrowNew(gIAEClass, "offset < 0"); 184 return false; 185 } 186 mLength = mEnv->GetArrayLength(mRef) - mOffset; 187 if (mLength < mMinSize ) { 188 mEnv->ThrowNew(gIAEClass, "length - offset < n"); 189 return false; 190 } 191 return true; 192 } 193 194 // Bind the array. 195 196 void bind() { 197 mBase = (T*) mEnv->GetPrimitiveArrayCritical(mRef, (jboolean *) 0); 198 mData = mBase + mOffset; 199 } 200 201 void commitChanges() { 202 mReleaseParam = 0; 203 } 204 205 T* mData; 206 int mLength; 207 208private: 209 T* mBase; 210 JNIEnv* mEnv; 211 JArray mRef; 212 jint mOffset; 213 jint mMinSize; 214 int mReleaseParam; 215}; 216 217typedef ArrayHelper<jfloatArray, float> FloatArrayHelper; 218typedef ArrayHelper<jcharArray, unsigned short> UnsignedShortArrayHelper; 219typedef ArrayHelper<jintArray, int> IntArrayHelper; 220typedef ArrayHelper<jbyteArray, unsigned char> ByteArrayHelper; 221 222inline float distance2(float x, float y, float z) { 223 return x * x + y * y + z * z; 224} 225 226inline float distance(float x, float y, float z) { 227 return sqrtf(distance2(x, y, z)); 228} 229 230static 231void util_computeBoundingSphere(JNIEnv *env, jclass clazz, 232 jfloatArray positions_ref, jint positionsOffset, jint positionsCount, 233 jfloatArray sphere_ref, jint sphereOffset) { 234 FloatArrayHelper positions(env, positions_ref, positionsOffset, 0); 235 FloatArrayHelper sphere(env, sphere_ref, sphereOffset, 4); 236 237 bool checkOK = positions.check() && sphere.check(); 238 if (! checkOK) { 239 return; 240 } 241 242 positions.bind(); 243 sphere.bind(); 244 245 if ( positionsCount < 1 ) { 246 env->ThrowNew(gIAEClass, "positionsCount < 1"); 247 return; 248 } 249 250 const float* pSrc = positions.mData; 251 252 // find bounding box 253 float x0 = *pSrc++; 254 float x1 = x0; 255 float y0 = *pSrc++; 256 float y1 = y0; 257 float z0 = *pSrc++; 258 float z1 = z0; 259 260 for(int i = 1; i < positionsCount; i++) { 261 { 262 float x = *pSrc++; 263 if (x < x0) { 264 x0 = x; 265 } 266 else if (x > x1) { 267 x1 = x; 268 } 269 } 270 { 271 float y = *pSrc++; 272 if (y < y0) { 273 y0 = y; 274 } 275 else if (y > y1) { 276 y1 = y; 277 } 278 } 279 { 280 float z = *pSrc++; 281 if (z < z0) { 282 z0 = z; 283 } 284 else if (z > z1) { 285 z1 = z; 286 } 287 } 288 } 289 290 // Because we know our input meshes fit pretty well into bounding boxes, 291 // just take the diagonal of the box as defining our sphere. 292 float* pSphere = sphere.mData; 293 float dx = x1 - x0; 294 float dy = y1 - y0; 295 float dz = z1 - z0; 296 *pSphere++ = x0 + dx * 0.5f; 297 *pSphere++ = y0 + dy * 0.5f; 298 *pSphere++ = z0 + dz * 0.5f; 299 *pSphere++ = distance(dx, dy, dz) * 0.5f; 300 301 sphere.commitChanges(); 302} 303 304static void normalizePlane(float* p) { 305 float rdist = 1.0f / distance(p[0], p[1], p[2]); 306 for(int i = 0; i < 4; i++) { 307 p[i] *= rdist; 308 } 309} 310 311static inline float dot3(float x0, float y0, float z0, float x1, float y1, float z1) { 312 return x0 * x1 + y0 * y1 + z0 * z1; 313} 314 315static inline float signedDistance(const float* pPlane, float x, float y, float z) { 316 return dot3(pPlane[0], pPlane[1], pPlane[2], x, y, z) + pPlane[3]; 317} 318 319// Return true if the sphere intersects or is inside the frustum 320 321static bool sphereHitsFrustum(const float* pFrustum, const float* pSphere) { 322 float x = pSphere[0]; 323 float y = pSphere[1]; 324 float z = pSphere[2]; 325 float negRadius = -pSphere[3]; 326 for (int i = 0; i < 6; i++, pFrustum += 4) { 327 if (signedDistance(pFrustum, x, y, z) <= negRadius) { 328 return false; 329 } 330 } 331 return true; 332} 333 334static void computeFrustum(const float* m, float* f) { 335 float m3 = m[3]; 336 float m7 = m[7]; 337 float m11 = m[11]; 338 float m15 = m[15]; 339 // right 340 f[0] = m3 - m[0]; 341 f[1] = m7 - m[4]; 342 f[2] = m11 - m[8]; 343 f[3] = m15 - m[12]; 344 normalizePlane(f); 345 f+= 4; 346 347 // left 348 f[0] = m3 + m[0]; 349 f[1] = m7 + m[4]; 350 f[2] = m11 + m[8]; 351 f[3] = m15 + m[12]; 352 normalizePlane(f); 353 f+= 4; 354 355 // top 356 f[0] = m3 - m[1]; 357 f[1] = m7 - m[5]; 358 f[2] = m11 - m[9]; 359 f[3] = m15 - m[13]; 360 normalizePlane(f); 361 f+= 4; 362 363 // bottom 364 f[0] = m3 + m[1]; 365 f[1] = m7 + m[5]; 366 f[2] = m11 + m[9]; 367 f[3] = m15 + m[13]; 368 normalizePlane(f); 369 f+= 4; 370 371 // far 372 f[0] = m3 - m[2]; 373 f[1] = m7 - m[6]; 374 f[2] = m11 - m[10]; 375 f[3] = m15 - m[14]; 376 normalizePlane(f); 377 f+= 4; 378 379 // near 380 f[0] = m3 + m[2]; 381 f[1] = m7 + m[6]; 382 f[2] = m11 + m[10]; 383 f[3] = m15 + m[14]; 384 normalizePlane(f); 385} 386 387static 388int util_frustumCullSpheres(JNIEnv *env, jclass clazz, 389 jfloatArray mvp_ref, jint mvpOffset, 390 jfloatArray spheres_ref, jint spheresOffset, jint spheresCount, 391 jintArray results_ref, jint resultsOffset, jint resultsCapacity) { 392 float frustum[6*4]; 393 int outputCount; 394 int* pResults; 395 float* pSphere; 396 FloatArrayHelper mvp(env, mvp_ref, mvpOffset, 16); 397 FloatArrayHelper spheres(env, spheres_ref, spheresOffset, spheresCount * 4); 398 IntArrayHelper results(env, results_ref, resultsOffset, resultsCapacity); 399 400 bool initializedOK = mvp.check() && spheres.check() && results.check(); 401 if (! initializedOK) { 402 return -1; 403 } 404 405 mvp.bind(); 406 spheres.bind(); 407 results.bind(); 408 409 computeFrustum(mvp.mData, frustum); 410 411 // Cull the spheres 412 413 pSphere = spheres.mData; 414 pResults = results.mData; 415 outputCount = 0; 416 for(int i = 0; i < spheresCount; i++, pSphere += 4) { 417 if (sphereHitsFrustum(frustum, pSphere)) { 418 if (outputCount < resultsCapacity) { 419 *pResults++ = i; 420 } 421 outputCount++; 422 } 423 } 424 results.commitChanges(); 425 return outputCount; 426} 427 428/* 429 public native int visibilityTest(float[] ws, int wsOffset, 430 float[] positions, int positionsOffset, 431 char[] indices, int indicesOffset, int indexCount); 432 */ 433 434static 435int util_visibilityTest(JNIEnv *env, jclass clazz, 436 jfloatArray ws_ref, jint wsOffset, 437 jfloatArray positions_ref, jint positionsOffset, 438 jcharArray indices_ref, jint indicesOffset, jint indexCount) { 439 440 FloatArrayHelper ws(env, ws_ref, wsOffset, 16); 441 FloatArrayHelper positions(env, positions_ref, positionsOffset, 0); 442 UnsignedShortArrayHelper indices(env, indices_ref, indicesOffset, 0); 443 444 bool checkOK = ws.check() && positions.check() && indices.check(); 445 if (! checkOK) { 446 // Return value will be ignored, because an exception has been thrown. 447 return -1; 448 } 449 450 if (indices.mLength < indexCount) { 451 env->ThrowNew(gIAEClass, "length < offset + indexCount"); 452 // Return value will be ignored, because an exception has been thrown. 453 return -1; 454 } 455 456 ws.bind(); 457 positions.bind(); 458 indices.bind(); 459 460 return visibilityTest(ws.mData, 461 positions.mData, positions.mLength, 462 indices.mData, indexCount); 463} 464 465#define I(_i, _j) ((_j)+ 4*(_i)) 466 467static 468void multiplyMM(float* r, const float* lhs, const float* rhs) 469{ 470 for (int i=0 ; i<4 ; i++) { 471 register const float rhs_i0 = rhs[ I(i,0) ]; 472 register float ri0 = lhs[ I(0,0) ] * rhs_i0; 473 register float ri1 = lhs[ I(0,1) ] * rhs_i0; 474 register float ri2 = lhs[ I(0,2) ] * rhs_i0; 475 register float ri3 = lhs[ I(0,3) ] * rhs_i0; 476 for (int j=1 ; j<4 ; j++) { 477 register const float rhs_ij = rhs[ I(i,j) ]; 478 ri0 += lhs[ I(j,0) ] * rhs_ij; 479 ri1 += lhs[ I(j,1) ] * rhs_ij; 480 ri2 += lhs[ I(j,2) ] * rhs_ij; 481 ri3 += lhs[ I(j,3) ] * rhs_ij; 482 } 483 r[ I(i,0) ] = ri0; 484 r[ I(i,1) ] = ri1; 485 r[ I(i,2) ] = ri2; 486 r[ I(i,3) ] = ri3; 487 } 488} 489 490static 491void util_multiplyMM(JNIEnv *env, jclass clazz, 492 jfloatArray result_ref, jint resultOffset, 493 jfloatArray lhs_ref, jint lhsOffset, 494 jfloatArray rhs_ref, jint rhsOffset) { 495 496 FloatArrayHelper resultMat(env, result_ref, resultOffset, 16); 497 FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16); 498 FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 16); 499 500 bool checkOK = resultMat.check() && lhs.check() && rhs.check(); 501 502 if ( !checkOK ) { 503 return; 504 } 505 506 resultMat.bind(); 507 lhs.bind(); 508 rhs.bind(); 509 510 multiplyMM(resultMat.mData, lhs.mData, rhs.mData); 511 512 resultMat.commitChanges(); 513} 514 515static 516void multiplyMV(float* r, const float* lhs, const float* rhs) 517{ 518 mx4transform(rhs[0], rhs[1], rhs[2], rhs[3], lhs, r); 519} 520 521static 522void util_multiplyMV(JNIEnv *env, jclass clazz, 523 jfloatArray result_ref, jint resultOffset, 524 jfloatArray lhs_ref, jint lhsOffset, 525 jfloatArray rhs_ref, jint rhsOffset) { 526 527 FloatArrayHelper resultV(env, result_ref, resultOffset, 4); 528 FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16); 529 FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 4); 530 531 bool checkOK = resultV.check() && lhs.check() && rhs.check(); 532 533 if ( !checkOK ) { 534 return; 535 } 536 537 resultV.bind(); 538 lhs.bind(); 539 rhs.bind(); 540 541 multiplyMV(resultV.mData, lhs.mData, rhs.mData); 542 543 resultV.commitChanges(); 544} 545 546// --------------------------------------------------------------------------- 547 548static jfieldID nativeBitmapID = 0; 549 550void nativeUtilsClassInit(JNIEnv *env, jclass clazz) 551{ 552 jclass bitmapClass = env->FindClass("android/graphics/Bitmap"); 553 nativeBitmapID = env->GetFieldID(bitmapClass, "mNativeBitmap", "I"); 554} 555 556static int checkFormat(SkBitmap::Config config, int format, int type) 557{ 558 switch(config) { 559 case SkBitmap::kIndex8_Config: 560 if (format == GL_PALETTE8_RGBA8_OES) 561 return 0; 562 case SkBitmap::kARGB_8888_Config: 563 case SkBitmap::kA8_Config: 564 if (type == GL_UNSIGNED_BYTE) 565 return 0; 566 case SkBitmap::kARGB_4444_Config: 567 case SkBitmap::kRGB_565_Config: 568 switch (type) { 569 case GL_UNSIGNED_SHORT_4_4_4_4: 570 case GL_UNSIGNED_SHORT_5_6_5: 571 case GL_UNSIGNED_SHORT_5_5_5_1: 572 return 0; 573 case GL_UNSIGNED_BYTE: 574 if (format == GL_LUMINANCE_ALPHA) 575 return 0; 576 } 577 break; 578 default: 579 break; 580 } 581 return -1; 582} 583 584static int getInternalFormat(SkBitmap::Config config) 585{ 586 switch(config) { 587 case SkBitmap::kA8_Config: 588 return GL_ALPHA; 589 case SkBitmap::kARGB_4444_Config: 590 return GL_RGBA; 591 case SkBitmap::kARGB_8888_Config: 592 return GL_RGBA; 593 case SkBitmap::kIndex8_Config: 594 return GL_PALETTE8_RGBA8_OES; 595 case SkBitmap::kRGB_565_Config: 596 return GL_RGB; 597 default: 598 return -1; 599 } 600} 601 602static int getType(SkBitmap::Config config) 603{ 604 switch(config) { 605 case SkBitmap::kA8_Config: 606 return GL_UNSIGNED_BYTE; 607 case SkBitmap::kARGB_4444_Config: 608 return GL_UNSIGNED_SHORT_4_4_4_4; 609 case SkBitmap::kARGB_8888_Config: 610 return GL_UNSIGNED_BYTE; 611 case SkBitmap::kIndex8_Config: 612 return -1; // No type for compressed data. 613 case SkBitmap::kRGB_565_Config: 614 return GL_UNSIGNED_SHORT_5_6_5; 615 default: 616 return -1; 617 } 618} 619 620static jint util_getInternalFormat(JNIEnv *env, jclass clazz, 621 jobject jbitmap) 622{ 623 SkBitmap const * nativeBitmap = 624 (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID); 625 const SkBitmap& bitmap(*nativeBitmap); 626 SkBitmap::Config config = bitmap.getConfig(); 627 return getInternalFormat(config); 628} 629 630static jint util_getType(JNIEnv *env, jclass clazz, 631 jobject jbitmap) 632{ 633 SkBitmap const * nativeBitmap = 634 (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID); 635 const SkBitmap& bitmap(*nativeBitmap); 636 SkBitmap::Config config = bitmap.getConfig(); 637 return getType(config); 638} 639 640static jint util_texImage2D(JNIEnv *env, jclass clazz, 641 jint target, jint level, jint internalformat, 642 jobject jbitmap, jint type, jint border) 643{ 644 SkBitmap const * nativeBitmap = 645 (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID); 646 const SkBitmap& bitmap(*nativeBitmap); 647 SkBitmap::Config config = bitmap.getConfig(); 648 if (internalformat < 0) { 649 internalformat = getInternalFormat(config); 650 } 651 if (type < 0) { 652 type = getType(config); 653 } 654 int err = checkFormat(config, internalformat, type); 655 if (err) 656 return err; 657 bitmap.lockPixels(); 658 const int w = bitmap.width(); 659 const int h = bitmap.height(); 660 const void* p = bitmap.getPixels(); 661 if (internalformat == GL_PALETTE8_RGBA8_OES) { 662 if (sizeof(SkPMColor) != sizeof(uint32_t)) { 663 err = -1; 664 goto error; 665 } 666 const size_t size = bitmap.getSize(); 667 const size_t palette_size = 256*sizeof(SkPMColor); 668 const size_t imageSize = size + palette_size; 669 void* const data = malloc(imageSize); 670 if (data) { 671 void* const pixels = (char*)data + palette_size; 672 SkColorTable* ctable = bitmap.getColorTable(); 673 memcpy(data, ctable->lockColors(), ctable->count() * sizeof(SkPMColor)); 674 memcpy(pixels, p, size); 675 ctable->unlockColors(false); 676 glCompressedTexImage2D(target, level, internalformat, w, h, border, imageSize, data); 677 free(data); 678 } else { 679 err = -1; 680 } 681 } else { 682 glTexImage2D(target, level, internalformat, w, h, border, internalformat, type, p); 683 } 684error: 685 bitmap.unlockPixels(); 686 return err; 687} 688 689static jint util_texSubImage2D(JNIEnv *env, jclass clazz, 690 jint target, jint level, jint xoffset, jint yoffset, 691 jobject jbitmap, jint format, jint type) 692{ 693 SkBitmap const * nativeBitmap = 694 (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID); 695 const SkBitmap& bitmap(*nativeBitmap); 696 SkBitmap::Config config = bitmap.getConfig(); 697 if (format < 0) { 698 format = getInternalFormat(config); 699 if (format == GL_PALETTE8_RGBA8_OES) 700 return -1; // glCompressedTexSubImage2D() not supported 701 } 702 int err = checkFormat(config, format, type); 703 if (err) 704 return err; 705 bitmap.lockPixels(); 706 const int w = bitmap.width(); 707 const int h = bitmap.height(); 708 const void* p = bitmap.getPixels(); 709 glTexSubImage2D(target, level, xoffset, yoffset, w, h, format, type, p); 710 bitmap.unlockPixels(); 711 return 0; 712} 713 714/* 715 * JNI registration 716 */ 717 718static void 719lookupClasses(JNIEnv* env) { 720 gIAEClass = (jclass) env->NewGlobalRef( 721 env->FindClass("java/lang/IllegalArgumentException")); 722 gUOEClass = (jclass) env->NewGlobalRef( 723 env->FindClass("java/lang/UnsupportedOperationException")); 724} 725 726static JNINativeMethod gMatrixMethods[] = { 727 { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM }, 728 { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV }, 729}; 730 731static JNINativeMethod gVisiblityMethods[] = { 732 { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere }, 733 { "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres }, 734 { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest }, 735}; 736 737static JNINativeMethod gUtilsMethods[] = { 738 {"nativeClassInit", "()V", (void*)nativeUtilsClassInit }, 739 { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat }, 740 { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType }, 741 { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D }, 742 { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D }, 743}; 744 745typedef struct _ClassRegistrationInfo { 746 const char* classPath; 747 JNINativeMethod* methods; 748 size_t methodCount; 749} ClassRegistrationInfo; 750 751static ClassRegistrationInfo gClasses[] = { 752 {"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)}, 753 {"android/opengl/Visibility", gVisiblityMethods, NELEM(gVisiblityMethods)}, 754 {"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)}, 755}; 756 757int register_android_opengl_classes(JNIEnv* env) 758{ 759 lookupClasses(env); 760 int result = 0; 761 for (int i = 0; i < NELEM(gClasses); i++) { 762 ClassRegistrationInfo* cri = &gClasses[i]; 763 result = AndroidRuntime::registerNativeMethods(env, 764 cri->classPath, cri->methods, cri->methodCount); 765 if (result < 0) { 766 LOGE("Failed to register %s: %d", cri->classPath, result); 767 break; 768 } 769 } 770 return result; 771} 772 773} // namespace android 774 775