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 ALOGI("%s: %d verts", label, pPoly->n); 77 for(int i = 0; i < pPoly->n; i++) { 78 Poly_vert* pV = & pPoly->vert[i]; 79 ALOGI("[%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 extern void setGLDebugLevel(int level); 560 void setTracingLevel(JNIEnv *env, jclass clazz, jint level) 561 { 562 setGLDebugLevel(level); 563 } 564 565 static 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 593 static 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 611 static 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 629 static jint util_getInternalFormat(JNIEnv *env, jclass clazz, 630 jobject jbitmap) 631 { 632 SkBitmap const * nativeBitmap = 633 (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID); 634 const SkBitmap& bitmap(*nativeBitmap); 635 SkBitmap::Config config = bitmap.config(); 636 return getInternalFormat(config); 637 } 638 639 static jint util_getType(JNIEnv *env, jclass clazz, 640 jobject jbitmap) 641 { 642 SkBitmap const * nativeBitmap = 643 (SkBitmap const *)env->GetIntField(jbitmap, nativeBitmapID); 644 const SkBitmap& bitmap(*nativeBitmap); 645 SkBitmap::Config config = bitmap.config(); 646 return getType(config); 647 } 648 649 static 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->GetIntField(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 } 693 error: 694 bitmap.unlockPixels(); 695 return err; 696 } 697 698 static 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->GetIntField(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 727 static jclass nioAccessClass; 728 static jclass bufferClass; 729 static jmethodID getBasePointerID; 730 static jmethodID getBaseArrayID; 731 static jmethodID getBaseArrayOffsetID; 732 static jfieldID positionID; 733 static jfieldID limitID; 734 static jfieldID elementSizeShiftID; 735 736 /* Cache method IDs each time the class is loaded. */ 737 738 static void 739 nativeClassInitBuffer(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 759 static void * 760 getPointer(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 (void *) (jint) pointer; 777 } 778 return NULL; 779 } 780 781 class BufferHelper { 782 public: 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 811 private: 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 */ 831 static 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 */ 860 static 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 */ 879 static 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 */ 890 static 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 */ 922 static 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 */ 951 static 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 */ 966 static 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; 978 } 979 980 /** 981 * Read the image width from a PKM header 982 */ 983 static 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 */ 1000 static int 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 1018 static JNINativeMethod gMatrixMethods[] = { 1019 { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM }, 1020 { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV }, 1021 }; 1022 1023 static 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 1029 static 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 1038 static 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 1050 typedef struct _ClassRegistrationInfo { 1051 const char* classPath; 1052 JNINativeMethod* methods; 1053 size_t methodCount; 1054 } ClassRegistrationInfo; 1055 1056 static 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 1063 int 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