レンダリング済みフォントサポートクラスサンプル
できた。
プロポーショナルフォントを扱う場合にはフォントのメトリクス構造体から情報を取ってきて描画位置を調整する必要があるのだが、アルファベットの小文字に特殊なやつ(fとかj)がいて、通常の領域をはみ出して描画することがある。
こういった文字を扱うのは難しい話で、画像を生成する処理と描画する処理が一体になっていれば少し大きな画像を作ってズラして描画することで対策できるのだが、画像を作るだけの処理だとそうもいかない。座標補正情報が画像に含まれていないのでそのまま描画すると位置がズレるし、画像を大きくしないと文字が切れる。厄介な話である。
問題の対処のためにFont#infoメソッドを追加してフォントのメトリクスを取得できるようにした。このサンプルではそれを使っている。このあたりは非常にややこしいので詳細を説明する気は起こらない。GetGlyphOutlineとかでぐぐれば出てくる。
このサンプルはレンダリング済みフォントを作るクラスと使うクラスが入っている。ImageFontMakerオブジェクトに文字列を突っ込んでoutputメソッドを呼ぶと文字列をシャッフルして画像を一枚作り、それをバイナリで読み込んで対応表をくっつけてマーシャルで吐き出す。ImageFontのほうはファイル名とdraw_font_exのハッシュ部分のパラメータを渡すとファイルを読み込んで情報を構築するので、あとはdrawメソッドで文字を描画する。という感じになっている。
基本そのまま使えるかもしれないが、改行処理とか入れようとすると幅とか取得するメソッドが必要になってくるんじゃないかと思う。
#!ruby -Ks require 'dxruby' # レンダリング済みフォントを作るクラス class ImageFontMaker ImageFontData = Struct.new(:x, :y, :width, :ox) ImageFontSaveData = Struct.new(:data_hash, :image, :height) def initialize(size, fontname = nil) @font = Font.new(size, fontname) @hash = {} end def add_data(str) str.each_char do |c| @hash[c] = @font.info(c) end end def [](c) @hash[c] end def output(filename) # 必要な画像サイズを調べる # 計算がややこしいことになってるのはfやjなど左右にはみ出す特殊な文字の対応 width = 0 height = 0 keys = @hash.keys.shuffle keys.each do |k| v = @hash[k] ox = v.gmpt_glyphorigin_x < 0 ? -v.gmpt_glyphorigin_x : 0 cx = (v.gm_blackbox_x + v.gmpt_glyphorigin_x) > v.gm_cellinc_x + ox ? (v.gm_blackbox_x + v.gmpt_glyphorigin_x) : v.gm_cellinc_x + ox width += cx if width > 640 height += @font.size width = cx end end height += @font.size # 画像生成 image = Image.new(640, height, C_BLACK) # 文字描き込み x = 0 y = 0 data_hash = {} keys.each do |k| v = @hash[k] ox = v.gmpt_glyphorigin_x < 0 ? -v.gmpt_glyphorigin_x : 0 cx = (v.gm_blackbox_x + v.gmpt_glyphorigin_x) > v.gm_cellinc_x + ox ? (v.gm_blackbox_x + v.gmpt_glyphorigin_x) : v.gm_cellinc_x + ox if x + cx > 640 x = 0 y += @font.size end image.draw_font(x + ox, y, k, @font) data_hash[k] = ImageFontData.new(x, y, cx, ox) x += cx end # いったん保存 image.save(filename + ".png") # バイナリで読み込み image_binary = nil open(filename + ".png", "rb") do |fh| image_binary = fh.read end # マーシャルしてファイルへ imagefont = ImageFontSaveData.new(data_hash, image_binary, @font.size) open(filename, "wb") do |fh| fh.write(Marshal.dump(imagefont)) end end end # レンダリング済みフォントを使うクラス class ImageFont # 生成時のパラメータはdraw_font_exのハッシュ部分と同じ def initialize(filename, param = nil) open(filename, "rb") do |fh| temp = Marshal.load(fh.read) @data_hash = temp.data_hash @image = Image.load_from_file_in_memory(temp.image) @height = temp.height end @cache = {} @param = param end # 文字列描画 def draw(x, y, s, rt = Window) s.each_char do |c| temp = @data_hash[c] if !@cache.has_key?(c) image = @image.slice(temp.x, temp.y, temp.width, @height) @cache[c] = image.effect_image_font(@param) image.delayed_dispose end rt.draw(x - temp.ox, y, @cache[c]) x += temp.width - temp.ox end end end Window.bgcolor = [100,100,100] # レンダリング済みフォント作成 imagefontmaker = ImageFontMaker.new(48, "MS Pゴシック") imagefontmaker.add_data(" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefさghijklmnopqrstuvwxyzあいうえおかきくけこがぎぐげごしすせそざじずぜぞたちつてとだぢづでどなにぬねのはひふへほばびぶべぼぱぴぷぺぽまみむめもやゆよらりるれろわをんっぁぃぅぇぉゃゅょ、。") imagefontmaker.output("ImageFont.dat") # レンダリング済みフォント読み込み imagefont = ImageFont.new("ImageFont.dat", :shadow=>true, :edge=>true, :edge_color=>C_CYAN) Window.loop do imagefont.draw(50, 60, "きょうはあさからよるだった。") imagefont.draw(50, 108, "fあいうえおかきくけこ") imagefont.draw(50, 156, "jさしすせそたちつてと") imagefont.draw(50, 204, "kなにぬねのはひふへほ") end