Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Simple Bitmap Font implementation using VertexArray -- is this correct approach?  (Read 6212 times)

0 Members and 1 Guest are viewing this topic.

PythonFanboy

  • Newbie
  • *
  • Posts: 5
    • View Profile
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) and create a quad for each character in a string. Each quad has appropriate texture coordinates applied.

Example font (PNG file):

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.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
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).
Laurent Gomila - SFML developer

PythonFanboy

  • Newbie
  • *
  • Posts: 5
    • View Profile
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?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
What you do is exactly what sf::Text does. Is it really slow? Do your strings change that often?
« Last Edit: November 03, 2013, 11:28:11 pm by Laurent »
Laurent Gomila - SFML developer

PythonFanboy

  • Newbie
  • *
  • Posts: 5
    • View Profile
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.