皆さんお待ちかねの(?) GDIを使った描画を扱います GDIとはGraphics Device Interfaceの略で グラフィカルオブジェクトの表示と、ディスプレイやプリンターのような出力デバイスへの転送のためのWindowsの規格です 今回は ・ペンを使った直線、曲線の描画 ・ブラシを使った円、矩形の描画 ・ビットマップ画像の描画 ・フォント指定した文字列の描画 の基本的な描画を行います &ref(paint.GIF); まず、Win32APIの描画の概念図から &ref(hdc.jpg); 真ん中のHDCというのは、デバイスコンテキストのハンドル(ポインタ)です デバイスコンテキストとは、出力対象を指します 大抵の場合、出力対象とはアプリケーションのクライアント領域(描画領域)を指します HDCに結ばれているものを描画オブジェクトと呼びます(正式な呼び方ではないかもしれませんが…) ・HPENはペンのハンドルです。 ペンは線を書くのに使います。 ・HBITMAPはビットマップのハンドルです。 ビットマップ画像(.bmp)を指します。 ここで、ビットマップ以外は取り扱えないの?って話になるのですが GDI+を使えばそれ以外のフォーマットの画像でも簡単に読み込み描画することができます(それについてはまた今度) ・HBRUSHはブラシのハンドルです。 ブラシは図形の塗りつぶしに使います。 ・HFONTはフォントのハンドルです。 フォントは文字を書くときに文字サイズや書式を決めます。 ・HRGNはリージョンのハンドルです。 リージョンは、ウィンドウの形の決めることができます 今回は使いません、機会があったら取り上げようと思います。 ・HPALETTEはパレットのハンドルです。 ここでは取り扱いません SIZE(30){ COLOR(#ffaa00){デバイスコンテキストには関連付けられた描画オブジェクトが必ず1種類ずつ存在します}} つまり常にデバイスコンテキストは ペン、ビットマップ、ブラシ、フォント、リージョン、パレットの一種類ずつを持っています (イメージ的には、千手観音みたいなもの) ここで注意してほしいのは、 SIZE(30){ COLOR(#ffaa00){デバイスコンテキストは同時に同じ種類の描画オブジェクトを2つ以上持てません}} たとえば、ペンを2つ同時に持つことはできません なので 太い線を書いた後、細い線を書きたいなと思ったら 太いペンで太い線を書いた後、細いペンに持ちかえなければなりません ブラシやフォントに関しても同じです ------------------------------------------------------------------- 今回のソースコードです。 //------------------------------------------------------// // ウィンドウ描画メッセージ処理 // // 引数: hWnd ウィンドウハンドル //------------------------------------------------------// void OnPaint(HWND hWnd){ // 描画開始 PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd,&ps); // ペン作成 HPEN hPen = CreatePen(PS_SOLID,5,RGB(255,0,0)); HPEN hNullPen = CreatePen(PS_NULL,1,RGB(0,0,0)); HPEN hDefPen = (HPEN)SelectObject(hdc,hPen); // ブラシ作成 HBRUSH hBrush = CreateSolidBrush(RGB(0,255,0)); HBRUSH hDefBrush = (HBRUSH)SelectObject(hdc,hBrush); // ビットマップ読み込み HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL); // インスタンスハンドル取得 HBITMAP hBmp = (HBITMAP)LoadImage(hInstance,_T("test.bmp"),IMAGE_BITMAP,0,0,LR_DEFAULTCOLOR | LR_SHARED | LR_LOADFROMFILE); BITMAP Bmp; GetObject(hBmp,sizeof(BITMAP),&Bmp); HDC hBmpDC = CreateCompatibleDC(hdc); HBITMAP hDefBmp = (HBITMAP)SelectObject(hBmpDC, hBmp); // フォント作成 LOGFONT logfont = {}; logfont.lfHeight = 30; // 文字高さ logfont.lfWidth = 12; // 文字幅 logfont.lfEscapement = 30; // 文字送りの方向とX軸の角度 logfont.lfOrientation = logfont.lfEscapement; // ベースラインとX軸との角度 logfont.lfWeight = FW_BOLD; // フォントの太さ logfont.lfItalic = TRUE; // イタリック体指定 logfont.lfUnderline = TRUE; // 下線付き指定 logfont.lfStrikeOut = TRUE; // 打ち消し線指定 logfont.lfCharSet = SHIFTJIS_CHARSET; // キャラクタセット logfont.lfOutPrecision = OUT_DEFAULT_PRECIS; // 出力精度 logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; // クリッピングの精度 logfont.lfQuality = DEFAULT_QUALITY; // 出力品質 logfont.lfPitchAndFamily= FF_DONTCARE|DEFAULT_PITCH;// ファミリー _tcscpy(logfont.lfFaceName,_T("MS ゴシック")); // フォント名 HFONT hFont = CreateFontIndirect(&logfont); HFONT hDefFont = (HFONT)SelectObject(hdc,hFont); // 直線描画 MoveToEx(hdc,10,100,NULL); // 始点をセット LineTo(hdc,10,200); // 始点から割り当てられたペンで終点まで直線描画 // 曲線描画 POINT pt[4] = {}; pt[0].x = 50;pt[0].y = 50; pt[1].x = 100;pt[1].y = 150; pt[2].x = 50;pt[2].y = 250; pt[3].x = 150;pt[3].y = 250; // 点情報から割り当てられたペンでベジェ曲線描画、点の数は3の倍数+1でなければならない PolyBezier(hdc,pt,4); // 楕円描画 SelectObject(hdc,hNullPen); // 枠がいらない RECT PointRect; PointRect.left = -5; PointRect.top = -5; PointRect.right = 5; PointRect.bottom = 5; for(int i = 0;i < 4;++i) Ellipse(hdc,pt[i].x + PointRect.left,pt[i].y + PointRect.top,pt[i].x + PointRect.right,pt[i].y + PointRect.bottom); // 矩形描画 SelectObject(hdc,hNullPen); // 枠がいらない RECT rect = {30,130,50,150}; Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom); // ブラシで指定された矩形を塗りつぶす // 画像表示 BitBlt(hdc,200,0,Bmp.bmWidth,Bmp.bmHeight,hBmpDC,0,0,SRCCOPY); int X = 0; int Y = 0; // 文字列描画 const TCHAR* szText = _T("Hello World"); SetTextColor(hdc,RGB(0,0,255)); // 文字色を青に変更 TextOut(hdc,X,Y,szText,(int)_tcslen(szText)); // 文字列描画 // 使い終わったら元の描画オブジェクトに戻す SelectObject(hdc,hDefPen); SelectObject(hdc,hDefBrush); SelectObject(hBmpDC, hDefBmp); SelectObject(hdc, hDefFont); // 使わなくなった描画オブジェクトは破棄 DeleteObject(hPen); DeleteObject(hNullPen); DeleteObject(hBrush); DeleteObject(hBmp); DeleteObject(hDefFont); // 使わなくなったデバイスコンテキストを破棄 DeleteDC(hBmpDC); // 描画終了 EndPaint(hWnd,&ps); } //------------------------------------------------------// // ウィンドウ破棄メッセージ処理 // // 引数: hWnd ウィンドウハンドル //------------------------------------------------------// void OnDestroy(HWND hWnd){ PostQuitMessage(0); } //-----------------------------------------------------// // ウィンドウプロシージャ // デフォルトの処理はDefWindowProc関数で行う // // 引数: hWnd ウィンドウハンドル // msg メッセージ // wParam パラメータ // lParam パラメータ // // 戻り値:処理結果 //-----------------------------------------------------// LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){ switch(msg){ HANDLE_MSG(hWnd,WM_PAINT,OnPaint); HANDLE_MSG(hWnd,WM_DESTROY,OnDestroy); } return DefWindowProc(hWnd, msg, wParam, lParam); } 描画を行うタイミングというものがあります(好きな時に描画していいわけではありません) それは、WM_PAINTというメッセージが送られてきたときです このWM_PAINTは、ウィンドウが作成されたときやウィンドウのサイズが変わったりしたときに ウィンドウプロシージャに送られてくるメッセージです メッセージクラッカーで自作のOnPaint関数に処理させます HANDLE_MSG(hWnd,WM_PAINT,OnPaint); 描画するときは 必ずBeginPaint関数とEndPaint関数の間で描画しなければなりません // 描画開始 PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd,&ps); // ここに描画処理 // 描画終了 EndPaint(hWnd,&ps); ------------------------------------------------------ ペンを作成するには、 CreatePen関数を使います // ペン作成 HPEN hPen = CreatePen(PS_SOLID,5,RGB(255,0,0)); HPEN hNullPen = CreatePen(PS_NULL,1,RGB(0,0,0)); CreatePen関数の 第一引数はペンのスタイルです PS_SOLID 普通の線 PS_DASH 破線 PS_DOT 点線 PS_NULL 空のペン(描画しない) などがあります 第2引数はペンの太さです 第3引数はペンの色です 引数がCOLORREFとなっていますが RGBマクロを使うとよいでしょう 各種色成分は0〜255の範囲で指定します たとえば、RGB(255,0,0)は赤になります もっと詳しく知りたい人はmsdn公式を見てください http://msdn.microsoft.com/ja-jp/library/cc428348.aspx ペンを作っただけでは描画に反映されません デバイスコンテキストにペンを割り当てなければなりません SIZE(30){ COLOR(#ffaa00){描画オブジェクトをデバイスコンテキストに割り当てるにはSelectObject関数を使います}} HPEN hDefPen = (HPEN)SelectObject(hdc,hPen); 他の描画オブジェクトに関しても割り当てないと反映されません たとえば、ブラシなどは次のようにキャストして使います HBRUSH hDefBrush = (HBRUSH)SelectObject(hdc,hBrush); SelectObject関数の戻り値はHGDIOBJ型となっていますが、 これを割り当てた描画オブジェクト型にキャストします ここで気を付けてほしいのはデフォルトの描画オブジェクトが戻り値として返ってきます SIZE(30){ COLOR(#ffaa00){デフォルトの描画オブジェクトは描画終了時にデバイスコンテキストに戻さなければいけません}} // 使い終わったら元の描画オブジェクトに戻す SelectObject(hdc,hDefPen); SelectObject(hdc,hDefBrush); SelectObject(hBmpDC, hDefBmp); SelectObject(hdc, hDefFont); 直線を描画するには、MoveTo関数で始点をセットし LineTo関数で始点から終点までデバイスコンテキストに割り当てられたペンで描画します // 直線描画 MoveToEx(hdc,始点x座標,始点y座標,NULL); // 始点をセット LineTo(hdc,終点y座標,終点y座標); // 始点から割り当てられたペンで終点まで直線描画 続いて、曲線(ベジェ曲線)を描いてみます ベジェ曲線とは端点(始点と終点)と制御点 を指定してやるだけで形状が決まる曲線です 3次元ベジェ曲線を描くためには最低4つの点が必要です (3次元ベジェが最も一般的なベジェ曲線) &ref(bezier.GIF) 緑の枠に囲まれた部分を凸包と呼び、 曲線は必ず凸包の内部に収まるという性質があります // 曲線描画 POINT pt[4] = {}; pt[0].x = 50;pt[0].y = 50; pt[1].x = 100;pt[1].y = 150; pt[2].x = 50;pt[2].y = 250; pt[3].x = 150;pt[3].y = 250; // 点情報から割り当てられたペンでベジェ曲線描画、点の数は3の倍数+1でなければならない PolyBezier(hdc,点の配列,点の個数(3の倍数+1)); ----------------------------------------------------- 続いてブラシを作ってみます ブラシは図形の塗りつぶしに使います 単色の塗りつぶし用ブラシ作成にはCreateSolidBrush関数を使います 色を指定して作成します // ブラシ作成 HBRUSH hBrush = CreateSolidBrush(RGB(0,255,0)); ここでは、説明しませんが 網目模様などのパターン描画にはCreateHatchBrush関数 画像のイメージをタイル状にしたブラシ作成にはCreatePatternBrush関数 などが用意されています ブラシをデバイスコンテキストに割り当てた後 HBRUSH hDefBrush = (HBRUSH)SelectObject(hdc,hBrush); 図形を描画します 円(楕円)を描画するにはEllipse関数を使います // 楕円描画 SelectObject(hdc,hNullPen);// 枠がいらない RECT PointRect; PointRect.left = -5; PointRect.top = -5; PointRect.right = 5; PointRect.bottom = 5; for(int i = 0;i < 4;++i) Ellipse(hdc,pt[i].x + PointRect.left,pt[i].y + PointRect.top,pt[i].x + PointRect.right,pt[i].y + PointRect.bottom); 矩形を描画するには Rectangle関数を使います // 矩形描画 SelectObject(hdc,hNullPen); // 枠がいらない RECT rect = {30,130,50,150}; Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom); // ブラシで指定された矩形を塗りつぶす 最後に、新しく作成(Createなんとか)した描画オブジェクト およびデバイスコンテキストは解放しなければなりません // 使わなくなった描画オブジェクトは破棄 DeleteObject(hPen); DeleteObject(hNullPen); DeleteObject(hBrush); DeleteObject(hBmp); DeleteObject(hDefFont); // 使わなくなったデバイスコンテキストを破棄 DeleteDC(hBmpDC); 描画オブジェクトに関してはDeleteObject関数を デバイスコンテキストに関してはDeleteDC関数を を使います さらに詳しい対応関係はMSDN公式を参照してください http://msdn.microsoft.com/en-us/library/ms724291%28VS.85%29.aspx ここで全てを取り上げるの量的に不可能なので その他の描画に関しての情報はMSDN公式を見てください MSDN公式 http://msdn.microsoft.com/ja-jp/library/cc428835.aspx 全ソースコードは下から &ref(main.cpp); プログラムで使う画像はこちら &ref(test.bmp); #vote((^ω^)やったお[0],何これwww意味不すぎwww[0],。(`ω´#)。あぁん?最近、だらしねぇな[0])