DIRECTX1 DirectDraw 7 開放されたサーフェスの復帰

DirectDrawの最後に、サーフェスの復帰について説明知ます。

サーフェスは、アプリが切り替わると破壊されます。中身もなくなります。そのままでは描画ができなくなるので、自分でサーフェスを復帰させなければなりません。まず、サーフェスがどのように破壊されるか見るために、フルスクリーンでウィンドウを持ったアプリを作ってみます。

プロジェクトは、SDI,あとは適当でOKです。DxDraw.hをディレクトリにコピーして・・・というのは4、5と同じです。オブジェクトを作り、DDRAW.h & Dxdraw,hをインクルードしてビルドしてエラーが無いことを確かめてください。コードは手っ取り早く以前のプロジェクトからコピーしましょう。必要な部分は以下の通り。リソースにビットマップを読み込むのも忘れずに。クリッパーはなくても良いでしょう。

1 FrameのPreCreateWindow
 

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
 return FALSE;
 // TODO: この位置で CREATESTRUCT cs を修正して、Window クラスやスタイルを
 // 修正してください。
 
 cs.style = WS_OVERLAPPED | WS_CAPTION | FWS_ADDTOTITLE
 | WS_SYSMENU;

 
 cs.dwExStyle=WS_EX_TOPMOST;
 
 return TRUE;
}
 

2 Viewクラスは以下の通り。サーフェスのクリアは無しです。
 

// CDx_fullwinView クラスのメッセージ ハンドラ

int CDx_fullwinView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
 if (CView::OnCreate(lpCreateStruct) == -1)
 return -1;

// TODO: この位置に固有の作成用コードを追加してください
 
 
//----- フルスクリーンモードの場合
HRESULT ret;
HWND m_hWnd=NULL;
ret = ddx.InitDxDraw( GetParent()->m_hWnd);
if (ret != DD_OK){
 AfxMessageBox("InitDxDraw in Oncreate error");
 return -1;
}
//----- サーフェスの生成
ret = ddx.MakePrimarySurface();
if (ret != DD_OK){
 AfxMessageBox("Surface error");
 
 return -1;
}

 
 return 0;
}
 

これで、スクリーンモードが変更してもウィンドウを持ったままのDirectDrawのアプリが完成です。さらにサーフェスが動いていることを確認するためにキーダウンハンドラに以下のコードを付け加えます。上キーを押してビットマップをロードし、下キーを押してフロントバッファに転送します。ついでにエスケープで抜けるようにしておきましょう。
 

if(nChar == VK_UP){
 ddx.MakeOff1();
 
 //----- ビットマップをロードする
 CBitmap Bitmap;
 
 CDC DC;
 DC.CreateCompatibleDC(0);
 Bitmap.LoadBitmap(IDB_BITMAP1);
 DC.SelectObject(&Bitmap);
 
 //----- ビットマップをバックバッファに転送
 HDC hdcSurf;
 
if(ddx.lpOS1->GetDC(&hdcSurf)== DD_OK){
 BitBlt(hdcSurf, 0,0,640, 480,DC,0,0,SRCCOPY);
 ddx.lpOS1->ReleaseDC(hdcSurf);
 DeleteDC(DC);
}
}
 
if(nChar == VK_DOWN){
 RECT rcSrc;
 
 rcSrc.top = 0;
 rcSrc.bottom = 200;
 rcSrc.left = 0;
 rcSrc.right = 200;
 ddx.lpDDSFrontBuffer->BltFast(100, 100, ddx.lpOS1, &rcSrc, DDBLTFAST_NOCOLORKEY);
}
 

これで実験の準備ができました。

ビルドして実行しましょう。UPキーを押してからDownキーを押してCGが表示すればOKです。一度終了してから、再度起動します。カーソルキーを押す前にウィンドウを移動します。そしてウィンドウ以外の部分をクリックします。するとアプリが切り替わって解像度が元に戻ります。再びDirectDrawのウィンドウを選択すると、解像度が変わります。

そうしておいてから、カーソルキーの上、下、と押します。すると、今度はCGは表示されません。これはサーフェスが破壊されているからです。そこで、以下のコードを追加します。入れる場所はキーダウンハンドラです。

 

if(nChar == VK_UP){
 
if(ddx.lpDDSFrontBuffer != NULL)
if(ddx.lpDDSFrontBuffer->IsLost() == DDERR_SURFACELOST)
       ddx.lpDDSFrontBuffer->Restore();
if(ddx.lpBackBuffer != NULL)
    if(ddx.lpBackBuffer->IsLost() == DDERR_SURFACELOST)
       ddx.lpBackBuffer->Restore();
if(ddx.lpOS1 != NULL)
     if(ddx.lpOS1->IsLost() == DDERR_SURFACELOST){
        ddx.lpOS1->Restore();
}

 
ddx.MakeOff1();
 
//----- ビットマップをロードする
CBitmap Bitmap;
 
CDC DC;
DC.CreateCompatibleDC(0);
Bitmap.LoadBitmap(IDB_BITMAP1);
DC.SelectObject(&Bitmap);
 
//----- ビットマップをバックバッファに転送
HDC hdcSurf;
 
if(ddx.lpOS1->GetDC(&hdcSurf)== DD_OK){
 BitBlt(hdcSurf, 0,0,640, 480,DC,0,0,SRCCOPY);
 ddx.lpOS1->ReleaseDC(hdcSurf);
 DeleteDC(DC);
}
}
 

このコードは破壊された(開放された?)サーフェスを元に戻します。サーフェスが作成されていることを確認してから、破壊されているか(失われているか?)調べて、破壊されていればリストアします。

この例では、そのままオフスクリーンサーフェスを作り直して(すでにある場合は作らないようになっていますが)ビットマップも読み直しているので普通に表示されるようになります。

実際にゲームを作る場合、このサーフェスの検査とリストアは、どこでやるべきでしょうか。それは画面の転送と、アプリにフォーカスがあったときでしょう。

さらにそのとき、サーフェスの中身も読み直さなければなりません。つまり、アプリを作る場合、常にどのサーフェスにどのビットマップが入っているのか、保存しておく必要があります。