I have a BGR BufferedImage, how do I convert it to an ARGB BufferedImage? (I need transparency)
You know what, I actually got it to convert, it was actually easy, and I put the data contents in a ByteBuffer.
But when I pass this ByteBuffer as a parameter to make a texture, which is my intention, JOGL doesn’t “like” the ARGB format and I get a screwed-up image. How can I make it display an ARGB image onto a texture?
Here is my code to make the ByteBuffer from the ARGB BufferedImage: (probably wrong)
int[] dataInts = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
data = ByteBuffer.allocateDirect(dataInts.length * BufferUtils.SIZEOF_INT);
data.order(ByteOrder.nativeOrder());
data.asIntBuffer().put(dataInts, 0, dataInts.length);
break;
Here is the call using this buffer:
if(mipmapped)
glu.gluBuild2DMipmaps(target, GL.GL_RGB8, img.getWidth(), img.getHeight(), GL.GL_RGB, GL.GL_UNSIGNED_BYTE, dest);
else
gl.glTexImage2D(target, 0, GL.GL_RGB, img.getWidth(), img.getHeight(), 0, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, dest);
That’s because OpenGL wants RGBA, nor ARGB. You need to do some byt shuffling when you copy the stuff into the buffer. Here’s the routines we use:
protected ByteBuffer convertImage(BufferedImage img, boolean invert)
{
ByteBuffer ret_val = null;
Raster raster = img.getRaster();
DataBuffer buffer = raster.getDataBuffer();
byte[] b_data;
int[] i_data;
byte[] tmp = new byte[4];
int height = img.getHeight(null);
int width = img.getWidth(null);
// OpenGL only like RGBA, not ARGB. Flip the order where necessary.
// Also, some cards don't like dealing with BGR textures, so
// automatically flip the bytes around the RGB for those that have it
// reversed.
switch(img.getType())
{
case BufferedImage.TYPE_4BYTE_ABGR:
b_data = ((DataBufferByte)buffer).getData();
ret_val = ByteBuffer.allocateDirect(b_data.length);
ret_val.order(ByteOrder.nativeOrder());
if(invert)
{
int row_size = width * 4;
int offset = b_data.length - row_size;
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++)
{
tmp[0] = b_data[offset + j * 4 + 3];
tmp[1] = b_data[offset + j * 4 + 2];
tmp[2] = b_data[offset + j * 4 + 1];
tmp[3] = b_data[offset + j * 4];
ret_val.put(tmp, 0, 4);
}
offset -= row_size;
}
}
else
{
int row_size = width * 4;
int offset = 0;
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++)
{
tmp[0] = b_data[offset + j * 4 + 1];
tmp[1] = b_data[offset + j * 4 + 2];
tmp[2] = b_data[offset + j * 4 + 3];
tmp[3] = b_data[offset + j * 4];
ret_val.put(tmp, 0, 4);
}
offset += row_size;
}
}
break;
case BufferedImage.TYPE_3BYTE_BGR:
b_data = ((DataBufferByte)buffer).getData();
ret_val = ByteBuffer.allocateDirect(b_data.length);
ret_val.order(ByteOrder.nativeOrder());
// Force the format change.
format = FORMAT_RGB;
if(invert)
{
int row_size = width * 3;
int offset = b_data.length - row_size;
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++)
{
tmp[0] = b_data[offset + j * 3 + 2];
tmp[1] = b_data[offset + j * 3 + 1];
tmp[2] = b_data[offset + j * 3];
ret_val.put(tmp, 0, 3);
}
offset -= row_size;
}
}
else
ret_val.put(b_data, 0, b_data.length);
break;
case BufferedImage.TYPE_INT_RGB:
i_data = ((DataBufferInt)buffer).getData();
ret_val = ByteBuffer.allocateDirect(i_data.length * 4);
ret_val.order(ByteOrder.nativeOrder());
if(invert)
{
IntBuffer buf = ret_val.asIntBuffer();
int offset = i_data.length - width;
for(int i = 0; i < height; i++)
{
buf.put(i_data, offset, width);
offset -= width;
}
}
else
{
IntBuffer buf = ret_val.asIntBuffer();
buf.put(i_data, 0, i_data.length);
}
break;
case BufferedImage.TYPE_INT_ARGB:
System.out.println("ARGB conversion not implemented yet");
break;
default:
System.out.println("Unsupported image type " + img.getType());
}
return ret_val;
}
I used an ABGR version of my image with that code you posted, but the image is badly distorted. Do you have any idea what could be wrong? The only line I had to take out from your code was “format = FORMAT_RGB” because I did not know of neither of those variables and neither did the compiler.
When something like that happens, it is usually because one byte is off somewhere. Possible reasons for this may be you didn’t swap the position of the A component - notice that you have ABGR, but OpenGL only like RGBA or BGRA (though I’ve not had any of the BGR forms work on my ATI video cards). Another thing to look for is which buffer type - IntBuffer or ByteBuffer and that you are passing the correct format type to GL. In your glTexImage2D call, make sure you are passing GL.GL_RGBA, not GL.GL_RGB as the constant.
That said, I’ve changed our code quite a bit since this example posted above to now use getRGB() directly from BufferedImage, so that it does the base colour conversion for me.
hmmm… I don’t think any of those are my problem. Here’s what I’m doing:
ByteBuffer data = convertImage(ABGRBufferedImage, true);
int target = GL.GL_TEXTURE_2D;
if(mipmapped)
glu.gluBuild2DMipmaps(target, GL.GL_RGBA, img.getWidth(), img.getHeight(), GL.GL_RGB, GL.GL_UNSIGNED_BYTE, dest);
else
gl.glTexImage2D(target, 0, GL.GL_RGBA, img.getWidth(), img.getHeight(), 0, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, dest);
…
//when using
gl.glEnable(GL.GL_TEXTURE_2D);
//gl.glBlendFunc(GL.GL_ONE, GL.GL_SRC_ALPHA);
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
gl.glEnable(GL.GL_BLEND);
gl.glBindTexture(GL.GL_TEXTURE_2D, thisTexturesNumber);
The first thing that springs to mind is the gluBuild2DMipMaps call. Do you really want to make RGB output images from RGBA input? Check the args there to make sure that is what you intended to do.
hmmm… no luck…
would you happen to have any jogl source code that uses transparency in textures? I would really appreciate it
The conversion of any (preloaded) type of image, transparent or not, can be really easy, if you just draw it onto a BufferedImage that already has the correct format. This conversion could be somewhat slow (don’t know), but textures are often created in advance, when speed is not yet an issue.
public static byte[] awtToGL(Image image) {
int width = image.getWidth(null);
int height = image.getHeight(null);
BufferedImage converter = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
Graphics2D drawer = converter.createGraphics();
drawer.drawImage(image, 0, 0, null);
int[] ints = converter.getRGB(0, 0, width, height, null, 0,
width);
byte[] bytes = new byte[width * height * 4];
for (int i = 0, j = 0; i < ints.length;) {
int color = ints[i++];
bytes[j++] = (byte) ((color & 0x00FF0000) >> 16);
bytes[j++] = (byte) ((color & 0x0000FF00) >> 8);
bytes[j++] = (byte) ((color & 0x000000FF));
bytes[j++] = (byte) ((color & 0xFF000000) >> 24);
}
return bytes;
}
The required byteorder (RGBA) is not provided by BufferedImage, so I converted it by manually masking the correct bits in the last step.
To bind the generated Teture, use:
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, 4,
width, height, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE,
bytes);