Direct Draw で「ドラえもん」を描く

DirectX の GetBackBuffer で BackBuffer を取得して、直接 GDI 関数で「ドラえもん」を描きます。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. プロジェクトのフォルダーに次のファイルを格納して下さい。
    見つからないときは、ここから Object Class Library から取得して下さい。
    ソースプログラムは、プログラムの説明を参考にして作成して下さい。
    ファイル名 ファイルの説明
    Draemon.cpp Main Program File
    Ddutil.cpp c:\Dx90SDK\Samples\C++\Common からコピー
    Ddutil.h c:\Dx90SDK\Samples\C++\Common からコピー
    Dxutil.h c:\Dx90SDK\Samples\C++\Common からコピー
  2. ヘッダファイルの取り込みとライブラリのリンクと #define です。
    ライブラリをリンクする方法には「プロジェクト/プロパティからリンカ/入力/追加する依存関係」で設定する方法と、 今回のように #pragma でリンクする方法があります。
        #include    <windows.h>
        #include    <ddraw.h>
        #include    "ddutil.h"
    
        #pragma once
        #pragma comment(lib,"dxguid.lib")
        #pragma comment(lib,"dxerr9.lib")
        #pragma comment(lib,"ddraw.lib")
        #pragma comment(lib,"winmm.lib")
    
        // Defines, constants, and global variables
        #define SAFE_DELETE(p)  { if (p) { delete (p);     (p)=NULL; } }
        #define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p)=NULL; } }
        #define SAFE_DELOBJ(p)  { if (p) { DeleteObject(p); (p)=NULL; } }
        #define ERMSG(x)        MessageBox(hWnd,x,"DirectDraw Program",MB_OK);
        
  3. Direct Draw の CDisplay と描画可能フラグの定義です。
    「ドラえもん」の画像は StockObject だけで描くことは出来ないのでペンとブラシを生成します。
    Windows では描画の都度ペンとブラシを生成しますが、DirectX では頻繁に描画が繰り返されるので「メモリを使い切ってハングアップ」してしまいました。 (^_^;)
    DirectX でペンやブラシを使うときは、初期化の時に生成しておいて、終了時に開放してやります。
        CDisplay*   g_pDisplay    = NULL;
        BOOL        g_bActive     = FALSE;
    
        // 描画に使用するオブジェクト
        HBRUSH          g_Brush1= NULL;
        HBRUSH          g_Brush2= NULL;
        HBRUSH          g_Brush3= NULL;
        HBRUSH          g_Brush4= NULL;
        HPEN            g_Pen1= NULL;
        HPEN            g_Pen2= NULL;
        HPEN            g_Pen3= NULL;
        
  4. WinMain() はいつもと同じです。
        int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR pCmdLine, int nCmdShow)
        {   MSG     msg;
    
            WNDCLASS wc = { CS_CLASSDC,WndProc,0L,0L,hInst,
                            NULL,NULL,NULL,NULL,NAME };
            if (RegisterClass(&wc)==0)    return FALSE;
    
            HWND hWnd = CreateWindow(NAME,NAME,WS_OVERLAPPEDWINDOW,50,50,0,0,
                                     GetDesktopWindow(),NULL,wc.hInstance,NULL);
            if (hWnd==NULL)   return FALSE;
    
            if (FAILED(InitDraw(hWnd)))
            {   if (g_pDisplay)
                    g_pDisplay->GetDirectDraw()->SetCooperativeLevel(NULL, DDSCL_NORMAL);
                ERMSG("DirectDraw init failed. The sample will now exit.");
                return FALSE;
            }
    
            ShowWindow(hWnd, nCmdShow);
            UpdateWindow(hWnd);
            while(TRUE)
            {   if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
                {   if (0 == GetMessage(&msg, NULL, 0, 0))  return (int)msg.wParam;
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
                else
                {   if (g_bActive)
                    {   if (FAILED(DisplayFrame()))
                        {   SAFE_DELETE(g_pDisplay);
                            ERMSG("Displaying the next frame failed");
                            return FALSE;
                        }
                    }
                    else    WaitMessage();
                }
            }
        }
        
  5. CALLBACK 関数です。
    何かキーが押されるとプログラムを終了します。
    g_pDisplay->UpdateBounds() が描画をウインドウサイズに合わせる関数です。
    この行をコメントにすると、ウインドウサイズが変わっても描画サイズは変わりません。
        LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
        {   switch (msg)
            {   case WM_KEYDOWN:
                    PostMessage(hWnd,WM_CLOSE,0,0);
                    return 0L;
                case WM_PAINT:
                    if (g_pDisplay)
                    {   if (DisplayFrame() == DDERR_SURFACELOST)
                        {   PostMessage(hWnd,WM_CLOSE,0,0);
                        }
                    }
                    break;
                case WM_MOVE:
                    if (g_pDisplay)    g_pDisplay->UpdateBounds();
                    return 0L;
                case WM_SIZE:
                    if (SIZE_MAXHIDE==wParam||SIZE_MINIMIZED==wParam)  g_bActive= FALSE;
                    else   g_bActive= TRUE;
                    if (g_pDisplay)    g_pDisplay->UpdateBounds();
                    break;
                case WM_DESTROY:
                    FreeDraw();
                    PostQuitMessage(0);
                    return 0L;
            }
            return DefWindowProc(hWnd, msg, wParam, lParam);
        }
        
  6. Direct Draw の初期化です。
    初期化が正常に終了すると、「ドラえもん」を描画するためのペンとブラシを生成します。
        HRESULT InitDraw(HWND hWnd)
        {   HRESULT     hr;
    
            g_pDisplay = new CDisplay();
            if (FAILED(hr = g_pDisplay->CreateWindowedDisplay(hWnd,640,480)))
            {   ERMSG("Failed initializing DirectDraw");
                return hr;
            }
            g_Pen1= CreatePen(PS_SOLID, 3, RGB(0, 0, 255));
            g_Pen2= CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
            g_Pen3= CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
            g_Brush1= CreateSolidBrush(RGB(0, 0, 255));
            g_Brush2= CreateSolidBrush(RGB(255, 255, 255));
            g_Brush3= CreateSolidBrush(RGB(0, 0, 0));
            g_Brush4= CreateSolidBrush(RGB(255, 0, 0));
            return S_OK;
        }
        
  7. 「ドラえもん」の描画は DisplayFrame() 関数で行います。
    g_pDisplay->GetBackBuffer(); で BackBuffer を取得して直接描画します。
    取得するサーフェスは DirectX7 から変わっていません。
    サーフェスが取得できたらデバイスコンテキストを取得して描画します。
        HRESULT DisplayFrame()
        {   HRESULT                 hr;
            LPDIRECTDRAWSURFACE7    pBack;   // DirectDraw back surface
            HDC                     hdc;
    
            g_pDisplay->Clear(RGB(255,255,255));
            pBack= g_pDisplay->GetBackBuffer();
            if (pBack->GetDC(&hdc)==DD_OK)
            {   // 顔1
                SelectObject(hdc, g_Pen1);
                SelectObject(hdc, g_Brush1);
                Ellipse(hdc, 100, 100, 400, 400);
                // 顔2
                SelectObject(hdc, g_Brush2);
                Ellipse(hdc, 120, 160, 380, 400);
                // 白目
                SelectObject(hdc, g_Pen2);
                SelectObject(hdc, g_Brush2);
                Ellipse(hdc, 185, 105, 250, 200);
                Ellipse(hdc, 250, 105, 315, 200);
                // 黒目
                SelectObject(hdc, g_Pen2);
                SelectObject(hdc, g_Brush3);
                Ellipse(hdc, 230, 150, 250, 175);
                Ellipse(hdc, 250, 150, 270, 175);
                // 鼻の線
                SelectObject(hdc, g_Pen2);
                MoveToEx(hdc, 250, 220, NULL);
                LineTo(hdc, 250, 260);
                // 鼻
                SelectObject(hdc, g_Pen3);
                SelectObject(hdc, g_Brush4);
                Ellipse(hdc, 230, 180, 270, 220);
                // 口
                Chord(hdc, 140, 120, 360, 380, 140, 260, 360, 260);
                // ヒゲ左
                SelectObject(hdc, g_Pen2);
                MoveToEx(hdc, 150, 205, NULL);
                LineTo(hdc, 210, 215);
                MoveToEx(hdc, 140, 230, NULL);
                LineTo(hdc, 210, 230);
                MoveToEx(hdc, 150, 255, NULL);
                LineTo(hdc, 210, 245);
                // ヒゲ右
                MoveToEx(hdc, 350, 205, NULL);
                LineTo(hdc, 290, 215);
                MoveToEx(hdc, 360, 230, NULL);
                LineTo(hdc, 290, 230);
                MoveToEx(hdc, 350, 255, NULL);
                LineTo(hdc, 290, 245);
                pBack->ReleaseDC(hdc);
            }
            if (FAILED(hr= g_pDisplay->Present())) return hr;
            return S_OK;
        }
        
  8. 取得したオブジェクトの開放です。
    ブラシやペンも開放して下さい。
        VOID FreeDraw()
        {   SAFE_DELETE(g_pDisplay);
            SAFE_DELOBJ(g_Brush1);
            SAFE_DELOBJ(g_Brush2);
            SAFE_DELOBJ(g_Brush3);
            SAFE_DELOBJ(g_Brush4);
            SAFE_DELOBJ(g_Pen1);
            SAFE_DELOBJ(g_Pen2);
            SAFE_DELOBJ(g_Pen3);
        }
        

超初心者のプログラム入門(DirectX9 game program)