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