util.cpp revision b57dd722f1dc0663417da37d3a82f8283ad3c982
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 153class ByteArrayGetter { 154public: 155 static void* Get(JNIEnv* _env, jbyteArray array, jboolean* is_copy) { 156 return _env->GetByteArrayElements(array, is_copy); 157 } 158}; 159class BooleanArrayGetter { 160public: 161 static void* Get(JNIEnv* _env, jbooleanArray array, jboolean* is_copy) { 162 return _env->GetBooleanArrayElements(array, is_copy); 163 } 164}; 165class CharArrayGetter { 166public: 167 static void* Get(JNIEnv* _env, jcharArray array, jboolean* is_copy) { 168 return _env->GetCharArrayElements(array, is_copy); 169 } 170}; 171class ShortArrayGetter { 172public: 173 static void* Get(JNIEnv* _env, jshortArray array, jboolean* is_copy) { 174 return _env->GetShortArrayElements(array, is_copy); 175 } 176}; 177class IntArrayGetter { 178public: 179 static void* Get(JNIEnv* _env, jintArray array, jboolean* is_copy) { 180 return _env->GetIntArrayElements(array, is_copy); 181 } 182}; 183class LongArrayGetter { 184public: 185 static void* Get(JNIEnv* _env, jlongArray array, jboolean* is_copy) { 186 return _env->GetLongArrayElements(array, is_copy); 187 } 188}; 189class FloatArrayGetter { 190public: 191 static void* Get(JNIEnv* _env, jfloatArray array, jboolean* is_copy) { 192 return _env->GetFloatArrayElements(array, is_copy); 193 } 194}; 195class DoubleArrayGetter { 196public: 197 static void* Get(JNIEnv* _env, jdoubleArray array, jboolean* is_copy) { 198 return _env->GetDoubleArrayElements(array, is_copy); 199 } 200}; 201 202class ByteArrayReleaser { 203public: 204 static void Release(JNIEnv* _env, jbyteArray array, jbyte* data, jint mode) { 205 _env->ReleaseByteArrayElements(array, data, mode); 206 } 207}; 208class BooleanArrayReleaser { 209public: 210 static void Release(JNIEnv* _env, jbooleanArray array, jboolean* data, jint mode) { 211 _env->ReleaseBooleanArrayElements(array, data, mode); 212 } 213}; 214class CharArrayReleaser { 215public: 216 static void Release(JNIEnv* _env, jcharArray array, jchar* data, jint mode) { 217 _env->ReleaseCharArrayElements(array, data, mode); 218 } 219}; 220class ShortArrayReleaser { 221public: 222 static void Release(JNIEnv* _env, jshortArray array, jshort* data, jint mode) { 223 _env->ReleaseShortArrayElements(array, data, mode); 224 } 225}; 226class IntArrayReleaser { 227public: 228 static void Release(JNIEnv* _env, jintArray array, jint* data, jint mode) { 229 _env->ReleaseIntArrayElements(array, data, mode); 230 } 231}; 232class LongArrayReleaser { 233public: 234 static void Release(JNIEnv* _env, jlongArray array, jlong* data, jint mode) { 235 _env->ReleaseLongArrayElements(array, data, mode); 236 } 237}; 238class FloatArrayReleaser { 239public: 240 static void Release(JNIEnv* _env, jfloatArray array, jfloat* data, jint mode) { 241 _env->ReleaseFloatArrayElements(array, data, mode); 242 } 243}; 244class DoubleArrayReleaser { 245public: 246 static void Release(JNIEnv* _env, jdoubleArray array, jdouble* data, jint mode) { 247 _env->ReleaseDoubleArrayElements(array, data, mode); 248 } 249}; 250 251template<class JArray, class T, class ArrayGetter, class ArrayReleaser> 252class ArrayHelper { 253public: 254 ArrayHelper(JNIEnv* env, JArray ref, jint offset, jint minSize) { 255 mEnv = env; 256 mRef = ref; 257 mOffset = offset; 258 mMinSize = minSize; 259 mBase = 0; 260 mReleaseParam = JNI_ABORT; 261 } 262 263 ~ArrayHelper() { 264 if (mBase) { 265 ArrayReleaser::Release(mEnv, mRef, mBase, mReleaseParam); 266 } 267 } 268 269 // We seperate the bounds check from the initialization because we want to 270 // be able to bounds-check multiple arrays, and we can't throw an exception 271 // after we've called GetPrimitiveArrayCritical. 272 273 // Return true if the bounds check succeeded 274 // Else instruct the runtime to throw an exception 275 276 bool check() { 277 if ( ! mRef) { 278 doThrowIAE(mEnv, "array == null"); 279 return false; 280 } 281 if ( mOffset < 0) { 282 doThrowIAE(mEnv, "offset < 0"); 283 return false; 284 } 285 mLength = mEnv->GetArrayLength(mRef) - mOffset; 286 if (mLength < mMinSize ) { 287 doThrowIAE(mEnv, "length - offset < n"); 288 return false; 289 } 290 return true; 291 } 292 293 // Bind the array. 294 295 void bind() { 296 mBase = (T*) ArrayGetter::Get(mEnv, mRef, (jboolean *) 0); 297 mData = mBase + mOffset; 298 } 299 300 void commitChanges() { 301 mReleaseParam = 0; 302 } 303 304 T* mData; 305 int mLength; 306 307private: 308 T* mBase; 309 JNIEnv* mEnv; 310 JArray mRef; 311 jint mOffset; 312 jint mMinSize; 313 int mReleaseParam; 314}; 315 316typedef ArrayHelper<jfloatArray, float, FloatArrayGetter, FloatArrayReleaser> FloatArrayHelper; 317typedef ArrayHelper<jcharArray, unsigned short, CharArrayGetter, CharArrayReleaser> UnsignedShortArrayHelper; 318typedef ArrayHelper<jintArray, int, IntArrayGetter, IntArrayReleaser> IntArrayHelper; 319typedef ArrayHelper<jbyteArray, unsigned char, ByteArrayGetter, ByteArrayReleaser> ByteArrayHelper; 320 321inline float distance2(float x, float y, float z) { 322 return x * x + y * y + z * z; 323} 324 325inline float distance(float x, float y, float z) { 326 return sqrtf(distance2(x, y, z)); 327} 328 329static 330void util_computeBoundingSphere(JNIEnv *env, jclass clazz, 331 jfloatArray positions_ref, jint positionsOffset, jint positionsCount, 332 jfloatArray sphere_ref, jint sphereOffset) { 333 FloatArrayHelper positions(env, positions_ref, positionsOffset, 0); 334 FloatArrayHelper sphere(env, sphere_ref, sphereOffset, 4); 335 336 bool checkOK = positions.check() && sphere.check(); 337 if (! checkOK) { 338 return; 339 } 340 341 positions.bind(); 342 sphere.bind(); 343 344 if ( positionsCount < 1 ) { 345 doThrowIAE(env, "positionsCount < 1"); 346 return; 347 } 348 349 const float* pSrc = positions.mData; 350 351 // find bounding box 352 float x0 = *pSrc++; 353 float x1 = x0; 354 float y0 = *pSrc++; 355 float y1 = y0; 356 float z0 = *pSrc++; 357 float z1 = z0; 358 359 for(int i = 1; i < positionsCount; i++) { 360 { 361 float x = *pSrc++; 362 if (x < x0) { 363 x0 = x; 364 } 365 else if (x > x1) { 366 x1 = x; 367 } 368 } 369 { 370 float y = *pSrc++; 371 if (y < y0) { 372 y0 = y; 373 } 374 else if (y > y1) { 375 y1 = y; 376 } 377 } 378 { 379 float z = *pSrc++; 380 if (z < z0) { 381 z0 = z; 382 } 383 else if (z > z1) { 384 z1 = z; 385 } 386 } 387 } 388 389 // Because we know our input meshes fit pretty well into bounding boxes, 390 // just take the diagonal of the box as defining our sphere. 391 float* pSphere = sphere.mData; 392 float dx = x1 - x0; 393 float dy = y1 - y0; 394 float dz = z1 - z0; 395 *pSphere++ = x0 + dx * 0.5f; 396 *pSphere++ = y0 + dy * 0.5f; 397 *pSphere++ = z0 + dz * 0.5f; 398 *pSphere++ = distance(dx, dy, dz) * 0.5f; 399 400 sphere.commitChanges(); 401} 402 403static void normalizePlane(float* p) { 404 float rdist = 1.0f / distance(p[0], p[1], p[2]); 405 for(int i = 0; i < 4; i++) { 406 p[i] *= rdist; 407 } 408} 409 410static inline float dot3(float x0, float y0, float z0, float x1, float y1, float z1) { 411 return x0 * x1 + y0 * y1 + z0 * z1; 412} 413 414static inline float signedDistance(const float* pPlane, float x, float y, float z) { 415 return dot3(pPlane[0], pPlane[1], pPlane[2], x, y, z) + pPlane[3]; 416} 417 418// Return true if the sphere intersects or is inside the frustum 419 420static bool sphereHitsFrustum(const float* pFrustum, const float* pSphere) { 421 float x = pSphere[0]; 422 float y = pSphere[1]; 423 float z = pSphere[2]; 424 float negRadius = -pSphere[3]; 425 for (int i = 0; i < 6; i++, pFrustum += 4) { 426 if (signedDistance(pFrustum, x, y, z) <= negRadius) { 427 return false; 428 } 429 } 430 return true; 431} 432 433static void computeFrustum(const float* m, float* f) { 434 float m3 = m[3]; 435 float m7 = m[7]; 436 float m11 = m[11]; 437 float m15 = m[15]; 438 // right 439 f[0] = m3 - m[0]; 440 f[1] = m7 - m[4]; 441 f[2] = m11 - m[8]; 442 f[3] = m15 - m[12]; 443 normalizePlane(f); 444 f+= 4; 445 446 // left 447 f[0] = m3 + m[0]; 448 f[1] = m7 + m[4]; 449 f[2] = m11 + m[8]; 450 f[3] = m15 + m[12]; 451 normalizePlane(f); 452 f+= 4; 453 454 // top 455 f[0] = m3 - m[1]; 456 f[1] = m7 - m[5]; 457 f[2] = m11 - m[9]; 458 f[3] = m15 - m[13]; 459 normalizePlane(f); 460 f+= 4; 461 462 // bottom 463 f[0] = m3 + m[1]; 464 f[1] = m7 + m[5]; 465 f[2] = m11 + m[9]; 466 f[3] = m15 + m[13]; 467 normalizePlane(f); 468 f+= 4; 469 470 // far 471 f[0] = m3 - m[2]; 472 f[1] = m7 - m[6]; 473 f[2] = m11 - m[10]; 474 f[3] = m15 - m[14]; 475 normalizePlane(f); 476 f+= 4; 477 478 // near 479 f[0] = m3 + m[2]; 480 f[1] = m7 + m[6]; 481 f[2] = m11 + m[10]; 482 f[3] = m15 + m[14]; 483 normalizePlane(f); 484} 485 486static 487jint util_frustumCullSpheres(JNIEnv *env, jclass clazz, 488 jfloatArray mvp_ref, jint mvpOffset, 489 jfloatArray spheres_ref, jint spheresOffset, jint spheresCount, 490 jintArray results_ref, jint resultsOffset, jint resultsCapacity) { 491 float frustum[6*4]; 492 int outputCount; 493 int* pResults; 494 float* pSphere; 495 FloatArrayHelper mvp(env, mvp_ref, mvpOffset, 16); 496 FloatArrayHelper spheres(env, spheres_ref, spheresOffset, spheresCount * 4); 497 IntArrayHelper results(env, results_ref, resultsOffset, resultsCapacity); 498 499 bool initializedOK = mvp.check() && spheres.check() && results.check(); 500 if (! initializedOK) { 501 return -1; 502 } 503 504 mvp.bind(); 505 spheres.bind(); 506 results.bind(); 507 508 computeFrustum(mvp.mData, frustum); 509 510 // Cull the spheres 511 512 pSphere = spheres.mData; 513 pResults = results.mData; 514 outputCount = 0; 515 for(int i = 0; i < spheresCount; i++, pSphere += 4) { 516 if (sphereHitsFrustum(frustum, pSphere)) { 517 if (outputCount < resultsCapacity) { 518 *pResults++ = i; 519 } 520 outputCount++; 521 } 522 } 523 results.commitChanges(); 524 return outputCount; 525} 526 527/* 528 public native int visibilityTest(float[] ws, int wsOffset, 529 float[] positions, int positionsOffset, 530 char[] indices, int indicesOffset, int indexCount); 531 */ 532 533static 534jint util_visibilityTest(JNIEnv *env, jclass clazz, 535 jfloatArray ws_ref, jint wsOffset, 536 jfloatArray positions_ref, jint positionsOffset, 537 jcharArray indices_ref, jint indicesOffset, jint indexCount) { 538 539 FloatArrayHelper ws(env, ws_ref, wsOffset, 16); 540 FloatArrayHelper positions(env, positions_ref, positionsOffset, 0); 541 UnsignedShortArrayHelper indices(env, indices_ref, indicesOffset, 0); 542 543 bool checkOK = ws.check() && positions.check() && indices.check(); 544 if (! checkOK) { 545 // Return value will be ignored, because an exception has been thrown. 546 return -1; 547 } 548 549 if (indices.mLength < indexCount) { 550 doThrowIAE(env, "length < offset + indexCount"); 551 return -1; 552 } 553 554 ws.bind(); 555 positions.bind(); 556 indices.bind(); 557 558 return visibilityTest(ws.mData, 559 positions.mData, positions.mLength, 560 indices.mData, indexCount); 561} 562 563#define I(_i, _j) ((_j)+ 4*(_i)) 564 565static 566void multiplyMM(float* r, const float* lhs, const float* rhs) 567{ 568 for (int i=0 ; i<4 ; i++) { 569 const float rhs_i0 = rhs[ I(i,0) ]; 570 float ri0 = lhs[ I(0,0) ] * rhs_i0; 571 float ri1 = lhs[ I(0,1) ] * rhs_i0; 572 float ri2 = lhs[ I(0,2) ] * rhs_i0; 573 float ri3 = lhs[ I(0,3) ] * rhs_i0; 574 for (int j=1 ; j<4 ; j++) { 575 const float rhs_ij = rhs[ I(i,j) ]; 576 ri0 += lhs[ I(j,0) ] * rhs_ij; 577 ri1 += lhs[ I(j,1) ] * rhs_ij; 578 ri2 += lhs[ I(j,2) ] * rhs_ij; 579 ri3 += lhs[ I(j,3) ] * rhs_ij; 580 } 581 r[ I(i,0) ] = ri0; 582 r[ I(i,1) ] = ri1; 583 r[ I(i,2) ] = ri2; 584 r[ I(i,3) ] = ri3; 585 } 586} 587 588static 589void util_multiplyMM(JNIEnv *env, jclass clazz, 590 jfloatArray result_ref, jint resultOffset, 591 jfloatArray lhs_ref, jint lhsOffset, 592 jfloatArray rhs_ref, jint rhsOffset) { 593 594 FloatArrayHelper resultMat(env, result_ref, resultOffset, 16); 595 FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16); 596 FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 16); 597 598 bool checkOK = resultMat.check() && lhs.check() && rhs.check(); 599 600 if ( !checkOK ) { 601 return; 602 } 603 604 resultMat.bind(); 605 lhs.bind(); 606 rhs.bind(); 607 608 multiplyMM(resultMat.mData, lhs.mData, rhs.mData); 609 610 resultMat.commitChanges(); 611} 612 613static 614void multiplyMV(float* r, const float* lhs, const float* rhs) 615{ 616 mx4transform(rhs[0], rhs[1], rhs[2], rhs[3], lhs, r); 617} 618 619static 620void util_multiplyMV(JNIEnv *env, jclass clazz, 621 jfloatArray result_ref, jint resultOffset, 622 jfloatArray lhs_ref, jint lhsOffset, 623 jfloatArray rhs_ref, jint rhsOffset) { 624 625 FloatArrayHelper resultV(env, result_ref, resultOffset, 4); 626 FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16); 627 FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 4); 628 629 bool checkOK = resultV.check() && lhs.check() && rhs.check(); 630 631 if ( !checkOK ) { 632 return; 633 } 634 635 resultV.bind(); 636 lhs.bind(); 637 rhs.bind(); 638 639 multiplyMV(resultV.mData, lhs.mData, rhs.mData); 640 641 resultV.commitChanges(); 642} 643 644// --------------------------------------------------------------------------- 645 646extern void setGLDebugLevel(int level); 647void setTracingLevel(JNIEnv *env, jclass clazz, jint level) 648{ 649 setGLDebugLevel(level); 650} 651 652static int checkFormat(SkColorType colorType, int format, int type) 653{ 654 switch(colorType) { 655 case kIndex_8_SkColorType: 656 if (format == GL_PALETTE8_RGBA8_OES) 657 return 0; 658 case kN32_SkColorType: 659 case kAlpha_8_SkColorType: 660 if (type == GL_UNSIGNED_BYTE) 661 return 0; 662 case kARGB_4444_SkColorType: 663 case kRGB_565_SkColorType: 664 switch (type) { 665 case GL_UNSIGNED_SHORT_4_4_4_4: 666 case GL_UNSIGNED_SHORT_5_6_5: 667 case GL_UNSIGNED_SHORT_5_5_5_1: 668 return 0; 669 case GL_UNSIGNED_BYTE: 670 if (format == GL_LUMINANCE_ALPHA) 671 return 0; 672 } 673 break; 674 default: 675 break; 676 } 677 return -1; 678} 679 680static int getInternalFormat(SkColorType colorType) 681{ 682 switch(colorType) { 683 case kAlpha_8_SkColorType: 684 return GL_ALPHA; 685 case kARGB_4444_SkColorType: 686 return GL_RGBA; 687 case kN32_SkColorType: 688 return GL_RGBA; 689 case kIndex_8_SkColorType: 690 return GL_PALETTE8_RGBA8_OES; 691 case kRGB_565_SkColorType: 692 return GL_RGB; 693 default: 694 return -1; 695 } 696} 697 698static int getType(SkColorType colorType) 699{ 700 switch(colorType) { 701 case kAlpha_8_SkColorType: 702 return GL_UNSIGNED_BYTE; 703 case kARGB_4444_SkColorType: 704 return GL_UNSIGNED_SHORT_4_4_4_4; 705 case kN32_SkColorType: 706 return GL_UNSIGNED_BYTE; 707 case kIndex_8_SkColorType: 708 return -1; // No type for compressed data. 709 case kRGB_565_SkColorType: 710 return GL_UNSIGNED_SHORT_5_6_5; 711 default: 712 return -1; 713 } 714} 715 716static jint util_getInternalFormat(JNIEnv *env, jclass clazz, 717 jobject jbitmap) 718{ 719 SkBitmap nativeBitmap; 720 GraphicsJNI::getSkBitmap(env, jbitmap, &nativeBitmap); 721 return getInternalFormat(nativeBitmap.colorType()); 722} 723 724static jint util_getType(JNIEnv *env, jclass clazz, 725 jobject jbitmap) 726{ 727 SkBitmap nativeBitmap; 728 GraphicsJNI::getSkBitmap(env, jbitmap, &nativeBitmap); 729 return getType(nativeBitmap.colorType()); 730} 731 732static jint util_texImage2D(JNIEnv *env, jclass clazz, 733 jint target, jint level, jint internalformat, 734 jobject jbitmap, jint type, jint border) 735{ 736 SkBitmap bitmap; 737 GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); 738 SkColorType colorType = bitmap.colorType(); 739 if (internalformat < 0) { 740 internalformat = getInternalFormat(colorType); 741 } 742 if (type < 0) { 743 type = getType(colorType); 744 } 745 int err = checkFormat(colorType, internalformat, type); 746 if (err) 747 return err; 748 bitmap.lockPixels(); 749 const int w = bitmap.width(); 750 const int h = bitmap.height(); 751 const void* p = bitmap.getPixels(); 752 if (internalformat == GL_PALETTE8_RGBA8_OES) { 753 if (sizeof(SkPMColor) != sizeof(uint32_t)) { 754 err = -1; 755 goto error; 756 } 757 const size_t size = bitmap.getSize(); 758 const size_t palette_size = 256*sizeof(SkPMColor); 759 const size_t imageSize = size + palette_size; 760 void* const data = malloc(imageSize); 761 if (data) { 762 void* const pixels = (char*)data + palette_size; 763 SkColorTable* ctable = bitmap.getColorTable(); 764 memcpy(data, ctable->readColors(), ctable->count() * sizeof(SkPMColor)); 765 memcpy(pixels, p, size); 766 glCompressedTexImage2D(target, level, internalformat, w, h, border, imageSize, data); 767 free(data); 768 } else { 769 err = -1; 770 } 771 } else { 772 glTexImage2D(target, level, internalformat, w, h, border, internalformat, type, p); 773 } 774error: 775 bitmap.unlockPixels(); 776 return err; 777} 778 779static jint util_texSubImage2D(JNIEnv *env, jclass clazz, 780 jint target, jint level, jint xoffset, jint yoffset, 781 jobject jbitmap, jint format, jint type) 782{ 783 SkBitmap bitmap; 784 GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); 785 SkColorType colorType = bitmap.colorType(); 786 if (format < 0) { 787 format = getInternalFormat(colorType); 788 if (format == GL_PALETTE8_RGBA8_OES) 789 return -1; // glCompressedTexSubImage2D() not supported 790 } 791 int err = checkFormat(colorType, format, type); 792 if (err) 793 return err; 794 bitmap.lockPixels(); 795 const int w = bitmap.width(); 796 const int h = bitmap.height(); 797 const void* p = bitmap.getPixels(); 798 glTexSubImage2D(target, level, xoffset, yoffset, w, h, format, type, p); 799 bitmap.unlockPixels(); 800 return 0; 801} 802 803/* 804 * ETC1 methods. 805 */ 806 807static jclass nioAccessClass; 808static jclass bufferClass; 809static jmethodID getBasePointerID; 810static jmethodID getBaseArrayID; 811static jmethodID getBaseArrayOffsetID; 812static jfieldID positionID; 813static jfieldID limitID; 814static jfieldID elementSizeShiftID; 815 816/* Cache method IDs each time the class is loaded. */ 817 818static void 819nativeClassInitBuffer(JNIEnv *env) 820{ 821 jclass nioAccessClassLocal = FindClassOrDie(env, "java/nio/NIOAccess"); 822 nioAccessClass = MakeGlobalRefOrDie(env, nioAccessClassLocal); 823 getBasePointerID = GetStaticMethodIDOrDie(env, nioAccessClass, 824 "getBasePointer", "(Ljava/nio/Buffer;)J"); 825 getBaseArrayID = GetStaticMethodIDOrDie(env, nioAccessClass, 826 "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;"); 827 getBaseArrayOffsetID = GetStaticMethodIDOrDie(env, nioAccessClass, 828 "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); 829 830 jclass bufferClassLocal = FindClassOrDie(env, "java/nio/Buffer"); 831 bufferClass = MakeGlobalRefOrDie(env, bufferClassLocal); 832 positionID = GetFieldIDOrDie(env, bufferClass, "position", "I"); 833 limitID = GetFieldIDOrDie(env, bufferClass, "limit", "I"); 834 elementSizeShiftID = GetFieldIDOrDie(env, bufferClass, "_elementSizeShift", "I"); 835} 836 837static void * 838getPointer(JNIEnv *_env, jobject buffer, jint *remaining) 839{ 840 jint position; 841 jint limit; 842 jint elementSizeShift; 843 jlong pointer; 844 845 position = _env->GetIntField(buffer, positionID); 846 limit = _env->GetIntField(buffer, limitID); 847 elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); 848 *remaining = (limit - position) << elementSizeShift; 849 pointer = _env->CallStaticLongMethod(nioAccessClass, 850 getBasePointerID, buffer); 851 if (pointer != 0L) { 852 return reinterpret_cast<void *>(pointer); 853 } 854 return NULL; 855} 856 857class BufferHelper { 858public: 859 BufferHelper(JNIEnv *env, jobject buffer) { 860 mEnv = env; 861 mBuffer = buffer; 862 mData = NULL; 863 mRemaining = 0; 864 } 865 866 bool checkPointer(const char* errorMessage) { 867 if (mBuffer) { 868 mData = getPointer(mEnv, mBuffer, &mRemaining); 869 if (mData == NULL) { 870 doThrowIAE(mEnv, errorMessage); 871 } 872 return mData != NULL; 873 } else { 874 doThrowIAE(mEnv, errorMessage); 875 return false; 876 } 877 } 878 879 inline void* getData() { 880 return mData; 881 } 882 883 inline jint remaining() { 884 return mRemaining; 885 } 886 887private: 888 JNIEnv* mEnv; 889 jobject mBuffer; 890 void* mData; 891 jint mRemaining; 892}; 893 894/** 895 * Encode a block of pixels. 896 * 897 * @param in a pointer to a ETC1_DECODED_BLOCK_SIZE array of bytes that represent a 898 * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R 899 * value of pixel (x, y). 900 * 901 * @param validPixelMask is a 16-bit mask where bit (1 << (x + y * 4)) indicates whether 902 * the corresponding (x,y) pixel is valid. Invalid pixel color values are ignored when compressing. 903 * 904 * @param out an ETC1 compressed version of the data. 905 * 906 */ 907static void etc1_encodeBlock(JNIEnv *env, jclass clazz, 908 jobject in, jint validPixelMask, jobject out) { 909 if (validPixelMask < 0 || validPixelMask > 15) { 910 doThrowIAE(env, "validPixelMask"); 911 return; 912 } 913 BufferHelper inB(env, in); 914 BufferHelper outB(env, out); 915 if (inB.checkPointer("in") && outB.checkPointer("out")) { 916 if (inB.remaining() < ETC1_DECODED_BLOCK_SIZE) { 917 doThrowIAE(env, "in's remaining data < DECODED_BLOCK_SIZE"); 918 } else if (outB.remaining() < ETC1_ENCODED_BLOCK_SIZE) { 919 doThrowIAE(env, "out's remaining data < ENCODED_BLOCK_SIZE"); 920 } else { 921 etc1_encode_block((etc1_byte*) inB.getData(), validPixelMask, 922 (etc1_byte*) outB.getData()); 923 } 924 } 925} 926 927/** 928 * Decode a block of pixels. 929 * 930 * @param in an ETC1 compressed version of the data. 931 * 932 * @param out a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a 933 * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R 934 * value of pixel (x, y). 935 */ 936static void etc1_decodeBlock(JNIEnv *env, jclass clazz, 937 jobject in, jobject out){ 938 BufferHelper inB(env, in); 939 BufferHelper outB(env, out); 940 if (inB.checkPointer("in") && outB.checkPointer("out")) { 941 if (inB.remaining() < ETC1_ENCODED_BLOCK_SIZE) { 942 doThrowIAE(env, "in's remaining data < ENCODED_BLOCK_SIZE"); 943 } else if (outB.remaining() < ETC1_DECODED_BLOCK_SIZE) { 944 doThrowIAE(env, "out's remaining data < DECODED_BLOCK_SIZE"); 945 } else { 946 etc1_decode_block((etc1_byte*) inB.getData(), 947 (etc1_byte*) outB.getData()); 948 } 949 } 950} 951 952/** 953 * Return the size of the encoded image data (does not include size of PKM header). 954 */ 955static jint etc1_getEncodedDataSize(JNIEnv *env, jclass clazz, 956 jint width, jint height) { 957 return etc1_get_encoded_data_size(width, height); 958} 959 960/** 961 * Encode an entire image. 962 * @param in pointer to the image data. Formatted such that 963 * pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset; 964 * @param out pointer to encoded data. Must be large enough to store entire encoded image. 965 */ 966static void etc1_encodeImage(JNIEnv *env, jclass clazz, 967 jobject in, jint width, jint height, 968 jint pixelSize, jint stride, jobject out) { 969 if (pixelSize < 2 || pixelSize > 3) { 970 doThrowIAE(env, "pixelSize must be 2 or 3"); 971 return; 972 } 973 BufferHelper inB(env, in); 974 BufferHelper outB(env, out); 975 if (inB.checkPointer("in") && outB.checkPointer("out")) { 976 jint imageSize = stride * height; 977 jint encodedImageSize = etc1_get_encoded_data_size(width, height); 978 if (inB.remaining() < imageSize) { 979 doThrowIAE(env, "in's remaining data < image size"); 980 } else if (outB.remaining() < encodedImageSize) { 981 doThrowIAE(env, "out's remaining data < encoded image size"); 982 } else { 983 etc1_encode_image((etc1_byte*) inB.getData(), width, height, pixelSize, stride, 984 (etc1_byte*) outB.getData()); 985 } 986 } 987} 988 989/** 990 * Decode an entire image. 991 * @param in the encoded data. 992 * @param out pointer to the image data. Will be written such that 993 * pixel (x,y) is at pIn + pixelSize * x + stride * y. Must be 994 * large enough to store entire image. 995 */ 996static void etc1_decodeImage(JNIEnv *env, jclass clazz, 997 jobject in, jobject out, 998 jint width, jint height, 999 jint pixelSize, jint stride) { 1000 if (pixelSize < 2 || pixelSize > 3) { 1001 doThrowIAE(env, "pixelSize must be 2 or 3"); 1002 return; 1003 } 1004 BufferHelper inB(env, in); 1005 BufferHelper outB(env, out); 1006 if (inB.checkPointer("in") && outB.checkPointer("out")) { 1007 jint imageSize = stride * height; 1008 jint encodedImageSize = etc1_get_encoded_data_size(width, height); 1009 if (inB.remaining() < encodedImageSize) { 1010 doThrowIAE(env, "in's remaining data < encoded image size"); 1011 } else if (outB.remaining() < imageSize) { 1012 doThrowIAE(env, "out's remaining data < image size"); 1013 } else { 1014 etc1_decode_image((etc1_byte*) inB.getData(), (etc1_byte*) outB.getData(), 1015 width, height, pixelSize, stride); 1016 } 1017 } 1018} 1019 1020/** 1021 * Format a PKM header 1022 */ 1023static void etc1_formatHeader(JNIEnv *env, jclass clazz, 1024 jobject header, jint width, jint height) { 1025 BufferHelper headerB(env, header); 1026 if (headerB.checkPointer("header") ){ 1027 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) { 1028 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE"); 1029 } else { 1030 etc1_pkm_format_header((etc1_byte*) headerB.getData(), width, height); 1031 } 1032 } 1033} 1034 1035/** 1036 * Check if a PKM header is correctly formatted. 1037 */ 1038static jboolean etc1_isValid(JNIEnv *env, jclass clazz, 1039 jobject header) { 1040 jboolean result = false; 1041 BufferHelper headerB(env, header); 1042 if (headerB.checkPointer("header") ){ 1043 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) { 1044 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE"); 1045 } else { 1046 result = etc1_pkm_is_valid((etc1_byte*) headerB.getData()); 1047 } 1048 } 1049 return result ? JNI_TRUE : JNI_FALSE; 1050} 1051 1052/** 1053 * Read the image width from a PKM header 1054 */ 1055static jint etc1_getWidth(JNIEnv *env, jclass clazz, 1056 jobject header) { 1057 jint result = 0; 1058 BufferHelper headerB(env, header); 1059 if (headerB.checkPointer("header") ){ 1060 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) { 1061 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE"); 1062 } else { 1063 result = etc1_pkm_get_width((etc1_byte*) headerB.getData()); 1064 } 1065 } 1066 return result; 1067} 1068 1069/** 1070 * Read the image height from a PKM header 1071 */ 1072static jint etc1_getHeight(JNIEnv *env, jclass clazz, 1073 jobject header) { 1074 jint result = 0; 1075 BufferHelper headerB(env, header); 1076 if (headerB.checkPointer("header") ){ 1077 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) { 1078 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE"); 1079 } else { 1080 result = etc1_pkm_get_height((etc1_byte*) headerB.getData()); 1081 } 1082 } 1083 return result; 1084} 1085 1086/* 1087 * JNI registration 1088 */ 1089 1090static const JNINativeMethod gMatrixMethods[] = { 1091 { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM }, 1092 { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV }, 1093}; 1094 1095static const JNINativeMethod gVisibilityMethods[] = { 1096 { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere }, 1097 { "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres }, 1098 { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest }, 1099}; 1100 1101static const JNINativeMethod gUtilsMethods[] = { 1102 { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat }, 1103 { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType }, 1104 { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D }, 1105 { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D }, 1106 { "setTracingLevel", "(I)V", (void*)setTracingLevel }, 1107}; 1108 1109static const JNINativeMethod gEtc1Methods[] = { 1110 { "encodeBlock", "(Ljava/nio/Buffer;ILjava/nio/Buffer;)V", (void*) etc1_encodeBlock }, 1111 { "decodeBlock", "(Ljava/nio/Buffer;Ljava/nio/Buffer;)V", (void*) etc1_decodeBlock }, 1112 { "getEncodedDataSize", "(II)I", (void*) etc1_getEncodedDataSize }, 1113 { "encodeImage", "(Ljava/nio/Buffer;IIIILjava/nio/Buffer;)V", (void*) etc1_encodeImage }, 1114 { "decodeImage", "(Ljava/nio/Buffer;Ljava/nio/Buffer;IIII)V", (void*) etc1_decodeImage }, 1115 { "formatHeader", "(Ljava/nio/Buffer;II)V", (void*) etc1_formatHeader }, 1116 { "isValid", "(Ljava/nio/Buffer;)Z", (void*) etc1_isValid }, 1117 { "getWidth", "(Ljava/nio/Buffer;)I", (void*) etc1_getWidth }, 1118 { "getHeight", "(Ljava/nio/Buffer;)I", (void*) etc1_getHeight }, 1119}; 1120 1121typedef struct _ClassRegistrationInfo { 1122 const char* classPath; 1123 const JNINativeMethod* methods; 1124 size_t methodCount; 1125} ClassRegistrationInfo; 1126 1127static const ClassRegistrationInfo gClasses[] = { 1128 {"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)}, 1129 {"android/opengl/Visibility", gVisibilityMethods, NELEM(gVisibilityMethods)}, 1130 {"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)}, 1131 {"android/opengl/ETC1", gEtc1Methods, NELEM(gEtc1Methods)}, 1132}; 1133 1134int register_android_opengl_classes(JNIEnv* env) 1135{ 1136 nativeClassInitBuffer(env); 1137 int result = 0; 1138 for (int i = 0; i < NELEM(gClasses); i++) { 1139 const ClassRegistrationInfo* cri = &gClasses[i]; 1140 result = RegisterMethodsOrDie(env, cri->classPath, cri->methods, cri->methodCount); 1141 } 1142 return result; 1143} 1144 1145} // namespace android 1146