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