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 20 #include <math.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <assert.h> 25 #include <dlfcn.h> 26 27 #include <GLES/gl.h> 28 #include <ETC1/etc1.h> 29 30 #include <core/SkBitmap.h> 31 32 #include "android_runtime/AndroidRuntime.h" 33 34 #undef LOG_TAG 35 #define LOG_TAG "OpenGLUtil" 36 #include <utils/Log.h> 37 #include "utils/misc.h" 38 39 #include "poly.h" 40 41 namespace android { 42 43 static inline 44 void 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 51 class MallocHelper { 52 public: 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 68 private: 69 void* mData; 70 }; 71 72 #if 0 73 static 74 void 75 print_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 84 static 85 int 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 152 static void doThrowIAE(JNIEnv* env, const char* msg) { 153 jniThrowException(env, "java/lang/IllegalArgumentException", msg); 154 } 155 156 template<class JArray, class T> 157 class ArrayHelper { 158 public: 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 212 private: 213 T* mBase; 214 JNIEnv* mEnv; 215 JArray mRef; 216 jint mOffset; 217 jint mMinSize; 218 int mReleaseParam; 219 }; 220 221 typedef ArrayHelper<jfloatArray, float> FloatArrayHelper; 222 typedef ArrayHelper<jcharArray, unsigned short> UnsignedShortArrayHelper; 223 typedef ArrayHelper<jintArray, int> IntArrayHelper; 224 typedef ArrayHelper<jbyteArray, unsigned char> ByteArrayHelper; 225 226 inline float distance2(float x, float y, float z) { 227 return x * x + y * y + z * z; 228 } 229 230 inline float distance(float x, float y, float z) { 231 return sqrtf(distance2(x, y, z)); 232 } 233 234 static 235 void 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 308 static 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 315 static 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 319 static 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 325 static 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 338 static 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 391 static 392 int 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 438 static 439 int 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 470 static 471 void 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 493 static 494 void 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 518 static 519 void multiplyMV(float* r, const float* lhs, const float* rhs) 520 { 521 mx4transform(rhs[0], rhs[1], rhs[2], rhs[3], lhs, r); 522 } 523 524 static 525 void 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 551 static jfieldID nativeBitmapID = 0; 552 553 void nativeUtilsClassInit(JNIEnv *env, jclass clazz) 554 { 555 jclass bitmapClass = env->FindClass("android/graphics/Bitmap"); 556 nativeBitmapID = env->GetFieldID(bitmapClass, "mNativeBitmap", "I"); 557 } 558 559 static int checkFormat(SkBitmap::Config config, int format, int type) 560 { 561 switch(config) { 562 case SkBitmap::kIndex8_Config: 563 if (format == GL_PALETTE8_RGBA8_OES) 564 return 0; 565 case SkBitmap::kARGB_8888_Config: 566 case SkBitmap::kA8_Config: 567 if (type == GL_UNSIGNED_BYTE) 568 return 0; 569 case SkBitmap::kARGB_4444_Config: 570 case SkBitmap::kRGB_565_Config: 571 switch (type) { 572 case GL_UNSIGNED_SHORT_4_4_4_4: 573 case GL_UNSIGNED_SHORT_5_6_5: 574 case GL_UNSIGNED_SHORT_5_5_5_1: 575 return 0; 576 case GL_UNSIGNED_BYTE: 577 if (format == GL_LUMINANCE_ALPHA) 578 return 0; 579 } 580 break; 581 default: 582 break; 583 } 584 return -1; 585 } 586 587 static int getInternalFormat(SkBitmap::Config config) 588 { 589 switch(config) { 590 case SkBitmap::kA8_Config: 591 return GL_ALPHA; 592 case SkBitmap::kARGB_4444_Config: 593 return GL_RGBA; 594 case SkBitmap::kARGB_8888_Config: 595 return GL_RGBA; 596 case SkBitmap::kIndex8_Config: 597 return GL_PALETTE8_RGBA8_OES; 598 case SkBitmap::kRGB_565_Config: 599 return GL_RGB; 600 default: 601 return -1; 602 } 603 } 604 605 static int getType(SkBitmap::Config config) 606 { 607 switch(config) { 608 case SkBitmap::kA8_Config: 609 return GL_UNSIGNED_BYTE; 610 case SkBitmap::kARGB_4444_Config: 611 return GL_UNSIGNED_SHORT_4_4_4_4; 612 case SkBitmap::kARGB_8888_Config: 613 return GL_UNSIGNED_BYTE; 614 case SkBitmap::kIndex8_Config: 615 return -1; // No type for compressed data. 616 case SkBitmap::kRGB_565_Config: 617 return GL_UNSIGNED_SHORT_5_6_5; 618 default: 619 return -1; 620 } 621 } 622 623 static jint util_getInternalFormat(JNIEnv *env, jclass clazz, 624 jobject jbitmap) 625 { 626 SkBitmap const * nativeBitmap = 627 (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID); 628 const SkBitmap& bitmap(*nativeBitmap); 629 SkBitmap::Config config = bitmap.getConfig(); 630 return getInternalFormat(config); 631 } 632 633 static jint util_getType(JNIEnv *env, jclass clazz, 634 jobject jbitmap) 635 { 636 SkBitmap const * nativeBitmap = 637 (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID); 638 const SkBitmap& bitmap(*nativeBitmap); 639 SkBitmap::Config config = bitmap.getConfig(); 640 return getType(config); 641 } 642 643 static jint util_texImage2D(JNIEnv *env, jclass clazz, 644 jint target, jint level, jint internalformat, 645 jobject jbitmap, jint type, jint border) 646 { 647 SkBitmap const * nativeBitmap = 648 (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID); 649 const SkBitmap& bitmap(*nativeBitmap); 650 SkBitmap::Config config = bitmap.getConfig(); 651 if (internalformat < 0) { 652 internalformat = getInternalFormat(config); 653 } 654 if (type < 0) { 655 type = getType(config); 656 } 657 int err = checkFormat(config, internalformat, type); 658 if (err) 659 return err; 660 bitmap.lockPixels(); 661 const int w = bitmap.width(); 662 const int h = bitmap.height(); 663 const void* p = bitmap.getPixels(); 664 if (internalformat == GL_PALETTE8_RGBA8_OES) { 665 if (sizeof(SkPMColor) != sizeof(uint32_t)) { 666 err = -1; 667 goto error; 668 } 669 const size_t size = bitmap.getSize(); 670 const size_t palette_size = 256*sizeof(SkPMColor); 671 const size_t imageSize = size + palette_size; 672 void* const data = malloc(imageSize); 673 if (data) { 674 void* const pixels = (char*)data + palette_size; 675 SkColorTable* ctable = bitmap.getColorTable(); 676 memcpy(data, ctable->lockColors(), ctable->count() * sizeof(SkPMColor)); 677 memcpy(pixels, p, size); 678 ctable->unlockColors(false); 679 glCompressedTexImage2D(target, level, internalformat, w, h, border, imageSize, data); 680 free(data); 681 } else { 682 err = -1; 683 } 684 } else { 685 glTexImage2D(target, level, internalformat, w, h, border, internalformat, type, p); 686 } 687 error: 688 bitmap.unlockPixels(); 689 return err; 690 } 691 692 static jint util_texSubImage2D(JNIEnv *env, jclass clazz, 693 jint target, jint level, jint xoffset, jint yoffset, 694 jobject jbitmap, jint format, jint type) 695 { 696 SkBitmap const * nativeBitmap = 697 (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID); 698 const SkBitmap& bitmap(*nativeBitmap); 699 SkBitmap::Config config = bitmap.getConfig(); 700 if (format < 0) { 701 format = getInternalFormat(config); 702 if (format == GL_PALETTE8_RGBA8_OES) 703 return -1; // glCompressedTexSubImage2D() not supported 704 } 705 int err = checkFormat(config, format, type); 706 if (err) 707 return err; 708 bitmap.lockPixels(); 709 const int w = bitmap.width(); 710 const int h = bitmap.height(); 711 const void* p = bitmap.getPixels(); 712 glTexSubImage2D(target, level, xoffset, yoffset, w, h, format, type, p); 713 bitmap.unlockPixels(); 714 return 0; 715 } 716 717 /* 718 * ETC1 methods. 719 */ 720 721 static jclass nioAccessClass; 722 static jclass bufferClass; 723 static jmethodID getBasePointerID; 724 static jmethodID getBaseArrayID; 725 static jmethodID getBaseArrayOffsetID; 726 static jfieldID positionID; 727 static jfieldID limitID; 728 static jfieldID elementSizeShiftID; 729 730 /* Cache method IDs each time the class is loaded. */ 731 732 static void 733 nativeClassInitBuffer(JNIEnv *_env) 734 { 735 jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess"); 736 nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal); 737 738 jclass bufferClassLocal = _env->FindClass("java/nio/Buffer"); 739 bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal); 740 741 getBasePointerID = _env->GetStaticMethodID(nioAccessClass, 742 "getBasePointer", "(Ljava/nio/Buffer;)J"); 743 getBaseArrayID = _env->GetStaticMethodID(nioAccessClass, 744 "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;"); 745 getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass, 746 "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); 747 positionID = _env->GetFieldID(bufferClass, "position", "I"); 748 limitID = _env->GetFieldID(bufferClass, "limit", "I"); 749 elementSizeShiftID = 750 _env->GetFieldID(bufferClass, "_elementSizeShift", "I"); 751 } 752 753 static void * 754 getPointer(JNIEnv *_env, jobject buffer, jint *remaining) 755 { 756 jint position; 757 jint limit; 758 jint elementSizeShift; 759 jlong pointer; 760 jint offset; 761 void *data; 762 763 position = _env->GetIntField(buffer, positionID); 764 limit = _env->GetIntField(buffer, limitID); 765 elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); 766 *remaining = (limit - position) << elementSizeShift; 767 pointer = _env->CallStaticLongMethod(nioAccessClass, 768 getBasePointerID, buffer); 769 if (pointer != 0L) { 770 return (void *) (jint) pointer; 771 } 772 return NULL; 773 } 774 775 class BufferHelper { 776 public: 777 BufferHelper(JNIEnv *env, jobject buffer) { 778 mEnv = env; 779 mBuffer = buffer; 780 mData = NULL; 781 mRemaining = 0; 782 } 783 784 bool checkPointer(const char* errorMessage) { 785 if (mBuffer) { 786 mData = getPointer(mEnv, mBuffer, &mRemaining); 787 if (mData == NULL) { 788 doThrowIAE(mEnv, errorMessage); 789 } 790 return mData != NULL; 791 } else { 792 doThrowIAE(mEnv, errorMessage); 793 return false; 794 } 795 } 796 797 inline void* getData() { 798 return mData; 799 } 800 801 inline jint remaining() { 802 return mRemaining; 803 } 804 805 private: 806 JNIEnv* mEnv; 807 jobject mBuffer; 808 void* mData; 809 jint mRemaining; 810 }; 811 812 /** 813 * Encode a block of pixels. 814 * 815 * @param in a pointer to a ETC1_DECODED_BLOCK_SIZE array of bytes that represent a 816 * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R 817 * value of pixel (x, y). 818 * 819 * @param validPixelMask is a 16-bit mask where bit (1 << (x + y * 4)) indicates whether 820 * the corresponding (x,y) pixel is valid. Invalid pixel color values are ignored when compressing. 821 * 822 * @param out an ETC1 compressed version of the data. 823 * 824 */ 825 static void etc1_encodeBlock(JNIEnv *env, jclass clazz, 826 jobject in, jint validPixelMask, jobject out) { 827 if (validPixelMask < 0 || validPixelMask > 15) { 828 doThrowIAE(env, "validPixelMask"); 829 return; 830 } 831 BufferHelper inB(env, in); 832 BufferHelper outB(env, out); 833 if (inB.checkPointer("in") && outB.checkPointer("out")) { 834 if (inB.remaining() < ETC1_DECODED_BLOCK_SIZE) { 835 doThrowIAE(env, "in's remaining data < DECODED_BLOCK_SIZE"); 836 } else if (outB.remaining() < ETC1_ENCODED_BLOCK_SIZE) { 837 doThrowIAE(env, "out's remaining data < ENCODED_BLOCK_SIZE"); 838 } else { 839 etc1_encode_block((etc1_byte*) inB.getData(), validPixelMask, 840 (etc1_byte*) outB.getData()); 841 } 842 } 843 } 844 845 /** 846 * Decode a block of pixels. 847 * 848 * @param in an ETC1 compressed version of the data. 849 * 850 * @param out a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a 851 * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R 852 * value of pixel (x, y). 853 */ 854 static void etc1_decodeBlock(JNIEnv *env, jclass clazz, 855 jobject in, jobject out){ 856 BufferHelper inB(env, in); 857 BufferHelper outB(env, out); 858 if (inB.checkPointer("in") && outB.checkPointer("out")) { 859 if (inB.remaining() < ETC1_ENCODED_BLOCK_SIZE) { 860 doThrowIAE(env, "in's remaining data < ENCODED_BLOCK_SIZE"); 861 } else if (outB.remaining() < ETC1_DECODED_BLOCK_SIZE) { 862 doThrowIAE(env, "out's remaining data < DECODED_BLOCK_SIZE"); 863 } else { 864 etc1_decode_block((etc1_byte*) inB.getData(), 865 (etc1_byte*) outB.getData()); 866 } 867 } 868 } 869 870 /** 871 * Return the size of the encoded image data (does not include size of PKM header). 872 */ 873 static jint etc1_getEncodedDataSize(JNIEnv *env, jclass clazz, 874 jint width, jint height) { 875 return etc1_get_encoded_data_size(width, height); 876 } 877 878 /** 879 * Encode an entire image. 880 * @param in pointer to the image data. Formatted such that 881 * pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset; 882 * @param out pointer to encoded data. Must be large enough to store entire encoded image. 883 */ 884 static void etc1_encodeImage(JNIEnv *env, jclass clazz, 885 jobject in, jint width, jint height, 886 jint pixelSize, jint stride, jobject out) { 887 if (pixelSize < 2 || pixelSize > 3) { 888 doThrowIAE(env, "pixelSize must be 2 or 3"); 889 return; 890 } 891 BufferHelper inB(env, in); 892 BufferHelper outB(env, out); 893 if (inB.checkPointer("in") && outB.checkPointer("out")) { 894 jint imageSize = stride * height; 895 jint encodedImageSize = etc1_get_encoded_data_size(width, height); 896 if (inB.remaining() < imageSize) { 897 doThrowIAE(env, "in's remaining data < image size"); 898 } else if (outB.remaining() < encodedImageSize) { 899 doThrowIAE(env, "out's remaining data < encoded image size"); 900 } else { 901 int result = etc1_encode_image((etc1_byte*) inB.getData(), 902 width, height, pixelSize, 903 stride, 904 (etc1_byte*) outB.getData()); 905 } 906 } 907 } 908 909 /** 910 * Decode an entire image. 911 * @param in the encoded data. 912 * @param out pointer to the image data. Will be written such that 913 * pixel (x,y) is at pIn + pixelSize * x + stride * y. Must be 914 * large enough to store entire image. 915 */ 916 static void etc1_decodeImage(JNIEnv *env, jclass clazz, 917 jobject in, jobject out, 918 jint width, jint height, 919 jint pixelSize, jint stride) { 920 if (pixelSize < 2 || pixelSize > 3) { 921 doThrowIAE(env, "pixelSize must be 2 or 3"); 922 return; 923 } 924 BufferHelper inB(env, in); 925 BufferHelper outB(env, out); 926 if (inB.checkPointer("in") && outB.checkPointer("out")) { 927 jint imageSize = stride * height; 928 jint encodedImageSize = etc1_get_encoded_data_size(width, height); 929 if (inB.remaining() < encodedImageSize) { 930 doThrowIAE(env, "in's remaining data < encoded image size"); 931 } else if (outB.remaining() < imageSize) { 932 doThrowIAE(env, "out's remaining data < image size"); 933 } else { 934 int result = etc1_decode_image((etc1_byte*) inB.getData(), 935 (etc1_byte*) outB.getData(), 936 width, height, pixelSize, 937 stride); 938 } 939 } 940 } 941 942 /** 943 * Format a PKM header 944 */ 945 static void etc1_formatHeader(JNIEnv *env, jclass clazz, 946 jobject header, jint width, jint height) { 947 BufferHelper headerB(env, header); 948 if (headerB.checkPointer("header") ){ 949 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) { 950 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE"); 951 } else { 952 etc1_pkm_format_header((etc1_byte*) headerB.getData(), width, height); 953 } 954 } 955 } 956 957 /** 958 * Check if a PKM header is correctly formatted. 959 */ 960 static jboolean etc1_isValid(JNIEnv *env, jclass clazz, 961 jobject header) { 962 jboolean result = false; 963 BufferHelper headerB(env, header); 964 if (headerB.checkPointer("header") ){ 965 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) { 966 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE"); 967 } else { 968 result = etc1_pkm_is_valid((etc1_byte*) headerB.getData()); 969 } 970 } 971 return result; 972 } 973 974 /** 975 * Read the image width from a PKM header 976 */ 977 static jint etc1_getWidth(JNIEnv *env, jclass clazz, 978 jobject header) { 979 jint result = 0; 980 BufferHelper headerB(env, header); 981 if (headerB.checkPointer("header") ){ 982 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) { 983 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE"); 984 } else { 985 result = etc1_pkm_get_width((etc1_byte*) headerB.getData()); 986 } 987 } 988 return result; 989 } 990 991 /** 992 * Read the image height from a PKM header 993 */ 994 static int etc1_getHeight(JNIEnv *env, jclass clazz, 995 jobject header) { 996 jint result = 0; 997 BufferHelper headerB(env, header); 998 if (headerB.checkPointer("header") ){ 999 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) { 1000 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE"); 1001 } else { 1002 result = etc1_pkm_get_height((etc1_byte*) headerB.getData()); 1003 } 1004 } 1005 return result; 1006 } 1007 1008 /* 1009 * JNI registration 1010 */ 1011 1012 static JNINativeMethod gMatrixMethods[] = { 1013 { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM }, 1014 { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV }, 1015 }; 1016 1017 static JNINativeMethod gVisibilityMethods[] = { 1018 { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere }, 1019 { "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres }, 1020 { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest }, 1021 }; 1022 1023 static JNINativeMethod gUtilsMethods[] = { 1024 {"nativeClassInit", "()V", (void*)nativeUtilsClassInit }, 1025 { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat }, 1026 { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType }, 1027 { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D }, 1028 { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D }, 1029 }; 1030 1031 static JNINativeMethod gEtc1Methods[] = { 1032 { "encodeBlock", "(Ljava/nio/Buffer;ILjava/nio/Buffer;)V", (void*) etc1_encodeBlock }, 1033 { "decodeBlock", "(Ljava/nio/Buffer;Ljava/nio/Buffer;)V", (void*) etc1_decodeBlock }, 1034 { "getEncodedDataSize", "(II)I", (void*) etc1_getEncodedDataSize }, 1035 { "encodeImage", "(Ljava/nio/Buffer;IIIILjava/nio/Buffer;)V", (void*) etc1_encodeImage }, 1036 { "decodeImage", "(Ljava/nio/Buffer;Ljava/nio/Buffer;IIII)V", (void*) etc1_decodeImage }, 1037 { "formatHeader", "(Ljava/nio/Buffer;II)V", (void*) etc1_formatHeader }, 1038 { "isValid", "(Ljava/nio/Buffer;)Z", (void*) etc1_isValid }, 1039 { "getWidth", "(Ljava/nio/Buffer;)I", (void*) etc1_getWidth }, 1040 { "getHeight", "(Ljava/nio/Buffer;)I", (void*) etc1_getHeight }, 1041 }; 1042 1043 typedef struct _ClassRegistrationInfo { 1044 const char* classPath; 1045 JNINativeMethod* methods; 1046 size_t methodCount; 1047 } ClassRegistrationInfo; 1048 1049 static ClassRegistrationInfo gClasses[] = { 1050 {"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)}, 1051 {"android/opengl/Visibility", gVisibilityMethods, NELEM(gVisibilityMethods)}, 1052 {"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)}, 1053 {"android/opengl/ETC1", gEtc1Methods, NELEM(gEtc1Methods)}, 1054 }; 1055 1056 int register_android_opengl_classes(JNIEnv* env) 1057 { 1058 nativeClassInitBuffer(env); 1059 int result = 0; 1060 for (int i = 0; i < NELEM(gClasses); i++) { 1061 ClassRegistrationInfo* cri = &gClasses[i]; 1062 result = AndroidRuntime::registerNativeMethods(env, 1063 cri->classPath, cri->methods, cri->methodCount); 1064 if (result < 0) { 1065 LOGE("Failed to register %s: %d", cri->classPath, result); 1066 break; 1067 } 1068 } 1069 return result; 1070 } 1071 1072 } // namespace android 1073