[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] Re: BOUNCE pygame-users@seul.org: Non-member submission from ["David Keeney" <dkeeney@travelbyroad.net>]



Lì giovedì, 2004/06/24 alle 20:57, -0500, listbox@travelbyroad.net ha 
> > I have borrowed some code from pyUI, changed it a little bit and made it
> > into a Font class that load any font available from pygame and render it
> > into a texture. it is just 100 lines but I don't know the policy of this
> > list about long emails containing code. just send? :)
> 
> I would appreciate receiving the code, if you don't mind sharing it.  I 
> will add it to my sourceforge.net file release as well (at 
> pduel.sourceforge.net).

as I said I wrote this code after looking at PyUI way of doing it. mine
is a little bit more flexible but the general idea is PyUI's. surely I
don't mind sharing something that is already shared and is mine only for
about 10%... here it goes:

class Font(object):
    """Render a whole font on a texture and use it to render text.

    Heavily inspired by PyUI, distribute under the same license, please.
    """

    def __init__(self, fontname=None, filename=None, size=10):
        """Pass to me a file name or a system font name."""
        self.fontname = fontname
        self.filename = filename
        self.size = size

        if self.filename:
            self.font = pygame.font.Font(fontfile, size)
        else:
            self.font = pygame.font.SysFont(fontname, size)

        self.metrics = []
        self.create_texture()

    def create_texture(self, lowchar=32, highchar=127):
        """Create a texture holding the whole font.

        Note that only chars in the range [32,127], i.e., standard ASCII
        are generated by default.
        """
        self.lowchar = lowchar
        surfaces = []

        for i in range(lowchar, highchar+1):
            try:
                surf = self.font.render(chr(i), 1, (255,255,255,255))
            except:
                surfaces.append(None)
            else:
                surfaces.append(surf)

        tw = reduce(lambda x, y: x + (y is not None and y.get_width() or 0),
                    surfaces, 0)

        # FIXME: copied this from pyui, need to fix to calculate minimum
	# dimensions to not waste texture memory
        if tw > 1300:
            tsize = 512
        else:
            tsize = 256

	# this taken from pyui too but it also works with generic surface
	# and plain convert(): need to investigate if we really gain speed
	# this way
        self.image = pygame.surface.Surface(
            (tsize,tsize), HWSURFACE|SRCALPHA, 32).convert_alpha()
        self.image.fill((0,0,0,0))

        # create the texture image
        x = y = c = h = 0
        for surf in surfaces:
            if surf is None:
                self.metrics.append((0, 0, (0,0,0,0)))
                continue

            if x + surf.get_width() > tsize:
                y += h
                x = h = 0

            self.image.blit(surf, (x,y))

            l = float(x)/tsize
            b = 1.0 - float(y)/tsize
            r = float(x + surf.get_width()) / tsize
            t = 1.0 - float(y + surf.get_height()) / tsize

            self.metrics.append(
                (surf.get_width(), surf.get_height(), (l,t,r,b)))

            x += surf.get_width()
            c += 1
            h = max(h, surf.get_height())

        # copy texture image to texture and create procedure lists

        self.texture = glGenTextures(1)

        data = pygame.image.tostring(self.image, "RGBA", 1)

        glBindTexture(GL_TEXTURE_2D, self.texture)
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tsize, tsize, 0,
                     GL_RGBA, GL_UNSIGNED_BYTE, data)
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)

        self.procedures = []
        for w, h, coords in self.metrics:
            # remember: coords is (left,top,right,bottom)
            if w == 0 and h == 0:
                self.procedures.append(0)
                continue
	    # TODO: we probably can generale all the lists by calling 
	    # glGenLists once
            l = glGenLists(1)
            glNewList(l, GL_COMPILE)
            # we don't bind the texture because the draw routine will do
            # that only once to speed-up long strings rendering
            glBegin(GL_QUADS)
            glTexCoord2f(coords[0], coords[1]) ; glVertex2i(0, 0)
            glTexCoord2f(coords[0], coords[3]) ; glVertex2i(0, h)
            glTexCoord2f(coords[2], coords[3]) ; glVertex2i(w, h)
            glTexCoord2f(coords[2], coords[1]) ; glVertex2i(w, 0)
            glEnd()
            glEndList()
            self.procedures.append(l)

    def draw_text(self, text, pos=(0,0), color=(255,255,255,255)):
        """Draw the given text.

        Note that we pass color as unsigned bytes, we are in 2D after all.
        """
        glBindTexture(GL_TEXTURE_2D, self.texture)
	# TODO: pass blend function as a parameter? usually text overlays
	# don't have alpha but we use GL_SRC_ALPHA anyway because i like it.
        glBlendFunc(GL_SRC_ALPHA, GL_ONE);
        glColor4ub(*color)

        glPushMatrix()
        glTranslatef(pos[0], pos[1], 0)
        for c in text:
            w = self.metrics[ord(c)-self.lowchar][0]
            glCallList(self.procedures[ord(c)-self.lowchar])
            glTranslatef(w, 0, 0)
        glPopMatrix()

    def test(self):
        """Draw the whole texture on a 1x1 quad, as a test.
	
	Orthographic projection should be already setup to use 'screen 
	coordinates' in the GL window.
	"""
        size = self.image.get_width()
        glBindTexture(GL_TEXTURE_2D, self.texture)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE);
        glColor4f(1.0,1.0,1.0,1.0)
        glBegin(GL_QUADS)
        glTexCoord2f(0.0, 0.0); glVertex3f( 0.0,  0.0, 0.0)
        glTexCoord2f(1.0, 0.0); glVertex3f(size,  0.0, 0.0)
        glTexCoord2f(1.0, 1.0); glVertex3f(size, size, 0.0)
        glTexCoord2f(0.0, 1.0); glVertex3f( 0.0, size, 0.0)
        glEnd()


hope this helps,
federico

-- 
Federico Di Gregorio                         http://people.initd.org/fog
Debian GNU/Linux Developer                                fog@debian.org
INIT.D Developer                                           fog@initd.org
  All'inizio ho scritto un programma proprietario, in esclusiva per il
   cliente; è stato tristissimo, perché mi ha succhiato un pezzo di
   anima.                                           -- Alessandro Rubini