glFreeType.py :  » Game-2D-3D » PyOpenGL » PyOpenGL-Demo-3.0.1b1 » PyOpenGL-Demo » NeHe » lesson43 » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Game 2D 3D » PyOpenGL 
PyOpenGL » PyOpenGL Demo 3.0.1b1 » PyOpenGL Demo » NeHe » lesson43 » glFreeType.py
#  A quick and simple opengl font library that uses GNU freetype2, written
#  and distributed as part of a tutorial for nehe.gamedev.net.
#  Sven Olsen, 2003
#  Translated to PyOpenGL by Brian Leair, 2004
# 
#



# import freetype
# We are going to use Python Image Library's font handling
# From PIL 1.1.4:
import ImageFont
from OpenGL.GL import *
from OpenGL.GLU import *


# Python 2.2 defines these directly
try:
  True
except NameError:
  True = 1==1
  False = 1==0


def is_font_available (ft, facename):
  """ Returns true if FreeType can find the requested face name 
    Pass the basname of the font e.g. "arial" or "times new roman"
  """
  if (facename in ft.available_fonts ()):
    return True
  return False


def next_p2 (num):
  """ If num isn't a power of 2, will return the next higher power of two """
  rval = 1
  while (rval<num):
    rval <<= 1
  return rval



def make_dlist (ft, ch, list_base, tex_base_list):
  """ Given an integer char code, build a GL texture into texture_array,
    build a GL display list for display list number display_list_base + ch.
    Populate the glTexture for the integer ch and construct a display
    list that renders the texture for ch.
    Note, that display_list_base and texture_base are supposed
    to be preallocated for 128 consecutive display lists and and 
    array of textures.
  """

  # //The first thing we do is get FreeType to render our character
  # //into a bitmap.  This actually requires a couple of FreeType commands:
  # //Load the Glyph for our character.
  # //Move the face's glyph into a Glyph object.
  # //Convert the glyph to a bitmap.
  # //This reference will make accessing the bitmap easier
  # - This is the 2 dimensional Numeric array

  # Use our helper function to get the widths of
  # the bitmap data that we will need in order to create
  # our texture.
  glyph = ft.getmask (chr (ch))
  glyph_width, glyph_height = glyph.size 
  # We are using PIL's wrapping for FreeType. As a result, we don't have 
  # direct access to glyph.advance or other attributes, so we add a 1 pixel pad.
  width = next_p2 (glyph_width + 1)
  height = next_p2 (glyph_height + 1)


  # python GL will accept lists of integers or strings, but not Numeric arrays
  # so, we buildup a string for our glyph's texture from the Numeric bitmap 

  # Here we fill in the data for the expanded bitmap.
  # Notice that we are using two channel bitmap (one for
  # luminocity and one for alpha), but we assign
  # both luminocity and alpha to the value that we
  # find in the FreeType bitmap. 
  # We use the ?: operator so that value which we use
  # will be 0 if we are in the padding zone, and whatever
  # is the the Freetype bitmap otherwise.
  expanded_data = ""
  for j in xrange (height):
    for i in xrange (width):
      if (i >= glyph_width) or (j >= glyph_height):
        value = chr (0)
        expanded_data += value
        expanded_data += value
      else:
        value = chr (glyph.getpixel ((i, j)))
        expanded_data += value
        expanded_data += value

  # -------------- Build the gl texture ------------

  # Now we just setup some texture paramaters.
  ID = glGenTextures (1)
  tex_base_list [ch] = ID
  glBindTexture (GL_TEXTURE_2D, ID)
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)

  border = 0
  # Here we actually create the texture itself, notice
  # that we are using GL_LUMINANCE_ALPHA to indicate that
  # we are using 2 channel data.
  glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height,
    border, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data )

  # With the texture created, we don't need to expanded data anymore
  expanded_data = None



  # --- Build the gl display list that draws the texture for this character ---

  # So now we can create the display list
  glNewList (list_base + ch, GL_COMPILE)

  if (ch == ord (" ")):
    glyph_advance = glyph_width
    glTranslatef(glyph_advance, 0, 0)
    glEndList()
  else:

    glBindTexture (GL_TEXTURE_2D, ID)

    glPushMatrix()

    # // first we need to move over a little so that
    # // the character has the right amount of space
    # // between it and the one before it.
    # glyph_left = glyph.bbox [0]
    # glTranslatef(glyph_left, 0, 0)

    # // Now we move down a little in the case that the
    # // bitmap extends past the bottom of the line 
    # // this is only true for characters like 'g' or 'y'.
    # glyph_descent = glyph.decent
    # glTranslatef(0, glyph_descent, 0)

    # //Now we need to account for the fact that many of
    # //our textures are filled with empty padding space.
    # //We figure what portion of the texture is used by 
    # //the actual character and store that information in 
    # //the x and y variables, then when we draw the
    # //quad, we will only reference the parts of the texture
    # //that we contain the character itself.
    x=float (glyph_width) / float (width)
    y=float (glyph_height) / float (height)

    # //Here we draw the texturemaped quads.
    # //The bitmap that we got from FreeType was not 
    # //oriented quite like we would like it to be,
    # //so we need to link the texture to the quad
    # //so that the result will be properly aligned.
    glBegin(GL_QUADS)
    glTexCoord2f(0,0), glVertex2f(0,glyph_height)
    glTexCoord2f(0,y), glVertex2f(0,0)
    glTexCoord2f(x,y), glVertex2f(glyph_width,0)
    glTexCoord2f(x,0), glVertex2f(glyph_width, glyph_height)
    glEnd()
    glPopMatrix()

    # Note, PIL's FreeType interface hides the advance from us.
    # Normal PIL clients are rendering an entire string through FreeType, not
    # a single character at a time like we are doing here.
    # Because the advance value is hidden from we will advance
    # the "pen" based upon the rendered glyph's width. This is imperfect.
    glTranslatef(glyph_width + 0.75, 0, 0)

    # //increment the raster position as if we were a bitmap font.
    # //(only needed if you want to calculate text length)
    # //glBitmap(0,0,0,0,face->glyph->advance.x >> 6,0,NULL)

    # //Finnish the display list
    glEndList()

  return

