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