2012年7月29日日曜日

Direct2Dの初期化と単純な描画コード

Direct2Dの解説は
プログラミングガイド から色々たどれば基本的な事は分かるのではないかという気がします。

このページではDirect2Dの基本的な利用方法 を参考にデバイスの初期化をやってみます。

まず、win32API的な普通のウインドウを出せる事が前提です。
(これはVC++のファイル→新規作成→プロジェクト→Win32プロジェクトとかで簡単に作れるので問題ないかと思います。)

インポートライブラリの追加

ソリューションエクスプローラーのプロジェクトを右クリックするかメニューのプロジェクトからプロパティを開き、構成プロパティ→リンカ→入力を開きます。

追加のファイルに「D2D1.lib」を指定します。



 Direct2Dの初期化部分は通常のウインドウの初期化部分と明確に分けて見通しを良くしたかったのでD2Dという名前のクラスを作ってそこに記述しました。そのクラスのCreateDevice()という関数で初期化の例を示しています。

はじめにこのクラスのヘッダファイル(D2D.h)と本体(D2D.cpp)を掲載します。
次にこのクラスの使い方を説明します。
最後にDirect2Dの解説をしようかと思いましたが、それは次節に譲ります。

D2D.h

/*  D2D.h
D2D:このクラスの説明

Direct2Dの初期化を行います。
ただしこのクラスではDirect3DやGDIとの相互運用はできません。
(ID2D1HwndRenderTargetの初期化を行うクラスです。)

ウインドウ作成した後、
 CreateDevice(const HWND hWnd)
 で作成済みのウインドウハンドルを渡して下さい。

 初期化の内容は
 ID2D1FactoryとID2D1HwndRenderTargetの作成です。
 --------------------------------------------
作成したリソースの開放処理はデストラクタで行われます。
 --------------------------------------------
 ウインドウのサイズ変更時にはResizeWindow(HWND hWnd)にウインドウハンドルを
 渡してください。
 (ウインドウプロシージャでWM_SIZEメッセージを受け取った時に呼ぶ例)
 
 case WM_SIZE: 
     d2d.ResizeWindow(hWnd);
  break;

これをやらない場合は、ウインドウサイズに合わせて描画対象も変形します。
-------------------------------------------
 RenderTest()で描画テストが行えます。
 (ウインドウプロシージャのWM_PAINTメッセージ受信時に呼ぶ例)

  case WM_PAINT:
  hdc = BeginPaint(hWnd, &ps);
  d2d.RenderTest(hWnd);
  EndPaint(hWnd, &ps);
  break;
------------------------------------------
*/
#pragma once
#include <D2d1.h>

class D2D
{
public:
 D2D();

 ~D2D(void)
 {
  DestroyDevice();
 };

 bool CreateDevice(const HWND hWnd);
 //D2Dの機能を使う前の初期化を行います。
 //引数には描画対象となる作成済みウインドウのハンドルを渡して下さい

void RenderTest();
 //描画テストです
 //この関数を呼ぶ場合はCreateDeviceで初期化を済ませる必要があります。

 void ResizeWindow(HWND hWnd);
 // ウインドウのサイズを更新します。
 // ウインドウプロシージャでウインドウサイズの変更の
 //通知を受け取った時等に呼んで下さい。
 
private:
 //コピーや代入は禁止しています。
 D2D(const D2D& t); 
 D2D& operator=(const D2D& t);

 void DestroyDevice();
 //CreateDeviceで初期化したD2Dのデバイスを解体します。
 //デストラクタから呼ばれます。

 ID2D1Factory* pD2DFactory ;//ファクトリ
 ID2D1HwndRenderTarget* pHwndRenderTarget ;//レンダーターゲット

};

D2D.cpp


#include "D2D.h"
#include <crtdbg.h>

D2D::D2D(void)
{
 pD2DFactory = NULL;
 pHwndRenderTarget = NULL;
}

bool D2D::CreateDevice(HWND hWnd)
{
 HRESULT hr; //成否メッセージ戻り値

    // Direct2D ファクトリの作成
 // レンダーターゲットを作るためにこれが必要です

 if(pD2DFactory == NULL)
 {
   hr = ::D2D1CreateFactory(     
   D2D1_FACTORY_TYPE_SINGLE_THREADED,&pD2DFactory_);     
   
   if(!SUCCEEDED(hr)) 
     return false; 
   
 }

 //< レンダーターゲット(描画対象)の作成 >
 // ウインドウとDirect2Dの描画装置を関連付けます。
 // ウインドウと関連付けた描画装置は
 //レンダーターゲットという名前で呼びます。
 //ブラシなどの描画道具の作成もレンダーターゲットが行います。
    if( pHwndRenderTarget == NULL) 
    { 
        RECT rect; 
        ::GetClientRect(hWnd, &rect); 
        D2D1_SIZE_U size = 
            D2D1::Size<UINT>(rect.right, rect.bottom); 

        hr = pD2DFactory->CreateHwndRenderTarget(
                D2D1::RenderTargetProperties(),  
                D2D1::HwndRenderTargetProperties(hWnd, size), 
                &pHwndRenderTarget);
        if(!SUCCEEDED(hr)) 
   return false; 
    } 

    return true; 
}

