38章 グラフィックス・デバイス・インターフェース(GDI)入門(その3)

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=shift_jis"></meta>
<title>38章 グラフィックス・デバイス・インターフェース(GDI)入門(その3)
</title>
</head>

<body bgcolor="WHITE">
<font size="5">38章 グラフィックス・デバイス・インターフェース(GDI)入門(その3)
</font>
<hr>
<p align="CENTER">
<a href="chap38.lzh">ダウンロード</a>
<p>
 今回は、ビットマップの表示と簡単なアニメーションを行います。面白いので、是非ともダウンロードして動かして下さい。
<hr>
<p>
 では、実際の動作をさせているウインドウプロシージャを見てみましょう。
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト38-1
<tr><td>
<pre>
/* C言語で始めるWindowsプログラミング */
/* 38章のサンプルプログラム */
/* Programmed by Y.Kondo */
/* 注:TABサイズは4で見てください */
/* このファイルでは、メインウインドウのウインド*/
/*ウプロシージャが定義されている */

#define STRICT
#include &lt;windows.h&gt;
#include &lt;stdio.h&gt;
#include "wndproc.h"
#include "resource.h"

/*===============================================================================*/

/* このファイル内でのみ用いられる関数のプロトタイプ宣言 */
static LRESULT Wm_CreateProc(HWND,LPCREATESTRUCT);
static LRESULT Wm_CommandProc(HWND,WORD,WORD,HWND);
static LRESULT Wm_TimerProc(HWND,DWORD);
static LRESULT Wm_PaintProc(HWND);
static LRESULT Wm_DestroyProc(HWND);


/* メインウインドウのウインドウプロシージャ */
LRESULT CALLBACK WindowProc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)
{
  switch(message)
   {
       case    WM_CREATE:
           return  Wm_CreateProc(hwnd,(LPCREATESTRUCT)lparam);
       case    WM_COMMAND:
           return  Wm_CommandProc(hwnd,HIWORD(wparam),LOWORD(wparam),(HWND)lparam);
       case    WM_TIMER:           /*  タイマー    */
           return  Wm_TimerProc(hwnd,wparam);
       case    WM_PAINT:           /*  再描画処理              */
           return  Wm_PaintProc(hwnd);
       case    WM_DESTROY:         /*  ウインドウの破壊後処理  */
           return  Wm_DestroyProc(hwnd);
   }
   return  DefWindowProc(hwnd,message,wparam,lparam);
}

/*===============================================================================*/

/* このファイルの中だけで用いる変数 */
HDC MemoryDC; /* メモリー上のデバイスコンテキスト */
HBITMAP hBitmap; /* メモリー上のデバイスコンテキストでのビットマップ */
HBITMAP hDefault; /* メモリー上の既定値のビットマップ */
int Ac; /* アニメーションのカウンタ */
UINT TimerID; /* タイマーID */

static LRESULT Wm_CreateProc(HWND hwnd,LPCREATESTRUCT cs)
{<b>
  HDC dc;

  dc=GetDC(hwnd);                     /*画面のデバイスコンテキストを取得する*/
   MemoryDC=CreateCompatibleDC(dc);    /*画面と同じデバイスコンテキストをメモリ上に作成する*/
   ReleaseDC(hwnd,dc);
   hBitmap=LoadBitmap(cs-&gt;hInstance,MAKEINTRESOURCE(IDB_BITMAP1));
   hDefault=SelectObject(MemoryDC,hBitmap);</b>
   Ac=0;
   TimerID=0;
   return  0;
}
static LRESULT Wm_CommandProc(HWND hwnd,WORD wNotifyCode,WORD wID,HWND hwndCtl)
{
  RECT    rect={0,0,200,200}; /*  矩形は、1ピクセル多く取る必要がある    */
   if(wID==ID_START)
   {
       if((TimerID=SetTimer(hwnd,2,500,NULL))==0)
       {
           MessageBox(hwnd,"現在、タイマーを利用出来ません","エラー",MB_OK|MB_ICONSTOP);
       }
       else
       {
           Ac=0;
           EnableMenuItem(GetMenu(hwnd),ID_START,MF_BYCOMMAND|MF_GRAYED);
           InvalidateRect(hwnd,&rect;,FALSE);
           UpdateWindow(hwnd);
       }
   }
   return  0;
}

static LRESULT Wm_TimerProc(HWND hwnd,DWORD wTimerID)
{
  RECT    rect={0,0,200,200}; /*  矩形は、1ピクセル多く取る必要がある    */
   if(TimerID==wTimerID)
   {
       Ac++;
       if(Ac==6)
       {   
           Ac=5;   /*  オーバーラン防止    */
           TimerID=KillTimer(hwnd,TimerID);
           EnableMenuItem(GetMenu(hwnd),ID_START,MF_BYCOMMAND|MF_ENABLED);
       }
       else
       {
           InvalidateRect(hwnd,&rect;,FALSE);
           UpdateWindow(hwnd);
       }
   }
   return  0;
}

