最終更新 2003 09/17
サンプルのダウンロード → API_SetWindowRgn.lzh(42k)
全ソースコード
ExtCreateRegion 関数
SetWindowRgn 関数
対応しているバージョン
95, 98, Me, NT3.51以降, 2000, XP
使用するヘッダとライブラリ
winuser.h
user32.lib
|
ビットマップの形をしたウィンドウを作るには、ビットマップの形をしたリージョン
という物を作って、それをウィンドウに設定します。
流れとしては
1.ビットマップを読み込む
2.リージョンを作る
3.リージョンをウィンドウに設定する
この3行程だけなのですが、ビットマップの形を読み取ってそこからリージョンを
作るのがかなり厄介です。
1.について、ビットマップをメモリへ読み込む方法は別の項で説明しているので
詳しい方法などはそちらをご覧下さい。
2.について、やり方はいくつかあるそうですが、自分が知っている方法は
ビットマップのピクセルを1つずつ読み取って、表示する部分ならそこを
リージョンに追加するという方法だけです。
たぶん、それが一番単純で分かりやすい方法だと思います。
リージョンは範囲という意味で、文字通り絵や文字を表示する範囲のことを指します。
リージョンを設定すると、リージョンに指定した範囲にだけ、絵や文字が表示されて
リージョンの範囲外には絵や文字は表示されないので透明になります。
これをウィンドウに設定することで、リージョンの形をしたウィンドウになります。
ビットマップのピクセルを読み取るのに GetPixel 関数を使っているので
処理速度も遅いですが、たぶん Pentium 100MHlz とか、その辺のスペックで
なければ気になる遅さにはならないと思います。
遅いといっても10秒もかかるなんて遅さにはならないはずです。
かかってもせいぜい、1、2秒でしょう。
詳しく解説するととても長くなってしまうので、考え方だけ説明します。
【 ビットマップの形をしたリージョン作成の考え方 】
図1
上はビットマップを拡大した図だと思って下さい。
黒い部分が表示するところで、白い部分は表示しないところです。
左から順番に色を読み取って、白なら処理を飛ばしてすぐ次の色の読み取りへ
移ります。
黒があったら、そこを開始点として、それよりあとに黒がいくつ連続して
続くかをカウントします。
カウント中に白が見つかったら、そこでカウントを終了して、位置情報を
リージョンに設定します。
図2
そうやっていくと、図2のようなパターンの場合、赤枠で囲った3つの位置情報が
リージョンに登録されることになります。
この作業をビットマップの一番上のラインから一番下のラインまで繰り返すと
最後には黒い部分だけのリージョンが完成するという仕組みです。
【 リージョンのデータ設定の流れ 】
リージョンのデータは RGNDATA 構造体のポインタをとって、そこに指定します。
RGNDATA 構造体に必要なメモリは GloballAlloc に GMEM_MOVEABLE を指定して
確保します。
RGNDATA で使うメモリはどれだけ必要なのかが分からないので、あとで
GlobalReAlloc() 関数で必要なメモリを追加して確保できるように
GMEM_MOVEABLE を指定します。
GlobalAlloc() 関数に GMEM_MOVEABLE を指定した場合、必ずメモリを
ロックしなければなりません。
ロックすると、ロックしたメモリのアドレスが返ってくるので、それを
RGNDATA 構造体のポインタに格納します。
ひと通りデータを格納したら、最後に ExtCreateRegion() 関数で
リージョンを作成して、リージョンハンドルを受け取ります。
リージョンハンドルは、ウィンドウズ上の全てのリージョンにつけられる
通し番号みたいなものです。
リージョンハンドルを受け取れたら、それを SetWindowRgn() 関数で
ウィンドウに設定します。
ウィンドウにリージョンを設定してしまえば、リージョンを作成した時に
確保したメモリは必要なくなるので、GlobalFree() で RGNDATA に使用した
メモリを解放します。
【 最後の仕上げにビットマップをウィンドウに表示する 】
リージョンは描画範囲を設定するだけのものなので、ウィンドウは
ビットマップの輪郭の形にはなりますが、ビットマップ自体の絵は
表示されません。
サンプルのスクリーンショットのように、同時にビットマップの
絵も表示したいときは、自分で処理する必要があります。
ゲームと違ってウィンドウ自体を描画するには WM_PAINT メッセージで
処理を行います。
WM_PAINT メッセージは、「ウィンドウを再描画しなされ」と
ウィンドウズから命令が来たときに呼ばれます。
具体的には、ウィンドウを作成した直後、ウィンドウがアクティブに
なった直後、プログラム中に再描画を行う関数を呼び出した時・・・
などです。
ゲームと同じように、メッセージがないとき(アイドル時)に
ビットマップを描画する方法でも良いのですが、ウィンドウ自体を
描画する場合は、再描画要求があった( WM_PAINT が呼ばれた )
ときにだけ処理する方が、システムにかかる負担が軽くなるので
効率が良いです。
ゲームの場合は絶えず絵を表示し続けなければならないので
アイドル時に処理するのが良い方法です。
WM_PAINT 中に描画を行う際に厄介なのは、HDC を取得するのに
PAINTSTRUCT 構造体と BeginPaint()、EndPaint() 関数を
使用しないといけない事です。
WM_PAINT 中では、GetDC() と ReleaseDC() を使って HDC を
取得してはいけないという決まりになっています。
「何で?」と聞かれても私には分かりません。
ウィンドウズのシステムを設計した人は、相当賢いはずなので
そうなった背景には細かい理由が多々あるのかと思いますが
仕様である以上、面倒でもウィンドウズのCプログラマは
その仕様に従うしかないのです。
以下に WM_PAINT メッセージ中に HDC を取得する方法を示してみます。
HDC が取得できてしまえば、あとは BitBlt で取得した HDC へ
ビットマップを転送すれば終了です。
表示するビットマップは、あらかじめメモリへ読み込んでおきます。
リージョンを作るときに読み込んだのと同じビットマップを
同じ方法で読み込めばよいだけですね。
【 ウィンドウをドラッグして移動するには? 】
ひとつ疑問が残ります。
ビットマップの形をしたウィンドウを作る場合、タイトルバーやウィンドウの
境界線が邪魔なので、ウィンドウはポップアップウィンドウで作成します。
ポップアップウィンドウの作り方は
ウィンドウを作って表示させる最小プログラム4
をご覧下さい。
ですが、そうするとタイトルバーがないため、ウィンドウを移動させる事が
できなくなってしまいます。
これを解決する方法は
タイトルバーのないウィンドウをドラッグして移動させる
で説明しているので、そちらをご覧下さい。
|