1 package com.jme3.renderer.android; 2 3 import android.graphics.Bitmap; 4 import android.opengl.GLES20; 5 import android.opengl.GLUtils; 6 import com.jme3.asset.AndroidImageInfo; 7 import com.jme3.math.FastMath; 8 import com.jme3.texture.Image; 9 import com.jme3.texture.Image.Format; 10 import java.nio.ByteBuffer; 11 import javax.microedition.khronos.opengles.GL10; 12 13 public class TextureUtil { 14 15 public static int convertTextureFormat(Format fmt){ 16 switch (fmt){ 17 case Alpha16: 18 case Alpha8: 19 return GL10.GL_ALPHA; 20 case Luminance8Alpha8: 21 case Luminance16Alpha16: 22 return GL10.GL_LUMINANCE_ALPHA; 23 case Luminance8: 24 case Luminance16: 25 return GL10.GL_LUMINANCE; 26 case RGB10: 27 case RGB16: 28 case BGR8: 29 case RGB8: 30 case RGB565: 31 return GL10.GL_RGB; 32 case RGB5A1: 33 case RGBA16: 34 case RGBA8: 35 return GL10.GL_RGBA; 36 37 case Depth: 38 return GLES20.GL_DEPTH_COMPONENT; 39 case Depth16: 40 return GLES20.GL_DEPTH_COMPONENT16; 41 case Depth24: 42 case Depth32: 43 case Depth32F: 44 throw new UnsupportedOperationException("Unsupported depth format: " + fmt); 45 46 case DXT1A: 47 throw new UnsupportedOperationException("Unsupported format: " + fmt); 48 default: 49 throw new UnsupportedOperationException("Unrecognized format: " + fmt); 50 } 51 } 52 53 private static void buildMipmap(Bitmap bitmap) { 54 int level = 0; 55 int height = bitmap.getHeight(); 56 int width = bitmap.getWidth(); 57 58 while (height >= 1 || width >= 1) { 59 //First of all, generate the texture from our bitmap and set it to the according level 60 GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0); 61 62 if (height == 1 || width == 1) { 63 break; 64 } 65 66 //Increase the mipmap level 67 level++; 68 69 height /= 2; 70 width /= 2; 71 Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true); 72 73 //bitmap.recycle(); 74 bitmap = bitmap2; 75 } 76 } 77 78 /** 79 * <code>uploadTextureBitmap</code> uploads a native android bitmap 80 * @param target 81 * @param bitmap 82 * @param generateMips 83 * @param powerOf2 84 */ 85 public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean generateMips, boolean powerOf2) 86 { 87 if (!powerOf2) 88 { 89 int width = bitmap.getWidth(); 90 int height = bitmap.getHeight(); 91 if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height)) 92 { 93 // scale to power of two 94 width = FastMath.nearestPowerOfTwo(width); 95 height = FastMath.nearestPowerOfTwo(height); 96 Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true); 97 //bitmap.recycle(); 98 bitmap = bitmap2; 99 } 100 } 101 102 if (generateMips) 103 { 104 buildMipmap(bitmap); 105 } 106 else 107 { 108 GLUtils.texImage2D(target, 0, bitmap, 0); 109 //bitmap.recycle(); 110 } 111 } 112 113 public static void uploadTexture( 114 Image img, 115 int target, 116 int index, 117 int border, 118 boolean tdc, 119 boolean generateMips, 120 boolean powerOf2){ 121 122 if (img.getEfficentData() instanceof AndroidImageInfo){ 123 // If image was loaded from asset manager, use fast path 124 AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData(); 125 uploadTextureBitmap(target, imageInfo.getBitmap(), generateMips, powerOf2); 126 return; 127 } 128 129 // Otherwise upload image directly. 130 // Prefer to only use power of 2 textures here to avoid errors. 131 132 Image.Format fmt = img.getFormat(); 133 ByteBuffer data; 134 if (index >= 0 || img.getData() != null && img.getData().size() > 0){ 135 data = img.getData(index); 136 }else{ 137 data = null; 138 } 139 140 int width = img.getWidth(); 141 int height = img.getHeight(); 142 int depth = img.getDepth(); 143 144 boolean compress = false; 145 int internalFormat = -1; 146 int format = -1; 147 int dataType = -1; 148 149 switch (fmt){ 150 case Alpha16: 151 case Alpha8: 152 format = GLES20.GL_ALPHA; 153 dataType = GLES20.GL_UNSIGNED_BYTE; 154 break; 155 case Luminance8: 156 format = GLES20.GL_LUMINANCE; 157 dataType = GLES20.GL_UNSIGNED_BYTE; 158 break; 159 case Luminance8Alpha8: 160 format = GLES20.GL_LUMINANCE_ALPHA; 161 dataType = GLES20.GL_UNSIGNED_BYTE; 162 break; 163 case Luminance16Alpha16: 164 format = GLES20.GL_LUMINANCE_ALPHA; 165 dataType = GLES20.GL_UNSIGNED_BYTE; 166 break; 167 case Luminance16: 168 format = GLES20.GL_LUMINANCE; 169 dataType = GLES20.GL_UNSIGNED_BYTE; 170 break; 171 case RGB565: 172 format = GLES20.GL_RGB; 173 internalFormat = GLES20.GL_RGB565; 174 dataType = GLES20.GL_UNSIGNED_SHORT_5_6_5; 175 break; 176 case ARGB4444: 177 format = GLES20.GL_RGBA; 178 dataType = GLES20.GL_UNSIGNED_SHORT_4_4_4_4; 179 break; 180 case RGB10: 181 format = GLES20.GL_RGB; 182 dataType = GLES20.GL_UNSIGNED_BYTE; 183 break; 184 case RGB16: 185 format = GLES20.GL_RGB; 186 dataType = GLES20.GL_UNSIGNED_BYTE; 187 break; 188 case RGB5A1: 189 format = GLES20.GL_RGBA; 190 internalFormat = GLES20.GL_RGB5_A1; 191 dataType = GLES20.GL_UNSIGNED_SHORT_5_5_5_1; 192 break; 193 case RGB8: 194 format = GLES20.GL_RGB; 195 dataType = GLES20.GL_UNSIGNED_BYTE; 196 break; 197 case BGR8: 198 format = GLES20.GL_RGB; 199 dataType = GLES20.GL_UNSIGNED_BYTE; 200 break; 201 case RGBA16: 202 format = GLES20.GL_RGBA; 203 internalFormat = GLES20.GL_RGBA4; 204 dataType = GLES20.GL_UNSIGNED_BYTE; 205 break; 206 case RGBA8: 207 format = GLES20.GL_RGBA; 208 dataType = GLES20.GL_UNSIGNED_BYTE; 209 break; 210 case DXT1A: 211 format = GLES20.GL_COMPRESSED_TEXTURE_FORMATS; 212 dataType = GLES20.GL_UNSIGNED_BYTE; 213 case Depth: 214 format = GLES20.GL_DEPTH_COMPONENT; 215 dataType = GLES20.GL_UNSIGNED_BYTE; 216 break; 217 case Depth16: 218 format = GLES20.GL_DEPTH_COMPONENT; 219 internalFormat = GLES20.GL_DEPTH_COMPONENT16; 220 dataType = GLES20.GL_UNSIGNED_BYTE; 221 break; 222 case Depth24: 223 case Depth32: 224 case Depth32F: 225 throw new UnsupportedOperationException("Unsupported depth format: " + fmt); 226 default: 227 throw new UnsupportedOperationException("Unrecognized format: " + fmt); 228 } 229 230 if (internalFormat == -1) 231 { 232 internalFormat = format; 233 } 234 235 if (data != null) 236 GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1); 237 238 int[] mipSizes = img.getMipMapSizes(); 239 int pos = 0; 240 if (mipSizes == null){ 241 if (data != null) 242 mipSizes = new int[]{ data.capacity() }; 243 else 244 mipSizes = new int[]{ width * height * fmt.getBitsPerPixel() / 8 }; 245 } 246 247 // XXX: might want to change that when support 248 // of more than paletted compressions is added.. 249 if (compress){ 250 data.clear(); 251 GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, 252 1 - mipSizes.length, 253 format, 254 width, 255 height, 256 0, 257 data.capacity(), 258 data); 259 return; 260 } 261 262 for (int i = 0; i < mipSizes.length; i++){ 263 int mipWidth = Math.max(1, width >> i); 264 int mipHeight = Math.max(1, height >> i); 265 int mipDepth = Math.max(1, depth >> i); 266 267 if (data != null){ 268 data.position(pos); 269 data.limit(pos + mipSizes[i]); 270 } 271 272 if (compress && data != null){ 273 GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, 274 i, 275 format, 276 mipWidth, 277 mipHeight, 278 0, 279 data.remaining(), 280 data); 281 }else{ 282 GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 283 i, 284 internalFormat, 285 mipWidth, 286 mipHeight, 287 0, 288 format, 289 dataType, 290 data); 291 } 292 293 pos += mipSizes[i]; 294 } 295 } 296 297 } 298