1
votes

I'm trying to optimize my app by having compressed textures. Since a lot of my textures require alpha I can't use ETC1.

I've successfully compressed textures to other formats using hints from this post. Android OpenGL Texture Compression

Problem is I can't seem to adapt my code, to read this textures. The only thing I get with this code is black (colour, instead of texture).

Here is the method, that loads textures:

public static int loadCompressedTexture(Context context, int resourceId){
    final int[] textureObjectIds = new int[1];

    glGenTextures(1, textureObjectIds, 0);

    if(textureObjectIds[0] == 0){
        logTextureHelper(Log.WARN, "Could not generate a new OpenGL texture object");
        return 0;
    }

    final InputStream bitmap = context.getResources().openRawResource(resourceId);
    byte[] buffer;
    ByteBuffer bf;

    try {
        buffer = new byte[bitmap.available()];
        bitmap.read(buffer);
        bf  = ByteBuffer.wrap(buffer);

        glBindTexture(GL_TEXTURE_2D, textureObjectIds[0]);//binds texture to texture object

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);//minimization filter
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        //texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);//send texture data to OpenGL to the CURRENTLY BOUND object
        GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, 0, GL10.GL_PALETTE4_RGBA8_OES, 512, 512, 0, bf.capacity(), bf);


        //glGenerateMipmap(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, 0); //unbind texture
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    return textureObjectIds[0];
}

I currently trying to load resource that is 512x512px, compressed with PvrTexTool, using PVRTC 4bpp RGBA encoding.

Can anyone see what the problem is? Or better yet, point me to an example that works? From what I found I could only find examples for ETC1, who use some ETCUtil to load textures.

EDIT2: Ok I've solved it. There were 2 problems. First you need to use these texture filters.

GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

Secondly I was not aware that pvrtc had a seperate header. The correct offset is thus 67 bytes. PVRTC format specification

This code now works correctly:

            buffer = new byte[bitmap.available()];
            bitmap.read(buffer);

            int offset = 67; // 52 bit = header, 15 bit = metadata
            bf = ByteBuffer.wrap(buffer, offset, buffer.length-offset);
            bf.order(ByteOrder.LITTLE_ENDIAN);

            long version     = bf.getInt(0) & 0xFFFFFFFFL;
            long flags       = bf.getInt(4);
            long pixelFormat = bf.getLong(8);
            long colorF      = bf.getInt(16);
            long chanel      = bf.getInt(20);
            int height       = bf.getInt(24);
            int width        = bf.getInt(28);
            long depth       = bf.getInt(32);
            long nsurf       = bf.getInt(36);
            long nface       = bf.getInt(40);
            long mipC        = bf.getInt(44);
            long mSize       = bf.getInt(48);
            long fourCC      = bf.getInt(52)& 0xFFFFFFFFL;
            long key         = bf.getInt(56)& 0xFFFFFFFFL;
            long dataSize    = bf.getInt(60)& 0xFFFFFFFFL;



//              Log.d("TextureHelper","Buffer size: "+version+" "+flags+" "+pixelFormat+" "+colorF+" "+chanel+" "+height+" w: "+width+" h: "+height+" "+depth+" "+nsurf+" "+nface+" "+mipC+" "+mSize);
//              Log.d("TextureHelper","Buffer size: "+fourCC+" "+key+" "+dataSize);


            glBindTexture(GL_TEXTURE_2D, textureObjectIds[0]);

            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
            glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
            glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);


            GLES20.glCompressedTexImage2D(GL_TEXTURE_2D, 0,GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, width, height, 0, bf.capacity()-offset, bf);
            Log.d("TextureHelper","Buffer size: "+bf.capacity()+"   : "+buffer.length+" error:"+GLES20.glGetError());

            glBindTexture(GL_TEXTURE_2D, 0); //unbind texture
1
The available() method on InputStream is not guaranteed to give the entire length of the resource. Can you check what it returns, and make sure that it matches the length of the file?Reto Koradi
I've checked. The size is the same in windows file properties in byte[] buffer and in ByteBuffer bf, it is 131139 bytes.Jani

1 Answers

2
votes

You want to load PVRTC 4bpp RGBA texture via glCompressedTexImage2D? You should use COMPRESSED_RGBA_PVRTC_4BPPV1_IMG instead of GL10.GL_PALETTE4_RGBA8_OES.

IMG_texture_compression_pvrtc https://www.khronos.org/registry/gles/extensions/IMG/IMG_texture_compression_pvrtc.txt

I'm not sure but it seems Android GLES20 doesn't have those constants, so you need to define it.

// PowerVR Texture compression constants
public static final int GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00;
public static final int GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8C01;
public static final int GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02;
public static final int GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03;

And you should check glGetError after glCompressedTexImage2D calling. The value of glGetError should be GL_NO_ERROR.