SFML community forums

Bindings - other languages => Python => Topic started by: PythonFanboy on November 03, 2013, 12:02:08 pm

Title: Simple Bitmap Font implementation using VertexArray -- is this correct approach?
Post by: PythonFanboy on November 03, 2013, 12:02:08 pm
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. Don't really know if my approach is optimal.

I'm using VertexArray (http://www.python-sfml.org/api/graphics.html#id22 (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): (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 example usage with benchmark (FPS counter) is attached.

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

Note, that I've asked same question on StackOverflow, but nobody replied, so I've decided here would be a better place to ask.
Title: Re: Simple Bitmap Font implementation using VertexArray -- is this correct approach?
Post by: Laurent on November 03, 2013, 12:57:26 pm
What exactly are you asking? Is there any problem with your implementation? It seems like it's ok, from what you said (I haven't looked at your implementation).
Title: Re: Simple Bitmap Font implementation using VertexArray -- is this correct approach?
Post by: PythonFanboy on November 03, 2013, 01:15:02 pm
I'm rather new to OpenGL (or 3D in general), so I just wanted to ask if my approach is correct. Above code works, but it seems rather slow and expensive. Every time a string to display changes, new vertices must be calculated. Do quads as a primitive type is optimal, or maybe triangles strips should be used in this situation?
Title: Re: Simple Bitmap Font implementation using VertexArray -- is this correct approach?
Post by: Laurent on November 03, 2013, 03:50:17 pm
What you do is exactly what sf::Text does. Is it really slow? Do your strings change that often?
Title: Re: Simple Bitmap Font implementation using VertexArray -- is this correct approach?
Post by: PythonFanboy on November 03, 2013, 11:02:27 pm
Great, thanks! It's very slow compared to sf::Text, but I think now, that is probably due to speed difference between python interpreter and compiled C code. Anyway thanks for clarification.