Displaying an Image in JOGL (Part 1)
From TankaarWiki
After covering Drawing First 2D Graphics in JOGL and rotation we come to most challenging part of the JOGL/OpenGL. Rendering a 2D image on the screen. You have a beautiful image you have drawn using some nice picture editor software or have downloaded it from web. It may be .JPEG, .GIF , .PNG format etc. Now you need to put it on your OpenGL Canvas. Below is code to do the same. Here entire code is produced for the sake of completion. Also comments have been left out to keep this code clean and compact. The important section of the code is explained later in this page by giving line comments.
For present just look at few things. We have a image of leaf on the right which we want to display. This image has a special size i.e. 128px by 128px. You can use any other image instead of this but for time being remember that size has to be of the form 2m X 2n, where m and n are integers. So our size 128 X 128 = 27 X 27 certainly matches the criteria, with m = 7 and n = 7. Also remember to import image you want to display in the same package in which your class in residing and instead of "leaf.jpg" you must give the name of the image file you are using.
Before we go deeper into this code let us look at this special size of image (2m X 2n). You can try other size of image too, it may work or it may not work. It all depends on the OpenGL version supported by your graphic drivers. If your driver supports higher version of OpenGL you don't have to worry about the size of image and any size will work. In other cases, only this special size images will be permitted. So in any case 2m X 2n sized images will work. You can see the limitation here. Actually we will put 2D image as a texture and there are certain limitations on size of texture which OpenGL supports in earlier versions.
This certainly does not mean placing an arbitrary sized image is not possible if your graphics driver supports little older version of OpenGL. Just after this code a simple work around is mentioned which allows placing arbitrary sized image irrespective of the OpenGL version supported by the driver.
package com.tankaar.learnjogl; import java.awt.Graphics2D; import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.io.IOException; import java.nio.ByteBuffer; import javax.imageio.ImageIO; import javax.media.opengl.GL; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCanvas; import javax.media.opengl.GLEventListener; import javax.swing.JFrame; public class Image2DGraphics { public Image2DGraphics() { JFrame frame = new JFrame("2D Graphics"); frame.setSize(300,300); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); GLCanvas canvas = new GLCanvas(); canvas.addGLEventListener(new JOGLListener()); frame.add(canvas); frame.setVisible(true); } public static void main(String args[]){ new Image2DGraphics(); } private class JOGLListener implements GLEventListener { @Override public void display(GLAutoDrawable drawable) { System.out.println("DISPLAY CALLED"); GL gl = drawable.getGL(); gl.glMatrixMode(GL.GL_PROJECTION); gl.glLoadIdentity(); gl.glOrtho(0, 300, 300, 0, 0, 1); gl.glMatrixMode(GL.GL_MODELVIEW); gl.glDisable(GL.GL_DEPTH_TEST); gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); gl.glClear(GL.GL_COLOR_BUFFER_BIT); gl.glBlendFunc (GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); gl.glEnable (GL.GL_BLEND); BufferedImage bufferedImage = null; int w = 0; int h = 0; try { bufferedImage = ImageIO.read(Image2DGraphics.class.getResource("leaf.jpg")); w = bufferedImage.getWidth(); h = bufferedImage.getHeight(); } catch (IOException e) { e.printStackTrace(); } WritableRaster raster = Raster.createInterleavedRaster (DataBuffer.TYPE_BYTE, w, h, 4, null); ComponentColorModel colorModel= new ComponentColorModel (ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {8,8,8,8}, true, false, ComponentColorModel.TRANSLUCENT, DataBuffer.TYPE_BYTE); BufferedImage dukeImg = new BufferedImage (colorModel, raster, false, null); Graphics2D g = dukeImg.createGraphics(); g.drawImage(bufferedImage, null, null); DataBufferByte dukeBuf = (DataBufferByte)raster.getDataBuffer(); byte[] dukeRGBA = dukeBuf.getData(); ByteBuffer bb = ByteBuffer.wrap(dukeRGBA); bb.position(0); bb.mark(); gl.glBindTexture(GL.GL_TEXTURE_2D, 13); gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP); gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP); gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE); gl.glTexImage2D (GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, w, h, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, bb); int left = 100; int top = 100; gl.glEnable(GL.GL_TEXTURE_2D); gl.glBindTexture (GL.GL_TEXTURE_2D, 13); gl.glBegin (GL.GL_POLYGON); gl.glTexCoord2d (0, 0); gl.glVertex2d (left,top); gl.glTexCoord2d(1,0); gl.glVertex2d (left + w, top); gl.glTexCoord2d(1,1); gl.glVertex2d (left + w, top + h); gl.glTexCoord2d(0,1); gl.glVertex2d (left, top + h); gl.glEnd (); gl.glFlush(); } @Override public void displayChanged(GLAutoDrawable arg0, boolean arg1, boolean arg2) { System.out.println("DISPLAY CHANGED CALLED"); } @Override public void init(GLAutoDrawable drawable) { System.out.println("INIT CALLED"); } @Override public void reshape(GLAutoDrawable arg0, int arg1, int arg2, int arg3, int arg4) { System.out.println("RESHAPE CALLED"); } } }
If you have tried the above code and it works fine, you are ready to go deeper into it. Following code presents a section of the above but it is commented to make steps clear. Note that this code contains many functions of OpenGL. Comments do not cover the details of all of these OpenGL functions. The aim of the tutorial is to get you started. It does not aim to provide complete reference to OpenGL.
//first we create a BufferedImage object reference BufferedImage bufferedImage = null; //initalizing width and height of image int w = 0; int h = 0; try { //Creating BufferedImage by reading it from file bufferedImage = ImageIO.read(Image2DGraphics.class.getResource("leaf.jpg")); //Setting width and height of image equal to BufferedImage width and height w = bufferedImage.getWidth(); h = bufferedImage.getHeight(); } catch (IOException e) { e.printStackTrace(); } //Creating a writable raster on which we write BufferedImage WritableRaster raster = Raster.createInterleavedRaster (DataBuffer.TYPE_BYTE, w, h, 4, null); //Making ComponenetColorModel for the BufferedImage ComponentColorModel colorModel= new ComponentColorModel (ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {8,8,8,8}, true, false, ComponentColorModel.TRANSLUCENT, DataBuffer.TYPE_BYTE); //Creating BufferedImage from the given WritableRaster and ComponentColorModel BufferedImage dukeImg = new BufferedImage (colorModel, raster, false, null); //Creating graphics object on which to draw image Graphics2D g = dukeImg.createGraphics(); //Draw original image on new image graphics object g.drawImage(bufferedImage, null, null); //Retriving DataBufferByte which is backing this WritableRaster Object DataBufferByte dukeBuf = (DataBufferByte)raster.getDataBuffer(); //Storing data of DataBufferByte in byte array byte[] dukeRGBA = dukeBuf.getData(); //Wrapping this byte array in ByteBuffer object ByteBuffer bb = ByteBuffer.wrap(dukeRGBA); //Marking position to start for reading this ByteBuffer bb.position(0); bb.mark(); //Here we put image as a Texture //Each texture is assigned a unique integer value which is used when binding it //Here value assigned is 13 gl.glBindTexture(GL.GL_TEXTURE_2D, 13); gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP); gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP); gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE); gl.glTexImage2D (GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, w, h, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, bb); int left = 100; int top = 100; gl.glEnable(GL.GL_TEXTURE_2D); //We use the texture number 13, i.e. the one we have created gl.glBindTexture (GL.GL_TEXTURE_2D, 13); gl.glBegin (GL.GL_POLYGON); //Texture is rectangular region of 1 X 1 size //Mapping top left of rectangular texture with top left of rectangular region on which we want texture //and so on for remaining 3 points gl.glTexCoord2d (0, 0); gl.glVertex2d (left,top); gl.glTexCoord2d(1,0); gl.glVertex2d (left + w, top); gl.glTexCoord2d(1,1); gl.glVertex2d (left + w, top + h); gl.glTexCoord2d(0,1); gl.glVertex2d (left, top + h); gl.glEnd (); gl.glFlush();
To make your code flexible such that it is able to display any size image without any problem irrespective of OpenGL version just do the following.
First create a static function in the above class as given below. This static function "ceilingPow2" which if passed an integer n returns integer of type 2k (where k is non negative integer) such that 2(k-1) < n <= 2k
private static int ceilingPow2(int n) { int pow2 = 1; while (n > pow2) { pow2 = pow2<<1; } return pow2; }
After this use this function to modify width (w) and height(h) as shown. So in case of "umbrella.jpg", on console you will see
WIDTH : 32
HEIGHT : 64
int w = 0; int h = 0; try { bufferedImage = ImageIO.read(Image2DGraphics.class.getResource("umbrella.jpg")); w = ceilingPow2(bufferedImage.getWidth()); System.out.println("WIDTH : " + w); h = ceilingPow2(bufferedImage.getHeight()); System.out.println("HEIGHT : " + h); } catch (IOException e) { e.printStackTrace(); }
Rest of the code remains same. So with this knowledge you can display any image on in JOGL. But this alone is not sufficient. You may like to to do more with the images. Suppose you have a big image and you want to display only a part of it. Displaying an Image in JOGL (Part 2) shows you how to accomplish this goal.

