第8章 クリッピング

8-4 Clipperを使ったクリッピング

DirectDrawでは、描画座標のチェックと座標値の修正を行ってくれるクリッピング機能がある。
DirectDrawのクリッピング機能は、描画したい画像をサーフェイスからはみ出さないようにするだけでなく、サーフェイス内の特定の領域内部にしか描画をしたくない場合にも適用することができる。

※クリッピングに指定した矩形内にしか描画されない
※描画時、領域をはみ出していてもエラーにならず、はみ出していない部分だけが描画される

(a) クリッピング機能の導入方法

クリッピング機能を使うには、IDirectDrawClipperインターフェイスを使う。IDirectDrawClipperインターフェイスはIDirectDraw7インターフェイスのCreateClipperメソッドによって作成できる。サーフェイス全体を描画可能領域としてクリッパーを設定するには、次のように行う。

LPDIRECTDRAWCLIPPER g_pDDClipper; ・・・・・・・・・1

g_pDD->CreateClipper(0, &g_pDDClipper, NULL); ・・・2
g_pDDClipper->SetHWnd(0, hWnd); ・・・・・・・・・・3
g_pDDSBack->SetClipper(g_pDDClipper); ・・・・・・・4
  1. IDirectDrawClipperオブジェクトを宣言
  2. Cipperオブジェクトを作成
  3. WindowsハンドルをClipperオブジェクトにセット
  4. バック・バッファにClipperオブジェクトをセット
    (画像の描画はバック・バッファに対して行うため、バック・バッファにクリッパーをセットすれば、画像転送時、クリッピング処理が行われる)

(b) クリッピング領域への画像転送方法

クリッパーがセットされたサーフェイス(バック・バッファ)に画像を転送するには、BltFastメソッドではなくBltメソッドを利用しなければならない(BltFastメソッドを利用すると、描画に失敗する)。

【Bltメソッドの構文】

Blt(
  転送先の矩形のアドレス,
  元となるサーフェイス,
  画像の矩形のアドレス,
  パラメータ,
  NULL);

※BltFastとは違い、転送先のXY座標ではなく、転送先の矩形を指定していることに注意!!

【使用例】

RECT rcDest = {100, 100, 150, 200};
RECT rcSrc = {0, 0, 50, 100};

g_pDDSBack->Blt(&rcDest, g_pDDSChar, &rcSrc, DDBLT_KEYSRC | DDBLT_NOWAIT, NULL);

※Bltメソッドは、BltFastメソッドよりも多くの機能を持つ。詳細は別章で解説する

(c) 授業用プログラムに組み込むには?

8章3で作成したプログラムに、クリッパーを組み込む方法を紹介する。まず、DirectDrawオブジェクトの初期化処理を次のように修正する。

myDray.cpp
    ・
    ・
//-----------------------------------------------------------------------------
// グローバル変数
//-----------------------------------------------------------------------------
static LPDIRECTDRAWPALETTE g_pDDPal = NULL;     // プライマリ・サーフェイス・パレット
static char szStartBmp[] = "STARTBMP";          // スタート画像リソース名
static char szGameBmp[] = "GAMEBMP";            // ゲーム画像リソース名
static char szCharaBmp[] = "CHARABMP";          // ゲーム画像リソース名
static char szStringBmp[] = "STRINGBMP";        // ゲーム画像リソース名
static LPDIRECTDRAWCLIPPER g_pDDClipper;  // Clipperオブジェクトの宣言
    ・
    ・
//-----------------------------------------------------------------------------
// 関数名 : InitializeDraw() 
// 機能概要: Direct Draw オブジェクトの生成
// 戻り値 : 正常終了のとき:DD_OK、異常終了のとき:エラーコード
//-----------------------------------------------------------------------------
HRESULT InitializeDraw(HWND hWnd)
{
    ・
    ・
    ・
    // バックバッファの生成
    ZeroMemory(&ddscaps, sizeof(ddscaps));
    ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
    hRet = g_pDDSPrimary->GetAttachedSurface(&ddscaps, &g_pDDSBack);
    if (hRet != DD_OK)
        return InitFail(hWnd, hRet, "GetAttachedSurface FAILED");

    // クリッパーを生成し、バック・バッファに割り当てる
    hRet = g_pDD->CreateClipper(0, &g_pDDClipper, NULL);
    if (hRet != DD_OK)
        return InitFail(hWnd, hRet, "CreateClipper FAILED");
    hRet = g_pDDClipper->SetHWnd(0, hWnd);
    if (hRet != DD_OK)
        return InitFail(hWnd, hRet, "SetHWnd FAILED");
    hRet = g_pDDSBack->SetClipper(g_pDDClipper);
    if (hRet != DD_OK)
        return InitFail(hWnd, hRet, "SetClipper FAILED");

    // パレットの生成
    ・
    ・
    ・

    return DD_OK;

}

