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

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

<body bgcolor="WHITE">
<font size="5">39章 グラフィックス・デバイス・インターフェース(GDI)入門(その4)
</font>
<hr>
<p align="LEFT">
<p align="CENTER">
<p align="RIGHT">
<p align="CENTER">
<a href="chap39.lzh">ダウンロード</a>
<p>
 今回は、ビットマップリソースを用いないアニメーションを行ってみましょう。<br>
 視点を変えれば、今回のテーマは5章の再描画と非常によく似ています。ある意味簡単です。
<hr>
<p>
 では、早速、ソースから見ていきましょう。<br>
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト39-1
<tr><td>
<pre>
/* C言語で始めるWindowsプログラミング */
/* 39章のサンプルプログラム */
/* Programmed by Y.Kondo */
/* 注:TABサイズは4で見てください */
/* このファイルではWinMain関数が定義して*/
/*ある */

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

/* アプリケーションエントリーポイント */
int WINAPI WinMain(HINSTANCE hInstance,
                 HINSTANCE    hPrevInstance,
                  LPSTR        CmdLine,
                  int          CmdShow)
{
  HWND            hwnd;   /*  メインウインドウのウインドウハンドル    */
   MSG             msg;    /*  メッセージキューから取得したメッセージ  */
   WNDCLASS        wc;     /*  ウインドウクラス登録用の構造体          */
<b>
  wc.style        =CS_HREDRAW|CS_VREDRAW;</b>
   wc.lpfnWndProc  =WindowProc;
   wc.cbClsExtra   =0;
   wc.cbWndExtra   =0;
   wc.hInstance    =hInstance;
   wc.hIcon        =LoadIcon(hInstance,MAKEINTRESOURCE(IDI_ICON1));
   wc.hCursor      =LoadCursor(NULL,IDC_ARROW);
   wc.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
   wc.lpszMenuName =MAKEINTRESOURCE(IDR_MENU1);
   wc.lpszClassName="MainWindowClass";
   
   if(RegisterClass(&wc;)==0)               /*  ウインドウクラス登録    */
       return  0;

  hwnd=CreateWindow(  "MainWindowClass",  /*  ウインドウ作成          */  
                       "39章サンプル",
                       WS_OVERLAPPEDWINDOW,
                       CW_USEDEFAULT,
                       CW_USEDEFAULT,
                       CW_USEDEFAULT,
                       CW_USEDEFAULT,
                       NULL,
                       (HMENU)NULL,
                       hInstance,
                       0);
   if(hwnd==NULL)
       return  0;

  ShowWindow(hwnd,CmdShow);           /*  ウインドウの表示        */
   UpdateWindow(hwnd);                 /*  ウインドウの最初の更新  */

  while(GetMessage(&msg;,NULL,0,0))    /*  メッセージループ        */
   {
       TranslateMessage(&msg;);
       DispatchMessage(&msg;);
   }
   return  msg.wParam;
}
</pre>
</table>
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト39-2
<tr><td>
<pre>
/* C言語で始めるWindowsプログラミング */
/* 39章のサンプルプログラム */
/* Programmed by Y.Kondo */
/* 注:TABサイズは4で見てください */
/* このファイルでは、メインウインドウのウインド*/
/*ウプロシージャが定義されている */

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

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

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

