Jani
Jani

Reputation: 190

Problems reading ATI compressed textures

I'm trying to use compressed textures on my android application. I have a problem loading textures, textures seem to be "cut-off" on the right side of the object.

For compressing textures I'm using ATI's "TheCompressonator". For testing I'm using Nexus 5.

I'm suspecting the problem is with my "calculated" size of the texture, but can't find any refenreces / specification of this compression format.

Does anyone know how to properly read this file format?

Here is screenshot from nexus:

Nexus 5, blowfish right side is cut off

Here is how it should have looked (don't mind black object textures, the image for that was missing) Samsung tab 2, this is how blowfish should look

Here is my code snippet.

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);

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

    int height = bf.getInt(16);
    int width = bf.getInt(12);


    int size = ((height + 3) / 4) * ((width + 3) / 4) * 16;
    Log.d("TextureHelper","Buffer size: "+width+" "+height+" "+size);

    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,ATC_RGBA_EXPLICIT_ALPHA_AMD, width, height, 0, size, bf);
    Log.d("TextureHelper","Buffer size: "+bf.capacity()+"   : "+buffer.length+" error:"+GLES20.glGetError());

    glBindTexture(GL_TEXTURE_2D, 0); //unbind texture

} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

return textureObjectIds[0];

EDIT: Solution

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

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

            int height = bf.getInt(16);
            int width = bf.getInt(12);


            int size = ((height + 3) / 4) * ((width + 3) / 4) * 16;
            Log.d("TextureHelper","Buffer size: "+width+" "+height+" "+size);


            bf.compact();///////SOLUTION!
            bf.position(0);


            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,ATC_RGBA_EXPLICIT_ALPHA_AMD, width, height, 0, size, bf);
            Log.d("TextureHelper","Buffer size: "+bf.capacity()+"   : "+buffer.length+" error:"+GLES20.glGetError());

            glBindTexture(GL_TEXTURE_2D, 0); //unbind texture

Note: You need to have data at 0 position, if you just offset position(128) it will throw invalid pointer exception.

Upvotes: 0

Views: 131

Answers (1)

PsychoBoyJack
PsychoBoyJack

Reputation: 66

Your size computation looks correct, but you're uploading the header as image data. After you extract the width and height from the header, offset your byte buffer by the appropriate amount to skip the header and start pointing at the image data.

There are a few ways you could do this, but something like this may work as an example (this could be optimized to remove the second bytebuffer). Also, I'm not sure what texture format you're using, but let's assume your header is, oh, 124 bytes long.

// assumption that buffer[] is the fully loaded contents of the file
byte[] buffer = ... // load entire file from input stream

// read the header
ByteBuffer bfHeader = ByteBuffer.wrap(buffer);
bfHeader.order(ByteOrder.LITTLE_ENDIAN);

int height = bfHeader.getInt(16);
int width = bfHeader.getInt(12);

// offset to image data
int headerSize = 124; // (replace with correct header size)
ByteBuffer bfData = ByteBuffer.wrap(buffer, headerSize, buffer.length-headerSize);
bfData.order(ByteOrder.LITTLE_ENDIAN);

// load image data
int size = ((height + 3) / 4) * ((width + 3) / 4) * 16;
glBindTexture(GL_TEXTURE_2D, textureObjectIds[0]);
GLES20.glCompressedTexImage2D(GL_TEXTURE_2D, 0,ATC_RGBA_EXPLICIT_ALPHA_AMD, width, height, 0, size, bfData);

Upvotes: 1

Related Questions