static LRESULT Wm_PaintProc(HWND hwnd)
{
  PAINTSTRUCT ps;
   HDC         PaintDC;

  if(GetUpdateRect(hwnd,NULL,TRUE))
   {
       PaintDC=BeginPaint(hwnd,&ps;);   /*  ウインドウ描画用のデバイスコンテキスト取得  */
       /*  描画を行う  (ここから)    */<b>
       BitBlt(PaintDC,0,0,200,200,MemoryDC,Ac*200,0,SRCCOPY);</b>
       /*  描画を行う  (ここまで)    */
       EndPaint(hwnd,&ps;);             /*  ウインドウ描画用のデバイスコンテキスト開放  */
   }
   return  0;
}

static LRESULT Wm_DestroyProc(HWND hwnd)
{
  KillTimer(hwnd,TimerID);            /*  タイマーを切る  */<b>
   SelectObject(MemoryDC,hDefault);    /*  デフォルトのビットマップに戻す  */
   DeleteObject(hBitmap);              /*  ロードしたビットマップを廃棄する    */
   DeleteDC(MemoryDC);                 /*  メモリー上のデバイスコンテキストを廃棄する*/</b>
   PostQuitMessage(0);
   return  0;
}
</pre>
</table>
<p>
 ビットマップを表示する上で重要な箇所を太文字にしました。
<hr>
<p>
<b>
・ビットマップ表示の概略
</b>
<p>
・GetDC関数で、ディスプレーのデバイスコンテキストを取得する<br>
・CreateCompatibleDC関数で、メモリ上に表示デバイスと関連付けられていないデバイスコンテキストを作成する<br>
・ReleaseDC関数で、GetDC関数で取得したデバイスコンテキストを開放する<br>
・ビットマップリソースをLoadBitmap関数で読み込む<br>
・メモリ上のデバイスコンテキストのビットマップをリソースから読み込んだビットマップとSelectObject関数で置き換える<br>
・BitBlt関数で、メモリ上のデバイスコンテキストのビットマップを画面のビットマップに転送する<br>・メモリ上のデバイスコンテキストのビットマップをSelectObject関数で元に戻す<br>
・ロードしたビットマップをDeleteDC関数で廃棄する<br>
<hr>
<p>
<b>
・アニメーションの概略
</b>
<p>
・上記のビットマップ表示の概略で読み込むビットマップを表示するビットマップの整数倍長いビットマップにする。<br>
<table border="1"><tr><td><img src="/web/20150829044621im_/http://web.kyoto-inet.or.jp:80/people/ysskondo/from36/chap38_2.gif"></table>
・SetTimer関数にミリ秒単位でWM_TIMERメッセージをPostする間隔を与えてタイマー識別子を取得する<br>
・WM_TIMERメッセージに応答して、表示するビットマップの位置をずらせる情報を変える<br>
・表示画面をInvalidateRect関数で無効化する<br>
・UpdateWindow関数で、強制的にWM_PAINTを発行する<br>
・アニメーションが終わるかアプリケーションが終了した場合、KillTimer関数でタイマーを開放して、WM_TIMERをPostしないようにする<br>
<hr>
<p>
<b>
・ビットマップ表示の為の関数概略
</b>
<p>
<b>
・GetDC関数
</b>
<p>
 ディスプレーのデバイスコンテキストの取得に用います。この関数を用いて描画出来るのは、ウインドウのクライエント領域だけです。デバイスコンテキストには、表示デバイスの各種の情報を含んでいますので、今回は、この情報を取得する為だけに用いています。
<p>
<b>
・CreateCompatibleDC関数
</b>
<p>
 表示デバイスコンテキストと互換性のあるデバイスコンテキストをメモリ上に作成し、そのハンドルを返します。<br>
 この関数で作成されたデバイスコンテキストは、表示デバイスと関連付けられていませんが、描画関数を利用して、ビットマップに描画する事が可能です。しかし、既定値のビットマップの大きさは、1ピクセル×1ピクセルです。
<p>
<b>
・ReleaseDC関数
</b>
<p>
 GetDC関数で取得したデバイスコンテキストを開放します。<br>
 デバイスコンテキストは、取得や作成の方法により、開放したり廃棄する方法が異なりますので、要注意です。
<p>
<b>
・SelectObject関数
</b>
<p>
 この関数は、前章にも登場しましたが、再度違う角度から説明します。<br>
 ビットマップも描画オブジェクトの一つです。CreateCompatibleDC関数の既定値のビットマップは1ピクセル×1ピクセルなので、リソースからロードしたビットマップと交換します。戻り値は、以前に選択されていたビットマップのハンドルです。<br>
 同様に、リソースからロードしたビットマップを廃棄するには、この関数を使って、元に戻す必要があります。この場合、引数は既定値のビットマップのハンドル。戻り値は、現在選択されているビットマップのハンドルです。なぜ、この様な処理を行うかというと、選択されているユーザー定義の描画オブジェクトは廃棄出来ないからです。これは、前章で説明したペンやブラシなどにも当てはまります。尚、ストックオブジェクトは廃棄出来ません。
