2024/02/04(日) [n年前の日記]
#1 [basic] FreeBASICでexeに画像バイナリを含めたい。その2
_昨日、
の実験の続き。FreeBASICで生成した実行形式ファイル(exeファイル)の中に、画像のバイナリデータを含めてしまって、それをウインドウに描画したい。
環境は Windows10 x64 22H2 + FreeBASIC 1.10.1 32bit。
調べた範囲では、方法は4つある。昨日メモした内容を再度メモ。
リソースファイル利用、bin2bas利用、asm記述の利用については昨日試した。asm記述の利用だけは上手く行かなかったけど…。
今回は、バイナリファイルをオブジェクトファイルに変換してリンカで結合する方法を試してみた。
環境は Windows10 x64 22H2 + FreeBASIC 1.10.1 32bit。
調べた範囲では、方法は4つある。昨日メモした内容を再度メモ。
- Windows限定になるけれど、リソースファイルに画像データを含めてしまう。BASICから FindResource() や LoadResource() 等でアクセスする。
- bin2bas と言うツールを使って、バイナリをBASICの配列の形にする。ただ、この方法はファイルサイズ制限があるらしい。
- FreeBASICはアセンブラの記述も盛り込めるので、asm - end asm や .incbin を使ってバイナリを内包してしまう。
- バイナリをオブジェクトファイルに変換して、リンカで結合して内包させる。
リソースファイル利用、bin2bas利用、asm記述の利用については昨日試した。asm記述の利用だけは上手く行かなかったけど…。
今回は、バイナリファイルをオブジェクトファイルに変換してリンカで結合する方法を試してみた。
◎ オブジェクトファイル化して結合する方法 :
以下で詳しく説明されてた。ありがたや。
_How to embed any binary file into your FB executable - freebasic.net
まずは、バイナリファイルのオブジェクトファイル化。MinGW や MSYS2 についてくる ld.exe、または、objcopy.exe を利用して、バイナリファイル、 _image_png.png を、オブジェクトファイル image_png.o に変換する。
これを、.bas と一緒にして、FreeBASIC でコンパイルする。
.bas のソースは以下。
_loadpngld.bas
生成された loadpngld.exe を実行したら、ウインドウ内にpng画像が描画された。
ということで、この方法でも画像データを exe に内包して利用できると分かった。
ただ、この方法は、ld か objcopy が必要になる点がちょっと厳しいかもしれない。MinGW や MSYS2 をインストールしてある環境なら、 _GNU Binutils パッケージ をインストールして ld や objcopy を使えるけれど、FreeBASIC しかインストールしてない環境ではオブジェクトファイル化が難しいだろうなと…。
少し解説。
ld もしくは objcopy を使って、バイナリファイルをオブジェクトファイル化すると、そのオブジェクトファイルにはいくつかのシンボルが入ってる。objdump -t hoge.o でシンボルを確認できる。
hoge.png というファイルをオブジェクト化したなら、以下のようなシンボルになる。
注意点。FreeBASIC が32bitか、64bitかで、FreeBASICからアクセスする際のシンボル名が微妙に変わるらしい。32bit版は、先頭の「_」が無くなるのだとか。
FreeBASIC側の仕様についてもメモ。
___FB_64BIT__
#ifdef - #else - #endif を使えば、Windowsとそれ以外、64bitとそれ以外、といった具合に条件を分けてコンパイルできる。
_条件付きコンパイル
FreeBASICでは、シンボル?の前に「@」がつくと、アドレスを示す値になるらしい。あちこちのソースに出現していて、これは一体何だろうと思ってた…。また、ptr はポインタ変数であることを示してる。
_演算子 @ (のアドレス)
_POINTER | PTR
_How to embed any binary file into your FB executable - freebasic.net
まずは、バイナリファイルのオブジェクトファイル化。MinGW や MSYS2 についてくる ld.exe、または、objcopy.exe を利用して、バイナリファイル、 _image_png.png を、オブジェクトファイル image_png.o に変換する。
ld -r -b binary -o image_png.o image_png.png or objcopy -I binary -O elf32-i386 -B i386 image_png.png image_png.o
これを、.bas と一緒にして、FreeBASIC でコンパイルする。
fbc loadpngld.bas image_png.o
.bas のソースは以下。
_loadpngld.bas
' 画像読み込み用ライブラリ FBImage を使う #include once "FBImage.bi" ' カレントディレクトリを exeファイルのある場所にする chdir exepath() ' リンクしたバイナリオブジェクトの先頭アドレスと終了アドレス #If defined( __FB_WIN32__ ) And Not defined( __FB_64BIT__ ) ' Windows 32bit extern image_png_png_start alias "binary_image_png_png_start" as byte extern image_png_png_end alias "binary_image_png_png_end" as byte #Else ' Windows 64bit and other extern image_png_png_start alias "_binary_image_png_png_start" as byte extern image_png_png_end alias "_binary_image_png_png_end" as byte #endif ' デスクトップ解像度を指定 Dim As Integer scrw = 512 Dim As Integer scrh = 288 ' ウインドウサイズと色深度(bpp)を指定 screenres scrw, scrh, 32 ' バイナリデータの先頭アドレスとサイズを取得 dim as byte ptr imgdata = @image_png_png_start dim as Integer imgdata_length = @image_png_png_end - @image_png_png_start ' メモリ上にある画像データを FBImage で読み込む。ポインタとバイト数を渡す var img = LoadRGBAMemory(imgdata, imgdata_length) ' 画像を描画。RGB=(255, 0, 255) のピクセルは透明色として扱う Put (16, 16), img, TRANS ' キー入力があるまで待ち続ける sleep ' 画像を使い終わったので破棄 ImageDestroy img
生成された loadpngld.exe を実行したら、ウインドウ内にpng画像が描画された。
ということで、この方法でも画像データを exe に内包して利用できると分かった。
ただ、この方法は、ld か objcopy が必要になる点がちょっと厳しいかもしれない。MinGW や MSYS2 をインストールしてある環境なら、 _GNU Binutils パッケージ をインストールして ld や objcopy を使えるけれど、FreeBASIC しかインストールしてない環境ではオブジェクトファイル化が難しいだろうなと…。
少し解説。
ld もしくは objcopy を使って、バイナリファイルをオブジェクトファイル化すると、そのオブジェクトファイルにはいくつかのシンボルが入ってる。objdump -t hoge.o でシンボルを確認できる。
> objdump -t image_png.o image_png.o: file format pe-i386 SYMBOL TABLE: [ 0](sec 1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000000 _binary_image_png_png_start [ 1](sec -1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000c4a8 _binary_image_png_png_size [ 2](sec 1)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x0000c4a8 _binary_image_png_png_end
hoge.png というファイルをオブジェクト化したなら、以下のようなシンボルになる。
- 開始アドレスを示すシンボルは _binary_hoge_png_start に。
- 終了アドレスを示すシンボルは _binary_hoge_png_end に。
注意点。FreeBASIC が32bitか、64bitかで、FreeBASICからアクセスする際のシンボル名が微妙に変わるらしい。32bit版は、先頭の「_」が無くなるのだとか。
32bit: binary_image_png_png_start binary_image_png_png_end 64bit: _binary_image_png_png_start _binary_image_png_png_end
FreeBASIC側の仕様についてもメモ。
- __FB_WIN32__ は、Windows上でコンパイルする際に定義されるシンボル。
- __FB_64BIT__ は、64bitアプリとしてコンパイルする際に定義されるシンボル。
___FB_64BIT__
#ifdef - #else - #endif を使えば、Windowsとそれ以外、64bitとそれ以外、といった具合に条件を分けてコンパイルできる。
_条件付きコンパイル
FreeBASICでは、シンボル?の前に「@」がつくと、アドレスを示す値になるらしい。あちこちのソースに出現していて、これは一体何だろうと思ってた…。また、ptr はポインタ変数であることを示してる。
_演算子 @ (のアドレス)
_POINTER | PTR
[ ツッコむ ]
#2 [basic] FreeBASICで反転描画してみたかったができなかった
FreeBASIC は put を使って画像を描画することができる。ただ、水平反転、垂直反転描画ができないような気もする…。これではSEGAのゲームギアじゃないか…。
*1
put に与える画像横幅、画像縦幅をマイナス値にしたらあっさり反転描画できたりしないかなと気になったので試してみた。
環境は Windows10 x64 22H2 + FreeBASIC 1.10.1 32bit。
結果を先に書くけど、ダメだった。そんな美味しい話は無さそう。
_obj.bmp
_put_flip.bas
fbc put_flip.bas で、put_flip.exe を生成。実行結果は以下。
ダメだった。
しかし、コレってどういう状態になってるんだろう…?
put に与える画像横幅、画像縦幅をマイナス値にしたらあっさり反転描画できたりしないかなと気になったので試してみた。
環境は Windows10 x64 22H2 + FreeBASIC 1.10.1 32bit。
結果を先に書くけど、ダメだった。そんな美味しい話は無さそう。
_obj.bmp
_put_flip.bas
' put flip test
#ifdef __FB_WIN32__
' Windowsの場合、mmsystemを利用
#include "windows.bi"
#include "win/mmsystem.bi"
#endif
' fbgfxモードを使う
#Include "fbgfx.bi"
Using fb
' 時間計測用の変数
Dim As Double start_time, prev_time, now_time, delta, one_frame, next_time
Dim As Integer frame_count
Dim As String fps_text = "FPS"
Dim As Integer scrw, scrh ' ウインドウサイズ
Dim As Integer imgw, imgh ' 画像サイズ
chdir exepath() ' カレントディレクトリを exeファイルのある場所にする
' ウインドウサイズと色深度を指定
scrw = 512
scrh = 288
Screenres scrw, scrh, 32
' 画像読み込み。FreeBASIC標準のbmp読み込みを使う場合
Dim img As any ptr = ImageCreate(128, 64)
Bload "obj.bmp", img
imageinfo img, imgw, imgh ' 画像の幅と高さを取得
#ifdef __FB_WIN32__
timeBeginPeriod(1) ' タイマー精度を1msecに向上
#endif
Dim As Double MAX_FPS = 60.0 ' FPS
one_frame = 1.0 / MAX_FPS ' 1フレームあたりの本来の時間
start_time = Timer ' 開始時間を取得
prev_time = start_time
frame_count = 0
Dim As Boolean running = True
Dim As Double x, y
x = scrw / 2
y = scrh / 2
Dim As Double anime_t = 0.0
' メインループ
While (running)
' 前回フレームから何秒経過したか取得。単位は秒(小数点以下有り)
now_time = Timer
delta = now_time - prev_time
prev_time = now_time
next_time = now_time + one_frame
If delta < 0 Then delta = one_frame
If now_time >= start_time Then
If (now_time - start_time) >= 1.0 Then
' 1秒経過したのでFPSを取得
fps_text = "FPS: " & frame_count
start_time += 1.0
frame_count = 0
End If
Else
start_time = now_time
End If
frame_count += 1
' ESCキー、qキー、ウインドウの閉じるボタンを検出
Dim As String k = inkey$
If k = Chr$(27) Or k = "q" Or k = Chr$(255) + "k" Then
running = False ' メインループ終了
End If
anime_t += delta ' アニメ表示用カウンタを更新
ScreenLock ' 描画開始
color RGB(255, 255, 255), RGB(52, 164, 255)
cls ' 画面クリア
' 画像を描画
Dim As Integer n, sx, sy, sw, sh
n = Int(anime_t / 0.5) Mod 2 ' 0 or 1
sw = (imgw / 2) ' 幅
sh = imgh ' 高さ
sx = 0 ' 描画元 x
sy = 0 ' 描画元 y
If n = 1 Then
sx += sw
sw = -sw
End If
Put (x - (sw / 2), y - (sh / 2)), img, (sx, sy) - Step(sw, sh), TRANS
' 文字列を描画
Draw String (10, 10), fps_text
Draw String (scrw / 2 - (8 * 6), scrh * 0.8), "HELLO WORLD"
ScreenUnlock ' 描画終了
If Timer < next_time Then
' 本来の1フレーム時間がまだ経過してないので sleep させる
Dim As Double wait_ms = (next_time - Timer) * 1000.0
If wait_ms > 0.0 Then sleep wait_ms
End If
Wend
While Inkey <> "": Wend ' キーバッファを空にする
#ifdef __FB_WIN32__
timeEndPeriod(1) ' タイマー精度を本来のスペックに戻す
#endif
ImageDestroy img ' 画像を使い終わったので破棄
fbc put_flip.bas で、put_flip.exe を生成。実行結果は以下。
ダメだった。
しかし、コレってどういう状態になってるんだろう…?
◎ 2024/02/05追記 :
反転描画を試みる際に、表示位置もずれるように書いちゃってることに気づいた。まあ、どのみち反転描画はできてないので、このままで。
[ ツッコむ ]
以上、1 日分です。

