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