VC++でGDI+ そにょ8 〜ダブルバッファリング〜

ダブルバッファの手順とサンプル

バイスコンテキストから生成したGraphicsオブジェクトに描画している場合、ウィンドウを動かしたりすると画面がちらつく。これは描画の処理が追いついていないために、一度消して真っ白になった背景が見えてしまうからである。こうしたちらつきを防ぐのがダブルバッファリング(double buffering)である。画面表示に限らず、音声再生など色々な場所で用いられる技術である。これをGDI+もで実装する*1
手順は以下のとおり

  1. クライアント領域と同じサイズのBitmapオブジェクト"offScreenBitmap"を用意する。
  2. "offScreenBitmap"からGraphicsオブジェクト"offScreen"を作成する。
  3. "offScreen"に対して描画を行う。
  4. バイスコンテキストハンドルからGraphicsオブジェクト"onScreen"を作成する。
  5. "offScreenBitmap"を"onScreen"に描画する。

以下にサンプルを示す。hDCはBeginPaint()関数で得られたデバイスコンテキストハンドル、width、heightはクライアント領域のサイズである。

Bitmap* offScreenBitmap = new Bitmap(width, height);
Graphics* offScreen = new Graphics(offScreenBitmap);

// ここに色々な描画処理
offScreen->DrawImage(image, 0, 0);

Graphics* onScreen = new Graphics(hDC);

onScreen->DrawImage(offScreenBitmap, 0, 0);

GDI+を使う場合おそらくどのアプリケーションでもこの処理は必要となるので、グラフィックスのクラスにまとめて利用している。これまでに紹介した画像の読み込みや描画もイメージクラスとしてまとめている。参考までに以下に少しだけ書いておく。

グラフィックス統括クラス

各Graphicsメソッドを全部書き加えるのは面倒なので"offScreen"を"Screen"としてpublicメンバにしている。今のところリサイズ等には対応していない。

class XGraphics
{
private:
    HWND m_hWnd;
    UINT m_width;
    UINT m_height;
    Graphics* m_onScreen;
    Bitmap* m_offScreenBmp;
public:
    Graphics* Screen;
    
    XGraphics(HWND hwnd);
    XGraphics(HWND hwnd, int width, int height);
    
    ~XGraphics();
    
    Status Update(void);
      // 以下略

グラフィックス統括クラスのコンストラク

ウィンドウハンドルを受け取ってクライアント領域のサイズを計算する。

XGraphics::XGraphics(HWND hwnd)
{
    m_hWnd = hwnd;

    struct tagRECT lpRect;
    GetWindowRect(m_hWnd, &lpRect);
    m_width = lpRect.right - lpRect.left;
    m_height = lpRect.bottom - lpRect.top;
    
    m_offScreenBmp = new Bitmap(m_width, m_height);

    m_onScreen = new Graphics(m_hWnd);
    Screen = new Graphics(m_offScreenBmp);
}

描画更新メソッド

オフスクリーンに描画されたものをオンスクリーンに描画する。

Status XGraphics::Update(void)
{
    Status st;
    PAINTSTRUCT PS;

    BeginPaint(m_hWnd, &PS);

    st = m_onScreen->DrawImage(m_offScreenBmp, 0, 0);

    EndPaint(m_hWnd, &PS);

    return st;
}

イメージ統括クラス

class XImage
{
public:
    Gdiplus::Bitmap* m_bitmap;
public:
    XImage();
    XImage(LPCWSTR pFileName);
    XImage(UINT pResourceID, LPCTSTR pResourceType = RT_BITMAP, HMODULE hInstance = NULL);
    
    ~XImage();
    
    bool Load(LPCWSTR pFile);
    bool Load(UINT pResourceID, LPCTSTR pResourseType = RT_BITMAP, HMODULE hInstance = NULL);
        
    operator Gdiplus::Bitmap*() const;
}

イメージ統括クラスのコンストラク

Load関数はそにょ4で作ったLoadFromResourceとほぼ同じである。返り値のBitmapオブジェクトへのポインタをメンバ変数のm_bitmapに記録するだけの違いである。

XImage::XImage(UINT pResourceID, LPCTSTR pResourseType, HMODULE hInstance)
{
    m_bitmap = NULL;
    Load(pResourceID, pResourseType, hInstance);
}

Bitmap*への変換

これを実装しておけばXImageオブジェクトもGraphics::DrawImageメソッドに利用できる。

XImage::operator Gdiplus::Bitmap*() const
{
    return m_bitmap;
}

*1:GDIでの実装は機会があれば。