SuzudoraExの開発に当たり、SDLを導入してIO周りの処理にかかるコストを削減することにしたわけだが、SDLの実装方法について毎回調べるのは不毛だと気付いたので実装メモを残していこうと思う。
まず、WindowsだろうがLinuxだろうがインストール方法は別サイトにいくらでもあるので除外。
ここではライブラリが開発環境に組み込まれたことを前提とする。
また、SDLmain.libライブラリはOSによるスタートアップ(エントリポイント)の依存コードを減らすために組み込むライブラリなので、当サイトでは扱わないものとする。
メモにあるソースコードを見るに当たり、
SDLの関数リファレンスを参照してほしい。
SDL最少コード
以下のコードは全く意味のないコードだがSDLを使用するための必要最低限のコードである。
[cpp]
if( SDL_Init( SDL_INIT_VIDEO ) != 0){
exit(1);
}
SDL_Quit();
[/cpp]
解説としては
SDL_InitでSDLサブシステムとして登録されているシンボルを指定して初期化する。
これを行わないと各サブシステムの関数呼び出し時にエラーが発生するらしい。
ここで出てくる
SDL_INIT_VIDEO等複数初期化したい場合はSDL.hに下記のように定義してあるので、論理和で結合して
SDL_Initに渡してやればいい。もしくは
SDL_INIT_EVERYTHINGで初期化する。
SDL_Quit()は後始末処理を行っているのでアプリケーション終了時に必ず呼び出すこと。
[cpp]
// SDL.h
#define SDL_INIT_TIMER 0x00000001
#define SDL_INIT_AUDIO 0x00000010
#define SDL_INIT_VIDEO 0x00000020
#define SDL_INIT_CDROM 0x00000100
#define SDL_INIT_JOYSTICK 0x00000200
#define SDL_INIT_NOPARACHUTE 0x00100000 /**< Don't catch fatal signals */
#define SDL_INIT_EVENTTHREAD 0x01000000 /**< Not supported on all OS's */
#define SDL_INIT_EVERYTHING 0x0000FFFF
[/cpp]
SDL_Initはまとめて論理和で結合して渡してもいいし、
SDL_Initを複数回呼び出していい。
重複するシンボルがあったとしても既に登録してあれば無視されるので安全が保たれている。
しかし注意したいのは初期化後、何らかの処理を行った後にさらに
SDL_Initを使用する場合だ。
SDL_Initは内部で次の3つの処理を行っている。そのため、エラーメッセージがクリアされてしまう場合があるため、(逆に言えば気にしないでいいならばSDL_Initを使用してもいい)素直に
SDL_InitSubSystemを使用して初期化したほうがいいだろう。
- SDL_Init処理
- エラーメッセージのクリア
- SDL_InitSubSystemの呼び出し
- パラシュートコードの登録
SDL_InitSubSystem の使用方法としては「スタートアップではSDL_INIT_VIDEOだけ初期化したけど、あとでジョイスティックの実装をすることになった。でもSDL_Initに付け足すとコードファイルが別々になって後々修正が大変」というこ時にジョイスティックの実装開始地点もしくは初期化処理に
SDL_InitSubSystem に
SDL_INIT_JOYSTICKを指定して初期化するもののようだ。
SDLのウィンドウ使用しないでSDLオブジェクトを利用する方法
SDLではSDLが用意した初期化処理を行い、SDLが生成するウィンドウを使用して各メソッド・オブジェクトを弄ることになる。こうすると、ユーザー側ではWindowsやLinux等のOSレベルのネイティブな処理を考えなくてよくなるが、自前で画像を加工しようと思っている筆者からすると有難迷惑にもほどがある。何とかSDLが生成するウィンドウを使用せずに、画像の読み込みを行ったオブジェクトの生データを横取りできないか考える。
調査したところ、割と簡単そうだった。
SDL_imageライブラリを導入した上で、Load_IMG関数を使用することによってSDL_Surface*が返ってくる。このSDL_Surfaceの公開メンバにwidth,height,picth等画像情報が含まれている。
これが一番 重要だが、SDL_Surface::pixelsの中身は画像データ配列の先頭アドレスが入っている。
これらを利用することで、 各画像形式の読み込み処理を意識せずにデータのみ横取りすることができると予想される。
まだ未検証なので、次の更新までには確認してみたい。
[追記] 確認が取れました。
こちらで実際に読み込んだデータを直接参照して描画できることを確認しています。
もし可能であるならば、SDLを意識せずにDIBを作成するラッパークラス等を作ってみたいと思う。
SDLウィンドウのウィンドウプロシージャを乗っ取る
SDLが生成するウィンドウからウィンドウプロシージャをフックするのは割と簡単。
SDL内部のグローバルな変数に"SDL_Window"があるので、これをGetWindowLong/SetWindowLongのターゲットに指定するとウィンドウプロシージャの乗っ取りができる。
しかし、乗っ取ったからと言って何か利便性があるのかと考える微妙である。
まぁ、SDLがサポートしていないウィンドウメッセージをどうしても使いたいという場合は乗っ取ったウィンドウプロシージャで処理してそれ以外はSDLのウィンドウプロシージャに丸投げする。という使い方ならできるかもしれない。
GWL_USERDATAは使用できると面白そうだけど、SDL内でオブジェクトのやり取りに使われていないことを確認しないと危なくて使えない。
何か有用性があればぜひ教えてほしい。