次に、DirectDrawオブジェクトの開放処理を次のように修正する。

//-----------------------------------------------------------------------------
// 関数名 : ReleaseDraw()
// 機能概要: Direct Draw オブジェクトの削除
//-----------------------------------------------------------------------------
void ReleaseDraw(void)
{
    if (g_pDD != NULL)
    {
        if (g_pDDClipper != NULL) // クリッピング・インターフェイス
        {
            g_pDDClipper->Release();
            g_pDDClipper = NULL;
        }
        if (g_pDDSStart != NULL)        // スタート画像用サーフェイス
        {
            ・
            ・
            ・

次に、BltFastメソッドを使っているところ(Start処理、Game処理、FPSの表示)を、すべてBltメソッドに置き換える。

例1)カラーキーを使わない場合の変換
 旧:hRet = g_pDDSBack->BltFast(0, 70, g_pDDSStart, &TitleRect, DDBLTFAST_NOCOLORKEY);
 ↓
 新:SetRect(&rcDest, 0, 70, TitleRect.right - TitleRect.left, 70 + TitleRect.bottom - TitleRect.top);
   hRet = g_pDDSBack->Blt(&rcDest, g_pDDSStart, &TitleRect, DDBLT_WAIT, NULL);

例2)カラーキーを使う場合の変換
 旧:hRet = g_pDDSBack->BltFast(0, 70, g_pDDSStart, &TitleRect, DDBLTFAST_SRCCOLORKEY);
 ↓
 新:SetRect(&rcDest, 0, 70, TitleRect.right - TitleRect.left, 70 + TitleRect.bottom - TitleRect.top);
   hRet = g_pDDSBack->Blt(&rcDest, g_pDDSStart, &TitleRect, DDBLT_KEYSRC | DDBLT_WAIT, NULL);
Game.cpp
  :
static RECT rcDest;    // 出力先RECT
  :
//-----------------------------------------------------------------------------
// 関数名 : ScreenOut()
// 機能概要: ゲーム画像描画処理
//-----------------------------------------------------------------------------
static void ScreenOut(void)
{
    HRESULT hRet;
    int i;

    /* ゲーム画面用背景 */
    SetRect(&rcDest, 0, 0, 640, 480);
    hRet = g_pDDSBack->Blt(&rcDest, g_pDDSGame, &ScreenRect, DDBLT_WAIT, NULL);
    if (hRet != DD_OK)
        return;

    /* 自キャラ */
    SetRect(&rcDest, MyChar.x, MyChar.y, MyChar.x +  MyChar.rect[MyChar.direction][MyChar.AnimeNo].right - MyChar.rect[MyChar.direction][MyChar.AnimeNo].left, MyChar.y + MyChar.rect[MyChar.direction][MyChar.AnimeNo].bottom - MyChar.rect[MyChar.direction][MyChar.AnimeNo].top);
hRet = g_pDDSBack->Blt(&rcDest, g_pDDSChara, &MyChar.rect[MyChar.direction][MyChar.AnimeNo], DDBLT_KEYSRC | DDBLT_WAIT, NULL);

if (hRet != DD_OK) return; /* 自ショット */ for (i=0 ; i<SHOT_MAX ; i++) { if (MyShot[i].life > 0) { SetRect(&rcDest, MyShot[i].x, MyShot[i].y, MyShot[i].x + MyShot[i].width, MyShot[i].y + MyShot[i].height); hRet = g_pDDSBack->Blt(&rcDest, g_pDDSChara, &MyShot[i].rect[MyShot[i].direction][MyShot[i].AnimeNo], DDBLT_KEYSRC | DDBLT_WAIT,NULL);
if (hRet != DD_OK) return; } } }
これ以外の修正箇所は各自で考えよう。



(d) 確認(自由問題)

8-3(a)〜8-3(b)で作成したプログラムを、8-4(c)のように修正し、描画時にはみ出した場合の処理を考えなくてもクリッピングが行われることを確認する。

(e) Clipperを使うメリットとデメリットについて

Clipperを使うにあたってのメリットとデメリットを考えてみる。

【メリット】

  1. 描画する画像がサーフェイスをはみ出した場合でも、はみ出した部分を自動的に切り取って、はみ出していない部分のみを表示してくれるため、はみ出した場合の処理を(ある程度)考える必要がない

【デメリット】

  1. BltメソッドはBltFastメソッドよりも多くの機能を持っているが、そのために、BltFastメソッドと比べて描画速度が若干遅い

どちらも一長一短があるので、Case by Caseで使い分けよう。


[ TOP ]