##################################################################### # -*- coding: iso-8859-1 -*- # # # # Frets on Fire # # Copyright (C) 2006 Sami Kyöstilä # # 2008 evilynux # # # # This program is free software; you can redistribute it and/or # # modify it under the terms of the GNU General Public License # # as published by the Free Software Foundation; either version 2 # # of the License, or (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU General Public License for more details. # # # # You should have received a copy of the GNU General Public License # # along with this program; if not, write to the Free Software # # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # # MA 02110-1301, USA. # ##################################################################### import pygame from OpenGL.GL import * import sys from Texture import Texture, TextureAtlas, TextureAtlasFullException from numpy import array, float32 DEFAULT_SCALE = 0.002 class CacheElement(object): def __init__(self,content): self.content = content self.accessed() def accessed(self): self.lastUse = pygame.time.get_ticks() class Cache(object): def __init__(self,maxCount=1024): self.elements = {} self.maxCount = maxCount def get(self,key): e = self.elements[key] e.accessed() return e.content def add(self,key,element): self.elements[key] = CacheElement(element) if len(self.elements) > self.maxCount: keys = self.elements.keys() keys.sort(key=lambda e:-self.elements[e].lastUse) for k in keys[self.maxCount:]: del self.elements[k] class Font: """A texture-mapped font.""" def __init__(self, fileName, size, bold = False, italic = False, underline = False, outline = True, scale = 1.0, reversed = False, systemFont = False, shadow = False, shadowoffsetx = .0022, shadowoffsety = .0005): pygame.font.init() self.size = size self.scale = scale self.outline = outline self.reversed = reversed self.shadow = shadow self.shadowoffsetx = shadowoffsetx self.shadowoffsety = shadowoffsety # Try loading a system font first if one was requested self.font = None if systemFont and sys.platform != "win32": try: self.font = pygame.font.SysFont(None, size) except: pass if not self.font: self.font = pygame.font.Font(fileName, size) self.font.set_bold(bold) self.font.set_italic(italic) self.font.set_underline(underline) self.stringsCache = Cache(256) self.square_prim = array([[.0,.0],[.0,.0],[.0,.0],[.0,.0]], dtype=float32) self.square_tex = array([[.0,.0],[.0,.0],[.0,.0],[.0,.0]], dtype=float32) def getStringSize(self, s, scale = DEFAULT_SCALE): """ Get the dimensions of a string when rendered with this font. @param s: String @param scale: Scale factor @return: (width, height) tuple """ scale *= self.scale try: t,w,h = self.stringsCache.get(s) except KeyError: w,h = self.font.size(s) return (w*scale, h*scale) def loadCache(self): pass # evilynux - Return scaling factor needed to fit text within maxwidth def scaleText(self, text, maxwidth, scale = DEFAULT_SCALE): w, h = self.getStringSize(text, scale) if w > maxwidth: scale *= maxwidth/w return scale def getHeight(self): """@return: The height of this font""" return self.font.get_height() * self.scale def getLineSpacing(self, scale = DEFAULT_SCALE): """@return: Recommanded line spacing of this font""" return self.font.get_linesize() * self.scale * scale #MFH - needed to find the centerline of text def getFontAscent(self, scale = DEFAULT_SCALE): """@return: Return the height in pixels for the font ascent. The ascent is the number of pixels from the font baseline to the top of the font. """ return self.font.get_ascent() * self.scale * scale #MFH - needed to find the centerline of text def getFontDescent(self, scale = DEFAULT_SCALE): """@return: Return the height in pixels for the font descent. The descent is the number of pixels from the font baseline to the bottom of the font. """ return self.font.get_descent() * self.scale * scale def setCustomGlyph(self, character, texture): """ Replace a character with a texture. @param character: Character to replace @param texture: L{Texture} instance """ pass def render(self, text, pos = (0, 0), direction = (1, 0), scale = DEFAULT_SCALE, shadowoffset = (.0022, .0005)): """ Draw some text. @param text: Text to draw @param pos: Text coordinate tuple (x, y) @param direction: Text direction vector (x, y, z) @param scale: Scale factor """ # deufeufeu : new drawing relaying only on pygame.font.render # : I know me miss special unicodes characters, but the gain # : is really important. # evilynux : Use arrays to increase performance def drawSquare(w,h,tw,th): self.square_prim[1,0] = self.square_prim[3,0] = w self.square_prim[2,1] = self.square_prim[3,1] = h self.square_tex[0,1] = self.square_tex[1,1] = th self.square_tex[1,0] = self.square_tex[3,0] = tw glEnableClientState(GL_VERTEX_ARRAY) glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointerf(self.square_prim) glTexCoordPointerf(self.square_tex) glDrawArrays(GL_TRIANGLE_STRIP, 0, self.square_prim.shape[0]) glDisableClientState(GL_VERTEX_ARRAY) glDisableClientState(GL_TEXTURE_COORD_ARRAY); if not text: return try: t,w,h = self.stringsCache.get(text) except KeyError: s = self.font.render(text, True, (255,255,255)) t = Texture() t.setFilter(GL_LINEAR, GL_LINEAR) t.setRepeat(GL_CLAMP, GL_CLAMP) t.loadSurface(s, alphaChannel = True) del s w, h = self.font.size(text) self.stringsCache.add(text,(t,w,h)) x, y = pos scale *= self.scale w, h = w*scale, h*scale tw,th = t.size glEnable(GL_TEXTURE_2D) glPushMatrix() glTranslatef(x,y,0) t.bind() if self.outline: glPushAttrib(GL_CURRENT_BIT) glColor4f(0, 0, 0, .25 * glGetDoublev(GL_CURRENT_COLOR)[3]) blur = 2 * DEFAULT_SCALE for offset in [(-.7, -.7), (0, -1), (.7, -.7), (-1, 0), (1, 0), (-.7, .7), (0, 1), (.7, .7)]: glTranslatef(blur * offset[0], blur * offset[1], 0) drawSquare(w,h,tw,th) glTranslatef(-blur * offset[0], -blur * offset[1], 0) glPopAttrib() if self.shadow: glPushAttrib(GL_CURRENT_BIT) glColor4f(0, 0, 0, 1) glTranslatef(shadowoffset[0], shadowoffset[1], 0) drawSquare(w,h,tw,th) glTranslatef(-shadowoffset[0], -shadowoffset[1], 0) glPopAttrib() drawSquare(w,h,tw,th) glPopMatrix() glDisable(GL_TEXTURE_2D) return