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 convertTextureFormat(Format fmt)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 buildMipmap(Bitmap bitmap)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 */ uploadTextureBitmap(final int target, Bitmap bitmap, boolean generateMips, boolean powerOf2)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 uploadTexture( Image img, int target, int index, int border, boolean tdc, boolean generateMips, boolean powerOf2)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