Gosu::Image.from_text
. Instead, I've added a render
method that accepts the same arguments. The render
method requires TexPlay, but everything else should work with just Gosu.require 'gosu'
require 'json'
require 'weakref'
class BitmapFont
WORD_BREAK = /\s|\b-|$/
CHARSET_RANGE = 0x20..0x7f
def initialize window, filename, height = nil
properties = File.open(filename) { |io| JSON.load io }
# probably shouldn't hold onto this, but...
@window = WeakRef.new window
@name = properties['name'] || File.basename(filename).sub(/\.[^\.]+$/, '')
# font metrics
@width = Integer properties['width']
@height = Integer properties['height'] || @width
@scale = height ? height.to_f / @height : 1
@exceptions = properties['exceptions'] || { }
# images
image_filename = if properties.include? 'image'
File.expand_path properties['image'], File.dirname(filename)
else
filename.sub /\.[^\.]+$/, '.png'
end
tile_width = properties['tile_width'] || @width
tile_height = properties['tile_height'] || @height
@images = *Gosu::Image.load_tiles(window,
image_filename, tile_width, tile_height, false)
end # initialize
def height
@height * @scale
end
def text_width text, factor_x = 1
text.each_char.inject(0) { |width, ch| width + char_width(ch) } * factor_x
end
def draw string, x, y, z, factor_x = 1, factor_y = 1, color = 0xffffffff, mode = :default
x_off = 0
@window.scale factor_x, factor_y, x, y do
string.each_char do |ch|
image = char_image(ch)
image.draw x + x_off, y, z, @scale, @scale, color, mode if image
x_off += char_width(ch)
end
end
end # draw
def draw_rel text, x, y, z, rel_x, rel_y, factor_x = 1, factor_y = 1, color = 0xffffffff, mode = :default
draw_width = text_width(text, factor_x)
draw_height = height * factor_y
@window.translate(-draw_width * rel_x, -draw_height * rel_y) do
draw text, x, y, z, factor_x, factor_y, color, mode
end
end
def draw_rot text, x, y, z, angle, factor_x = 1, factor_y = 1, color = 0xffffffff, mode = :default
@window.translate(x, y) do
@window.rotate(angle) do
draw_rel text, 0, 0, z, 0.5, 0.5, factor_x, factor_y, color, mode
end
end
end
def render text, line_spacing, max_width, align
raise NotImplementedError,
"Rendering to an image requires TexPlay to be installed", caller \
unless defined? ::TexPlay
lines = word_wrap(text, max_width)
draw_width = max_width
draw_height = (height + line_spacing).floor * lines.count
canvas = TexPlay.create_image @window.__getobj__, draw_width, draw_height,
:caching => false
y_off = 0
lines.each do |line|
render_line canvas, line, y_off, align
y_off += (height + line_spacing).floor
end
canvas
end # render
private
def parse_characters spec
case spec
when Enumerable
spec.inject([]) { |charset, subspec|
charset += parse_characters(subspec) }
when /^(.)-(.)$/
($1..$2).to_a
else
spec.characters
end
end
def char_image ch
if CHARSET_RANGE.include? ch.ord
index = ch.ord - CHARSET_RANGE.begin
@images[index]
end
end
def char_width ch
if @exceptions.include? ch
@exceptions[ch] * @scale
else
@width * @scale
end
end # char_width
def word_wrap text, max_width
lines = [ ]
first_index = 0
last_index = test_index = text.index(WORD_BREAK)
while test_index
if text_width(text[first_index ... test_index]) >= max_width
lines << text[first_index ... last_index]
first_index = last_index + 1
end
last_index = test_index
test_index = text.index WORD_BREAK, last_index + 1
end
lines << text[first_index .. last_index]
lines
end # word_wrap
def render_line canvas, line, y_off, align
x_off = case align
when :left then 0
when :center then (canvas.width - text_width(line)) / 2
when :right then canvas.width - text_width(line)
end
line.each_char do |ch|
image = char_image(ch)
draw_width = char_width(ch)
draw_height = height.floor
canvas.rect x_off, y_off, x_off + draw_width, y_off + draw_height,
:color => :white,
:fill => true,
:color_control => proc { |src, dst, x, y|
image.get_pixel(((x - x_off) / @scale).floor,
((y - y_off) / @scale).floor) || src || :none }
x_off += draw_width
end
end # render_line
end # BitmapFont
begin
require 'texplay'
rescue LoadError
$stderr.puts('TexPlay is not available; BitmapFont rendering will be ' +
'unavailable') if $DEBUG
end
Font#[]=
method that would replace the internal Image for any letter by a given image (Gosu, RMagick, blob, ...). That would be possible without messing with the rest of Font and would imply that there will always be a fallback for characters that are left undefined by the Bitmap font. Thoughts?
Gosu::Font.new(window, height)
?
block_given?
in Font#draw
. The C++ interface will probably not look as pretty though.
diff --git a/bitmap_font.rb b/bitmap_font.rb
index 9a11368..08b9370 100644
--- a/bitmap_font.rb
+++ b/bitmap_font.rb
@@ -49,7 +49,12 @@ class BitmapFont
@window.scale factor_x, factor_y, x, y do
string.each_char do |ch|
image = char_image(ch)
- image.draw x + x_off, y, z, @scale, @scale, color, mode if image
+
+ if block_given?
+ yield image, x + x_off, y
+ else
+ image.draw x + x_off, y, z, @scale, @scale, color, mode if image
+ end
x_off += char_width(ch)
end
> rendered font size in relation to the actual size in the ttf file
Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill