Loading PNG images with TWL's PNGDecoder

From LWJGL
Jump to: navigation, search

The PNGDecoder class is available as a stand alone JAR file and is below 10KB in size.

The usage of this PNG decoder is very simple:

import de.matthiasmann.twl.utils.PNGDecoder;

....

InputStream in = new FileInputStream("white_pixel.png");
try {
   PNGDecoder decoder = new PNGDecoder(in);

   System.out.println("width="+decoder.getWidth());
   System.out.println("height="+decoder.getHeight());

   ByteBuffer buf = ByteBuffer.allocateDirect(4*decoder.getWidth()*decoder.getHeight());
   decoder.decode(buf, decoder.getWidth()*4, Format.RGBA);
   buf.flip();
} finally {
   in.close();
}

Let's analyze this code step by step.

PNGDecoder decoder = new PNGDecoder(in);

This decodes the header of the PNG files and extracts information like width, height and color format. If the input stream is not a valid PNG file then an IOException is thrown. The following methods can be used to retrieve information about the PNG:

  • getWidth
  • getHeight
  • hasAlpha - returns true if this PNG has either an alpha channel or uses a transparent color
  • isRGB - returns true if this PNG is either true color or uses a palette, and false if it's gray scale
  • decideTextureFormat - adjust the requested color format based on the PNG and the capabilities of the decoder


ByteBuffer buf = ByteBuffer.allocateDirect(4*decoder.getWidth()*decoder.getHeight());
decoder.decode(buf, decoder.getWidth()*4, Format.RGBA);
buf.flip();

This will decode the image data of the PNG. First we need to allocate a ByteBuffer. In this case we want to decode the PNG as RGBA image which uses 4 bytes per pixel. The PNGDecoder has a stride parameter which specifies the distance in bytes from the start of a line to the start of the next line. In this case we set it equal to the number of bytes per line. It will write to the specified ByteBuffer starting at the current position. On return the position will be after the last written byte. If we specified a stride lager then the line length then this additional space is not included in the buffer position. If the remaining space in the ByteBuffer is not enough then a BufferOverflowException will be thrown.


The resulting ByteBuffer can then directly be used to upload the tetxure to OpenGL like this:

GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, decoder.getWidth(), decoder.getHeight(), 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buf);

Many OpenGL implementations use the BGRA format internally, using this format can speedup loading of textures on these system. Use Format.BGRA and GL12.GL_BGRA for this.


This is an easy and fast way to load PNG images for use with OpenGL.