util.cpp revision ed6b9dff563c5e22f040ff37e12c0d771e0478ae
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 "core_jni_helpers.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 41namespace android { 42 43static inline 44void mx4transform(float x, float y, float z, float w, const float* pM, float* pDest) { 45 pDest[0] = pM[0 + 4 * 0] * x + pM[0 + 4 * 1] * y + pM[0 + 4 * 2] * z + pM[0 + 4 * 3] * w; 46 pDest[1] = pM[1 + 4 * 0] * x + pM[1 + 4 * 1] * y + pM[1 + 4 * 2] * z + pM[1 + 4 * 3] * w; 47 pDest[2] = pM[2 + 4 * 0] * x + pM[2 + 4 * 1] * y + pM[2 + 4 * 2] * z + pM[2 + 4 * 3] * w; 48 pDest[3] = pM[3 + 4 * 0] * x + pM[3 + 4 * 1] * y + pM[3 + 4 * 2] * z + pM[3 + 4 * 3] * w; 49} 50 51class MallocHelper { 52public: 53 MallocHelper() { 54 mData = 0; 55 } 56 57 ~MallocHelper() { 58 if (mData != 0) { 59 free(mData); 60 } 61 } 62 63 void* alloc(size_t size) { 64 mData = malloc(size); 65 return mData; 66 } 67 68private: 69 void* mData; 70}; 71 72#if 0 73static 74void 75print_poly(const char* label, Poly* pPoly) { 76 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 84static 85int visibilityTest(float* pWS, float* pPositions, int positionsLength, 86 unsigned short* pIndices, int indexCount) { 87 MallocHelper mallocHelper; 88 int result = POLY_CLIP_OUT; 89 float* pTransformed = 0; 90 int transformedIndexCount = 0; 91 92 if ( indexCount < 3 ) { 93 return POLY_CLIP_OUT; 94 } 95 96 // Find out how many vertices we need to transform 97 // We transform every vertex between the min and max indices, inclusive. 98 // This is OK for the data sets we expect to use with this function, but 99 // for other loads it might be better to use a more sophisticated vertex 100 // cache of some sort. 101 102 int minIndex = 65536; 103 int maxIndex = -1; 104 for(int i = 0; i < indexCount; i++) { 105 int index = pIndices[i]; 106 if ( index < minIndex ) { 107 minIndex = index; 108 } 109 if ( index > maxIndex ) { 110 maxIndex = index; 111 } 112 } 113 114 if ( maxIndex * 3 > positionsLength) { 115 return -1; 116 } 117 118 transformedIndexCount = maxIndex - minIndex + 1; 119 pTransformed = (float*) mallocHelper.alloc(transformedIndexCount * 4 * sizeof(float)); 120 121 if (pTransformed == 0 ) { 122 return -2; 123 } 124 125 // Transform the vertices 126 { 127 const float* pSrc = pPositions + 3 * minIndex; 128 float* pDst = pTransformed; 129 for (int i = 0; i < transformedIndexCount; i++, pSrc += 3, pDst += 4) { 130 mx4transform(pSrc[0], pSrc[1], pSrc[2], 1.0f, pWS, pDst); 131 } 132 } 133 134 // Clip the triangles 135 136 Poly poly; 137 float* pDest = & poly.vert[0].sx; 138 for (int i = 0; i < indexCount; i += 3) { 139 poly.n = 3; 140 memcpy(pDest , pTransformed + 4 * (pIndices[i ] - minIndex), 4 * sizeof(float)); 141 memcpy(pDest + 4, pTransformed + 4 * (pIndices[i + 1] - minIndex), 4 * sizeof(float)); 142 memcpy(pDest + 8, pTransformed + 4 * (pIndices[i + 2] - minIndex), 4 * sizeof(float)); 143 result = poly_clip_to_frustum(&poly); 144 if ( result != POLY_CLIP_OUT) { 145 return result; 146 } 147 } 148 149 return result; 150} 151 152static void doThrowIAE(JNIEnv* env, const char* msg) { 153 jniThrowException(env, "java/lang/IllegalArgumentException", msg); 154} 155 156template<class JArray, class T> 157class ArrayHelper { 158public: 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 212private: 213 T* mBase; 214 JNIEnv* mEnv; 215 JArray mRef; 216 jint mOffset; 217 jint mMinSize; 218 int mReleaseParam; 219}; 220 221typedef ArrayHelper<jfloatArray, float> FloatArrayHelper; 222typedef ArrayHelper<jcharArray, unsigned short> UnsignedShortArrayHelper; 223typedef ArrayHelper<jintArray, int> IntArrayHelper; 224typedef ArrayHelper<jbyteArray, unsigned char> ByteArrayHelper; 225 226inline float distance2(float x, float y, float z) { 227 return x * x + y * y + z * z; 228} 229 230inline float distance(float x, float y, float z) { 231 return sqrtf(distance2(x, y, z)); 232} 233 234static 235void 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 308static 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 315static 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 319static 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 325static 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 338static 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 391static 392jint 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 438static 439jint 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 470static 471void multiplyMM(float* r, const float* lhs, const float* rhs) 472{ 473 for (int i=0 ; i<4 ; i++) { 474 const float rhs_i0 = rhs[ I(i,0) ]; 475 float ri0 = lhs[ I(0,0) ] * rhs_i0; 476 float ri1 = lhs[ I(0,1) ] * rhs_i0; 477 float ri2 = lhs[ I(0,2) ] * rhs_i0; 478 float ri3 = lhs[ I(0,3) ] * rhs_i0; 479 for (int j=1 ; j<4 ; j++) { 480 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 493static 494void 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 518static 519void multiplyMV(float* r, const float* lhs, const float* rhs) 520{ 521 mx4transform(rhs[0], rhs[1], rhs[2], rhs[3], lhs, r); 522} 523 524static 525void 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 551static jfieldID nativeBitmapID = 0; 552 553void nativeUtilsClassInit(JNIEnv *env, jclass clazz) 554{ 555 jclass bitmapClass = env->FindClass("android/graphics/Bitmap"); 556 nativeBitmapID = env->GetFieldID(bitmapClass, "mNativeBitmap", "J"); 557} 558 559extern void setGLDebugLevel(int level); 560void setTracingLevel(JNIEnv *env, jclass clazz, jint level) 561{ 562 setGLDebugLevel(level); 563} 564 565static 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 593static 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 611static 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 629static 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 637static 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 645static 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 } 689error: 690 bitmap.unlockPixels(); 691 return err; 692} 693 694static 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 723static jclass nioAccessClass; 724static jclass bufferClass; 725static jmethodID getBasePointerID; 726static jmethodID getBaseArrayID; 727static jmethodID getBaseArrayOffsetID; 728static jfieldID positionID; 729static jfieldID limitID; 730static jfieldID elementSizeShiftID; 731 732/* Cache method IDs each time the class is loaded. */ 733 734static void 735nativeClassInitBuffer(JNIEnv *env) 736{ 737 jclass nioAccessClassLocal = FindClassOrDie(env, "java/nio/NIOAccess"); 738 nioAccessClass = MakeGlobalRefOrDie(env, nioAccessClassLocal); 739 getBasePointerID = GetStaticMethodIDOrDie(env, nioAccessClass, 740 "getBasePointer", "(Ljava/nio/Buffer;)J"); 741 getBaseArrayID = GetStaticMethodIDOrDie(env, nioAccessClass, 742 "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;"); 743 getBaseArrayOffsetID = GetStaticMethodIDOrDie(env, nioAccessClass, 744 "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); 745 746 jclass bufferClassLocal = FindClassOrDie(env, "java/nio/Buffer"); 747 bufferClass = MakeGlobalRefOrDie(env, bufferClassLocal); 748 positionID = GetFieldIDOrDie(env, bufferClass, "position", "I"); 749 limitID = GetFieldIDOrDie(env, bufferClass, "limit", "I"); 750 elementSizeShiftID = GetFieldIDOrDie(env, bufferClass, "_elementSizeShift", "I"); 751} 752 753static void * 754getPointer(JNIEnv *_env, jobject buffer, jint *remaining) 755{ 756 jint position; 757 jint limit; 758 jint elementSizeShift; 759 jlong pointer; 760 761 position = _env->GetIntField(buffer, positionID); 762 limit = _env->GetIntField(buffer, limitID); 763 elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); 764 *remaining = (limit - position) << elementSizeShift; 765 pointer = _env->CallStaticLongMethod(nioAccessClass, 766 getBasePointerID, buffer); 767 if (pointer != 0L) { 768 return reinterpret_cast<void *>(pointer); 769 } 770 return NULL; 771} 772 773class BufferHelper { 774public: 775 BufferHelper(JNIEnv *env, jobject buffer) { 776 mEnv = env; 777 mBuffer = buffer; 778 mData = NULL; 779 mRemaining = 0; 780 } 781 782 bool checkPointer(const char* errorMessage) { 783 if (mBuffer) { 784 mData = getPointer(mEnv, mBuffer, &mRemaining); 785 if (mData == NULL) { 786 doThrowIAE(mEnv, errorMessage); 787 } 788 return mData != NULL; 789 } else { 790 doThrowIAE(mEnv, errorMessage); 791 return false; 792 } 793 } 794 795 inline void* getData() { 796 return mData; 797 } 798 799 inline jint remaining() { 800 return mRemaining; 801 } 802 803private: 804 JNIEnv* mEnv; 805 jobject mBuffer; 806 void* mData; 807 jint mRemaining; 808}; 809 810/** 811 * Encode a block of pixels. 812 * 813 * @param in a pointer to a ETC1_DECODED_BLOCK_SIZE array of bytes that represent a 814 * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R 815 * value of pixel (x, y). 816 * 817 * @param validPixelMask is a 16-bit mask where bit (1 << (x + y * 4)) indicates whether 818 * the corresponding (x,y) pixel is valid. Invalid pixel color values are ignored when compressing. 819 * 820 * @param out an ETC1 compressed version of the data. 821 * 822 */ 823static void etc1_encodeBlock(JNIEnv *env, jclass clazz, 824 jobject in, jint validPixelMask, jobject out) { 825 if (validPixelMask < 0 || validPixelMask > 15) { 826 doThrowIAE(env, "validPixelMask"); 827 return; 828 } 829 BufferHelper inB(env, in); 830 BufferHelper outB(env, out); 831 if (inB.checkPointer("in") && outB.checkPointer("out")) { 832 if (inB.remaining() < ETC1_DECODED_BLOCK_SIZE) { 833 doThrowIAE(env, "in's remaining data < DECODED_BLOCK_SIZE"); 834 } else if (outB.remaining() < ETC1_ENCODED_BLOCK_SIZE) { 835 doThrowIAE(env, "out's remaining data < ENCODED_BLOCK_SIZE"); 836 } else { 837 etc1_encode_block((etc1_byte*) inB.getData(), validPixelMask, 838 (etc1_byte*) outB.getData()); 839 } 840 } 841} 842 843/** 844 * Decode a block of pixels. 845 * 846 * @param in an ETC1 compressed version of the data. 847 * 848 * @param out a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a 849 * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R 850 * value of pixel (x, y). 851 */ 852static void etc1_decodeBlock(JNIEnv *env, jclass clazz, 853 jobject in, jobject out){ 854 BufferHelper inB(env, in); 855 BufferHelper outB(env, out); 856 if (inB.checkPointer("in") && outB.checkPointer("out")) { 857 if (inB.remaining() < ETC1_ENCODED_BLOCK_SIZE) { 858 doThrowIAE(env, "in's remaining data < ENCODED_BLOCK_SIZE"); 859 } else if (outB.remaining() < ETC1_DECODED_BLOCK_SIZE) { 860 doThrowIAE(env, "out's remaining data < DECODED_BLOCK_SIZE"); 861 } else { 862 etc1_decode_block((etc1_byte*) inB.getData(), 863 (etc1_byte*) outB.getData()); 864 } 865 } 866} 867 868/** 869 * Return the size of the encoded image data (does not include size of PKM header). 870 */ 871static jint etc1_getEncodedDataSize(JNIEnv *env, jclass clazz, 872 jint width, jint height) { 873 return etc1_get_encoded_data_size(width, height); 874} 875 876/** 877 * Encode an entire image. 878 * @param in pointer to the image data. Formatted such that 879 * pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset; 880 * @param out pointer to encoded data. Must be large enough to store entire encoded image. 881 */ 882static void etc1_encodeImage(JNIEnv *env, jclass clazz, 883 jobject in, jint width, jint height, 884 jint pixelSize, jint stride, jobject out) { 885 if (pixelSize < 2 || pixelSize > 3) { 886 doThrowIAE(env, "pixelSize must be 2 or 3"); 887 return; 888 } 889 BufferHelper inB(env, in); 890 BufferHelper outB(env, out); 891 if (inB.checkPointer("in") && outB.checkPointer("out")) { 892 jint imageSize = stride * height; 893 jint encodedImageSize = etc1_get_encoded_data_size(width, height); 894 if (inB.remaining() < imageSize) { 895 doThrowIAE(env, "in's remaining data < image size"); 896 } else if (outB.remaining() < encodedImageSize) { 897 doThrowIAE(env, "out's remaining data < encoded image size"); 898 } else { 899 etc1_encode_image((etc1_byte*) inB.getData(), width, height, pixelSize, stride, 900 (etc1_byte*) outB.getData()); 901 } 902 } 903} 904 905/** 906 * Decode an entire image. 907 * @param in the encoded data. 908 * @param out pointer to the image data. Will be written such that 909 * pixel (x,y) is at pIn + pixelSize * x + stride * y. Must be 910 * large enough to store entire image. 911 */ 912static void etc1_decodeImage(JNIEnv *env, jclass clazz, 913 jobject in, jobject out, 914 jint width, jint height, 915 jint pixelSize, jint stride) { 916 if (pixelSize < 2 || pixelSize > 3) { 917 doThrowIAE(env, "pixelSize must be 2 or 3"); 918 return; 919 } 920 BufferHelper inB(env, in); 921 BufferHelper outB(env, out); 922 if (inB.checkPointer("in") && outB.checkPointer("out")) { 923 jint imageSize = stride * height; 924 jint encodedImageSize = etc1_get_encoded_data_size(width, height); 925 if (inB.remaining() < encodedImageSize) { 926 doThrowIAE(env, "in's remaining data < encoded image size"); 927 } else if (outB.remaining() < imageSize) { 928 doThrowIAE(env, "out's remaining data < image size"); 929 } else { 930 etc1_decode_image((etc1_byte*) inB.getData(), (etc1_byte*) outB.getData(), 931 width, height, pixelSize, stride); 932 } 933 } 934} 935 936/** 937 * Format a PKM header 938 */ 939static void etc1_formatHeader(JNIEnv *env, jclass clazz, 940 jobject header, jint width, jint height) { 941 BufferHelper headerB(env, header); 942 if (headerB.checkPointer("header") ){ 943 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) { 944 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE"); 945 } else { 946 etc1_pkm_format_header((etc1_byte*) headerB.getData(), width, height); 947 } 948 } 949} 950 951/** 952 * Check if a PKM header is correctly formatted. 953 */ 954static jboolean etc1_isValid(JNIEnv *env, jclass clazz, 955 jobject header) { 956 jboolean result = false; 957 BufferHelper headerB(env, header); 958 if (headerB.checkPointer("header") ){ 959 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) { 960 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE"); 961 } else { 962 result = etc1_pkm_is_valid((etc1_byte*) headerB.getData()); 963 } 964 } 965 return result ? JNI_TRUE : JNI_FALSE; 966} 967 968/** 969 * Read the image width from a PKM header 970 */ 971static jint etc1_getWidth(JNIEnv *env, jclass clazz, 972 jobject header) { 973 jint result = 0; 974 BufferHelper headerB(env, header); 975 if (headerB.checkPointer("header") ){ 976 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) { 977 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE"); 978 } else { 979 result = etc1_pkm_get_width((etc1_byte*) headerB.getData()); 980 } 981 } 982 return result; 983} 984 985/** 986 * Read the image height from a PKM header 987 */ 988static jint etc1_getHeight(JNIEnv *env, jclass clazz, 989 jobject header) { 990 jint result = 0; 991 BufferHelper headerB(env, header); 992 if (headerB.checkPointer("header") ){ 993 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) { 994 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE"); 995 } else { 996 result = etc1_pkm_get_height((etc1_byte*) headerB.getData()); 997 } 998 } 999 return result; 1000} 1001 1002/* 1003 * JNI registration 1004 */ 1005 1006static JNINativeMethod gMatrixMethods[] = { 1007 { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM }, 1008 { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV }, 1009}; 1010 1011static JNINativeMethod gVisibilityMethods[] = { 1012 { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere }, 1013 { "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres }, 1014 { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest }, 1015}; 1016 1017static JNINativeMethod gUtilsMethods[] = { 1018 {"nativeClassInit", "()V", (void*)nativeUtilsClassInit }, 1019 { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat }, 1020 { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType }, 1021 { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D }, 1022 { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D }, 1023 { "setTracingLevel", "(I)V", (void*)setTracingLevel }, 1024}; 1025 1026static JNINativeMethod gEtc1Methods[] = { 1027 { "encodeBlock", "(Ljava/nio/Buffer;ILjava/nio/Buffer;)V", (void*) etc1_encodeBlock }, 1028 { "decodeBlock", "(Ljava/nio/Buffer;Ljava/nio/Buffer;)V", (void*) etc1_decodeBlock }, 1029 { "getEncodedDataSize", "(II)I", (void*) etc1_getEncodedDataSize }, 1030 { "encodeImage", "(Ljava/nio/Buffer;IIIILjava/nio/Buffer;)V", (void*) etc1_encodeImage }, 1031 { "decodeImage", "(Ljava/nio/Buffer;Ljava/nio/Buffer;IIII)V", (void*) etc1_decodeImage }, 1032 { "formatHeader", "(Ljava/nio/Buffer;II)V", (void*) etc1_formatHeader }, 1033 { "isValid", "(Ljava/nio/Buffer;)Z", (void*) etc1_isValid }, 1034 { "getWidth", "(Ljava/nio/Buffer;)I", (void*) etc1_getWidth }, 1035 { "getHeight", "(Ljava/nio/Buffer;)I", (void*) etc1_getHeight }, 1036}; 1037 1038typedef struct _ClassRegistrationInfo { 1039 const char* classPath; 1040 JNINativeMethod* methods; 1041 size_t methodCount; 1042} ClassRegistrationInfo; 1043 1044static ClassRegistrationInfo gClasses[] = { 1045 {"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)}, 1046 {"android/opengl/Visibility", gVisibilityMethods, NELEM(gVisibilityMethods)}, 1047 {"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)}, 1048 {"android/opengl/ETC1", gEtc1Methods, NELEM(gEtc1Methods)}, 1049}; 1050 1051int register_android_opengl_classes(JNIEnv* env) 1052{ 1053 nativeClassInitBuffer(env); 1054 int result = 0; 1055 for (int i = 0; i < NELEM(gClasses); i++) { 1056 ClassRegistrationInfo* cri = &gClasses[i]; 1057 result = RegisterMethodsOrDie(env, cri->classPath, cri->methods, cri->methodCount); 1058 } 1059 return result; 1060} 1061 1062} // namespace android 1063