正方形領域の図形を回転するには PlgBlt 関数を使います。
この API 関数は長方形領域から平行四辺形に変形して転送します。
しかし、平行四辺形の頂点座標を適切にセットすることで図形の回転が行えます。
ここでは、その具体的な方法(ソースコード)を紹介します。
図形の回転
BOOL funcRotateRect( HDC hScreenDC, // 転送先のデバイスコンテキスト・ハンドル LONG cx, // 転送先の中心点(横軸座標) LONG cy, // 転送先の中心点(縦軸座標) HDC hImageDC, // 転送元のデバイスコンテキスト・ハンドル LONG bx, // 転送元正方形の左上隅の x 座標 LONG by, // 転送元正方形の左上隅の y 座標 LONG sx, // 転送元正方形の横幅 LONG sy, // 転送元正方形の高さ DOUBLE nAngle // 回転角度(ラジアン) );
上記が正方形の図形を回転して PlgBlt 関数で描画する専用関数です。
回転させる角度 nAngle にはラジアンで指定します。
度数からラジアンに変換するための DEG2RAD マクロ関数を使ってます。
サンプル
下のソースコードをコピー&ペーストしてコンパイルすると図形の回転を確認できます。
ぜひ、試してみて下さい。
//------------------------------------------------------------------------------ // PlgBltで図形の回転サンプル //------------------------------------------------------------------------------ #define _USE_MATH_DEFINES #include <math.h> #include <tchar.h> #include <Windows.h> //------------------------------------------------ // break 付きのキーワード //------------------------------------------------ #define CASE break;case #define DEFAULT break;default //------------------------------------------------ // マクロ関数 //------------------------------------------------ #define LONGROUND(n) (LONG)((n) + 0.5) // LONG型変換付き四捨五入 #define DEG2RAD(n) (M_PI * (n) / 180) // 度数からラジアンに変換 //------------------------------------------------ // イメージの回転描画 //------------------------------------------------ static BOOL funcRotateRect( HDC hScreenDC, LONG cx, LONG cy, HDC hImageDC, LONG bx, LONG by, LONG sx, LONG sy, DOUBLE nAngle ) { DOUBLE hx = (sx / 2); // 矩形の横半分 DOUBLE hy = (sy / 2); // 矩形の縦半分 DOUBLE radius = sqrt((hx * hx) + (hy * hy)); // 矩形の回転半径 POINT po[ 3 ]; // 矩形の回転頂点 // 左上隅 nAngle -= (M_PI_4 + M_PI_2); po[0].x = (cx + LONGROUND(cos(nAngle) * radius)); po[0].y = (cy + LONGROUND(sin(nAngle) * radius)); // 右上隅 nAngle += M_PI_2; po[1].x = (cx + LONGROUND(cos(nAngle) * radius)); po[1].y = (cy + LONGROUND(sin(nAngle) * radius)); // 左下隅 nAngle += M_PI; po[2].x = (cx + LONGROUND(cos(nAngle) * radius)); po[2].y = (cy + LONGROUND(sin(nAngle) * radius)); return PlgBlt( hScreenDC, po, hImageDC, bx, by, sx, sy, NULL, 0, 0 ); } //------------------------------------------------ // メモリ・スクリーンの作成 //------------------------------------------------ static HDC funcCreateMemDC( HWND hWnd, LONG nWidth, LONG nHeight ) { HDC hDC; HDC hMemDC; HBITMAP hBitmap; // DCコンパチブルの作成 hDC = GetDC( hWnd ); hMemDC = CreateCompatibleDC( hDC ); hBitmap = CreateCompatibleBitmap( hDC, nWidth, nHeight ); SelectObject( hMemDC, hBitmap ); SelectObject( hMemDC, GetStockObject(DC_PEN) ); SelectObject( hMemDC, GetStockObject(DC_BRUSH) ); DeleteObject( hBitmap ); ReleaseDC( hWnd, hDC ); return hMemDC; } //------------------------------------------------ // 笑顔のイメージ描画 //------------------------------------------------ static VOID funcDrawImage( HDC hDC, LONG sx, LONG sy ) { // 横軸(7分割) LONG x1 = (sx * 1 / 7); LONG x2 = (sx * 2 / 7); LONG x3 = (sx * 3 / 7); LONG x4 = (sx * 4 / 7); LONG x5 = (sx * 5 / 7); LONG x6 = (sx * 6 / 7); // 縦軸(7分割) LONG y1 = (sy * 1 / 7); LONG y2 = (sy * 2 / 7); LONG y3 = (sy * 3 / 7); LONG y4 = (sy * 4 / 7); LONG y5 = (sy * 5 / 7); LONG y6 = (sy * 6 / 7); // 背景 RECT rc; SetRect( &rc, 0, 0, sx, sy ); SetDCBrushColor( hDC, RGB(0xFF,0xCC,0xFF) ); FillRect( hDC, &rc, (HBRUSH)GetStockObject(DC_BRUSH) ); // 笑顔 SetDCPenColor( hDC, RGB(0xCC,0xCC,0x00) ); SetDCBrushColor( hDC, RGB(0xFF,0xFF,0x00) ); Ellipse( hDC, 0, 0, sx, sy ); // 両目 SetDCPenColor( hDC, RGB(0xCC,0xCC,0x00) ); SetDCBrushColor( hDC, RGB(0xFF,0xFF,0xFF) ); Ellipse( hDC, x1, y2, x3, y4 ); Ellipse( hDC, x4, y2, x6, y4 ); // 黒目 SetDCPenColor( hDC, RGB(0x01,0x01,0x01) ); SetDCBrushColor( hDC, RGB(0x01,0x01,0x01) ); Ellipse( hDC, x2-7, y3-7, x2+8, y3+8 ); Ellipse( hDC, x5-7, y3-7, x5+8, y3+8 ); // 口 SetDCPenColor( hDC, RGB(0xCC,0x00,0x00) ); SetDCBrushColor( hDC, RGB(0xFF,0x00,0x00) ); RoundRect( hDC, x2, y5, x5, y6, x1, y1 ); } //------------------------------------------------ // WM_PAINTメッセージの処理 //------------------------------------------------ static VOID funcDrawPaint( HDC hScreenDC, HDC hImageDC, LONG cx, LONG cy, LONG sx, LONG sy, DOUBLE nAngle ) { // ペンとブラシ SelectObject( hScreenDC, GetStockObject(DC_PEN) ); SelectObject( hScreenDC, GetStockObject(NULL_BRUSH) ); // 笑顔の回転 funcRotateRect( hScreenDC, cx, cy, hImageDC, 0, 0, sx, sy, DEG2RAD(nAngle) ); // 笑顔の矩形 SetDCPenColor( hScreenDC, RGB(0xFF,0x00,0x00) ); Rectangle( hScreenDC, (cx - sx/2), (cy - sy/2), (cx + sx/2), (cy + sy/2) ); // 十字ライン SetDCPenColor( hScreenDC, RGB(0x00,0x00,0xFF) ); MoveToEx( hScreenDC, 0, cy, NULL ); LineTo( hScreenDC, 8192, cy ); MoveToEx( hScreenDC, cx, 0, NULL ); LineTo( hScreenDC, cx, 8192 ); } //------------------------------------------------ // タイトルバーに角度を表示 //------------------------------------------------ static VOID funcTitleAngle( HWND hWnd, DOUBLE nAngle ) { TCHAR szBuff[ 100 ]; TCHAR chSign; DOUBLE nMajor; DOUBLE nMinor; if ( nAngle == 0.0 ){ chSign = TEXT(' '); nMajor = 0; nMinor = 0; } else if ( nAngle < 0.0 ){ chSign = TEXT('-'); nMinor = modf( -nAngle, &nMajor ); } else{ chSign = TEXT('+'); nMinor = modf( +nAngle, &nMajor ); } nMinor *= 1000000000; wsprintf( szBuff, TEXT("PlgBltで図形の回転サンプル(nAngle=%c%u.%09u度)"), chSign, (UINT)nMajor, (UINT)nMinor ); SetWindowText( hWnd, szBuff ); InvalidateRect( hWnd, NULL, TRUE ); UpdateWindow( hWnd ); } //------------------------------------------------ // ウインドウのプロシージャ関数 //------------------------------------------------ extern LRESULT CALLBACK mainWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { static HDC hFaceChip; // 笑顔のイメージ static LONG nFaceW; // 笑顔の横サイズ static LONG nFaceH; // 笑顔の縦サイズ static LONG nFaceX; // 笑顔の横軸位置 static LONG nFaceY; // 笑顔の縦軸位置 static DOUBLE nAngle; // 回転の角度(度数) switch ( uMsg ){ CASE WM_CREATE: nAngle = 0.0; nFaceW = 300; nFaceH = 300; hFaceChip = funcCreateMemDC( hWnd, nFaceW, nFaceH ); funcDrawImage( hFaceChip, nFaceW, nFaceH ); funcTitleAngle( hWnd, nAngle ); CASE WM_CLOSE: DeleteDC( hFaceChip ); DestroyWindow( hWnd ); PostQuitMessage( 0 ); CASE WM_PAINT: { PAINTSTRUCT ps; HDC hDC; hDC = BeginPaint( hWnd, &ps ); funcDrawPaint( hDC, hFaceChip, nFaceX, nFaceY, nFaceW, nFaceH, nAngle ); EndPaint( hWnd, &ps ); } CASE WM_KEYDOWN: switch ( wParam ){ // 矢印キー CASE VK_UP: nAngle -= 180; CASE VK_DOWN: nAngle += 180; CASE VK_LEFT: nAngle -= 90; CASE VK_RIGHT: nAngle += 90; // テンキー CASE VK_NUMPAD0: nAngle = 0; CASE VK_NUMPAD1: nAngle -= 90; CASE VK_NUMPAD2: nAngle += 1; CASE VK_NUMPAD3: nAngle += 90; CASE VK_NUMPAD4: nAngle -= 5; CASE VK_NUMPAD5: nAngle += 1; CASE VK_NUMPAD6: nAngle += 5; CASE VK_NUMPAD7: nAngle -= 45; CASE VK_NUMPAD8: nAngle -= 1; CASE VK_NUMPAD9: nAngle += 45; DEFAULT: return 0; } funcTitleAngle( hWnd, nAngle ); CASE WM_SIZE: nFaceX = (LOWORD(lParam) / 2); nFaceY = (HIWORD(lParam) / 2); DEFAULT:return DefWindowProc( hWnd, uMsg, wParam, lParam ); } return 0; } //------------------------------------------------ // ウインドウ・クラスの登録 //------------------------------------------------ static ATOM funcWindowClass( HINSTANCE hInstance, LPCTSTR lpClassName ) { WNDCLASSEX wcex = { 0 }; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = (CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS); wcex.lpfnWndProc = mainWindowProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon( NULL, IDI_APPLICATION ); wcex.hIconSm = LoadIcon( NULL, IDI_APPLICATION ); wcex.hCursor = LoadCursor( NULL, IDC_ARROW ); wcex.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH ); wcex.lpszMenuName = NULL; wcex.lpszClassName = lpClassName; return RegisterClassEx( &wcex ); } //------------------------------------------------ // ウインドウの作成 //------------------------------------------------ static HWND funcCreateWindow( HINSTANCE hInstance, LPCTSTR lpClassName, LPCTSTR lpTitleName, int nCmdShow ) { HWND hWnd = CreateWindowEx( 0, // 拡張ウインドウ・スタイル lpClassName, // ウインドウのクラス名 lpTitleName, // ウインドウのタイトル名 WS_OVERLAPPEDWINDOW, // ウインドウのスタイル CW_USEDEFAULT, // ウインドウの横軸位置 CW_USEDEFAULT, // ウインドウの縦軸位置 CW_USEDEFAULT, // ウインドウの横幅 CW_USEDEFAULT, // ウインドウの高さ NULL, // 親ウインドウのハンドル NULL, // メニューバーのハンドル hInstance, // インスタンスのハンドル NULL ); // ウインドウ作成のデータ if ( hWnd != NULL ){ ShowWindow( hWnd, nCmdShow ); UpdateWindow( hWnd ); } return hWnd; } //------------------------------------------------ // メイン関数 //------------------------------------------------ extern int WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow ) { LPCTSTR lpClassName = TEXT("funcRotateRectWndClass"); LPCTSTR lpTitleName = TEXT("PlgBltで図形の回転サンプル"); MSG Msg; if ( funcWindowClass(hInstance,lpClassName) == 0 ){ return -1; } if ( funcCreateWindow(hInstance,lpClassName,lpTitleName,nCmdShow) == NULL ){ return -2; } while ( GetMessage(&Msg,NULL,0,0) > 0 ){ TranslateMessage( &Msg ); DispatchMessage( &Msg ); } UNREFERENCED_PARAMETER( hPrevInstance ); UNREFERENCED_PARAMETER( lpCmdLine ); return Msg.wParam; } //------------------------------------------------------------------------------ // End of funcRotateRect.cpp //------------------------------------------------------------------------------
キー操作
- [0]で0度に初期化。
- [8][2]で±1度の回転。
- [4][6]で±5度の回転。
- [7][9]で±45度の回転。
- [1][3]で±90度の回転。
- [↑][↓]で±180度の回転。
- [←][→]で±90度の回転。
実行結果
上記のGIF画像は[6]キーを押して、+5度ずつ0度~70度まで回転してます。
他にも次のGIF画像があります。(クリックすると拡大表示)
+5度回転 | -5度回転 | +45度回転 | -45度回転 |
![]() |
![]() |
![]() |
![]() |
※コメント投稿者のブログIDはブログ作成者のみに通知されます