# /// A fairly straight forward function that pushes
# /// a projection matrix that will make object world 
# /// coordinates identical to window coordinates.
def pushScreenCoordinateMatrix():
  glPushAttrib(GL_TRANSFORM_BIT)
  viewport = glGetIntegerv(GL_VIEWPORT)
  glMatrixMode(GL_PROJECTION)
  glPushMatrix()
  glLoadIdentity()
  gluOrtho2D(viewport[0],viewport[2],viewport[1],viewport[3])
  glPopAttrib()
  return


# Pops the projection matrix without changing the current
# MatrixMode.
def pop_projection_matrix():
  glPushAttrib(GL_TRANSFORM_BIT)
  glMatrixMode(GL_PROJECTION)
  glPopMatrix()
  glPopAttrib()
  return


class font_data:
  def __init__ (self, facename, pixel_height):
    # We haven't yet allocated textures or display lists
    self.m_allocated = False
    self.m_font_height = pixel_height
    self.m_facename = facename

    # Try to obtain the FreeType font
    try:
      ft = ImageFont.truetype (facename, pixel_height)
    except:
      raise ValueError, "Unable to locate true type font '%s'" % (facename)

    # Here we ask opengl to allocate resources for
    # all the textures and displays lists which we
    # are about to create.  
    self.m_list_base = glGenLists (128)

    # Consturct a list of 128 elements. This
    # list will be assigned the texture IDs we create for each glyph
    self.textures = [None] * 128

    # This is where we actually create each of the fonts display lists.
    for i in xrange (128):
      make_dlist (ft, i, self.m_list_base, self.textures);

    self.m_allocated = True


    # //We don't need the face information now that the display
    # //lists have been created, so we free the assosiated resources.
    # Note: Don't need this, as python will decref and dealloc the ft for us.
    ft = None
    return

  def glPrint (self, x, y, string):
    """
    # ///Much like Nehe's glPrint function, but modified to work
    # ///with freetype fonts.
    """
    # We want a coordinate system where things coresponding to window pixels.
    pushScreenCoordinateMatrix()
  
    # //We make the height about 1.5* that of
    h = float (self.m_font_height) / 0.63    
  
    # If There's No Text
    # Do Nothing
    if (string == None):
      pop_projection_matrix()
      return
    if (string == ""):
      pop_projection_matrix()
      return

    # //Here is some code to split the text that we have been
    # //given into a set of lines.  
    # //This could be made much neater by using
    # //a regular expression library such as the one avliable from
    # //boost.org (I've only done it out by hand to avoid complicating
    # //this tutorial with unnecessary library dependencies).
    # //Note: python string object has convenience method for this :)
    lines = string.split ("\n")

    glPushAttrib(GL_LIST_BIT | GL_CURRENT_BIT  | GL_ENABLE_BIT | GL_TRANSFORM_BIT)
    glMatrixMode(GL_MODELVIEW)
    glDisable(GL_LIGHTING)
    glEnable(GL_TEXTURE_2D)
    glDisable(GL_DEPTH_TEST)
    glEnable(GL_BLEND)
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

    glListBase(self.m_list_base)
    modelview_matrix = glGetFloatv(GL_MODELVIEW_MATRIX)

    # //This is where the text display actually happens.
    # //For each line of text we reset the modelview matrix
    # //so that the line's text will start in the correct position.
    # //Notice that we need to reset the matrix, rather than just translating
    # //down by h. This is because when each character is
    # //draw it modifies the current matrix so that the next character
    # //will be drawn immediatly after it.  
    for i in xrange (len (lines)):
      line = lines [i]

      glPushMatrix ()
      glLoadIdentity ()
      glTranslatef (x,y-h*i,0);
      glMultMatrixf (modelview_matrix);

      # //  The commented out raster position stuff can be useful if you need to
      # //  know the length of the text that you are creating.
      # //  If you decide to use it make sure to also uncomment the glBitmap command
      # //  in make_dlist().
      # //  glRasterPos2f(0,0);
      glCallLists (line)
      # //  rpos = glGetFloatv (GL_CURRENT_RASTER_POSITION)
      # //  float len=x-rpos[0];
      glPopMatrix()

    glPopAttrib()
    pop_projection_matrix()
    return

  def release (self):
    """ Release the gl resources for this Face.
      (This provides the functionality of KillFont () and font_data::clean ()
    """
    if (self.m_allocated):
      # Free up the glTextures and the display lists for our face
      glDeleteLists ( self.m_list_base, 128);
      for ID in self.textures:
        glDeleteTextures (ID);
      # Extra defensive. Clients that continue to try and use this object
      # will now trigger exceptions.
      self.list_base = None
      self.m_allocated = False
    return

  def __del__ (self):
    """ Python destructor for when no more refs to this Face object """
    self.release ()
    return


# Unit Test harness if this python module is run directly.
if __name__ == "__main__":
  print "testing availability of freetype font arial\n"
  ft = ImageFont.truetype ("arial.ttf", 15)
  if ft:
    print "Found the TrueType font 'arial.ttf'"
  else:
    print "faild to find the TrueTYpe font 'arial'\n"
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.