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 <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 jint 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 jint 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", "J"); 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(SkColorType colorType, int format, int type) 566 { 567 switch(colorType) { 568 case kIndex_8_SkColorType: 569 if (format == GL_PALETTE8_RGBA8_OES) 570 return 0; 571 case kN32_SkColorType: 572 case kAlpha_8_SkColorType: 573 if (type == GL_UNSIGNED_BYTE) 574 return 0; 575 case kARGB_4444_SkColorType: 576 case kRGB_565_SkColorType: 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(SkColorType colorType) 594 { 595 switch(colorType) { 596 case kAlpha_8_SkColorType: 597 return GL_ALPHA; 598 case kARGB_4444_SkColorType: 599 return GL_RGBA; 600 case kN32_SkColorType: 601 return GL_RGBA; 602 case kIndex_8_SkColorType: 603 return GL_PALETTE8_RGBA8_OES; 604 case kRGB_565_SkColorType: 605 return GL_RGB; 606 default: 607 return -1; 608 } 609 } 610 611 static int getType(SkColorType colorType) 612 { 613 switch(colorType) { 614 case kAlpha_8_SkColorType: 615 return GL_UNSIGNED_BYTE; 616 case kARGB_4444_SkColorType: 617 return GL_UNSIGNED_SHORT_4_4_4_4; 618 case kN32_SkColorType: 619 return GL_UNSIGNED_BYTE; 620 case kIndex_8_SkColorType: 621 return -1; // No type for compressed data. 622 case kRGB_565_SkColorType: 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->GetLongField(jbitmap, nativeBitmapID); 634 return getInternalFormat(nativeBitmap->colorType()); 635 } 636 637 static jint util_getType(JNIEnv *env, jclass clazz, 638 jobject jbitmap) 639 { 640 SkBitmap const * nativeBitmap = 641 (SkBitmap const *)env->GetLongField(jbitmap, nativeBitmapID); 642 return getType(nativeBitmap->colorType()); 643 } 644 645 static jint util_texImage2D(JNIEnv *env, jclass clazz, 646 jint target, jint level, jint internalformat, 647 jobject jbitmap, jint type, jint border) 648 { 649 SkBitmap const * nativeBitmap = 650 (SkBitmap const *)env->GetLongField(jbitmap, nativeBitmapID); 651 const SkBitmap& bitmap(*nativeBitmap); 652 SkColorType colorType = bitmap.colorType(); 653 if (internalformat < 0) { 654 internalformat = getInternalFormat(colorType); 655 } 656 if (type < 0) { 657 type = getType(colorType); 658 } 659 int err = checkFormat(colorType, internalformat, type); 660 if (err) 661 return err; 662 bitmap.lockPixels(); 663 const int w = bitmap.width(); 664 const int h = bitmap.height(); 665 const void* p = bitmap.getPixels(); 666 if (internalformat == GL_PALETTE8_RGBA8_OES) { 667 if (sizeof(SkPMColor) != sizeof(uint32_t)) { 668 err = -1; 669 goto error; 670 } 671 const size_t size = bitmap.getSize(); 672 const size_t palette_size = 256*sizeof(SkPMColor); 673 const size_t imageSize = size + palette_size; 674 void* const data = malloc(imageSize); 675 if (data) { 676 void* const pixels = (char*)data + palette_size; 677 SkColorTable* ctable = bitmap.getColorTable(); 678 memcpy(data, ctable->lockColors(), ctable->count() * sizeof(SkPMColor)); 679 memcpy(pixels, p, size); 680 ctable->unlockColors(); 681 glCompressedTexImage2D(target, level, internalformat, w, h, border, imageSize, data); 682 free(data); 683 } else { 684 err = -1; 685 } 686 } else { 687 glTexImage2D(target, level, internalformat, w, h, border, internalformat, type, p); 688 } 689 error: 690 bitmap.unlockPixels(); 691 return err; 692 } 693 694 static jint util_texSubImage2D(JNIEnv *env, jclass clazz, 695 jint target, jint level, jint xoffset, jint yoffset, 696 jobject jbitmap, jint format, jint type) 697 { 698 SkBitmap const * nativeBitmap = 699 (SkBitmap const *)env->GetLongField(jbitmap, nativeBitmapID); 700 const SkBitmap& bitmap(*nativeBitmap); 701 SkColorType colorType = bitmap.colorType(); 702 if (format < 0) { 703 format = getInternalFormat(colorType); 704 if (format == GL_PALETTE8_RGBA8_OES) 705 return -1; // glCompressedTexSubImage2D() not supported 706 } 707 int err = checkFormat(colorType, format, type); 708 if (err) 709 return err; 710 bitmap.lockPixels(); 711 const int w = bitmap.width(); 712 const int h = bitmap.height(); 713 const void* p = bitmap.getPixels(); 714 glTexSubImage2D(target, level, xoffset, yoffset, w, h, format, type, p); 715 bitmap.unlockPixels(); 716 return 0; 717 } 718 719 /* 720 * ETC1 methods. 721 */ 722 723 static jclass nioAccessClass; 724 static jclass bufferClass; 725 static jmethodID getBasePointerID; 726 static jmethodID getBaseArrayID; 727 static jmethodID getBaseArrayOffsetID; 728 static jfieldID positionID; 729 static jfieldID limitID; 730 static jfieldID elementSizeShiftID; 731 732 /* Cache method IDs each time the class is loaded. */ 733 734 static void 735 nativeClassInitBuffer(JNIEnv *_env) 736 { 737 jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess"); 738 nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal); 739 740 jclass bufferClassLocal = _env->FindClass("java/nio/Buffer"); 741 bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal); 742 743 getBasePointerID = _env->GetStaticMethodID(nioAccessClass, 744 "getBasePointer", "(Ljava/nio/Buffer;)J"); 745 getBaseArrayID = _env->GetStaticMethodID(nioAccessClass, 746 "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;"); 747 getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass, 748 "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); 749 positionID = _env->GetFieldID(bufferClass, "position", "I"); 750 limitID = _env->GetFieldID(bufferClass, "limit", "I"); 751 elementSizeShiftID = 752 _env->GetFieldID(bufferClass, "_elementSizeShift", "I"); 753 } 754 755 static void * 756 getPointer(JNIEnv *_env, jobject buffer, jint *remaining) 757 { 758 jint position; 759 jint limit; 760 jint elementSizeShift; 761 jlong pointer; 762 jint offset; 763 void *data; 764 765 position = _env->GetIntField(buffer, positionID); 766 limit = _env->GetIntField(buffer, limitID); 767 elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); 768 *remaining = (limit - position) << elementSizeShift; 769 pointer = _env->CallStaticLongMethod(nioAccessClass, 770 getBasePointerID, buffer); 771 if (pointer != 0L) { 772 return reinterpret_cast<void *>(pointer); 773 } 774 return NULL; 775 } 776 777 class BufferHelper { 778 public: 779 BufferHelper(JNIEnv *env, jobject buffer) { 780 mEnv = env; 781 mBuffer = buffer; 782 mData = NULL; 783 mRemaining = 0; 784 } 785 786 bool checkPointer(const char* errorMessage) { 787 if (mBuffer) { 788 mData = getPointer(mEnv, mBuffer, &mRemaining); 789 if (mData == NULL) { 790 doThrowIAE(mEnv, errorMessage); 791 } 792 return mData != NULL; 793 } else { 794 doThrowIAE(mEnv, errorMessage); 795 return false; 796 } 797 } 798 799 inline void* getData() { 800 return mData; 801 } 802 803 inline jint remaining() { 804 return mRemaining; 805 } 806 807 private: 808 JNIEnv* mEnv; 809 jobject mBuffer; 810 void* mData; 811 jint mRemaining; 812 }; 813 814 /** 815 * Encode a block of pixels. 816 * 817 * @param in a pointer to a ETC1_DECODED_BLOCK_SIZE array of bytes that represent a 818 * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R 819 * value of pixel (x, y). 820 * 821 * @param validPixelMask is a 16-bit mask where bit (1 << (x + y * 4)) indicates whether 822 * the corresponding (x,y) pixel is valid. Invalid pixel color values are ignored when compressing. 823 * 824 * @param out an ETC1 compressed version of the data. 825 * 826 */ 827 static void etc1_encodeBlock(JNIEnv *env, jclass clazz, 828 jobject in, jint validPixelMask, jobject out) { 829 if (validPixelMask < 0 || validPixelMask > 15) { 830 doThrowIAE(env, "validPixelMask"); 831 return; 832 } 833 BufferHelper inB(env, in); 834 BufferHelper outB(env, out); 835 if (inB.checkPointer("in") && outB.checkPointer("out")) { 836 if (inB.remaining() < ETC1_DECODED_BLOCK_SIZE) { 837 doThrowIAE(env, "in's remaining data < DECODED_BLOCK_SIZE"); 838 } else if (outB.remaining() < ETC1_ENCODED_BLOCK_SIZE) { 839 doThrowIAE(env, "out's remaining data < ENCODED_BLOCK_SIZE"); 840 } else { 841 etc1_encode_block((etc1_byte*) inB.getData(), validPixelMask, 842 (etc1_byte*) outB.getData()); 843 } 844 } 845 } 846 847 /** 848 * Decode a block of pixels. 849 * 850 * @param in an ETC1 compressed version of the data. 851 * 852 * @param out a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a 853 * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R 854 * value of pixel (x, y). 855 */ 856 static void etc1_decodeBlock(JNIEnv *env, jclass clazz, 857 jobject in, jobject out){ 858 BufferHelper inB(env, in); 859 BufferHelper outB(env, out); 860 if (inB.checkPointer("in") && outB.checkPointer("out")) { 861 if (inB.remaining() < ETC1_ENCODED_BLOCK_SIZE) { 862 doThrowIAE(env, "in's remaining data < ENCODED_BLOCK_SIZE"); 863 } else if (outB.remaining() < ETC1_DECODED_BLOCK_SIZE) { 864 doThrowIAE(env, "out's remaining data < DECODED_BLOCK_SIZE"); 865 } else { 866 etc1_decode_block((etc1_byte*) inB.getData(), 867 (etc1_byte*) outB.getData()); 868 } 869 } 870 } 871 872 /** 873 * Return the size of the encoded image data (does not include size of PKM header). 874 */ 875 static jint etc1_getEncodedDataSize(JNIEnv *env, jclass clazz, 876 jint width, jint height) { 877 return etc1_get_encoded_data_size(width, height); 878 } 879 880 /** 881 * Encode an entire image. 882 * @param in pointer to the image data. Formatted such that 883 * pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset; 884 * @param out pointer to encoded data. Must be large enough to store entire encoded image. 885 */ 886 static void etc1_encodeImage(JNIEnv *env, jclass clazz, 887 jobject in, jint width, jint height, 888 jint pixelSize, jint stride, jobject out) { 889 if (pixelSize < 2 || pixelSize > 3) { 890 doThrowIAE(env, "pixelSize must be 2 or 3"); 891 return; 892 } 893 BufferHelper inB(env, in); 894 BufferHelper outB(env, out); 895 if (inB.checkPointer("in") && outB.checkPointer("out")) { 896 jint imageSize = stride * height; 897 jint encodedImageSize = etc1_get_encoded_data_size(width, height); 898 if (inB.remaining() < imageSize) { 899 doThrowIAE(env, "in's remaining data < image size"); 900 } else if (outB.remaining() < encodedImageSize) { 901 doThrowIAE(env, "out's remaining data < encoded image size"); 902 } else { 903 int result = etc1_encode_image((etc1_byte*) inB.getData(), 904 width, height, pixelSize, 905 stride, 906 (etc1_byte*) outB.getData()); 907 } 908 } 909 } 910 911 /** 912 * Decode an entire image. 913 * @param in the encoded data. 914 * @param out pointer to the image data. Will be written such that 915 * pixel (x,y) is at pIn + pixelSize * x + stride * y. Must be 916 * large enough to store entire image. 917 */ 918 static void etc1_decodeImage(JNIEnv *env, jclass clazz, 919 jobject in, jobject out, 920 jint width, jint height, 921 jint pixelSize, jint stride) { 922 if (pixelSize < 2 || pixelSize > 3) { 923 doThrowIAE(env, "pixelSize must be 2 or 3"); 924 return; 925 } 926 BufferHelper inB(env, in); 927 BufferHelper outB(env, out); 928 if (inB.checkPointer("in") && outB.checkPointer("out")) { 929 jint imageSize = stride * height; 930 jint encodedImageSize = etc1_get_encoded_data_size(width, height); 931 if (inB.remaining() < encodedImageSize) { 932 doThrowIAE(env, "in's remaining data < encoded image size"); 933 } else if (outB.remaining() < imageSize) { 934 doThrowIAE(env, "out's remaining data < image size"); 935 } else { 936 int result = etc1_decode_image((etc1_byte*) inB.getData(), 937 (etc1_byte*) outB.getData(), 938 width, height, pixelSize, 939 stride); 940 } 941 } 942 } 943 944 /** 945 * Format a PKM header 946 */ 947 static void etc1_formatHeader(JNIEnv *env, jclass clazz, 948 jobject header, jint width, jint height) { 949 BufferHelper headerB(env, header); 950 if (headerB.checkPointer("header") ){ 951 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) { 952 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE"); 953 } else { 954 etc1_pkm_format_header((etc1_byte*) headerB.getData(), width, height); 955 } 956 } 957 } 958 959 /** 960 * Check if a PKM header is correctly formatted. 961 */ 962 static jboolean etc1_isValid(JNIEnv *env, jclass clazz, 963 jobject header) { 964 jboolean result = false; 965 BufferHelper headerB(env, header); 966 if (headerB.checkPointer("header") ){ 967 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) { 968 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE"); 969 } else { 970 result = etc1_pkm_is_valid((etc1_byte*) headerB.getData()); 971 } 972 } 973 return result ? JNI_TRUE : JNI_FALSE; 974 } 975 976 /** 977 * Read the image width from a PKM header 978 */ 979 static jint etc1_getWidth(JNIEnv *env, jclass clazz, 980 jobject header) { 981 jint result = 0; 982 BufferHelper headerB(env, header); 983 if (headerB.checkPointer("header") ){ 984 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) { 985 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE"); 986 } else { 987 result = etc1_pkm_get_width((etc1_byte*) headerB.getData()); 988 } 989 } 990 return result; 991 } 992 993 /** 994 * Read the image height from a PKM header 995 */ 996 static jint etc1_getHeight(JNIEnv *env, jclass clazz, 997 jobject header) { 998 jint result = 0; 999 BufferHelper headerB(env, header); 1000 if (headerB.checkPointer("header") ){ 1001 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) { 1002 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE"); 1003 } else { 1004 result = etc1_pkm_get_height((etc1_byte*) headerB.getData()); 1005 } 1006 } 1007 return result; 1008 } 1009 1010 /* 1011 * JNI registration 1012 */ 1013 1014 static JNINativeMethod gMatrixMethods[] = { 1015 { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM }, 1016 { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV }, 1017 }; 1018 1019 static JNINativeMethod gVisibilityMethods[] = { 1020 { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere }, 1021 { "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres }, 1022 { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest }, 1023 }; 1024 1025 static JNINativeMethod gUtilsMethods[] = { 1026 {"nativeClassInit", "()V", (void*)nativeUtilsClassInit }, 1027 { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat }, 1028 { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType }, 1029 { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D }, 1030 { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D }, 1031 { "setTracingLevel", "(I)V", (void*)setTracingLevel }, 1032 }; 1033 1034 static JNINativeMethod gEtc1Methods[] = { 1035 { "encodeBlock", "(Ljava/nio/Buffer;ILjava/nio/Buffer;)V", (void*) etc1_encodeBlock }, 1036 { "decodeBlock", "(Ljava/nio/Buffer;Ljava/nio/Buffer;)V", (void*) etc1_decodeBlock }, 1037 { "getEncodedDataSize", "(II)I", (void*) etc1_getEncodedDataSize }, 1038 { "encodeImage", "(Ljava/nio/Buffer;IIIILjava/nio/Buffer;)V", (void*) etc1_encodeImage }, 1039 { "decodeImage", "(Ljava/nio/Buffer;Ljava/nio/Buffer;IIII)V", (void*) etc1_decodeImage }, 1040 { "formatHeader", "(Ljava/nio/Buffer;II)V", (void*) etc1_formatHeader }, 1041 { "isValid", "(Ljava/nio/Buffer;)Z", (void*) etc1_isValid }, 1042 { "getWidth", "(Ljava/nio/Buffer;)I", (void*) etc1_getWidth }, 1043 { "getHeight", "(Ljava/nio/Buffer;)I", (void*) etc1_getHeight }, 1044 }; 1045 1046 typedef struct _ClassRegistrationInfo { 1047 const char* classPath; 1048 JNINativeMethod* methods; 1049 size_t methodCount; 1050 } ClassRegistrationInfo; 1051 1052 static ClassRegistrationInfo gClasses[] = { 1053 {"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)}, 1054 {"android/opengl/Visibility", gVisibilityMethods, NELEM(gVisibilityMethods)}, 1055 {"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)}, 1056 {"android/opengl/ETC1", gEtc1Methods, NELEM(gEtc1Methods)}, 1057 }; 1058 1059 int register_android_opengl_classes(JNIEnv* env) 1060 { 1061 nativeClassInitBuffer(env); 1062 int result = 0; 1063 for (int i = 0; i < NELEM(gClasses); i++) { 1064 ClassRegistrationInfo* cri = &gClasses[i]; 1065 result = AndroidRuntime::registerNativeMethods(env, 1066 cri->classPath, cri->methods, cri->methodCount); 1067 if (result < 0) { 1068 ALOGE("Failed to register %s: %d", cri->classPath, result); 1069 break; 1070 } 1071 } 1072 return result; 1073 } 1074 1075 } // namespace android 1076