lib/minigl/text.rb in minigl-2.2.9 vs lib/minigl/text.rb in minigl-2.3.0

- old
+ new

@@ -1,18 +1,116 @@ module MiniGL + # This class represents a font and exposes most of the methods from +Gosu::Font+, + # but allows the font to be created from an image, allowing for better customization + # and also using the +retro+ option. + # + # The image used to load the font must meet these criteria: + # * The characters should be laid out in lines of the same height in pixels. + # * The full image must have a height that is a multiple of that line height. + # * The characters should occupy the maximum available space in each line, i.e., + # if a character fits in the current line it must not be placed in the next + # one. In the last line there can be any amount of free space at the end. + class ImageFont + # The height of this font in pixels. + attr_reader :height + + # Creates an +ImageFont+. + # + # Parameters: + # [img_path] Identifier of an image fitting the description in the class documentation, + # as used in +Res.img+. + # [chars] A string containing all characters that will be present in the image, in the + # same order as they appear in the image. Do not include white space. + # [widths] An integer representing the width of the chars in pixels, if this is a fixed + # width font, or an array containing the width of each char, in the same order + # as they appear in the +chars+ string. + # [height] The height of the lines in the image (see description above). + # [space_width] The width of the white space character in this font. + # [global] Parameter that will be passed to +Res.img+ when loading the image. + # [ext] Parameter that will be passed to +Res.img+ when loading the image. + # [retro] Parameter that will be passed to +Res.img+ when loading the image. + def initialize(img_path, chars, widths, height, space_width, global = true, ext = '.png', retro = nil) + retro = Res.retro_images if retro.nil? + img = Res.img(img_path, global, false, ext, retro) + @chars = chars + @images = [] + @height = height + @space_width = space_width + wa = widths.is_a?(Array) + if wa && widths.length != chars.length + raise 'Wrong widths array size!' + end + x = y = 0 + (0...chars.length).each do |i| + @images.push(img.subimage(x, y, wa ? widths[i] : widths, height)) + new_x = x + (wa ? widths[i] : widths) + if i < chars.length - 1 && new_x + (wa ? widths[i+1] : widths) > img.width + x = 0 + y += height + else + x = new_x + end + end + end + + # Returns the width, in pixels, of a given string written by this font. + # + # Parameters: + # [text] The string to be measured + def markup_width(text) + text.chars.reduce(0) { |w, c| if c == ' '; w += @space_width; else; i = @chars.index(c); w += i ? @images[i].width : 0; end } + end + + # See <code>Gosu::Font#draw_markup_rel</code> for details. + def draw_markup_rel(text, x, y, z, rel_x, rel_y, scale_x, scale_y, color) + text = text.to_s unless text.is_a?(String) + if rel_x == 0.5 + x -= scale_x * markup_width(text) / 2 + elsif rel_x == 1 + x -= scale_x * markup_width(text) + end + if rel_y == 0.5 + y -= scale_y * @height / 2 + elsif rel_y == 1 + y -= scale_x * @height + end + text.each_char do |c| + if c == ' ' + x += scale_x * @space_width + next + end + i = @chars.index(c) + next if i.nil? + @images[i].draw(x, y, z, scale_x, scale_y, color) + x += scale_x * @images[i].width + end + end + + # See <code>Gosu::Font#draw_markup</code> for details. + def draw_markup(text, x, y, z, scale_x, scale_y, color) + draw_markup_rel(text, x, y, z, 0, 0, scale_x, scale_y, color) + end + + alias :draw_text_rel :draw_markup_rel + alias :draw_text :draw_markup + alias :text_width :markup_width + end + # This class provides methods for easily drawing one or multiple lines of # text, with control over the text alignment and coloring. class TextHelper # Creates a TextHelper. # # Parameters: # [font] A <code>Gosu::Font</code> that will be used to draw the text. # [line_spacing] When drawing multiple lines, the distance, in pixels, # between each line. - def initialize(font, line_spacing = 0) + def initialize(font, line_spacing = 0, scale_x = 1, scale_y = 1) @font = font @line_spacing = line_spacing + @scale_x = scale_x + @scale_y = scale_y end # Draws a single line of text. # # Parameters: @@ -46,11 +144,11 @@ # # *Obs.:* This method accepts named parameters, but +text+, +x+ and +y+ are # mandatory. def write_line(text, x = nil, y = nil, mode = :left, color = 0, alpha = 0xff, effect = nil, effect_color = 0, effect_size = 1, effect_alpha = 0xff, - z_index = 0) + z_index = 0, scale_x = nil, scale_y = nil) if text.is_a? Hash x = text[:x] y = text[:y] mode = text.fetch(:mode, :left) color = text.fetch(:color, 0) @@ -58,13 +156,17 @@ effect = text.fetch(:effect, nil) effect_color = text.fetch(:effect_color, 0) effect_size = text.fetch(:effect_size, 1) effect_alpha = text.fetch(:effect_alpha, 0xff) z_index = text.fetch(:z_index, 0) + scale_x = text.fetch(:scale_x, nil) + scale_y = text.fetch(:scale_y, nil) text = text[:text] end + scale_x = @scale_x if scale_x.nil? + scale_y = @scale_y if scale_y.nil? color = (alpha << 24) | color rel = case mode when :left then 0 when :center then 0.5 @@ -72,31 +174,31 @@ else 0 end if effect effect_color = (effect_alpha << 24) | effect_color if effect == :border - @font.draw_markup_rel text, x - effect_size, y - effect_size, z_index, rel, 0, 1, 1, effect_color - @font.draw_markup_rel text, x, y - effect_size, z_index, rel, 0, 1, 1, effect_color - @font.draw_markup_rel text, x + effect_size, y - effect_size, z_index, rel, 0, 1, 1, effect_color - @font.draw_markup_rel text, x + effect_size, y, z_index, rel, 0, 1, 1, effect_color - @font.draw_markup_rel text, x + effect_size, y + effect_size, z_index, rel, 0, 1, 1, effect_color - @font.draw_markup_rel text, x, y + effect_size, z_index, rel, 0, 1, 1, effect_color - @font.draw_markup_rel text, x - effect_size, y + effect_size, z_index, rel, 0, 1, 1, effect_color - @font.draw_markup_rel text, x - effect_size, y, z_index, rel, 0, 1, 1, effect_color + @font.draw_markup_rel text, x - effect_size, y - effect_size, z_index, rel, 0, scale_x, scale_y, effect_color + @font.draw_markup_rel text, x, y - effect_size, z_index, rel, 0, scale_x, scale_y, effect_color + @font.draw_markup_rel text, x + effect_size, y - effect_size, z_index, rel, 0, scale_x, scale_y, effect_color + @font.draw_markup_rel text, x + effect_size, y, z_index, rel, 0, scale_x, scale_y, effect_color + @font.draw_markup_rel text, x + effect_size, y + effect_size, z_index, rel, 0, scale_x, scale_y, effect_color + @font.draw_markup_rel text, x, y + effect_size, z_index, rel, 0, scale_x, scale_y, effect_color + @font.draw_markup_rel text, x - effect_size, y + effect_size, z_index, rel, 0, scale_x, scale_y, effect_color + @font.draw_markup_rel text, x - effect_size, y, z_index, rel, 0, scale_x, scale_y, effect_color elsif effect == :shadow - @font.draw_markup_rel text, x + effect_size, y + effect_size, z_index, rel, 0, 1, 1, effect_color + @font.draw_markup_rel text, x + effect_size, y + effect_size, z_index, rel, 0, scale_x, scale_y, effect_color end end - @font.draw_markup_rel text, x, y, z_index, rel, 0, 1, 1, color + @font.draw_markup_rel text, x, y, z_index, rel, 0, scale_x, scale_y, color end # Draws text, breaking lines when needed and when explicitly caused by the # "\n" character. # # Parameters: # [text] The text to be drawn. Line breaks are allowed. You can use the - # `<b>` tag for bold, `<i>` for italic and `<c=rrggbb>` for colors. + # `<b>` tag for bold, `<i>` for italic and `<c=rrggbb>` for colors. # [x] The horizontal reference for drawing the text. Works like in # +write_line+ for the +:left+, +:right+ and +:center+ modes. For the # +:justified+ mode, works the same as for +:left+. # [y] The vertical reference for drawing the text. All text will be drawn # from this point down. @@ -107,57 +209,60 @@ # [color] The color of the text, in hexadecimal RRGGBB format. # [alpha] The opacity of the text. Valid values vary from 0 (fully # transparent) to 255 (fully opaque). # [z_index] The z-order to draw the object. Objects with larger z-orders # will be drawn on top of the ones with smaller z-orders. - def write_breaking(text, x, y, width, mode = :left, color = 0, alpha = 0xff, z_index = 0) + def write_breaking(text, x, y, width, mode = :left, color = 0, alpha = 0xff, z_index = 0, scale_x = nil, scale_y = nil, line_spacing = nil) + line_spacing = @line_spacing if line_spacing.nil? + scale_x = @scale_x if scale_x.nil? + scale_y = @scale_y if scale_y.nil? color = (alpha << 24) | color text.split("\n").each do |p| if mode == :justified - y = write_paragraph_justified p, x, y, width, color, z_index + y = write_paragraph_justified p, x, y, width, color, z_index, scale_x, scale_y, line_spacing else rel = case mode when :left then 0 when :center then 0.5 when :right then 1 else 0 end - y = write_paragraph p, x, y, width, rel, color, z_index + y = write_paragraph p, x, y, width, rel, color, z_index, scale_x, scale_y, line_spacing end end end private - def write_paragraph(text, x, y, width, rel, color, z_index) + def write_paragraph(text, x, y, width, rel, color, z_index, scale_x, scale_y, line_spacing) line = '' line_width = 0 text.split(' ').each do |word| - w = @font.markup_width word - if line_width + w > width - @font.draw_markup_rel line.chop, x, y, z_index, rel, 0, 1, 1, color + w = @font.markup_width(word) + if line_width + w * scale_x > width + @font.draw_markup_rel line.chop, x, y, z_index, rel, 0, scale_x, scale_y, color line = '' line_width = 0 - y += @font.height + @line_spacing + y += (@font.height + line_spacing) * scale_y end line += "#{word} " - line_width += @font.markup_width "#{word} " + line_width += @font.markup_width("#{word} ") * scale_x end - @font.draw_markup_rel line.chop, x, y, z_index, rel, 0, 1, 1, color unless line.empty? - y + @font.height + @line_spacing + @font.draw_markup_rel line.chop, x, y, z_index, rel, 0, scale_x, scale_y, color unless line.empty? + y + (@font.height + line_spacing) * scale_y end - def write_paragraph_justified(text, x, y, width, color, z_index) - space_width = @font.text_width ' ' + def write_paragraph_justified(text, x, y, width, color, z_index, scale_x, scale_y, line_spacing) + space_width = @font.text_width(' ') * scale_x spaces = [[]] line_index = 0 new_x = x words = text.split(' ') words.each do |word| - w = @font.markup_width word - if new_x + w > x + width + w = @font.markup_width(word) + if new_x + w * scale_x > x + width space = x + width - new_x + space_width index = 0 while space > 0 spaces[line_index][index] += 1 space -= 1 @@ -168,22 +273,22 @@ spaces << [] line_index += 1 new_x = x end - new_x += @font.markup_width(word) + space_width + new_x += @font.markup_width(word) * scale_x + space_width spaces[line_index] << space_width end index = 0 spaces.each do |line| new_x = x line.each do |s| - @font.draw_markup words[index], new_x, y, z_index, 1, 1, color - new_x += @font.markup_width(words[index]) + s + @font.draw_markup(words[index], new_x, y, z_index, scale_x, scale_y, color) + new_x += @font.markup_width(words[index]) * scale_x + s index += 1 end - y += @font.height + @line_spacing + y += (@font.height + line_spacing) * scale_y end y end end end