void D2D::DestroyDevice()
{
    if(pHwndRenderTarget != NULL) 
    { 
        pHwndRenderTarget->Release(); 
        pHwndRenderTarget = NULL; 
    } 
  if(pD2DFactory != NULL) 
    { 
        pD2DFactory->Release();  
        pD2DFactory = NULL; 
    }  
}

void D2D::RenderTest()
{
 //RenderTestは描画テストをする関数です。
 //この関数を呼ぶ場合はCreateDeviceで初期化を済ませる必要があります。
 //初期化がうまくいっていないとアサートします。
 _ASSERT(pHwndRenderTarget);

 //ウィンドウが他のウィンドウに覆われていたら描画処理をしない
    if(pHwndRenderTarget->CheckWindowState() & D2D1_WINDOW_STATE_OCCLUDED) 
        return; 

 HRESULT hr; 
 // ブラシの作成 
 ID2D1SolidColorBrush* pGreenBrush  = NULL;

    hr = pHwndRenderTarget->CreateSolidColorBrush(        
            D2D1::ColorF(0.0F, 1.0F, 0.0F), &pGreenBrush); 
    if(!SUCCEEDED(hr))
  return;
 
 //---------描画開始--------
    pHwndRenderTarget->BeginDraw();  
 
    //背景クリア 
    pHwndRenderTarget->Clear(D2D1::ColorF(1.0F, 1.0F, 1.0F));      
 
    //円の描画 
    D2D1_ELLIPSE ellipse1 = 
        D2D1::Ellipse(D2D1::Point2F(120.0F, 120.0F), 100.0F, 100.0F); 
    pHwndRenderTarget->DrawEllipse(ellipse1, pGreenBrush, 10.0F); 
 
    //四角形の描画 
    D2D1_RECT_F rect1 =  
        D2D1::RectF(100.0F, 50.0F, 300.0F, 100.F); 
    pHwndRenderTarget->FillRectangle(&rect1, pGreenBrush);

    pHwndRenderTarget->EndDraw();
 //---------描画終了---------

 //ブラシの解放
 if(pGreenBrush != NULL) 
    { 
        pGreenBrush->Release(); 
        pGreenBrush = NULL; 
    }
}

void D2D::ResizeWindow(HWND hWnd)
{
    //レンダーターゲットの論理サイズの再設定 
 if(pHwndRenderTarget)
 {
  RECT rect; 
  ::GetClientRect(hWnd, &rect); 
  pHwndRenderTarget->Resize( D2D1::SizeU(rect.right, rect.bottom) ); 
 }
}

なお、VC++の設定及びwin32プロジェクトの作り方によってはD2D.cppファイルの頭に #include "StdAfx.h" を加える必要があります。

このクラスの利用方法


このクラス(D2D)のおおまかな利用方法は次の通りです。


1・D2Dクラスのオブジェクトを作成
2・ウインドウのハンドルを渡してDirect2Dを初期化
3・ ウインドウサイズの変更に対応
4・ 描画処理を記述

これらを、順を追って説明します。

1・D2Dのオブジェクトを作成


C++の基礎的な話になりますが、まずオブジェクトを作ります。
例えばオブジェクトの名前(変数名)をd2dにしたければ

 D2D d2d;

と言った感じです。

2・ウインドウのハンドルを渡して初期化

普通のwin32APIのプログラムならウインドウを作っている部分があると思います。例えば

hWnd = CreateWindow(・・・

 if (!hWnd)
{//初期化失敗時は終了
  return FALSE;
}

という様な部分です。
Direct2Dの初期化はこの後に D2DのCreateDevice(const HWND hWnd)を呼べばできます。その際、引数としてウインドウのハンドルを渡して下さい。

例えば次の通りです。

d2d.CreateDevice(hWnd)

CreateDevice(const HWND hWnd)は戻り値として初期化の成否を返すので、失敗した場合は次のようにしてアプリケーションを終わらせると良いかも知れません。

if (!d2d.CreateDevice(hWnd))
{
    //失敗したのアプリケーションを終わらせる。
}

3・ ウインドウサイズの変更に対応

ウインドウのサイズ変更時にはResizeWindow(HWND hWnd)にウインドウハンドルを 渡してください。
ウインドウプロシージャでWM_SIZEメッセージを受け取った時に呼ぶ例は
case WM_SIZE:
d2d.ResizeWindow(hWnd);
break;
といった感じです。
これをやらない場合は、ウインドウサイズに合わせて描画対象も変形します。

4・描画処理の記述

RenderTest()で描画テストが行えます。
 ウインドウプロシージャのWM_PAINTメッセージ受信時に呼ぶ例は

case WM_PAINT:
hdc = BeginPaint(hWnd, &amp;ps);
d2d.RenderTest();
EndPaint(hWnd, &amp;ps);
break;

という感じです。
実行結果はこんな感じです。


解体処理について

このクラスで使用したDirect2Dリソース等のオブジェクト類はデストラクタ任せで解体されます。つまり普通に作ったオブジェクトは何もしなくてもアプリケーション終了時に解体されます。

D2D* d2d;
d2d = new D2D;
という風にnewで作った場合は
delete d2d;
という風にdeleteした時点で解体されます。

Direct2Dの初期化

・・・さて、とりあえず上に書いたクラスの
CreateDevice(HWND hWnd)
というメソッドの中がDirect2Dを初期化する部分だと言えます。
簡単な解説は次の節で行うつもりです。

0 件のコメント: