Reputation: 197
I had my working method before, but I saw that there was something that was not optimized. I was trying to upload textures with a RGB_565 or a ARGB_4_4_4_4 color scheme, but I was using 3 bytes for the first and 4 for the last one. In fact if I use RGB_565 I am needing only 2 bytes (5+6+5 bits), the same for the second. So I did my modifications but now it's not working, and for sure is something I forgot or did bad.
You can find the old code commented out in the function if you wish to compare. Any help would be appreciated.
This is the code:
///
// Load texture from InputStream
//
private int loadTexture(InputStream is) {
int[] textureId = new int[1];
Bitmap reversedBitmap, bitmap;
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inPreferredConfig = Bitmap.Config.RGB_565;
reversedBitmap = BitmapFactory.decodeStream(is, null, opts);
//reversedBitmap = BitmapFactory.decodeStream(is);
if (reversedBitmap == null) {
throw new RuntimeException("Texture.loadTexture: depuracion");
}
int width = reversedBitmap.getWidth();
int height = reversedBitmap.getHeight();
Matrix flip = new Matrix();
flip.postScale(1f, -1f);
bitmap = Bitmap.createBitmap(reversedBitmap, 0, 0, width, height, flip, true);
reversedBitmap.recycle();
// int bitmapFormat = bitmap.getConfig() == Bitmap.Config.ARGB_8888 ? GLES20.GL_RGBA : GLES20.GL_RGB;
int bitmapFormat = bitmap.getConfig() == Bitmap.Config.ARGB_4444 ? GLES20.GL_RGBA4 : GLES20.GL_RGB565;
byte[] buffer = null;
/*
if (bitmapFormat == GLES20.GL_RGB) {
buffer = new byte[width * height * 3];
} else if (bitmapFormat == GLES20.GL_RGBA) {
buffer = new byte[width * height * 4];
}
*/
if (bitmapFormat == GLES20.GL_RGB565) {
buffer = new byte[width * height * 2];
} else if (bitmapFormat == GLES20.GL_RGBA4) {
buffer = new byte[width * height * 2];
}
int[] pixelArray;
pixelArray = new int[width * height];
bitmap.getPixels(pixelArray, 0, width, 0, 0, width, height);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
/*
int pixel = pixelArray[x + y * width];
if (bitmapFormat == GLES20.GL_RGB) {
buffer[(y * width + x) * 3 + 0] = (byte) ((pixel >> 16) & 0xFF);
buffer[(y * width + x) * 3 + 1] = (byte) ((pixel >> 8) & 0xFF);
buffer[(y * width + x) * 3 + 2] = (byte) ((pixel >> 0) & 0xFF);
} else if (bitmapFormat == GLES20.GL_RGBA) {
buffer[(y * width + x) * 4 + 0] = (byte) ((pixel >> 16) & 0xFF);
buffer[(y * width + x) * 4 + 1] = (byte) ((pixel >> 8) & 0xFF);
buffer[(y * width + x) * 4 + 2] = (byte) ((pixel >> 0) & 0xFF);
// ALPHA CHANNEL
buffer[(y * width + x) * 4 + 3] = (byte) ((pixel >> 24) & 0xFF);
}
*/
int pixel = pixelArray[x + y * width];
if (bitmapFormat == GLES20.GL_RGB565) {
/*
buffer[(y * width + x) * 3 + 0] = (byte) ((pixel >> 11) & 0x1F); // color rojo empieza en el bit 11 y se debe hacer and logico con 1F pues ocupa 5 caracteres
buffer[(y * width + x) * 3 + 1] = (byte) ((pixel >> 5) & 0x3F);
buffer[(y * width + x) * 3 + 2] = (byte) ((pixel >> 0) & 0x1F);
*/
byte red = (byte) ((pixel >> 11) & 0x1F);
byte green = (byte) ((pixel >> 5) & 0x3F);
byte blue = (byte) ((pixel >> 0) & 0x1F);
// desplazamos red tres dígitos a la izquierda que es lo que queda libre del byte al ser red de 5 bits
byte first_byte = (byte) (red << 3);
// desplazamos tres bits a la derecha y aplicamos la máscara con and lógico
byte auxiliar_green_first_byte = (byte) ((green >> 3) & 0x7); // máscara 7 => 0000000111
// ya podemos calcular el primer byte
first_byte = (byte) (first_byte | auxiliar_green_first_byte);
// creamos un nuevo auxiliar para manejar la parte baja de green
// desplazamos la parte baja de green cinco bits y hacemos un and lógico con la máscara E0 para dejar hueco a blue
byte auxiliar_green_second_byte = (byte) ((green << 5) & 0xE0); // máscara E0 => 11100000
// ya podemos calcular el segundo byte = auxiliar_green_second_byte | blue;
byte second_byte = (byte) (auxiliar_green_second_byte | blue);
// almacenamos los resultados del pixel
buffer[(y * width + x) * 2 + 0] = first_byte;
buffer[(y * width + x) * 2 + 1] = second_byte;
} else if (bitmapFormat == GLES20.GL_RGBA4) {
/*
buffer[(y * width + x) * 4 + 0] = (byte) ((pixel >> 16) & 0xFF);
buffer[(y * width + x) * 4 + 1] = (byte) ((pixel >> 8) & 0xFF);
buffer[(y * width + x) * 4 + 2] = (byte) ((pixel >> 0) & 0xFF);
// ALPHA CHANNEL
buffer[(y * width + x) * 4 + 3] = (byte) ((pixel >> 24) & 0xFF);
*/
byte red = (byte) ((pixel >> 8) & 0xF);
byte green = (byte) ((pixel >> 4) & 0xF);
byte blue = (byte) ((pixel >> 0) & 0xF);
byte alpha = (byte) ((pixel >> 12) & 0xF);
// movemos red 4 bits a la izquierda y aplicamos máscara 11110000
byte first_byte = (byte) ((red << 4) & 0xF0);
// tras haber desplazado red procedemos a calcular definitivamente fist_byte con red or green
first_byte = (byte) (first_byte | green); // green ya está desplazado en los 4 últimos bits, no hace falta manipularlo
// movemos blue 4 bits a la izquierda y aplicamos máscara 11110000
byte second_byte = (byte) ((blue << 4) & 0xF0);
// tras haber desplazado blue procedemos a calcular definitivamente second byte con la operación nuevo blue OR LOGICO alpha
second_byte = (byte) (second_byte | alpha); // alpha ya está desplazado en los 4 últimos bits, no hace falta manipularlo
buffer[(y * width + x) * 2 + 0] = first_byte;
buffer[(y * width + x) * 2 + 1] = second_byte;
}
}
}
ByteBuffer byteBuffer = null;
/*
if (bitmapFormat == GLES20.GL_RGB) { // 3 bytes, 1 por canal
byteBuffer = ByteBuffer.allocateDirect(width * height * 3);
} else if (bitmapFormat == GLES20.GL_RGBA4) { // 4 bytes, 1 por canal
byteBuffer = ByteBuffer.allocateDirect(width * height * 4);
}
*/
if (bitmapFormat == GLES20.GL_RGB565) { // 3 bytes, 1 por canal
byteBuffer = ByteBuffer.allocateDirect(width * height * 2);
} else if (bitmapFormat == GLES20.GL_RGBA4) { // 4 bytes, 1 por canal
byteBuffer = ByteBuffer.allocateDirect(width * height * 2);
}
byteBuffer.put(buffer).position(0);
GLES20.glGenTextures(1, textureId, 0);
this.m_TextureID = textureId[0];
bind();
setFilters(this.m_MinifyFilter, this.m_MagnifyFilter);
// GLES20.glTexParameteri ( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR );
// GLES20.glTexParameteri ( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR );
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
/*
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, bitmapFormat, width, height, 0,
bitmapFormat, GLES20.GL_UNSIGNED_BYTE, byteBuffer);
*/
if (bitmapFormat == GLES20.GL_RGB565) {
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, bitmapFormat, width, height, 0,
bitmapFormat, GLES20.GL_UNSIGNED_SHORT_5_6_5, byteBuffer);
} else if (bitmapFormat == GLES20.GL_RGBA4) {
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, bitmapFormat, width, height, 0,
bitmapFormat, GLES20.GL_UNSIGNED_SHORT_4_4_4_4, byteBuffer);
}
// this.textureId = textureId[0];
pixelArray = null; // Para que el recolector libere el tamaño del array de la imagen en memoria
if ((m_MinifyFilter == GLES20.GL_LINEAR_MIPMAP_LINEAR) ||
(m_MinifyFilter == GLES20.GL_LINEAR_MIPMAP_NEAREST) ||
(m_MinifyFilter == GLES20.GL_NEAREST_MIPMAP_LINEAR) ||
(m_MinifyFilter == GLES20.GL_NEAREST_MIPMAP_NEAREST)) {
GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
}
unbind();
bitmap.recycle();
try {
is.close();
} catch (IOException e) {
Log.e("Texture.loadTexture: ", " error closing inputStream");
}
return textureId[0];
}
EDIT 1:
I´m editing because I see a very poor result converting a rgb888 texture to an rgb565 one. I followed the method suggested by @samgak, everything looks to work ok but the color is very distorted. I´m uploading two captures, the first one is with rgb888 and looks well, like it should do. The second one is the conversion with the distorsion. Is it normal in this kind of transformation? If it is, I will have to keep the texture in the full rgb888 format
Well drawn frame:
Cubes are bounding volumes for colliding system
Solved. The problem was I was getting the bytebuffer in the wrong byte order (endianless/big endian) and swapping the first and the second byte as @samgak suggested.
Upvotes: 2
Views: 1261
Reputation: 24427
GLES20.GL_RGB565
and GLES20.GL_RGBA4
are not valid values to pass to glTexImage2D:
internalformat Specifies the internal format of the texture. Must be one of the following symbolic constants: GL_ALPHA, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA.
When uploading an RGB 565 texture using glTexImage2D, you should pass GLES20.GL_RGB
for format and internalformat, and GLES20.GL_UNSIGNED_SHORT_5_6_5
for the type. Likewise for an RGBA 4444 texture, pass GLES20.GL_RGBA
and GLES20.GL_UNSIGNED_SHORT_4_4_4_4
e.g:
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, width, height, 0,
GLES20.GL_RGB, GLES20.GL_UNSIGNED_SHORT_5_6_5, byteBuffer);
and
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0,
GLES20.GL_RGBA, GLES20.GL_UNSIGNED_SHORT_4_4_4_4, byteBuffer);
GLES20.GL_RGB565
and GLES20.GL_RGBA4
are used for specifying the internal format of a render buffer in calls to glRenderbufferStorage
Upvotes: 2