Loading images


Let's cover loading images from a file onto an OpenGL texture so that we can display them on screen.  We start by expanding on gutil.py from example 1.
    1 
2 import pygame
3
4 from OpenGL.GL import *
5 from OpenGL.GLU import *
6
7 def initializeDisplay(w, h):
8 pygame.display.set_mode((w,h), pygame.OPENGL|pygame.DOUBLEBUF)
9
10 glClearColor(0.0, 0.0, 0.0, 1.0)
11 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
12
13 glMatrixMode(GL_PROJECTION);
14 glLoadIdentity();
15 gluOrtho2D(0, w, 0, h);
16 glMatrixMode(GL_MODELVIEW);
17
18 #set up texturing
19 glEnable(GL_TEXTURE_2D)
20 glEnable(GL_BLEND);
21 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

We have added three lines to the initialize display function.  Line 19 enables texturing.  Line 20 enables alpha blending (transparency), and line 21 sets the function that OpenGL will use for alpha blending.

   24 def loadImage(image):
25 textureSurface = pygame.image.load(image)
26
27 textureData = pygame.image.tostring(textureSurface, "RGBA", 1)
28
29 width = textureSurface.get_width()
30 height = textureSurface.get_height()
31
32 texture = glGenTextures(1)
33 glBindTexture(GL_TEXTURE_2D, texture)
34 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
35 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
36 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData )
37
38 return texture, width, height
39
40
41 def delTexture(texture):
42 glDeleteTextures(texture)


The loadImage function takes a file name as an argument, and loads an image file to a texture.  Line 25 uses the PyGame image loader to open our file.  Line 27 takes this image and exports it as a string.  Note the RGBA in the function call, which allows us to use images with an alpha channel.

Lines 29 and 30 capture the width and the height from the PyGame image object.

Line 32 generates a new OpenGL texture.  Line 33 sets it as the active texture.  Lines 34 and 35 set the scaling methods that OpenGL will use for this texture.

Line 36 is where we take the image data that we dumped to a string on line 27, and insert the data into our texture.  It is very important to note that this step requires our image file dimensions to be compatible with OpenGL texture dimensions.   OpenGL texture sizes must be powers of two, for example 16 x 16 or 128 x 512.  The size of our texture here is determined directly by the size of our image.  So our image dimensions must be a power of two.

The final line of our function returns the texture id of our image, and it's width and height.

On line 41 we have a delTexture function.  It is important to remember that OpenGL textures exist outside of Python's garbage collection and memory management.  The texture id that loadImage returns is not a Python object.  It is only an id number.  So if we stop using the texture before our program exits, we need to explicitly free the memory in order to prevent memory leaks.

The function loadImage is all we really need to start displaying our image.  We can take the texture, bind it, and draw a textured quad using OpenGL.  However, doing this by calling glBegin, specifying the texture and vertex coordinates, and calling glEnd involves ten function calls.  The function overhead for that many calls per sprite is quite expensive, and if we want to be able to have large numbers of sprites in a game, we need a more efficient way to render our images.  Here are two further functions added to gutil.py.

   45 def createTexDL(texture, width, height):
46 newList = glGenLists(1)
47 glNewList(newList,GL_COMPILE);
48 glBindTexture(GL_TEXTURE_2D, texture)
49 glBegin(GL_QUADS)
50 glTexCoord2f(0, 0); glVertex2f(0, 0) # Bottom Left Of The Texture and Quad
51 glTexCoord2f(0, 1); glVertex2f(0, height) # Top Left Of The Texture and Quad
52 glTexCoord2f(1, 1); glVertex2f( width, height) # Top Right Of The Texture and Quad
53 glTexCoord2f(1, 0); glVertex2f(width, 0) # Bottom Right Of The Texture and Quad
54 glEnd()
55 glEndList()
56
57 return newList
58
59
60 def delDL(list):
61 glDeleteLists(list, 1)

The purpose of createTexDL, defined on line 45, is to improve rendering speed.  This function takes a texture id, it's width, and it's heigh, and uses it to create a display list that can be used to render the image to the screen very quickly.  Line 46 gets a list id from OpenGL.  Line 47 begins the definition of the list.  Lines 48 to 54 specify the OpenGL commands to load the texture, and render it to the screen.  Line 55 ends the list, capturing the commands on lines 48 to 54.  The function returns the display list id, which we can easily use to draw the image to the screen.

The delDL function is needed for the exact same reason as the delTexture function.  We want to be able to prevent memory leaks if we need to load objects and then forget them.

Now lets show an example of using our expanded gutil library.

example2a.py:
    1 #!/usr/bin/python
2
3 import gutil
4 import pygame
5 from pygame.locals import *
6 from OpenGL.GL import *
7
8
9 def main():
10 pygame.init()
11 gutil.initializeDisplay(800, 600)
12
13 glColor4f(1.0,1.0,1.0,1.0)
14
15 done = False
16
17 cowTex, w, h = gutil.loadImage('data/cow.png')
18 cow = gutil.createTexDL(cowTex, w, h)
19 alienTex, w, h = gutil.loadImage('data/alien.png')
20 alien = gutil.createTexDL(alienTex, w, h)
21
22 while not done:
23 glLoadIdentity()
24 glTranslatef(100, 100, 0)
25 glCallList(cow)
26 glTranslatef(400, 400, 0)
27 glCallList(alien)
28
29 pygame.display.flip()
30
31 eventlist = pygame.event.get()
32 for event in eventlist:
33 if event.type == QUIT \
34 or event.type == KEYDOWN and event.key == K_ESCAPE:
35 done = True
36
37 if __name__ == '__main__':
38 main()


This new example requires two external image files located in a data directory.  One named cow.png and one named alien.png.

Line 17 loads our cow image, and gets it's texture id, it's width, and it's height.  Then on the next line we use this to create our display list for drawing our cow.  Lines 19 and 20 repeat the process for the alien graphic.

Inside our main loop we have new code for displaying our images.  Line 23 resets our OpenGL rendering position.  Line 24 tells OpenGL we want to move to the x, y coordinates 100, 100.  Line 25 uses our display list to draw our cow.  Line 26 moves us an additional 400 pixels up and 400 pixels to the right.  Note that because we did not reset our position, we are now drawing at the coordinates 500, 500.  27 is another display list call, this time to draw our alien.



Up to Intro
Back to Example 1
On to Example 2b