2024/02/13(火) [n年前の日記]
#1 [basic] FreeBASICでCustom fontを使ってみたい
FreeBASICは、Draw String() を使うと、グラフィックス画面にテキストを描画することができる。ただ、使われるフォントが豆粒みたいに小さくて読みづらい…。もう少し大きいフォントを使いたい。
一応、Draw String はカスタムフォントを利用できるらしいので、そのあたりを試してみた。
ファイル一式は以下。
_fbcustomfont_20240213.zip
一応、Draw String はカスタムフォントを利用できるらしいので、そのあたりを試してみた。
ファイル一式は以下。
_fbcustomfont_20240213.zip
◎ カスタムフォントの仕様 :
bitmap font が敷き詰められたフォント画像(.bmp)を与えればすぐさま利用できるのかなと思ったけれど、どうやらそんな単純な仕様では無さそうで…。
_DrawString
_FBgfx Image and Font Buffers - FreeBASIC Wiki Manual | FBWiki
bitmap font画像を渡すまでは予想通りだったけど、そのbmp画像の中にフォント情報(対象文字コードや各文字の横幅)も含ませておかないといかんらしい。
まず、bmp画像の最初の1ラスター目が、font header になっていて、そこにフォント情報を byte 単位で書き込まないといけないらしい。実際の bitmap font部分は、2ラスター目から置かれている。
font header の内容は以下。
個人的に、この仕様はどうなんだと思えた。例えば画像編集ソフトでフォント画像を開いて、色調補正をしただけで、フォント情報が一発で破壊されてしまう。よろしくない気がする。いやまあ、フォント画像一つでカスタムフォントを扱いたい、フォント画像の中にフォント情報まで含めてしまいたい、と考えたらこうなったのだろうとは想像できるけど…。
_DrawString
_FBgfx Image and Font Buffers - FreeBASIC Wiki Manual | FBWiki
bitmap font画像を渡すまでは予想通りだったけど、そのbmp画像の中にフォント情報(対象文字コードや各文字の横幅)も含ませておかないといかんらしい。
まず、bmp画像の最初の1ラスター目が、font header になっていて、そこにフォント情報を byte 単位で書き込まないといけないらしい。実際の bitmap font部分は、2ラスター目から置かれている。
font header の内容は以下。
1 byte目 : Version情報。これは現在 0 しか用意されてないらしい。 2 byte目 : 開始文字コード。例えば ASCIIコードの「空白」から始まるなら、0x20になる。 3 byte目 : 終了文字コード。例えば ASCIIコードの「DEL」で終わるなら、0x7f になる。 4 byte目 : 開始文字 + 0 の横幅ドット数 5 byte目 : 開始文字 + 1 の横幅ドット数 6 byte目 : 開始文字 + 2 の横幅ドット数 ... ... N byte目 : 終了文字の横幅ドット数
個人的に、この仕様はどうなんだと思えた。例えば画像編集ソフトでフォント画像を開いて、色調補正をしただけで、フォント情報が一発で破壊されてしまう。よろしくない気がする。いやまあ、フォント画像一つでカスタムフォントを扱いたい、フォント画像の中にフォント情報まで含めてしまいたい、と考えたらこうなったのだろうとは想像できるけど…。
◎ 変換ツールを書いてみた :
とりあえず、使ってみよう…。
等幅フォント、色深度32bitに限定して、読み込んだ bitmapフォント画像(.bmp)に Font header情報を追加/書き込んでから、bmp画像として保存するツールを書いてみた。
例えば、以下のように、ASCIIコード 0x20 - 0x7f の文字が、横一列に並んでいるbitmapフォント画像を用意して…。
_fbfont_bs_pet2015_16x16.bmp
このbmp画像を読み込んで、Font header 情報を追加して、以下のようなカスタムフォント用画像として出力する。
_fbfont_pet2015_16x16.bmp
見た目で違いは分からないだろうけど、一番上に1ラスター分追加されていて、そこに Font header情報が書き込まれている。
変換ツールのソースは以下。
_make_mono_font.bas
fbc make_mono_font.bas でコンパイル。
使い方は以下。
等幅フォント、色深度32bitに限定して、読み込んだ bitmapフォント画像(.bmp)に Font header情報を追加/書き込んでから、bmp画像として保存するツールを書いてみた。
例えば、以下のように、ASCIIコード 0x20 - 0x7f の文字が、横一列に並んでいるbitmapフォント画像を用意して…。
_fbfont_bs_pet2015_16x16.bmp
このbmp画像を読み込んで、Font header 情報を追加して、以下のようなカスタムフォント用画像として出力する。
_fbfont_pet2015_16x16.bmp
見た目で違いは分からないだろうけど、一番上に1ラスター分追加されていて、そこに Font header情報が書き込まれている。
変換ツールのソースは以下。
_make_mono_font.bas
Const START_CHR = &H20 Const END_CHR = &H7f Const SCRW = 800 Const SCRH = 600 Dim As Integer sdepth = 32 If Command(1) = "" And Command(2) = "" Then Print "Usage: " & Command(0) & " INPUT.bmp OUTPUT.bmp" : End End If Dim As String infilename = Command(1) Dim As String outfilename = Command(2) Dim As Integer f Dim As Long w, h ' open bitmap file f = FreeFile() Open infilename For Binary Access Read As #f If Err > 0 Then Print "Error: Can not open file" : End ' get bitmap width and height Get #f, 19, w ' get width Get #f, , h ' get height Close #f ScreenRes SCRW, SCRH, sdepth ' load base font image Dim As Any Ptr base_font = ImageCreate(w, h, RGB(0, 0, 0)) bload infilename, base_font Print "Load : " & infilename ' create new font image Dim As Any Ptr out_font = ImageCreate(w, h + 1, RGB(255, 0, 255)) Put out_font, (0, 1), base_font, PSet ' get custom font header address Dim As ubyte Ptr headerp ImageInfo out_font, , , , , headerp ' set header headerp[0] = 0 headerp[1] = START_CHR ' first character code headerp[2] = END_CHR ' end character code ' get 1 character Width Dim As Integer cw = Int(w / (END_CHR - START_CHR + 1)) For i As Integer = 0 To (END_CHR - START_CHR) headerp[3 + i] = cw Next i bsave outfilename, out_font color RGB(255, 255, 255), RGB(0, 0, 0) Print "Save : " & outfilename Print "Push Any Key" sleep ImageDestroy out_font ImageDestroy base_font
fbc make_mono_font.bas でコンパイル。
使い方は以下。
Usage: make_mono_font.exe INPUT_FONT.bmp OUTPUT_FONT.bmp
◎ テスト表示してみる :
変換して生成されたフォント画像(.bmp)を読み込んで、カスタムフォントとして利用してみる。
_test_fbfont.bas
fbc test_fbfont.bas でコンパイル。
使い方は以下。表示してみたいフォント画像をコマンドラインオプションで指定する。
たしかに、カスタムフォントが利用できた。
再度メモ。ファイル一式は以下。
_fbcustomfont_20240213.zip
_test_fbfont.bas
Const SCRW = 512 Const SCRH = 288 Dim As Integer sdepth = 32 If Command(1) = "" Then Print "Usage : " & Command(0) & " INPUT_FONT.bmp" : end End If ScreenRes SCRW, SCRH, sdepth Dim As String infilename = Command(1) Dim As Integer f Dim As Long w, h ' open bitmap file f = FreeFile() Open infilename For Binary Access Read As #f If Err > 0 Then Print "Error: Can not open file" : End ' get bitmap width and height Get #f, 19, w ' get width Get #f, , h ' get height Close #f Dim As Any Ptr img = ImageCreate(w, h) bload infilename, img Color RGB(255, 255, 255), RGB(0, 0, 0) cls h = 34 Draw String (0, h * 0), "Hello World !", , img Draw String (0, h * 1), infilename, , img Draw String (0, h * 2), "0123456789", , img Draw String (0, h * 3), "@ABCDEFGHIJKLMNO", , img Draw String (0, h * 4), "PQRSTUVWXYZ[\]^_", , img Draw String (0, h * 5), "`abcdefghijklmno", , img Draw String (0, h * 6), "pqrstuvwxyz{|}~ ", , img sleep ImageDestroy img
fbc test_fbfont.bas でコンパイル。
使い方は以下。表示してみたいフォント画像をコマンドラインオプションで指定する。
Usage: test_fbfont.exe INPUT_FONT.bmp
たしかに、カスタムフォントが利用できた。
再度メモ。ファイル一式は以下。
_fbcustomfont_20240213.zip
◎ 他のフォントを利用した際のスクリーンショット :
以前、
_OpenGLでビットマップフォントを描画した際
のフォントデータを利用して試してみた。
courR18 (16x26)
profont (12x22)
東雲フォント shnm8x16r (8x16)
東雲フォント2倍拡大 shnm8x16r x 2 (16x32)
Terminus font (12x24)
各フォントの入手先やライセンスは以下を参照のこと。
_readme.md
courR18 (16x26)
profont (12x22)
東雲フォント shnm8x16r (8x16)
東雲フォント2倍拡大 shnm8x16r x 2 (16x32)
Terminus font (12x24)
各フォントの入手先やライセンスは以下を参照のこと。
_readme.md
[ ツッコむ ]
#2 [basic] FreeBASICで等幅ビットマップフォントのみを使って描画
FreeBASIC の Draw string でカスタムフォントを使ってみたけれど、フォント画像を用意するのが面倒臭いだろうなと思えてきた。これがもし、等幅フォントのみを扱うことを前提にするなら、もっと簡単にフォント画像を用意できそうだし、描画処理も簡単にできそうな気がする。試してみた。
環境は、Windows10 x64 22H2 + FreeBASIC 1.10.1 32bit。
環境は、Windows10 x64 22H2 + FreeBASIC 1.10.1 32bit。
◎ フォント画像の用意 :
まずは、以下のような文字が並んでいる画像を、等幅フォントで作成。
透明色=RGB(255, 0, 255) として扱う。以下のような画像になる。
これなら、1文字当たりの横幅、縦幅は、(画像横幅 / 16), (画像縦幅 / 6) で求められる。
後は、与えらえた文字列の、各文字に対応する領域を Put() で描画していけばいい。
!"#$%&'()*+,-./ 0123456789:;<=>? @ABCDEFGHIJKLMNO PQRSTUVWXYZ[\]^_ `abcdefghijklmno pqrstuvwxyz{|}~
透明色=RGB(255, 0, 255) として扱う。以下のような画像になる。
これなら、1文字当たりの横幅、縦幅は、(画像横幅 / 16), (画像縦幅 / 6) で求められる。
後は、与えらえた文字列の、各文字に対応する領域を Put() で描画していけばいい。
◎ 描画処理 :
そんな感じで、描画処理は以下のようになった。
画像の読み込み、テキスト描画をする部分。
_bitmapasciifont.bi
簡単な使用サンプル。
_simple_sample.bas
フォント画像を渡して描画するサンプル。
_test_bitmapasciifont.bas
スクリーンショットは以下。
ファイル一式を置いておきます。御自由にどうぞ。
_bitmapasciifont_20240213.zip
これなら、フォント画像を作るのも簡単なのではないかな…。
フォントの入手先は以下を参照のこと。
_readme.md
画像の読み込み、テキスト描画をする部分。
_bitmapasciifont.bi
簡単な使用サンプル。
_simple_sample.bas
#include "bitmapasciifont.bi" ScreenRes 320, 240, 32 Dim fnt As BitmapAsciiFont fnt.load_image("font_profont_12x22.bmp") fnt.draw_string(10, 10, "Hello World !") sleep fnt.destroy()
フォント画像を渡して描画するサンプル。
_test_bitmapasciifont.bas
#include "bitmapasciifont.bi" If Command(1) = "" Then Print "Usage: " & Command(0) & " INPUT_FONT.bmp" : end End If Dim As String fontfile = Command(1) Const SCRW = 640 Const SCRH = 480 Dim As Integer sdepth = 32 ScreenRes SCRW, SCRH, sdepth Dim fnt As BitmapAsciiFont fnt.load_image(fontfile) Dim As Integer h = fnt.cheight + 1 fnt.draw_string(0, h * 0, "Load: " & fontfile) fnt.draw_string(0, h * 1, "Hello World ! 0123456789") fnt.draw_string(0, h * 2, "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_") fnt.draw_string(0, h * 3, "`abcdefghijklmnopqrstuvwxyz{|}~ ") fnt.draw_string(0, h * 4, "Push any key to exit.") sleep fnt.destroy()
スクリーンショットは以下。
ファイル一式を置いておきます。御自由にどうぞ。
_bitmapasciifont_20240213.zip
これなら、フォント画像を作るのも簡単なのではないかな…。
フォントの入手先は以下を参照のこと。
_readme.md
[ ツッコむ ]
#3 [basic] FreeBASICでテキストファイル読み込みをしたい
FreeBASIC で、テキストファイルを行単位で読み込みたい。環境は Windows10 x64 22H2 + FreeBASIC 1.10.1 32bit。
以下が参考になった。
_ファイル入出力関連
_Freefile
_Open
_Eof
_LineinputPp
動作確認するために、サンプルソースを書いてみた。
_lineinput.bas
fbc lineinput.bas でコンパイル。
_test.txt を用意して読み込ませてみた。
_test.txt
たしかに、1行ずつ読み込みができた。
行末については、LF か CRLF があれば行末/改行コードとして扱う、と書いてある。本当にそうなっているのか、読み込むテキストファイルの改行コードを LF もしくは CRLF にして試してみたけど、どちらも行単位で読み込んでくれた。
以下が参考になった。
_ファイル入出力関連
_Freefile
_Open
_Eof
_LineinputPp
- FreeFile() で空いているファイル番号を取得して、
- Open でファイルを開いて、
- EOF() でファイルの終端かどうかをチェックしつつ、
- Line Input で1行読み込んで、
- 処理が終わったら Close
動作確認するために、サンプルソースを書いてみた。
_lineinput.bas
Const infile = "test.txt" ' Open file Dim As Long f = FreeFile() If (Open(infile For Input As #f)) Then Print "Error: Cano not open " & infile : End -1 End If ' Read lines Dim As Integer count = 0 Do until EOF(f) count += 1 Dim As String s Line Input #f, s ' Read 1 line Print Str(count) & ": " & s Loop ' Close file Close #f
fbc lineinput.bas でコンパイル。
_test.txt を用意して読み込ませてみた。
_test.txt
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
> lineinput.exe 1: Lorem ipsum dolor sit amet, 2: consectetur adipiscing elit, 3: 4: sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 5: 6: Ut enim ad minim veniam, quis nostrud exercitation ullamco 7: laboris nisi ut aliquip ex ea commodo consequat.
たしかに、1行ずつ読み込みができた。
行末については、LF か CRLF があれば行末/改行コードとして扱う、と書いてある。本当にそうなっているのか、読み込むテキストファイルの改行コードを LF もしくは CRLF にして試してみたけど、どちらも行単位で読み込んでくれた。
[ ツッコむ ]
以上、1 日分です。