manu
manu

Reputation: 197

Loading Texture in Android OpenglEs 2.0 result in no color at all

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: enter image description here

Cubes are bounding volumes for colliding system

enter image description here

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

Answers (1)

samgak
samgak

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

Related Questions