Bitmap fonts in SFML (OpenGL)
Question: I'm writting a simple bitmap font renderer in pySFML and wanted to ask is
there a better and faster way to approach this problem.

I'm using [VertexArray](http://www.python-sfml.org/api/graphics.html#id22) and
create a quad for each character in a string. Each quad has appropriate
texture coordinates applied.

Example font (PNG file): ![Bitmap font
example](http://i.stack.imgur.com/4evmH.png)

Font rendering code:

    
    
    import sfml
    
    
    class BitmapFont(object):
        '''
        Loads a bitmap font. 
        `chars` is string with all characters available in the font file, example: '+0123456789x'.
        `widths` is mapping between characters and character width in pixels.
        '''
        def __init__(self, path, chars, widths, colors=1, kerning=0):
            self.texture = sfml.Texture.from_file(path)
            self.colors = colors
            self.height = self.texture.height / self.colors
            self.chars = chars
            self.kerning = kerning
            self.widths = widths
    
            self.glyphs = []
            y = 0
            for color in range(self.colors):
                x = 0
                self.glyphs.append({})
                for char in self.chars:
                    glyph_pos = x, y
                    glyph_size = self.widths[char], self.height
                    glyph = sfml.Rectangle(glyph_pos, glyph_size)
                    self.glyphs[color][char] = glyph
                    x += glyph.width
                y += self.height
    
    
    class BitmapText(sfml.TransformableDrawable):
        '''Used to render text with `BitmapFonts`.'''
    
        def __init__(self, string='', font=None, color=0, align='left', position=(0, 0)):
            super().__init__()
            self.vertices = sfml.VertexArray(sfml.PrimitiveType.QUADS, 4)
            self.font = font
            self.color = color
            self._string = ''
            self.string = string
            self.position = position
    
        @property
        def string(self):
            return self._string
    
        @string.setter
        def string(self, value):
            '''Calculates new vertices each time string has changed.'''
            # This function is slowest and probably can be optimized.
    
            if value == self._string:
                return
            if len(value) != len(self._string):
                self.vertices.resize(4 * len(value))
            self._string = value
            x = 0
            y = 0
            vertices = self.vertices
            glyphs = self.font.glyphs[self.color]
            for i, char in enumerate(self._string):
                glyph = glyphs[char]
                p = i * 4
                vertices[p + 0].position = x, y
                vertices[p + 1].position = x + glyph.width, y
                vertices[p + 2].position = x + glyph.width, y + glyph.height
                vertices[p + 3].position = x, y + glyph.height
                vertices[p + 0].tex_coords = glyph.left, glyph.top
                vertices[p + 1].tex_coords = glyph.right, glyph.top
                vertices[p + 2].tex_coords = glyph.right, glyph.bottom
                vertices[p + 3].tex_coords = glyph.left, glyph.bottom
                x += glyph.width + self.font.kerning
    
        def draw(self, target, states):
            '''Draws whole string using texture from a font.'''
            states.texture = self.font.texture
            states.transform = self.transform
            target.draw(self.vertices, states)
    

Simple benchmark with FPS counter:

    
    
    from random import random, randint
    
    import sfml
    
    from font import BitmapFont, BitmapText
    
    
    font = sfml.Font.from_file('arial.ttf')
    
    bitmap_font = BitmapFont('font.png', chars='-x+0123456789 ', kerning=-3,
                             widths={'x': 21, '+': 18, '0': 18, '1': 14, '2': 18, '3': 18, '4': 19, '5': 18, '6': 18,
                                     '7': 17, '8': 18, '9': 18, '-': 17, ' ': 8})
    
    window = sfml.RenderWindow(sfml.VideoMode(960, 640), 'Font test')
    
    fps_text = sfml.Text('', font, 18)
    fps_text.position = 10, 10
    fps_text.color = sfml.Color.WHITE
    
    fps_text_shadow = sfml.Text('', font, 18)
    fps_text_shadow.position = 12, 12
    fps_text_shadow.color = sfml.Color.BLACK
    
    frame = fps = frame_time = 0
    
    clock = sfml.Clock()
    texts = [BitmapText('x01234 56789', font=bitmap_font, color=randint(0, bitmap_font.colors - 1)) for i in range(1000)]
    
    while window.is_open:
        for event in window.events:
            if type(event) is sfml.CloseEvent:
                window.close()
    
        time_delta = clock.restart().seconds
        if time_delta > .2:
            continue
    
        frame_time += time_delta
        if frame_time >= 1:
            fps = frame
            frame_time = frame = 0
            fps_text_shadow.string = fps_text.string = 'FPS: {fps}'.format(fps=fps)
        else:
            frame += 1
    
        window.clear(sfml.Color(63, 63, 63))
    
        for t in texts:
            t.position = random() * 960, random() * 640
            t.string = str(randint(0, 10000000))
            window.draw(t)
    
        window.draw(fps_text_shadow)
        window.draw(fps_text)
        window.display()
    

I'm using Python 3.3, pySFML 1.3, SFML 2.0 and Windows.


Answer: Laurent Gomila (author of SFML) confirmed in [other forum](http://en.sfml-
dev.org/forums/index.php?topic=13455.0), that my approach to bitmap fonts is
same as vector fonts implementation in SFML (namely VertexArray and quad for
each character).

