GDI、GDI+、DirectXの描画を実装した。 実行中に切り替えることができる。
前回は切替ができなかった。
今回はキーボードの 1, 2, 3 キーを押下するとそれぞれの描画アーキテクチャに切り替わる。
入手先
イメージ
ソースコード一覧
実行結果
初期表示
GDI
GDI+
DirectX9
課題
IPartWndProcに、以下のインタフェースを追加した。 これは相応しくないかもしれない。 でも、面倒だから一旦はこういうことにしておいた。 いずれ改善したい。
class IPartWndProc {
void Initialize() = 0;
void Finalize() = 0;
}
悪い理由
追加したInitialize()
とFinalize()
を使っているのは GdiWndProc, GdiPlusWndProc, DirectXWndProc の描画アーキテクチャclassだけ。
InitializeWndProc, KeyboardWndProc, では使っていない。
本来Initialize()
, Finalize()
のインタフェースを継承したclassはポリモーフィズムで一括して実行するべきだと思う。そうでないと意味がない。でも、やっていない。
そもそも、InitializeWndProc, KeyboardWndProc, ではInitialize()
, Finalize()
を使う必要がない。
実際にInitialize()
, Finalize()
を使っているのは、描画アーキテクチャclassだけ。
つまり「描画アーキテクチャを切り替えるときの初期化と終了処理」として利用されている。
「WindowMessageを処理するための準備としての初期化」ではない。
よって、IPartWndProcのインタフェースとして定義するのはふさわしくないと思う。
IPartWndProcとしてのInitialize, Finalizeのタイミングは描画アーキテクチャを切り替えるときではない。 おそらく、プログラムが起動したら1回だけ。 もし他にも違うタイミングの初期化や終了処理が必要なら、それぞれにインタフェースclassを設けるのが理想的だと思う。
改善案
描画アーキテクチャ用のインタフェースを継承するのが良いと思う。 IPartWndProcではなく、IDrawWndProcのようなclassでインタフェースを定義する。
以下の4パターンが考えられる。 今回は手抜きをしてパターン1で実装した。 理想はパターン3。 パターン2はメリットがなさそう。 パターン4は可能なのか検討できていない。
パターン1
class GdiWndProc : public IPartWndProc {};
class IPartWndProc {
public:
virtual LRESULT CALLBACK PartWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL* pIsReturn) =
virtual void Initialize() = 0;
virtual void Finalize() = 0;
};
問題点
Initialize, Finalize, の使い方がふさわしくない。 現在は描画アーキテクチャを変更するときに使用している。 それなら、IPartWndProcでなく、IDrawWndProcなど、描画用インタフェースとして実装すべき。
パターン2
class GdiWndProc : public IPartWndProc, IInitializable, IFinalizable {};
class IPartWndProc {
public:
virtual LRESULT CALLBACK PartWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL* pIsReturn) = 0;
};
class IInitializable {
public:
virtual void Initialize() = 0;
};
class IFinalizable {
public:
virtual void Finalize() = 0;
};
問題点
このインタフェースは、どの部分で、どういう意図で使われるのか。 それが判別できると、可読性が上がると思う。 どこでも自由な形で使えてしまえそうなパターン2では、汎用性が高すぎて使用箇所も用途もバラバラになってしまいそう。
パターン3
class GdiWndProc : public IDrawWndProc {}
class IDrawWndProc
{
virtual LRESULT CALLBACK PartWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL* pIsReturn) = 0;
virtual void Initialize() = 0;
virtual void Finalize() = 0;
}
問題点
PartWndProc関数が重複してしまう。 IPartWndProcとIDrawWndProcの両classにPartWndProc関数があることになる。 両classを継承することはできない。 見っともない。
パターン4
class GdiWndProc : public IDrawWndProc {}
class IDrawWndProc
{
virtual void Draw() = 0;
virtual void Initialize() = 0;
virtual void Finalize() = 0;
}
問題
WindowsMessageが受け取れなくなってしまう。 描画アーキテクチャごとに色々と差異があるのに可能なのか。判断できない。
- DirectXの初期化には
hWnd
が必要CreateWindow()
の後でやる必要があるがどうやって実装するか- 現状でも問題ないかも
- GDI+とDirectXでは
WM_ERASEBKGND
のときにreturn
して自動再描画を無効にしないとちらつく- でもGDIは必要ないっぽい
おそらく、Draw()
はWM_PAINT
のときに行う処理を実装する。
でも、DirectXはメッセージループでなく、メインループで描画するもののはず。
WndProc内の一部としてDirectXを実装してしまっていいのか不明。
GDI/GDI+/DirectX9やメッセージループのことも良くわかっていないので、Draw()
に統一できるのかどうか不透明。検討しきれない。