source: trunk/glFreeType.py @ 978

Last change on this file since 978 was 978, checked in by vondreele, 10 years ago

make MC/SA fortran stuff all REAL*8
add glFreeType.py - doesn't work correctly yet
implement glFreeType font in OpenGl? labels
add new menu items for RBs
MCSA anneal in a test mode no random start for fixed T0

File size: 10.9 KB
Line 
1#       A quick and simple opengl font library that uses GNU freetype2, written
2#       and distributed as part of a tutorial for nehe.gamedev.net.
3#       Sven Olsen, 2003
4#       Translated to PyOpenGL by Brian Leair, 2004
5#
6#
7
8# We are going to use Python Image Library's font handling
9# From PIL 1.1.4:
10import ImageFont
11from OpenGL.GL import *
12from OpenGL.GLU import *
13
14def is_font_available (ft, facename):
15        """ Returns true if FreeType can find the requested face name
16                Pass the basname of the font e.g. "arial" or "times new roman"
17        """
18        if (facename in ft.available_fonts ()):
19                return True
20        return False
21
22def next_p2 (num):
23        """ If num isn't a power of 2, will return the next higher power of two """
24        rval = 1
25        while (rval<num):
26                rval <<= 1
27        return rval
28
29def make_dlist (ft, ch, list_base, tex_base_list):
30        """ Given an integer char code, build a GL texture into texture_array,
31                build a GL display list for display list number display_list_base + ch.
32                Populate the glTexture for the integer ch and construct a display
33                list that renders the texture for ch.
34                Note, that display_list_base and texture_base are supposed
35                to be preallocated for 128 consecutive display lists and and
36                array of textures.
37        """
38
39        # //The first thing we do is get FreeType to render our character
40        # //into a bitmap.  This actually requires a couple of FreeType commands:
41        # //Load the Glyph for our character.
42        # //Move the face's glyph into a Glyph object.
43        # //Convert the glyph to a bitmap.
44        # //This reference will make accessing the bitmap easier
45        # - This is the 2 dimensional Numeric array
46
47        # Use our helper function to get the widths of
48        # the bitmap data that we will need in order to create
49        # our texture.
50        glyph = ft.getmask (chr (ch))
51        glyph_width, glyph_height = glyph.size
52        # We are using PIL's wrapping for FreeType. As a result, we don't have
53        # direct access to glyph.advance or other attributes, so we add a 1 pixel pad.
54        width = next_p2 (glyph_width + 1)
55        height = next_p2 (glyph_height + 1)
56
57
58        # python GL will accept lists of integers or strings, but not Numeric arrays
59        # so, we buildup a string for our glyph's texture from the Numeric bitmap
60
61        # Here we fill in the data for the expanded bitmap.
62        # Notice that we are using two channel bitmap (one for
63        # luminocity and one for alpha), but we assign
64        # both luminocity and alpha to the value that we
65        # find in the FreeType bitmap.
66        # We use the ?: operator so that value which we use
67        # will be 0 if we are in the padding zone, and whatever
68        # is the the Freetype bitmap otherwise.
69        expanded_data = ""
70        for j in xrange (height):
71                for i in xrange (width):
72                        if (i >= glyph_width) or (j >= glyph_height):
73                                value = chr (0)
74                                expanded_data += value
75                                expanded_data += value
76                        else:
77                                value = chr (glyph.getpixel ((i, j)))
78                                expanded_data += value
79                                expanded_data += value
80
81        # -------------- Build the gl texture ------------
82
83        # Now we just setup some texture paramaters.
84        ID = glGenTextures (1)
85        tex_base_list [ch] = ID
86        glBindTexture (GL_TEXTURE_2D, ID)
87        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
88        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
89
90        border = 0
91        # Here we actually create the texture itself, notice
92        # that we are using GL_LUMINANCE_ALPHA to indicate that
93        # we are using 2 channel data.
94        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height,
95                border, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data )
96
97        # With the texture created, we don't need to expanded data anymore
98        expanded_data = None
99
100        # --- Build the gl display list that draws the texture for this character ---
101
102        # So now we can create the display list
103        glNewList (list_base + ch, GL_COMPILE)
104
105        if (ch == ord (" ")):
106                glyph_advance = glyph_width
107                glTranslatef(glyph_advance, 0, 0)
108                glEndList()
109        else:
110
111                glBindTexture (GL_TEXTURE_2D, ID)
112
113                glPushMatrix()
114
115                # // first we need to move over a little so that
116                # // the character has the right amount of space
117                # // between it and the one before it.
118                # glyph_left = glyph.bbox [0]
119                # glTranslatef(glyph_left, 0, 0)
120
121                # // Now we move down a little in the case that the
122                # // bitmap extends past the bottom of the line
123                # // this is only true for characters like 'g' or 'y'.
124                # glyph_descent = glyph.decent
125                # glTranslatef(0, glyph_descent, 0)
126
127                # //Now we need to account for the fact that many of
128                # //our textures are filled with empty padding space.
129                # //We figure what portion of the texture is used by
130                # //the actual character and store that information in
131                # //the x and y variables, then when we draw the
132                # //quad, we will only reference the parts of the texture
133                # //that we contain the character itself.
134                x=float (glyph_width) / float (width)
135                y=float (glyph_height) / float (height)
136
137                # //Here we draw the texturemaped quads.
138                # //The bitmap that we got from FreeType was not
139                # //oriented quite like we would like it to be,
140                # //so we need to link the texture to the quad
141                # //so that the result will be properly aligned.
142                glBegin(GL_QUADS)
143                glTexCoord2f(0,0), glVertex2f(0,glyph_height)
144                glTexCoord2f(0,y), glVertex2f(0,0)
145                glTexCoord2f(x,y), glVertex2f(glyph_width,0)
146                glTexCoord2f(x,0), glVertex2f(glyph_width, glyph_height)
147                glEnd()
148                glPopMatrix()
149
150                # Note, PIL's FreeType interface hides the advance from us.
151                # Normal PIL clients are rendering an entire string through FreeType, not
152                # a single character at a time like we are doing here.
153                # Because the advance value is hidden from we will advance
154                # the "pen" based upon the rendered glyph's width. This is imperfect.
155                glTranslatef(glyph_width + 0.75, 0, 0)
156
157                # //increment the raster position as if we were a bitmap font.
158                # //(only needed if you want to calculate text length)
159                # //glBitmap(0,0,0,0,face->glyph->advance.x >> 6,0,NULL)
160
161                # //Finnish the display list
162                glEndList()
163
164        return
165
166# /// A fairly straight forward function that pushes
167# /// a projection matrix that will make object world
168# /// coordinates identical to window coordinates.
169def pushScreenCoordinateMatrix():
170        glPushAttrib(GL_TRANSFORM_BIT)
171        viewport = glGetIntegerv(GL_VIEWPORT)
172        glMatrixMode(GL_PROJECTION)
173        glPushMatrix()
174        glLoadIdentity()
175        gluOrtho2D(viewport[0],viewport[2],viewport[1],viewport[3])
176        glPopAttrib()
177        return
178
179
180# Pops the projection matrix without changing the current
181# MatrixMode.
182def pop_projection_matrix():
183        glPushAttrib(GL_TRANSFORM_BIT)
184        glMatrixMode(GL_PROJECTION)
185        glPopMatrix()
186        glPopAttrib()
187        return
188
189
190class font_data:
191        def __init__ (self, facename, pixel_height):
192                # We haven't yet allocated textures or display lists
193                self.m_allocated = False
194                self.m_font_height = pixel_height
195                self.m_facename = facename
196
197                # Try to obtain the FreeType font
198                try:
199                        ft = ImageFont.truetype (facename, pixel_height)
200           #ft = ImageFont.load ("D:\python27-64\Lib\site-packages\PIL\Images\courB08.pil")
201                except:
202                        raise ValueError, "Unable to locate font '%s'" % (facename)
203
204                # Here we ask opengl to allocate resources for
205                # all the textures and displays lists which we
206                # are about to create. 
207                self.m_list_base = glGenLists (128)
208
209                # Consturct a list of 128 elements. This
210                # list will be assigned the texture IDs we create for each glyph
211                self.textures = [None] * 128
212
213                # This is where we actually create each of the fonts display lists.
214                for i in range (1,128):
215                        make_dlist (ft, i, self.m_list_base, self.textures);
216
217                self.m_allocated = True
218
219
220                # //We don't need the face information now that the display
221                # //lists have been created, so we free the assosiated resources.
222                # Note: Don't need this, as python will decref and dealloc the ft for us.
223                ft = None
224                return
225
226        def glPrint (self, x, y, string):
227                """
228                # ///Much like Nehe's glPrint function, but modified to work
229                # ///with freetype fonts.
230                """
231                # We want a coordinate system where things coresponding to window pixels.
232                pushScreenCoordinateMatrix()
233       
234                # //We make the height about 1.5* that of
235                h = float (self.m_font_height) / 0.63           
236       
237                # If There's No Text
238                # Do Nothing
239                if (string == None):
240                        pop_projection_matrix()
241                        return
242                if (string == ""):
243                        pop_projection_matrix()
244                        return
245
246                # //Here is some code to split the text that we have been
247                # //given into a set of lines. 
248                # //This could be made much neater by using
249                # //a regular expression library such as the one avliable from
250                # //boost.org (I've only done it out by hand to avoid complicating
251                # //this tutorial with unnecessary library dependencies).
252                # //Note: python string object has convenience method for this :)
253                lines = string.split ("\n")
254
255                glPushAttrib(GL_LIST_BIT | GL_CURRENT_BIT  | GL_ENABLE_BIT | GL_TRANSFORM_BIT)
256                glMatrixMode(GL_MODELVIEW)
257                glDisable(GL_LIGHTING)
258                glEnable(GL_TEXTURE_2D)
259                glDisable(GL_DEPTH_TEST)
260                glEnable(GL_BLEND)
261                glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
262
263                glListBase(self.m_list_base)
264                modelview_matrix = glGetFloatv(GL_MODELVIEW_MATRIX)
265
266                # //This is where the text display actually happens.
267                # //For each line of text we reset the modelview matrix
268                # //so that the line's text will start in the correct position.
269                # //Notice that we need to reset the matrix, rather than just translating
270                # //down by h. This is because when each character is
271                # //draw it modifies the current matrix so that the next character
272                # //will be drawn immediatly after it. 
273                for i in xrange (len (lines)):
274                        line = lines [i]
275
276                        glPushMatrix ()
277                        glLoadIdentity ()
278                        glTranslatef (x,y-h*i,0);
279                        glMultMatrixf (modelview_matrix);
280
281                        # //  The commented out raster position stuff can be useful if you need to
282                        # //  know the length of the text that you are creating.
283                        # //  If you decide to use it make sure to also uncomment the glBitmap command
284                        # //  in make_dlist().
285                        # //    glRasterPos2f(0,0);
286                        glCallLists (line)
287                        # //    rpos = glGetFloatv (GL_CURRENT_RASTER_POSITION)
288                        # //    float len=x-rpos[0];
289                        glPopMatrix()
290
291                glPopAttrib()
292                pop_projection_matrix()
293                return
294
295        def release (self):
296                """ Release the gl resources for this Face.
297                        (This provides the functionality of KillFont () and font_data::clean ()
298                """
299                if (self.m_allocated):
300                        # Free up the glTextures and the display lists for our face
301                        glDeleteLists ( self.m_list_base, 128);
302                        for ID in self.textures:
303                                glDeleteTextures (ID);
304                        # Extra defensive. Clients that continue to try and use this object
305                        # will now trigger exceptions.
306                        self.list_base = None
307                        self.m_allocated = False
308                return
309
310        def __del__ (self):
311                """ Python destructor for when no more refs to this Face object """
312                self.release ()
313                return
314
315
316# Unit Test harness if this python module is run directly.
317if __name__ == "__main__":
318        print "testing availability of freetype font courier\n"
319        ft = ImageFont.load ("D:\python27-64\Lib\site-packages\PIL\Images\courB08.pil")
320        if ft:
321                print "Found the font"
322        else:
323                print "failed to find the font\n"
Note: See TracBrowser for help on using the repository browser.