/* メインウインドウのウインドウプロシージャ */
LRESULT CALLBACK WindowProc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)
{
  switch(message)
   {
       case    WM_CREATE:
           return  Wm_CreateProc(hwnd,(LPCREATESTRUCT)lparam);<b>
       case    WM_SIZE:            /*  ウインドウのサイズ変更  */</b>
           return  Wm_SizeProc(hwnd,wparam,LOWORD(lparam),HIWORD(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);
}

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

/* このファイルの中だけで用いる変数 */
UINT TimerID; /* タイマーID */
double th; /* 三角関数で線を描画する為の種 */<b>
int width; /* クライエント領域の幅 */
int height; /* クライエント領域の高さ */</b>

#define DELTA 0.05 /* 三角関数の引数の増分 */
#define LINE_NUM 100 /* 線の数 */

static LRESULT Wm_CreateProc(HWND hwnd,LPCREATESTRUCT cs)
{
  TimerID=0;
   th=-100000.0;
   /*  メニューの初期化    */
   EnableMenuItem(GetMenu(hwnd),ID_START,MF_BYCOMMAND|MF_ENABLED);
   EnableMenuItem(GetMenu(hwnd),ID_STOP,MF_BYCOMMAND|MF_GRAYED);

  return  0;
}
static LRESULT Wm_SizeProc(HWND hwnd,DWORD fwSizeType,WORD nWidth,WORD nHeight)
{
  width=nWidth;
   height=nHeight;
   return  0;
}
static LRESULT Wm_CommandProc(HWND hwnd,WORD wNotifyCode,WORD wID,HWND hwndCtl)
{
  RECT    rect={0,0,width,height};    /*  矩形は、1ピクセル多く取る必要がある    */
   if(wID==ID_START)
   {
       if((TimerID=SetTimer(hwnd,2,100,NULL))==0)
       {
           MessageBox(hwnd,"現在、タイマーを利用出来ません","エラー",MB_OK|MB_ICONSTOP);
       }
       else
       {
           EnableMenuItem(GetMenu(hwnd),ID_START,MF_BYCOMMAND|MF_GRAYED);
           EnableMenuItem(GetMenu(hwnd),ID_STOP,MF_BYCOMMAND|MF_ENABLED);
           InvalidateRect(hwnd,&rect;,TRUE);
           UpdateWindow(hwnd);
       }
   }
   else if(wID==ID_STOP)
   {
       KillTimer(hwnd,TimerID);
       EnableMenuItem(GetMenu(hwnd),ID_START,MF_BYCOMMAND|MF_ENABLED);
       EnableMenuItem(GetMenu(hwnd),ID_STOP,MF_BYCOMMAND|MF_GRAYED);
   }
   return  0;
}
<b>
static LRESULT Wm_TimerProc(HWND hwnd,DWORD wTimerID)
{
  RECT    rect={0,0,width,height};    /*矩形は、1ピクセル多く取る必要がある*/
   if(TimerID==wTimerID)
   {
       th+=DELTA;
       if(th&gt;100000.0)                  /*100000を越えたら元に戻る*/
           th=-100000.0;
       InvalidateRect(hwnd,&rect;,TRUE);
       UpdateWindow(hwnd);
   }
   return  0;
}
</b>
static LRESULT Wm_PaintProc(HWND hwnd)
{
  PAINTSTRUCT ps;
   HDC         PaintDC;

  if(GetUpdateRect(hwnd,NULL,TRUE))
   {
       PaintDC=BeginPaint(hwnd,&ps;);   /*  ウインドウ描画用のデバイスコンテキスト取得  */
       Draw(PaintDC,th,DELTA,LINE_NUM);/*  再描画  */
       EndPaint(hwnd,&ps;);             /*  ウインドウ描画用のデバイスコンテキスト開放  */
   }
   return  0;
}

static LRESULT Wm_DestroyProc(HWND hwnd)
{
  KillTimer(hwnd,TimerID);            /*  タイマーを切る  */
   PostQuitMessage(0);
   return  0;
}

/* 適当に作った(^^)線を描画さす関数 */<b>
/* Wm_PaintProc()からしか呼ばれない */</b>
/* HDC dc :デバイスコンテキスト */
/* doubel t :三角関数で線を描く最初の値 */
/* double d :増分 */
/* int n :描画する線の数 1以上  */
static void Draw(HDC dc,double t,double d,int n)
{
  int     c;
   double  r;
<b>
  /*ビューポートの原点を移動さす*/
   SetViewportOrgEx(dc,width/2,height/2,NULL);</b>
   /*  半径を決める    */
   r=(width+height)/8.0;
   for(c=0;c&lt;n;c++)
   {
       MoveToEx(dc,(int)(r*cos(3.0*t+d*c)+width/6.0*sin(t)),(int)(r*cos(t/3.0)*sin(t+d*c)),NULL);
       LineTo(dc,(int)(r*sin(t)*sin(3.0*t+d*c)+width/5.0*cos(t)),(int)(r*sin(t/2.0+d*c)));
   }
}
</pre>
</table>
<hr>
<p>
 基本的な説明は、先の章で説明しましたので、重要な差分のみを説明します。
<p>
 今回のアニメーションの特徴は、ウインドウサイズの変化に伴い、ウインドウのサイズが変化する事です。この為、リスト39-1のWNDCLASS構造体のstyleメンバ変数に、CS_HREDRAWとCS_VREDRAWを設定しています。この両方が設定してあると、ウインドウサイズを変更したときに、クライエント領域全てが無効になり、再描画が行われます。
<p>
<b>
・WM_SIZEメッセージ
</b>
<p>
 ウインドウのサイズが変更された後にSendされるメッセージです。<br>
 付加情報として、変更後のクライエント領域の幅と高さを持ちます。<br>
 今回は、この情報を元に、グローバル変数widthとheightを変更して、他のメッセージの応答に利用しています。
<p>
<b>
・WM_TIMERメッセージの応答でのInvalidateRect関数
</b>
<p>
 第3引数ですが、TRUEにしています。つまり、クライエント領域の背景を再描画しています。これをしないと、前回の描画イメージが残骸として残ります。
<p>
<b>
・WM_PAINTメッセージでの応答
</b>
<p>
 毎回、Draw関数を呼出して今の状態で描画すべきイメージを再計算しています。アニメーションゆえ、WM_TIMERメッセージに応答して、GetDCでデバイスコンテキストを取得して、描画した方がと思いますが、アニメーションの次の駒に行く前に、他のウインドウの移動などで再描画する必要があります。それゆえ、Windowsでは、全ての描画処理は基本的にWM_PAINTメッセージに応答する事で行います。
<p>
<b>
・SetViewportOrgEx関数
</b>
<p>
 ビューポートの座標の原点を移動さす関数です。<br>
 今回は、クライエント領域の中心を描画の中心としています。<br>
 よく似ている関数に、SetWindowOrgEx関数があります。
<hr>
<p>
 今回は簡単すぎましたね。次回は、今回のサンプルを発展させて、リージョンの説明をします。
<p>
 では、お楽しみに。
<p align="RIGHT">
2003年2月9日<br>
加筆2003年2月10日<br>
<hr>
<p align="RIGHT">
<a href="/web/20150829043606/http://web.kyoto-inet.or.jp:80/people/ysskondo/index.html">目次</a><br>
<hr>
<p align="RIGHT">
著作権者:近藤妥
</body>
</html>

  • 最終更新:2018-03-11 05:33:23

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

認証パスワード