我正在用 pySFML 编写一个简单的位图字体渲染器,并想问是否有更好更快的方法来解决这个问题。
我正在使用VertexArray并为字符串中的每个字符创建一个四边形。每个四边形都应用了适当的纹理坐标。
示例字体(PNG 文件):
字体渲染代码:
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)
使用 FPS 计数器的简单基准测试:
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()
我正在使用 Python 3.3、pySFML 1.3、SFML 2.0 和 Windows。