2 ** Copyright 2007, The Android Open Source Project
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
8 ** http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "GraphicsJNI.h"
29 #include <ETC1/etc1.h>
33 #include "core_jni_helpers.h"
36 #define LOG_TAG "OpenGLUtil"
37 #include <utils/Log.h>
38 #include "utils/misc.h"
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;
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);
65 int visibilityTest(float* pWS, float* pPositions, int positionsLength,
66 unsigned short* pIndices, int indexCount) {
67 int result = POLY_CLIP_OUT;
69 if ( indexCount < 3 ) {
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.
81 for(int i = 0; i < indexCount; i++) {
82 int index = pIndices[i];
83 if ( index < minIndex ) {
86 if ( index > maxIndex ) {
91 if ( maxIndex * 3 > positionsLength) {
95 int transformedIndexCount = maxIndex - minIndex + 1;
96 std::unique_ptr<float[]> holder{new float[transformedIndexCount * 4]};
97 float* pTransformed = holder.get();
99 if (pTransformed == 0 ) {
103 // Transform the vertices
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);
112 // Clip the triangles
115 float* pDest = & poly.vert[0].sx;
116 for (int i = 0; i < indexCount; i += 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) {
130 class ByteArrayGetter {
132 static void* Get(JNIEnv* _env, jbyteArray array, jboolean* is_copy) {
133 return _env->GetByteArrayElements(array, is_copy);
136 class BooleanArrayGetter {
138 static void* Get(JNIEnv* _env, jbooleanArray array, jboolean* is_copy) {
139 return _env->GetBooleanArrayElements(array, is_copy);
142 class CharArrayGetter {
144 static void* Get(JNIEnv* _env, jcharArray array, jboolean* is_copy) {
145 return _env->GetCharArrayElements(array, is_copy);
148 class ShortArrayGetter {
150 static void* Get(JNIEnv* _env, jshortArray array, jboolean* is_copy) {
151 return _env->GetShortArrayElements(array, is_copy);
154 class IntArrayGetter {
156 static void* Get(JNIEnv* _env, jintArray array, jboolean* is_copy) {
157 return _env->GetIntArrayElements(array, is_copy);
160 class LongArrayGetter {
162 static void* Get(JNIEnv* _env, jlongArray array, jboolean* is_copy) {
163 return _env->GetLongArrayElements(array, is_copy);
166 class FloatArrayGetter {
168 static void* Get(JNIEnv* _env, jfloatArray array, jboolean* is_copy) {
169 return _env->GetFloatArrayElements(array, is_copy);
172 class DoubleArrayGetter {
174 static void* Get(JNIEnv* _env, jdoubleArray array, jboolean* is_copy) {
175 return _env->GetDoubleArrayElements(array, is_copy);
179 class ByteArrayReleaser {
181 static void Release(JNIEnv* _env, jbyteArray array, jbyte* data, jint mode) {
182 _env->ReleaseByteArrayElements(array, data, mode);
185 class BooleanArrayReleaser {
187 static void Release(JNIEnv* _env, jbooleanArray array, jboolean* data, jint mode) {
188 _env->ReleaseBooleanArrayElements(array, data, mode);
191 class CharArrayReleaser {
193 static void Release(JNIEnv* _env, jcharArray array, jchar* data, jint mode) {
194 _env->ReleaseCharArrayElements(array, data, mode);
197 class ShortArrayReleaser {
199 static void Release(JNIEnv* _env, jshortArray array, jshort* data, jint mode) {
200 _env->ReleaseShortArrayElements(array, data, mode);
203 class IntArrayReleaser {
205 static void Release(JNIEnv* _env, jintArray array, jint* data, jint mode) {
206 _env->ReleaseIntArrayElements(array, data, mode);
209 class LongArrayReleaser {
211 static void Release(JNIEnv* _env, jlongArray array, jlong* data, jint mode) {
212 _env->ReleaseLongArrayElements(array, data, mode);
215 class FloatArrayReleaser {
217 static void Release(JNIEnv* _env, jfloatArray array, jfloat* data, jint mode) {
218 _env->ReleaseFloatArrayElements(array, data, mode);
221 class DoubleArrayReleaser {
223 static void Release(JNIEnv* _env, jdoubleArray array, jdouble* data, jint mode) {
224 _env->ReleaseDoubleArrayElements(array, data, mode);
228 template<class JArray, class T, class ArrayGetter, class ArrayReleaser>
231 ArrayHelper(JNIEnv* env, JArray ref, jint offset, jint minSize) {
237 mReleaseParam = JNI_ABORT;
242 ArrayReleaser::Release(mEnv, mRef, mBase, mReleaseParam);
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.
250 // Return true if the bounds check succeeded
251 // Else instruct the runtime to throw an exception
255 doThrowIAE(mEnv, "array == null");
259 doThrowIAE(mEnv, "offset < 0");
262 mLength = mEnv->GetArrayLength(mRef) - mOffset;
263 if (mLength < mMinSize ) {
264 doThrowIAE(mEnv, "length - offset < n");
273 mBase = (T*) ArrayGetter::Get(mEnv, mRef, (jboolean *) 0);
274 mData = mBase + mOffset;
277 void commitChanges() {
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;
298 inline float distance2(float x, float y, float z) {
299 return x * x + y * y + z * z;
302 inline float distance(float x, float y, float z) {
303 return sqrtf(distance2(x, y, z));
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);
313 bool checkOK = positions.check() && sphere.check();
321 if ( positionsCount < 1 ) {
322 doThrowIAE(env, "positionsCount < 1");
326 const float* pSrc = positions.mData;
336 for(int i = 1; i < positionsCount; i++) {
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;
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;
377 sphere.commitChanges();
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++) {
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;
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];
395 // Return true if the sphere intersects or is inside the frustum
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) {
410 static void computeFrustum(const float* m, float* f) {
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) {
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);
476 bool initializedOK = mvp.check() && spheres.check() && results.check();
477 if (! initializedOK) {
485 computeFrustum(mvp.mData, frustum);
489 pSphere = spheres.mData;
490 pResults = results.mData;
492 for(int i = 0; i < spheresCount; i++, pSphere += 4) {
493 if (sphereHitsFrustum(frustum, pSphere)) {
494 if (outputCount < resultsCapacity) {
500 results.commitChanges();
505 public native int visibilityTest(float[] ws, int wsOffset,
506 float[] positions, int positionsOffset,
507 char[] indices, int indicesOffset, int indexCount);
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) {
516 FloatArrayHelper ws(env, ws_ref, wsOffset, 16);
517 FloatArrayHelper positions(env, positions_ref, positionsOffset, 0);
518 UnsignedShortArrayHelper indices(env, indices_ref, indicesOffset, 0);
520 bool checkOK = ws.check() && positions.check() && indices.check();
522 // Return value will be ignored, because an exception has been thrown.
526 if (indices.mLength < indexCount) {
527 doThrowIAE(env, "length < offset + indexCount");
535 return visibilityTest(ws.mData,
536 positions.mData, positions.mLength,
537 indices.mData, indexCount);
540 #define I(_i, _j) ((_j)+ 4*(_i))
543 void multiplyMM(float* r, const float* lhs, const float* rhs)
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;
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) {
571 FloatArrayHelper resultMat(env, result_ref, resultOffset, 16);
572 FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16);
573 FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 16);
575 bool checkOK = resultMat.check() && lhs.check() && rhs.check();
585 multiplyMM(resultMat.mData, lhs.mData, rhs.mData);
587 resultMat.commitChanges();
591 void multiplyMV(float* r, const float* lhs, const float* rhs)
593 mx4transform(rhs[0], rhs[1], rhs[2], rhs[3], lhs, r);
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) {
602 FloatArrayHelper resultV(env, result_ref, resultOffset, 4);
603 FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16);
604 FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 4);
606 bool checkOK = resultV.check() && lhs.check() && rhs.check();
616 multiplyMV(resultV.mData, lhs.mData, rhs.mData);
618 resultV.commitChanges();
621 // ---------------------------------------------------------------------------
623 static int checkFormat(SkColorType colorType, int format, int type)
626 case kIndex_8_SkColorType:
627 if (format == GL_PALETTE8_RGBA8_OES)
629 case kN32_SkColorType:
630 case kAlpha_8_SkColorType:
631 if (type == GL_UNSIGNED_BYTE)
633 case kARGB_4444_SkColorType:
634 case kRGB_565_SkColorType:
636 case GL_UNSIGNED_SHORT_4_4_4_4:
637 case GL_UNSIGNED_SHORT_5_6_5:
638 case GL_UNSIGNED_SHORT_5_5_5_1:
640 case GL_UNSIGNED_BYTE:
641 if (format == GL_LUMINANCE_ALPHA)
651 static int getInternalFormat(SkColorType colorType)
654 case kAlpha_8_SkColorType:
656 case kARGB_4444_SkColorType:
658 case kN32_SkColorType:
660 case kIndex_8_SkColorType:
661 return GL_PALETTE8_RGBA8_OES;
662 case kRGB_565_SkColorType:
669 static int getType(SkColorType colorType)
672 case kAlpha_8_SkColorType:
673 return GL_UNSIGNED_BYTE;
674 case kARGB_4444_SkColorType:
675 return GL_UNSIGNED_SHORT_4_4_4_4;
676 case kN32_SkColorType:
677 return GL_UNSIGNED_BYTE;
678 case kIndex_8_SkColorType:
679 return -1; // No type for compressed data.
680 case kRGB_565_SkColorType:
681 return GL_UNSIGNED_SHORT_5_6_5;
687 static jint util_getInternalFormat(JNIEnv *env, jclass clazz,
690 SkBitmap nativeBitmap;
691 GraphicsJNI::getSkBitmap(env, jbitmap, &nativeBitmap);
692 return getInternalFormat(nativeBitmap.colorType());
695 static jint util_getType(JNIEnv *env, jclass clazz,
698 SkBitmap nativeBitmap;
699 GraphicsJNI::getSkBitmap(env, jbitmap, &nativeBitmap);
700 return getType(nativeBitmap.colorType());
703 static jint util_texImage2D(JNIEnv *env, jclass clazz,
704 jint target, jint level, jint internalformat,
705 jobject jbitmap, jint type, jint border)
708 GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
709 SkColorType colorType = bitmap.colorType();
710 if (internalformat < 0) {
711 internalformat = getInternalFormat(colorType);
714 type = getType(colorType);
716 int err = checkFormat(colorType, internalformat, type);
720 const int w = bitmap.width();
721 const int h = bitmap.height();
722 const void* p = bitmap.getPixels();
723 if (internalformat == GL_PALETTE8_RGBA8_OES) {
724 if (sizeof(SkPMColor) != sizeof(uint32_t)) {
728 const size_t size = bitmap.getSize();
729 const size_t palette_size = 256*sizeof(SkPMColor);
730 const size_t imageSize = size + palette_size;
731 void* const data = malloc(imageSize);
733 void* const pixels = (char*)data + palette_size;
734 SkColorTable* ctable = bitmap.getColorTable();
735 memcpy(data, ctable->readColors(), ctable->count() * sizeof(SkPMColor));
736 memcpy(pixels, p, size);
737 glCompressedTexImage2D(target, level, internalformat, w, h, border, imageSize, data);
743 glTexImage2D(target, level, internalformat, w, h, border, internalformat, type, p);
746 bitmap.unlockPixels();
750 static jint util_texSubImage2D(JNIEnv *env, jclass clazz,
751 jint target, jint level, jint xoffset, jint yoffset,
752 jobject jbitmap, jint format, jint type)
755 GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
756 SkColorType colorType = bitmap.colorType();
758 format = getInternalFormat(colorType);
759 if (format == GL_PALETTE8_RGBA8_OES)
760 return -1; // glCompressedTexSubImage2D() not supported
762 int err = checkFormat(colorType, format, type);
766 const int w = bitmap.width();
767 const int h = bitmap.height();
768 const void* p = bitmap.getPixels();
769 glTexSubImage2D(target, level, xoffset, yoffset, w, h, format, type, p);
770 bitmap.unlockPixels();
778 static jclass nioAccessClass;
779 static jclass bufferClass;
780 static jmethodID getBasePointerID;
781 static jmethodID getBaseArrayID;
782 static jmethodID getBaseArrayOffsetID;
783 static jfieldID positionID;
784 static jfieldID limitID;
785 static jfieldID elementSizeShiftID;
787 /* Cache method IDs each time the class is loaded. */
790 nativeClassInitBuffer(JNIEnv *env)
792 jclass nioAccessClassLocal = FindClassOrDie(env, "java/nio/NIOAccess");
793 nioAccessClass = MakeGlobalRefOrDie(env, nioAccessClassLocal);
794 getBasePointerID = GetStaticMethodIDOrDie(env, nioAccessClass,
795 "getBasePointer", "(Ljava/nio/Buffer;)J");
796 getBaseArrayID = GetStaticMethodIDOrDie(env, nioAccessClass,
797 "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;");
798 getBaseArrayOffsetID = GetStaticMethodIDOrDie(env, nioAccessClass,
799 "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
801 jclass bufferClassLocal = FindClassOrDie(env, "java/nio/Buffer");
802 bufferClass = MakeGlobalRefOrDie(env, bufferClassLocal);
803 positionID = GetFieldIDOrDie(env, bufferClass, "position", "I");
804 limitID = GetFieldIDOrDie(env, bufferClass, "limit", "I");
805 elementSizeShiftID = GetFieldIDOrDie(env, bufferClass, "_elementSizeShift", "I");
809 getPointer(JNIEnv *_env, jobject buffer, jint *remaining)
813 jint elementSizeShift;
816 position = _env->GetIntField(buffer, positionID);
817 limit = _env->GetIntField(buffer, limitID);
818 elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
819 *remaining = (limit - position) << elementSizeShift;
820 pointer = _env->CallStaticLongMethod(nioAccessClass,
821 getBasePointerID, buffer);
823 return reinterpret_cast<void *>(pointer);
830 BufferHelper(JNIEnv *env, jobject buffer) {
837 bool checkPointer(const char* errorMessage) {
839 mData = getPointer(mEnv, mBuffer, &mRemaining);
841 doThrowIAE(mEnv, errorMessage);
843 return mData != NULL;
845 doThrowIAE(mEnv, errorMessage);
850 inline void* getData() {
854 inline jint remaining() {
866 * Encode a block of pixels.
868 * @param in a pointer to a ETC1_DECODED_BLOCK_SIZE array of bytes that represent a
869 * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
870 * value of pixel (x, y).
872 * @param validPixelMask is a 16-bit mask where bit (1 << (x + y * 4)) indicates whether
873 * the corresponding (x,y) pixel is valid. Invalid pixel color values are ignored when compressing.
875 * @param out an ETC1 compressed version of the data.
878 static void etc1_encodeBlock(JNIEnv *env, jclass clazz,
879 jobject in, jint validPixelMask, jobject out) {
880 if (validPixelMask < 0 || validPixelMask > 15) {
881 doThrowIAE(env, "validPixelMask");
884 BufferHelper inB(env, in);
885 BufferHelper outB(env, out);
886 if (inB.checkPointer("in") && outB.checkPointer("out")) {
887 if (inB.remaining() < ETC1_DECODED_BLOCK_SIZE) {
888 doThrowIAE(env, "in's remaining data < DECODED_BLOCK_SIZE");
889 } else if (outB.remaining() < ETC1_ENCODED_BLOCK_SIZE) {
890 doThrowIAE(env, "out's remaining data < ENCODED_BLOCK_SIZE");
892 etc1_encode_block((etc1_byte*) inB.getData(), validPixelMask,
893 (etc1_byte*) outB.getData());
899 * Decode a block of pixels.
901 * @param in an ETC1 compressed version of the data.
903 * @param out a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a
904 * 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
905 * value of pixel (x, y).
907 static void etc1_decodeBlock(JNIEnv *env, jclass clazz,
908 jobject in, jobject out){
909 BufferHelper inB(env, in);
910 BufferHelper outB(env, out);
911 if (inB.checkPointer("in") && outB.checkPointer("out")) {
912 if (inB.remaining() < ETC1_ENCODED_BLOCK_SIZE) {
913 doThrowIAE(env, "in's remaining data < ENCODED_BLOCK_SIZE");
914 } else if (outB.remaining() < ETC1_DECODED_BLOCK_SIZE) {
915 doThrowIAE(env, "out's remaining data < DECODED_BLOCK_SIZE");
917 etc1_decode_block((etc1_byte*) inB.getData(),
918 (etc1_byte*) outB.getData());
924 * Return the size of the encoded image data (does not include size of PKM header).
926 static jint etc1_getEncodedDataSize(JNIEnv *env, jclass clazz,
927 jint width, jint height) {
928 return etc1_get_encoded_data_size(width, height);
932 * Encode an entire image.
933 * @param in pointer to the image data. Formatted such that
934 * pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset;
935 * @param out pointer to encoded data. Must be large enough to store entire encoded image.
937 static void etc1_encodeImage(JNIEnv *env, jclass clazz,
938 jobject in, jint width, jint height,
939 jint pixelSize, jint stride, jobject out) {
940 if (pixelSize < 2 || pixelSize > 3) {
941 doThrowIAE(env, "pixelSize must be 2 or 3");
944 BufferHelper inB(env, in);
945 BufferHelper outB(env, out);
946 if (inB.checkPointer("in") && outB.checkPointer("out")) {
947 jint imageSize = stride * height;
948 jint encodedImageSize = etc1_get_encoded_data_size(width, height);
949 if (inB.remaining() < imageSize) {
950 doThrowIAE(env, "in's remaining data < image size");
951 } else if (outB.remaining() < encodedImageSize) {
952 doThrowIAE(env, "out's remaining data < encoded image size");
954 etc1_encode_image((etc1_byte*) inB.getData(), width, height, pixelSize, stride,
955 (etc1_byte*) outB.getData());
961 * Decode an entire image.
962 * @param in the encoded data.
963 * @param out pointer to the image data. Will be written such that
964 * pixel (x,y) is at pIn + pixelSize * x + stride * y. Must be
965 * large enough to store entire image.
967 static void etc1_decodeImage(JNIEnv *env, jclass clazz,
968 jobject in, jobject out,
969 jint width, jint height,
970 jint pixelSize, jint stride) {
971 if (pixelSize < 2 || pixelSize > 3) {
972 doThrowIAE(env, "pixelSize must be 2 or 3");
975 BufferHelper inB(env, in);
976 BufferHelper outB(env, out);
977 if (inB.checkPointer("in") && outB.checkPointer("out")) {
978 jint imageSize = stride * height;
979 jint encodedImageSize = etc1_get_encoded_data_size(width, height);
980 if (inB.remaining() < encodedImageSize) {
981 doThrowIAE(env, "in's remaining data < encoded image size");
982 } else if (outB.remaining() < imageSize) {
983 doThrowIAE(env, "out's remaining data < image size");
985 etc1_decode_image((etc1_byte*) inB.getData(), (etc1_byte*) outB.getData(),
986 width, height, pixelSize, stride);
992 * Format a PKM header
994 static void etc1_formatHeader(JNIEnv *env, jclass clazz,
995 jobject header, jint width, jint height) {
996 BufferHelper headerB(env, header);
997 if (headerB.checkPointer("header") ){
998 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
999 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1001 etc1_pkm_format_header((etc1_byte*) headerB.getData(), width, height);
1007 * Check if a PKM header is correctly formatted.
1009 static jboolean etc1_isValid(JNIEnv *env, jclass clazz,
1011 jboolean result = false;
1012 BufferHelper headerB(env, header);
1013 if (headerB.checkPointer("header") ){
1014 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
1015 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1017 result = etc1_pkm_is_valid((etc1_byte*) headerB.getData());
1020 return result ? JNI_TRUE : JNI_FALSE;
1024 * Read the image width from a PKM header
1026 static jint etc1_getWidth(JNIEnv *env, jclass clazz,
1029 BufferHelper headerB(env, header);
1030 if (headerB.checkPointer("header") ){
1031 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
1032 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1034 result = etc1_pkm_get_width((etc1_byte*) headerB.getData());
1041 * Read the image height from a PKM header
1043 static jint etc1_getHeight(JNIEnv *env, jclass clazz,
1046 BufferHelper headerB(env, header);
1047 if (headerB.checkPointer("header") ){
1048 if (headerB.remaining() < ETC_PKM_HEADER_SIZE) {
1049 doThrowIAE(env, "header's remaining data < ETC_PKM_HEADER_SIZE");
1051 result = etc1_pkm_get_height((etc1_byte*) headerB.getData());
1061 static const JNINativeMethod gMatrixMethods[] = {
1062 { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM },
1063 { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV },
1066 static const JNINativeMethod gVisibilityMethods[] = {
1067 { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere },
1068 { "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres },
1069 { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest },
1072 static const JNINativeMethod gUtilsMethods[] = {
1073 { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat },
1074 { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType },
1075 { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D },
1076 { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D },
1079 static const JNINativeMethod gEtc1Methods[] = {
1080 { "encodeBlock", "(Ljava/nio/Buffer;ILjava/nio/Buffer;)V", (void*) etc1_encodeBlock },
1081 { "decodeBlock", "(Ljava/nio/Buffer;Ljava/nio/Buffer;)V", (void*) etc1_decodeBlock },
1082 { "getEncodedDataSize", "(II)I", (void*) etc1_getEncodedDataSize },
1083 { "encodeImage", "(Ljava/nio/Buffer;IIIILjava/nio/Buffer;)V", (void*) etc1_encodeImage },
1084 { "decodeImage", "(Ljava/nio/Buffer;Ljava/nio/Buffer;IIII)V", (void*) etc1_decodeImage },
1085 { "formatHeader", "(Ljava/nio/Buffer;II)V", (void*) etc1_formatHeader },
1086 { "isValid", "(Ljava/nio/Buffer;)Z", (void*) etc1_isValid },
1087 { "getWidth", "(Ljava/nio/Buffer;)I", (void*) etc1_getWidth },
1088 { "getHeight", "(Ljava/nio/Buffer;)I", (void*) etc1_getHeight },
1091 typedef struct _ClassRegistrationInfo {
1092 const char* classPath;
1093 const JNINativeMethod* methods;
1095 } ClassRegistrationInfo;
1097 static const ClassRegistrationInfo gClasses[] = {
1098 {"android/opengl/Matrix", gMatrixMethods, NELEM(gMatrixMethods)},
1099 {"android/opengl/Visibility", gVisibilityMethods, NELEM(gVisibilityMethods)},
1100 {"android/opengl/GLUtils", gUtilsMethods, NELEM(gUtilsMethods)},
1101 {"android/opengl/ETC1", gEtc1Methods, NELEM(gEtc1Methods)},
1104 int register_android_opengl_classes(JNIEnv* env)
1106 nativeClassInitBuffer(env);
1108 for (int i = 0; i < NELEM(gClasses); i++) {
1109 const ClassRegistrationInfo* cri = &gClasses[i];
1110 result = RegisterMethodsOrDie(env, cri->classPath, cri->methods, cri->methodCount);
1115 } // namespace android