プログラミングのメモ帳(C/C++/HSP)

日々のプログラミングで気づいた点や小技集を紹介します。(Windows 10/XP/Vista、VC2017、HSP)

PlgBltで図形を回転するサンプル

2012年03月19日 13時20分00秒 | グラフィック関連

正方形領域の図形を回転するには 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度回転

関連記事



コメント    この記事についてブログを書く
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« 付録C ゲームソースのテンプ... | トップ | 数式文字列の四則演算(整数値) »
最新の画像もっと見る

コメントを投稿

グラフィック関連」カテゴリの最新記事