酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

Windows Media Player用の視覚エフェクト(Visualizer)を作ってみるテスト(4)

Windows(というよりGDI?)でダブルバッファリングってどうするのだろ?

ダブルバッファで画面描画する

僕が今までやった事あるのはバッファを2面持って、カメラデバイスから受け取った画像をタイミングをずらしてV-RAMに直接転送するみたいな作りだったんだけど……と、半日悩んでダブルバッファリングに対応してきました……Windowsは難しいなぁ(´・ω・`)

つまりこういう事かな?表示しているバッファに対して直接描画せずに、一枚噛ませて一時描画用のバッファに対してBitmapを転送、その後表示用のバッファに転送みたいな。

上記の事を考慮した結果、このようなサンプルコードになりました。

//////////////////////////////////////////////////////////////////////////////
// CSample::Render
// Called when an effect should render itself to the screen.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CSample::Render(TimedLevel *pLevels, HDC hdc, RECT *prc)
{
  // draw using the current preset
  switch (m_nPreset)
  {
    case PRESET_BARS:
    {
      HDC hBackDC = NULL;
      HBITMAP hBackBitmap = NULL;

      // ダブルバッファ用の領域確保
      hBackDC = CreateCompatibleDC(hdc);
      hBackBitmap = CreateCompatibleBitmap(hdc, prc->right, prc->bottom);
      HBITMAP hOldBitmap = (HBITMAP)::SelectObject(hBackDC, hBackBitmap);

      // DLLのリソースから「IDB_BITMAP1」を読み出す
      HINSTANCE hInstance = ::GetModuleHandle("sample");  
      HBITMAP hBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP1));

      // BMPの大きさを把握 
      BITMAP bm = {0};
      ::GetObject(hBitmap, sizeof(BITMAP), (LPVOID)&bm);

      // BMPを表示領域全体に合わせて転送(まだ表示はしない)   
      HDC hMdc = ::CreateCompatibleDC(hBackDC);   
      ::SelectObject(hMdc, hBitmap);
      ::StretchBlt(hBackDC, 0, 0, prc->right - prc->left, prc->bottom - prc->top,
                   hMdc, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY );   

      // 一気に転送するお(^ω^)
      ::BitBlt(hdc, 0, 0, prc->right - prc->left, 
               prc->bottom - prc->top, hBackDC, 0, 0, SRCCOPY);

      // 後始末  
      if(hMdc) ::DeleteObject(hMdc);
      if(hBitmap) ::DeleteObject(hBitmap);
      if(hBackBitmap) ::DeleteObject(hBackBitmap);
      if(hBackDC)
      {
        ::SelectObject(hBackDC, hOldBitmap);
        ::DeleteDC(hBackDC);
      }
    }
    break;
  }

  return S_OK;
}

このコードを実行してみました。

はい、静止画では非常に分かりにくいですが美しく表示されております。

ただ、Renderは数十〜数百ミリ秒間隔で呼ばれているので、上記のサンプルコードのように毎回リソースの読み込みをするのはパフォーマンスに大きく影響します。

ここではサンプルコードの範囲内に収めたいのでこのようにしておりますが、実際にコードを書く際には気をつけてください。

追記

とっちゃんさんに「AddFontMemResourceEx()を使えばいいよん」と、ヒントを頂く事が出来たので、早速リソースにフォントを埋め込んで文字列を表示させてみたいと思います。でも、ちょっと大変そうなのでエントリを分けます。今日はAddFontMemResourceEx()を使える様にするところまでヽ(^o^)/

適当なパラメータを突っ込んでAddFontMemResourceEx()を呼ぶと「識別子が見当たりません」とビルドエラーが出ちゃいます。AddFontMemResourceEx()はWindows2000以降対応のWIN32APIなので、Visualizer Plug-In Wizerdから生成されたままのプロジェクトだと使用する事が出来ません。

StdAfx.h を開いて下記の定義を変更します。

#define STRICT
#ifndef _WIN32_WINNT
//#define _WIN32_WINNT 0x0400
#define _WIN32_WINNT 0x0500
#endif

以上で、「Windows Media Player用の視覚エフェクト(Visualizer)を作ってみるテスト」を終わらせて頂きます。

関連記事