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