<p>
<b>
・BitBlt関数
</b>
<p>
 CreateCompatibleDC関数で作成したメモリ上のデバイスコンテキストに関連付けられたビットマップを表示デバイスに転送する関数です。最後の引数で分かるように、ラスターオペレーションが行えます。<br>
 今回は、この関数を用いて、メモリ上のどの位置のビットマップを表示デバイスに転送するかで、アニメーションを行っています。
<p>
<b>
・DeleteObject関数
</b>
<p>
 ロードしたり作成した描画オブジェクトを廃棄する関数です。<br>
 廃棄するときは、その描画オブジェクトはデバイスコンテキストと関連付けられていてはいけません。
<p>
<b>
・DeleteDC関数
</b>
<p>
 CreateComaptibleDC関数で作成したデバイスコンテキストを廃棄します。
<hr>
<p>
<b>
・アニメーションの為の関数概略
</b>
<p>
 アニメーションを行う為には、タイマーを用います。Windowsの様なマルチタスク環境では、ループによるビジーウエイトは厳禁です。
<p>
<b>
・SetTimer関数
</b>
<p>
 定期的にWM_TIMERメッセージをPostしたり、独自のコールバック関数を呼んだりする関数です。<br>
 第4引数に独自のコールバック関数のアドレスを指定した場合は、第3引数で指定した間隔で、独自のコールバック関数が呼ばれます。今回の様に、NULLを指定した場合は、第1引数で指定したウインドウのウインドウプロシージャにWM_TIMERメッセージがPostされます。第2引数は、0以外のタイマーIDです。<br>
 タイマーの取得に成功すれば、0以外の値を返します。また、この値を利用して、KillTimer関数で、タイマーを止める事ができます。
<p>
<b>
・KillTimer関数
</b>
<p>
 SetTimer関数で取得したタイマーを廃棄し、タイマーを止めます。<br>
 引数として、SetTimer関数の戻り値を取ります。
<hr>
<p>
<b>
・その他
</b>
<p>
<b>
・WM_TIMERメッセージ
</b>
<p>
 WPARAMがタイマーID。LPARAMがSetTimerで登録した独自のコールバック関数のアドレスです。<br>
 今回のサンプルプログラムでは、WM_TIMERが呼ばれる毎に、アニメーションの駒をカウントして、InvalidateRect関数でアニメーションが描画される領域を無効化して、UpdateWindowで強制描画しています。
<p>
<b>
・InvalidateRect関数
</b>
<p>
 この関数は、指定した領域を無効化する関数です。普通、ウインドウが無効化されるのは、上に別のウインドウが重なった場合などですが、これを意図的に行う物です。<br>
 第1引数は、無効化するウインドウのウインドウハンドル。第2引数は、無効化する領域を表わすRECT構造体変数へのポインタ。第3引数は、BeginPaint関数が呼ばれるときに、背景の再描画を行うかどうかを決める物です。今回は、BitBlt関数でビットマップをコピーしていますので、背景の消去の必要が無いので、FALSEにしています。<br>
 あと、重要な事ですが、GDI関数はプロッタプリンタなどを考えてか、二重描画を避ける為に、1ピクセル少なく処理します。つまり、(0、0)から(20、20)へ直線を描画したつもりが、実際は、(0、0)から(19、19)へしか描画されません。代わりに、引き続いて、LineTo関数で描画する場合、(20、20)から描画されます。<br>
 同様に、InvalidateRect関数に渡すRECT構造体変数は、縦横1ピクセルづつ大きめに指定する必要があります。
<p>
<b>
・UpdateWindow関数
</b>
<p>
 WM_PAINTメッセージをSendして強制的に描画さす関数です。<br>
 本来、WM_PAINTは、メッセージキューに置かれるメッセージですが、WM_PAINTは、優先度が低いメッセージです。つまり、他のメッセージがメッセージキューにある場合、すぐさまGetMessage関数などに吸い上げられません。これは、画面のちらつきを抑えるためですが、レスポンスを向上さす為に、強制的にWM_PAINTを実行さす場合に用います。
<hr>
<p>
 今回は、ビットマップリソースを用いたビットマップの表示とアニメーションを行いました。次回は、ビットマップリソースを用いないアニメーションを行います。
<p>
 では、お楽しみに。
<p align="RIGHT">
2003年2月1日<br>
修正2003年2月4日<br>
<hr>
<p align="RIGHT">
<a href="/web/20150829044621/http://web.kyoto-inet.or.jp:80/people/ysskondo/index.html">目次</a><br>
<hr>
<p align="RIGHT">
著作権者:近藤妥
</body>
</html>

  • 最終更新:2018-03-11 05:32:56

このWIKIを編集するにはパスワード入力が必要です

認証パスワード