Putting Text on screen


When combining pygame and OpenGL for game making, there are some challenges to putting text on the screen.  This is how I do it.

Lets start with adding a function to gutil.py:
   64 def loadText(text, size = 12, color = (0,0,0), font = None, antialias = True):
65
66 fontObj = pygame.font.Font(font, size)
67 image = fontObj.render(text, antialias, color)
68 height = image.get_height()
69 width = image.get_width()
70 h = 16
71 while(h < height):
72 h = h*2
73 w = 16
74 while(w < width):
75 w = w*2
76
77 label_texture= glGenTextures(1)
78 glBindTexture(GL_TEXTURE_2D, label_texture)
79 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR )
80 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR )
81
82 emptyList = "\x00" * w*h*4
83 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, emptyList )
84
85 textureData = pygame.image.tostring(image, "RGBA", 1)
86 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image.get_width(), image.get_height(), GL_RGBA, GL_UNSIGNED_BYTE, textureData)
87
88 return label_texture, width, height, w, h

The loadText object takes a string that it will render to a texture.  Optionally it takes a font size, a color, a font name, and an antialias flag.  Note that the color parameteris passed to PyGame, and it expects values in the range 0-255 instead of 0-1.0 like we use with OpenGL.  Font is the filename for a TrueType font file, if it is None, then the PyGame default font is used.

Another important thing to note is that the text string doesn't support newline characters, so it's one line at a time.

Lines 66 and 67 use the PyGame font library to render our text to an image surface, then on lines 68 and 69 we get it's width and height.

Because we are using OpenGL, to use our image that has text on it, we need it's size to be a power of two.  So lines 70 to 75 find the smallest power of two values that our image can fit inside.

Lines 77 to 80 are preparing a texture exactly the same way we did with our image loader.  Lines 82 and 83 are a bit different though.  Line 82 is creating an data string the with null values, that is the power of two size we want for our new texture.  We use this string on line 83 to create a texture that is blank, and is large enough to contain our image.

Line 85 takes our image and converts it to a string.  Then on line 86 we copy the data into our new texture.

The function returns a texture id and dimensions, like the loadImage function, but it also returns an extra width height pair.  The first pair is the pixel dimensions the text fills, the second pair is the dimensions of the OpenGL texture.

So, now lets use the new function.

Example3a.py:
    1 #!/usr/bin/python
2
3 import gutil
4 import pygame
5 from pygame.locals import *
6 from OpenGL.GL import *
7
8 from Image import Image
9
10
11 def main():
12 pygame.init()
13 gutil.initializeDisplay(800, 600)
14
15 done = False
16
17 cow = Image('cow')
18 alien = Image('alien')
19
20 white = (255,255,255,255)
21 stringTex, w, h, tw, th = gutil.loadText("Cow!", color=white)
22 stringDL = gutil.createTexDL(stringTex, tw, th)
23
24 while not done:
25 glClear(GL_COLOR_BUFFER_BIT)
26 glLoadIdentity()
27 glColor4f(1.0,1.0,1.0,1.0)
28 glTranslatef(100, 400, 0)
29 glCallList(stringDL)
30
31 cow.draw(pos=(100,100),width=128,height=128)
32 alien.draw(pos=(400, 400),rotation=-15,color=(.9,.3,.2,1))
33
34
35 pygame.display.flip()
36
37 eventlist = pygame.event.get()
38 for event in eventlist:
39 if event.type == QUIT \
40 or event.type == KEYDOWN and event.key == K_ESCAPE:
41 done = True
42
43 if __name__ == '__main__':
44 main()
This code follows quite closely from example 2a.  Note that we define our color the way PyGame expects on line 20.  Line 21 generates a texture from the string "Cow!", then line 22 wraps it in a display list for efficient rendering.

Line 25 is new.  Here we are clearing the screen every frame.  In the past we didn't need to do this because we were drawing the same image in the same place every frame.  We still do this with our font texture, but the difference is that now we have some pixels that are semitransparent because of the font antialiasing.  If we weren't clearing the screen, we would be repeatedly drawing semitransparent pixels over top of the same location.  After a few frames, these pixels would be solid white.

Lines 26 to 29 draw the string texture on the screen, in a manner very similar to how we drew images in example 2a.



Up to index
Back to Example 2b
On to Example 3b