|
|
|
@ -18,14 +18,13 @@ import java.util.logging.Logger; |
|
|
|
|
public class TextureUtil { |
|
|
|
|
|
|
|
|
|
private static final Logger logger = Logger.getLogger(TextureUtil.class.getName()); |
|
|
|
|
|
|
|
|
|
//TODO Make this configurable through appSettings
|
|
|
|
|
public static boolean ENABLE_COMPRESSION = true; |
|
|
|
|
private static boolean NPOT = false; |
|
|
|
|
private static boolean ETC1support = false; |
|
|
|
|
private static boolean DXT1 = false; |
|
|
|
|
private static boolean DEPTH24 = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static void loadTextureFeatures(String extensionString) { |
|
|
|
|
ETC1support = extensionString.contains("GL_OES_compressed_ETC1_RGB8_texture"); |
|
|
|
|
DEPTH24 = extensionString.contains("GL_OES_depth24"); |
|
|
|
@ -36,21 +35,21 @@ public class TextureUtil { |
|
|
|
|
logger.log(Level.FINE, "Supports NPOT? {0}", NPOT); |
|
|
|
|
logger.log(Level.FINE, "Supports DXT1? {0}", DXT1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static void buildMipmap(Bitmap bitmap, boolean compress) { |
|
|
|
|
int level = 0; |
|
|
|
|
int height = bitmap.getHeight(); |
|
|
|
|
int width = bitmap.getWidth(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger.log(Level.FINEST, " - Generating mipmaps for bitmap using SOFTWARE"); |
|
|
|
|
|
|
|
|
|
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (height >= 1 || width >= 1) { |
|
|
|
|
//First of all, generate the texture from our bitmap and set it to the according level
|
|
|
|
|
if (compress) { |
|
|
|
|
logger.log(Level.FINEST, " - Uploading LOD level {0} ({1}x{2}) with compression.", new Object[]{level, width, height}); |
|
|
|
|
uploadBitmapAsCompressed(GLES20.GL_TEXTURE_2D, level, bitmap); |
|
|
|
|
uploadBitmapAsCompressed(GLES20.GL_TEXTURE_2D, level, bitmap, false, 0, 0); |
|
|
|
|
} else { |
|
|
|
|
logger.log(Level.FINEST, " - Uploading LOD level {0} ({1}x{2}) directly.", new Object[]{level, width, height}); |
|
|
|
|
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, level, bitmap, 0); |
|
|
|
@ -67,20 +66,26 @@ public class TextureUtil { |
|
|
|
|
|
|
|
|
|
// Recycle any bitmaps created as a result of scaling the bitmap.
|
|
|
|
|
// Do not recycle the original image (mipmap level 0)
|
|
|
|
|
if (level != 0){ |
|
|
|
|
if (level != 0) { |
|
|
|
|
bitmap.recycle(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bitmap = bitmap2; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
level++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static void uploadBitmapAsCompressed(int target, int level, Bitmap bitmap) { |
|
|
|
|
private static void uploadBitmapAsCompressed(int target, int level, Bitmap bitmap, boolean subTexture, int x, int y) { |
|
|
|
|
if (bitmap.hasAlpha()) { |
|
|
|
|
logger.log(Level.FINEST, " - Uploading bitmap directly. Cannot compress as alpha present."); |
|
|
|
|
GLUtils.texImage2D(target, level, bitmap, 0); |
|
|
|
|
if (subTexture) { |
|
|
|
|
GLUtils.texSubImage2D(target, level, x, y, bitmap); |
|
|
|
|
checkGLError(); |
|
|
|
|
} else { |
|
|
|
|
GLUtils.texImage2D(target, level, bitmap, 0); |
|
|
|
|
checkGLError(); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
// Convert to RGB565
|
|
|
|
|
int bytesPerPixel = 2; |
|
|
|
@ -97,15 +102,15 @@ public class TextureUtil { |
|
|
|
|
// Encode the image into the output bytebuffer
|
|
|
|
|
int encodedImageSize = ETC1.getEncodedDataSize(bitmap.getWidth(), bitmap.getHeight()); |
|
|
|
|
ByteBuffer compressedImage = BufferUtils.createByteBuffer(encodedImageSize); |
|
|
|
|
ETC1.encodeImage(inputImage, bitmap.getWidth(), |
|
|
|
|
bitmap.getHeight(), |
|
|
|
|
bytesPerPixel, |
|
|
|
|
bytesPerPixel * bitmap.getWidth(), |
|
|
|
|
compressedImage); |
|
|
|
|
|
|
|
|
|
ETC1.encodeImage(inputImage, bitmap.getWidth(), |
|
|
|
|
bitmap.getHeight(), |
|
|
|
|
bytesPerPixel, |
|
|
|
|
bytesPerPixel * bitmap.getWidth(), |
|
|
|
|
compressedImage); |
|
|
|
|
|
|
|
|
|
// Delete the input image buffer
|
|
|
|
|
BufferUtils.destroyDirectBuffer(inputImage); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create an ETC1Texture from the compressed image data
|
|
|
|
|
ETC1Texture etc1tex = new ETC1Texture(bitmap.getWidth(), bitmap.getHeight(), compressedImage); |
|
|
|
|
|
|
|
|
@ -113,55 +118,53 @@ public class TextureUtil { |
|
|
|
|
if (bytesPerPixel == 2) { |
|
|
|
|
int oldSize = (bitmap.getRowBytes() * bitmap.getHeight()); |
|
|
|
|
int newSize = compressedImage.capacity(); |
|
|
|
|
logger.log(Level.FINEST, " - Uploading compressed image to GL, oldSize = {0}, newSize = {1}, ratio = {2}", new Object[]{oldSize, newSize, (float)oldSize/newSize}); |
|
|
|
|
GLES20.glCompressedTexImage2D(target, |
|
|
|
|
level, |
|
|
|
|
ETC1.ETC1_RGB8_OES, |
|
|
|
|
bitmap.getWidth(), |
|
|
|
|
bitmap.getHeight(), |
|
|
|
|
0, |
|
|
|
|
etc1tex.getData().capacity(), |
|
|
|
|
etc1tex.getData()); |
|
|
|
|
|
|
|
|
|
logger.log(Level.FINEST, " - Uploading compressed image to GL, oldSize = {0}, newSize = {1}, ratio = {2}", new Object[]{oldSize, newSize, (float) oldSize / newSize}); |
|
|
|
|
if (subTexture) { |
|
|
|
|
GLES20.glCompressedTexSubImage2D(target, |
|
|
|
|
level, |
|
|
|
|
x, y, |
|
|
|
|
bitmap.getWidth(), |
|
|
|
|
bitmap.getHeight(), |
|
|
|
|
ETC1.ETC1_RGB8_OES, |
|
|
|
|
etc1tex.getData().capacity(), |
|
|
|
|
etc1tex.getData()); |
|
|
|
|
checkGLError(); |
|
|
|
|
} else { |
|
|
|
|
GLES20.glCompressedTexImage2D(target, |
|
|
|
|
level, |
|
|
|
|
ETC1.ETC1_RGB8_OES, |
|
|
|
|
bitmap.getWidth(), |
|
|
|
|
bitmap.getHeight(), |
|
|
|
|
0, |
|
|
|
|
etc1tex.getData().capacity(), |
|
|
|
|
etc1tex.getData()); |
|
|
|
|
checkGLError(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ETC1Util.loadTexture(target, level, 0, GLES20.GL_RGB,
|
|
|
|
|
// GLES20.GL_UNSIGNED_SHORT_5_6_5, etc1Texture);
|
|
|
|
|
// } else if (bytesPerPixel == 3) {
|
|
|
|
|
// ETC1Util.loadTexture(target, level, 0, GLES20.GL_RGB,
|
|
|
|
|
// GLES20.GL_UNSIGNED_BYTE, etc1Texture);
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BufferUtils.destroyDirectBuffer(compressedImage); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* <code>uploadTextureBitmap</code> uploads a native android bitmap |
|
|
|
|
*/ |
|
|
|
|
public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean needMips) { |
|
|
|
|
uploadTextureBitmap(target, bitmap, needMips, false, 0, 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* <code>uploadTextureBitmap</code> uploads a native android bitmap |
|
|
|
|
*/ |
|
|
|
|
public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean needMips, boolean subTexture, int x, int y) { |
|
|
|
|
boolean recycleBitmap = false; |
|
|
|
|
if (!NPOT || needMips) { |
|
|
|
|
// Power of 2 images are not supported by this GPU.
|
|
|
|
|
// OR
|
|
|
|
|
// Mipmaps were requested to be used.
|
|
|
|
|
// Currently OGLES does not support NPOT textures with mipmaps.
|
|
|
|
|
int width = bitmap.getWidth(); |
|
|
|
|
int height = bitmap.getHeight(); |
|
|
|
|
|
|
|
|
|
// If the image is not power of 2, rescale it
|
|
|
|
|
if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height)) { |
|
|
|
|
// Scale to power of two.
|
|
|
|
|
width = FastMath.nearestPowerOfTwo(width); |
|
|
|
|
height = FastMath.nearestPowerOfTwo(height); |
|
|
|
|
|
|
|
|
|
logger.log(Level.WARNING, " - Image is not POT, so scaling it to new resolution: {0}x{1}", new Object[]{width, height}); |
|
|
|
|
Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true); |
|
|
|
|
bitmap = bitmap2; |
|
|
|
|
|
|
|
|
|
// Flag to indicate that bitmap
|
|
|
|
|
// should be recycled at the end.
|
|
|
|
|
recycleBitmap = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
//TODO, maybe this should raise an exception when NPOT is not supported
|
|
|
|
|
|
|
|
|
|
boolean willCompress = ENABLE_COMPRESSION && ETC1support && !bitmap.hasAlpha(); |
|
|
|
|
if (needMips && willCompress) { |
|
|
|
@ -172,29 +175,39 @@ public class TextureUtil { |
|
|
|
|
if (willCompress) { |
|
|
|
|
// Image is compressed but mipmaps are not desired, upload directly.
|
|
|
|
|
logger.log(Level.FINEST, " - Uploading compressed bitmap. Mipmaps are not generated."); |
|
|
|
|
uploadBitmapAsCompressed(target, 0, bitmap); |
|
|
|
|
uploadBitmapAsCompressed(target, 0, bitmap, subTexture, x, y); |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
// Image is not compressed, mipmaps may or may not be desired.
|
|
|
|
|
logger.log(Level.FINEST, " - Uploading bitmap directly.{0}", |
|
|
|
|
(needMips ? |
|
|
|
|
" Mipmaps will be generated in HARDWARE" : |
|
|
|
|
" Mipmaps are not generated.")); |
|
|
|
|
GLUtils.texImage2D(target, 0, bitmap, 0); |
|
|
|
|
logger.log(Level.FINEST, " - Uploading bitmap directly.{0}", |
|
|
|
|
(needMips |
|
|
|
|
? " Mipmaps will be generated in HARDWARE" |
|
|
|
|
: " Mipmaps are not generated.")); |
|
|
|
|
if (subTexture) { |
|
|
|
|
System.err.println("x : " + x + " y :" + y + " , " + bitmap.getWidth() + "/" + bitmap.getHeight()); |
|
|
|
|
GLUtils.texSubImage2D(target, 0, x, y, bitmap); |
|
|
|
|
checkGLError(); |
|
|
|
|
} else { |
|
|
|
|
GLUtils.texImage2D(target, 0, bitmap, 0); |
|
|
|
|
checkGLError(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (needMips) { |
|
|
|
|
// No pregenerated mips available,
|
|
|
|
|
// generate from base level if required
|
|
|
|
|
GLES20.glGenerateMipmap(target); |
|
|
|
|
checkGLError(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (recycleBitmap) { |
|
|
|
|
bitmap.recycle(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static void uploadTextureAny(Image img, int target, int index, boolean needMips) { |
|
|
|
|
if (img.getEfficentData() instanceof AndroidImageInfo){ |
|
|
|
|
if (img.getEfficentData() instanceof AndroidImageInfo) { |
|
|
|
|
logger.log(Level.FINEST, " === Uploading image {0}. Using BITMAP PATH === ", img); |
|
|
|
|
// If image was loaded from asset manager, use fast path
|
|
|
|
|
AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData(); |
|
|
|
@ -206,14 +219,14 @@ public class TextureUtil { |
|
|
|
|
logger.log(Level.WARNING, "Generating mipmaps is only" |
|
|
|
|
+ " supported for Bitmap based or non-compressed images!"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Upload using slower path
|
|
|
|
|
logger.log(Level.FINEST, " - Uploading bitmap directly.{0}", |
|
|
|
|
(wantGeneratedMips ? |
|
|
|
|
" Mipmaps will be generated in HARDWARE" : |
|
|
|
|
" Mipmaps are not generated.")); |
|
|
|
|
logger.log(Level.FINEST, " - Uploading bitmap directly.{0}", |
|
|
|
|
(wantGeneratedMips |
|
|
|
|
? " Mipmaps will be generated in HARDWARE" |
|
|
|
|
: " Mipmaps are not generated.")); |
|
|
|
|
uploadTexture(img, target, index); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Image was uploaded using slower path, since it is not compressed,
|
|
|
|
|
// then compress it
|
|
|
|
|
if (wantGeneratedMips) { |
|
|
|
@ -223,48 +236,14 @@ public class TextureUtil { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static void unsupportedFormat(Format fmt) { |
|
|
|
|
throw new UnsupportedOperationException("The image format '" + fmt + "' is unsupported by the video hardware."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static void uploadTexture(Image img, |
|
|
|
|
int target, |
|
|
|
|
int index){ |
|
|
|
|
|
|
|
|
|
if (img.getEfficentData() instanceof AndroidImageInfo){ |
|
|
|
|
throw new RendererException("This image uses efficient data. " |
|
|
|
|
+ "Use uploadTextureBitmap instead."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Otherwise upload image directly.
|
|
|
|
|
// Prefer to only use power of 2 textures here to avoid errors.
|
|
|
|
|
Image.Format fmt = img.getFormat(); |
|
|
|
|
ByteBuffer data; |
|
|
|
|
if (index >= 0 || img.getData() != null && img.getData().size() > 0){ |
|
|
|
|
data = img.getData(index); |
|
|
|
|
}else{ |
|
|
|
|
data = null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int width = img.getWidth(); |
|
|
|
|
int height = img.getHeight(); |
|
|
|
|
int depth = img.getDepth(); |
|
|
|
|
|
|
|
|
|
if (!NPOT) { |
|
|
|
|
// Check if texture is POT
|
|
|
|
|
if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height)) { |
|
|
|
|
throw new RendererException("Non-power-of-2 textures " |
|
|
|
|
+ "are not supported by the video hardware " |
|
|
|
|
+ "and no scaling path available for image: " + img); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
boolean compress = false; |
|
|
|
|
int format = -1; |
|
|
|
|
int dataType = -1; |
|
|
|
|
|
|
|
|
|
switch (fmt){ |
|
|
|
|
private static AndroidGLImageFormat getImageFormat(Format fmt) throws UnsupportedOperationException { |
|
|
|
|
AndroidGLImageFormat imageFormat = new AndroidGLImageFormat(); |
|
|
|
|
switch (fmt) { |
|
|
|
|
case RGBA16: |
|
|
|
|
case RGB16: |
|
|
|
|
case RGB10: |
|
|
|
@ -273,69 +252,110 @@ public class TextureUtil { |
|
|
|
|
case Alpha16: |
|
|
|
|
case Depth32: |
|
|
|
|
case Depth32F: |
|
|
|
|
throw new UnsupportedOperationException("The image format '" |
|
|
|
|
throw new UnsupportedOperationException("The image format '" |
|
|
|
|
+ fmt + "' is not supported by OpenGL ES 2.0 specification."); |
|
|
|
|
case Alpha8: |
|
|
|
|
format = GLES20.GL_ALPHA; |
|
|
|
|
dataType = GLES20.GL_UNSIGNED_BYTE; |
|
|
|
|
imageFormat.format = GLES20.GL_ALPHA; |
|
|
|
|
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE; |
|
|
|
|
break; |
|
|
|
|
case Luminance8: |
|
|
|
|
format = GLES20.GL_LUMINANCE; |
|
|
|
|
dataType = GLES20.GL_UNSIGNED_BYTE; |
|
|
|
|
imageFormat.format = GLES20.GL_LUMINANCE; |
|
|
|
|
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE; |
|
|
|
|
break; |
|
|
|
|
case Luminance8Alpha8: |
|
|
|
|
format = GLES20.GL_LUMINANCE_ALPHA; |
|
|
|
|
dataType = GLES20.GL_UNSIGNED_BYTE; |
|
|
|
|
imageFormat.format = GLES20.GL_LUMINANCE_ALPHA; |
|
|
|
|
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE; |
|
|
|
|
break; |
|
|
|
|
case RGB565: |
|
|
|
|
format = GLES20.GL_RGB; |
|
|
|
|
dataType = GLES20.GL_UNSIGNED_SHORT_5_6_5; |
|
|
|
|
imageFormat.format = GLES20.GL_RGB; |
|
|
|
|
imageFormat.dataType = GLES20.GL_UNSIGNED_SHORT_5_6_5; |
|
|
|
|
break; |
|
|
|
|
case ARGB4444: |
|
|
|
|
format = GLES20.GL_RGBA4; |
|
|
|
|
dataType = GLES20.GL_UNSIGNED_SHORT_4_4_4_4; |
|
|
|
|
imageFormat.format = GLES20.GL_RGBA4; |
|
|
|
|
imageFormat.dataType = GLES20.GL_UNSIGNED_SHORT_4_4_4_4; |
|
|
|
|
break; |
|
|
|
|
case RGB5A1: |
|
|
|
|
format = GLES20.GL_RGBA; |
|
|
|
|
dataType = GLES20.GL_UNSIGNED_SHORT_5_5_5_1; |
|
|
|
|
imageFormat.format = GLES20.GL_RGBA; |
|
|
|
|
imageFormat.dataType = GLES20.GL_UNSIGNED_SHORT_5_5_5_1; |
|
|
|
|
break; |
|
|
|
|
case RGB8: |
|
|
|
|
format = GLES20.GL_RGB; |
|
|
|
|
dataType = GLES20.GL_UNSIGNED_BYTE; |
|
|
|
|
imageFormat.format = GLES20.GL_RGB; |
|
|
|
|
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE; |
|
|
|
|
break; |
|
|
|
|
case BGR8: |
|
|
|
|
format = GLES20.GL_RGB; |
|
|
|
|
dataType = GLES20.GL_UNSIGNED_BYTE; |
|
|
|
|
imageFormat.format = GLES20.GL_RGB; |
|
|
|
|
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE; |
|
|
|
|
break; |
|
|
|
|
case RGBA8: |
|
|
|
|
format = GLES20.GL_RGBA; |
|
|
|
|
dataType = GLES20.GL_UNSIGNED_BYTE; |
|
|
|
|
imageFormat.format = GLES20.GL_RGBA; |
|
|
|
|
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE; |
|
|
|
|
break; |
|
|
|
|
case Depth: |
|
|
|
|
case Depth16: |
|
|
|
|
case Depth24: |
|
|
|
|
format = GLES20.GL_DEPTH_COMPONENT; |
|
|
|
|
dataType = GLES20.GL_UNSIGNED_BYTE; |
|
|
|
|
imageFormat.format = GLES20.GL_DEPTH_COMPONENT; |
|
|
|
|
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE; |
|
|
|
|
break; |
|
|
|
|
case DXT1: |
|
|
|
|
if (!DXT1) { |
|
|
|
|
unsupportedFormat(fmt); |
|
|
|
|
} |
|
|
|
|
format = 0x83F0; |
|
|
|
|
dataType = GLES20.GL_UNSIGNED_BYTE; |
|
|
|
|
compress = true; |
|
|
|
|
imageFormat.format = 0x83F0; |
|
|
|
|
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE; |
|
|
|
|
imageFormat.compress = true; |
|
|
|
|
break; |
|
|
|
|
case DXT1A: |
|
|
|
|
if (!DXT1) { |
|
|
|
|
unsupportedFormat(fmt); |
|
|
|
|
} |
|
|
|
|
format = 0x83F1; |
|
|
|
|
dataType = GLES20.GL_UNSIGNED_BYTE; |
|
|
|
|
compress = true; |
|
|
|
|
imageFormat.format = 0x83F1; |
|
|
|
|
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE; |
|
|
|
|
imageFormat.compress = true; |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
throw new UnsupportedOperationException("Unrecognized format: " + fmt); |
|
|
|
|
} |
|
|
|
|
return imageFormat; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static class AndroidGLImageFormat { |
|
|
|
|
|
|
|
|
|
boolean compress = false; |
|
|
|
|
int format = -1; |
|
|
|
|
int dataType = -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static void uploadTexture(Image img, |
|
|
|
|
int target, |
|
|
|
|
int index) { |
|
|
|
|
|
|
|
|
|
if (img.getEfficentData() instanceof AndroidImageInfo) { |
|
|
|
|
throw new RendererException("This image uses efficient data. " |
|
|
|
|
+ "Use uploadTextureBitmap instead."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Otherwise upload image directly.
|
|
|
|
|
// Prefer to only use power of 2 textures here to avoid errors.
|
|
|
|
|
Image.Format fmt = img.getFormat(); |
|
|
|
|
ByteBuffer data; |
|
|
|
|
if (index >= 0 || img.getData() != null && img.getData().size() > 0) { |
|
|
|
|
data = img.getData(index); |
|
|
|
|
} else { |
|
|
|
|
data = null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int width = img.getWidth(); |
|
|
|
|
int height = img.getHeight(); |
|
|
|
|
|
|
|
|
|
if (!NPOT) { |
|
|
|
|
// Check if texture is POT
|
|
|
|
|
if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height)) { |
|
|
|
|
throw new RendererException("Non-power-of-2 textures " |
|
|
|
|
+ "are not supported by the video hardware " |
|
|
|
|
+ "and no scaling path available for image: " + img); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
AndroidGLImageFormat imageFormat = getImageFormat(fmt); |
|
|
|
|
|
|
|
|
|
if (data != null) { |
|
|
|
|
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1); |
|
|
|
@ -343,81 +363,134 @@ public class TextureUtil { |
|
|
|
|
|
|
|
|
|
int[] mipSizes = img.getMipMapSizes(); |
|
|
|
|
int pos = 0; |
|
|
|
|
if (mipSizes == null){ |
|
|
|
|
if (data != null) |
|
|
|
|
mipSizes = new int[]{ data.capacity() }; |
|
|
|
|
else |
|
|
|
|
mipSizes = new int[]{ width * height * fmt.getBitsPerPixel() / 8 }; |
|
|
|
|
if (mipSizes == null) { |
|
|
|
|
if (data != null) { |
|
|
|
|
mipSizes = new int[]{data.capacity()}; |
|
|
|
|
} else { |
|
|
|
|
mipSizes = new int[]{width * height * fmt.getBitsPerPixel() / 8}; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// XXX: might want to change that when support
|
|
|
|
|
// of more than paletted compressions is added..
|
|
|
|
|
/// NOTE: Doesn't support mipmaps
|
|
|
|
|
// if (compress){
|
|
|
|
|
// data.clear();
|
|
|
|
|
// GLES20.glCompressedTexImage2D(target,
|
|
|
|
|
// 1 - mipSizes.length,
|
|
|
|
|
// format,
|
|
|
|
|
// width,
|
|
|
|
|
// height,
|
|
|
|
|
// 0,
|
|
|
|
|
// data.capacity(),
|
|
|
|
|
// data);
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < mipSizes.length; i++){ |
|
|
|
|
int mipWidth = Math.max(1, width >> i); |
|
|
|
|
for (int i = 0; i < mipSizes.length; i++) { |
|
|
|
|
int mipWidth = Math.max(1, width >> i); |
|
|
|
|
int mipHeight = Math.max(1, height >> i); |
|
|
|
|
// int mipDepth = Math.max(1, depth >> i);
|
|
|
|
|
|
|
|
|
|
if (data != null){ |
|
|
|
|
if (data != null) { |
|
|
|
|
data.position(pos); |
|
|
|
|
data.limit(pos + mipSizes[i]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (compress && data != null){ |
|
|
|
|
if (imageFormat.compress && data != null) { |
|
|
|
|
GLES20.glCompressedTexImage2D(target, |
|
|
|
|
i, |
|
|
|
|
format, |
|
|
|
|
mipWidth, |
|
|
|
|
mipHeight, |
|
|
|
|
0, |
|
|
|
|
data.remaining(), |
|
|
|
|
data); |
|
|
|
|
}else{ |
|
|
|
|
i, |
|
|
|
|
imageFormat.format, |
|
|
|
|
mipWidth, |
|
|
|
|
mipHeight, |
|
|
|
|
0, |
|
|
|
|
data.remaining(), |
|
|
|
|
data); |
|
|
|
|
} else { |
|
|
|
|
GLES20.glTexImage2D(target, |
|
|
|
|
i, |
|
|
|
|
format, |
|
|
|
|
mipWidth, |
|
|
|
|
mipHeight, |
|
|
|
|
0, |
|
|
|
|
format, |
|
|
|
|
dataType, |
|
|
|
|
data); |
|
|
|
|
i, |
|
|
|
|
imageFormat.format, |
|
|
|
|
mipWidth, |
|
|
|
|
mipHeight, |
|
|
|
|
0, |
|
|
|
|
imageFormat.format, |
|
|
|
|
imageFormat.dataType, |
|
|
|
|
data); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pos += mipSizes[i]; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static void checkGLError() { |
|
|
|
|
int error; |
|
|
|
|
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { |
|
|
|
|
throw new RendererException("OpenGL Error " + error); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Update the texture currently bound to target at with data from the given Image at position x and y. The parameter |
|
|
|
|
* index is used as the zoffset in case a 3d texture or texture 2d array is being updated. |
|
|
|
|
* Update the texture currently bound to target at with data from the given |
|
|
|
|
* Image at position x and y. The parameter index is used as the zoffset in |
|
|
|
|
* case a 3d texture or texture 2d array is being updated. |
|
|
|
|
* |
|
|
|
|
* @param image Image with the source data (this data will be put into the texture) |
|
|
|
|
* @param image Image with the source data (this data will be put into the |
|
|
|
|
* texture) |
|
|
|
|
* @param target the target texture |
|
|
|
|
* @param index the mipmap level to update |
|
|
|
|
* @param x the x position where to put the image in the texture |
|
|
|
|
* @param y the y position where to put the image in the texture |
|
|
|
|
*/ |
|
|
|
|
public static void uploadSubTexture( |
|
|
|
|
Image image, |
|
|
|
|
int target, |
|
|
|
|
int index, |
|
|
|
|
int x, |
|
|
|
|
int y) { |
|
|
|
|
// FIXME and implement this!
|
|
|
|
|
} |
|
|
|
|
Image img, |
|
|
|
|
int target, |
|
|
|
|
int index, |
|
|
|
|
int x, |
|
|
|
|
int y) { |
|
|
|
|
if (img.getEfficentData() instanceof AndroidImageInfo) { |
|
|
|
|
AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData(); |
|
|
|
|
uploadTextureBitmap(target, imageInfo.getBitmap(), true, true, x, y); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Otherwise upload image directly.
|
|
|
|
|
// Prefer to only use power of 2 textures here to avoid errors.
|
|
|
|
|
Image.Format fmt = img.getFormat(); |
|
|
|
|
ByteBuffer data; |
|
|
|
|
if (index >= 0 || img.getData() != null && img.getData().size() > 0) { |
|
|
|
|
data = img.getData(index); |
|
|
|
|
} else { |
|
|
|
|
data = null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int width = img.getWidth(); |
|
|
|
|
int height = img.getHeight(); |
|
|
|
|
|
|
|
|
|
if (!NPOT) { |
|
|
|
|
// Check if texture is POT
|
|
|
|
|
if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height)) { |
|
|
|
|
throw new RendererException("Non-power-of-2 textures " |
|
|
|
|
+ "are not supported by the video hardware " |
|
|
|
|
+ "and no scaling path available for image: " + img); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
AndroidGLImageFormat imageFormat = getImageFormat(fmt); |
|
|
|
|
|
|
|
|
|
if (data != null) { |
|
|
|
|
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int[] mipSizes = img.getMipMapSizes(); |
|
|
|
|
int pos = 0; |
|
|
|
|
if (mipSizes == null) { |
|
|
|
|
if (data != null) { |
|
|
|
|
mipSizes = new int[]{data.capacity()}; |
|
|
|
|
} else { |
|
|
|
|
mipSizes = new int[]{width * height * fmt.getBitsPerPixel() / 8}; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < mipSizes.length; i++) { |
|
|
|
|
int mipWidth = Math.max(1, width >> i); |
|
|
|
|
int mipHeight = Math.max(1, height >> i); |
|
|
|
|
|
|
|
|
|
if (data != null) { |
|
|
|
|
data.position(pos); |
|
|
|
|
data.limit(pos + mipSizes[i]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (imageFormat.compress && data != null) { |
|
|
|
|
GLES20.glCompressedTexSubImage2D(target, i, x, y, mipWidth, mipHeight, imageFormat.format, data.remaining(), data); |
|
|
|
|
checkGLError(); |
|
|
|
|
} else { |
|
|
|
|
GLES20.glTexSubImage2D(target, i, x, y, mipWidth, mipHeight, imageFormat.format, imageFormat.dataType, data); |
|
|
|
|
checkGLError(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pos += mipSizes[i]; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|