web-dev-qa-db-ja.com

C ++ Win32で透明なウィンドウを作成する

私は、半透明のPNGのみを表示することを唯一の目的とする、非常にシンプルなWin32 C++アプリを作成しています。ウィンドウにはクロムを使用しないでください。また、すべての不透明度はPNG自体で制御する必要があります。

私の問題は、ウィンドウの下のコンテンツが変更されたときにウィンドウが再描画されないため、PNGの透明な領域がアプリケーションの最初の起動時にウィンドウの下にあったものに「スタック」することです。

新しいウィンドウを設定する行は次のとおりです。

hWnd = CreateWindowEx(WS_EX_TOPMOST, szWindowClass, szTitle, WS_POPUP, 0, height/2 - 20, 40, 102, NULL, NULL, hInstance, 0);

RegisterClassExを呼び出すために、バックグラウンド用にこのセットを用意しています。

wcex.hbrBackground = (HBRUSH)0;

WM_Paintメッセージのハンドラーを次に示します。

 case WM_Paint:
 {
   hdc = BeginPaint(hWnd, &ps);
   Gdiplus::Graphics graphics(hdc);
   graphics.DrawImage(*m_pBitmap, 0, 0);
   EndPaint(hWnd, &ps);
   break;
 }

注意すべきことの1つは、アプリケーションが常に画面の左側にドッキングされ、移動しないことです。ただし、ユーザーがアプリケーションの下でウィンドウを開いたり、閉じたり、移動したりすると、アプリケーションの下にあるものが変わる場合があります。

アプリケーションが最初に起動すると、完璧に見えます。 PNGの透明(および半透明)部分は完全に透けて見えます。ただし、アプリケーションの下のバックグラウンドが変更されても、バックグラウンドは更新されず、アプリケーションが最初に起動したときと同じままです。実際、WM_Paint(またはバックグラウンドが変更されたときにWM_ERASEBKGNDは呼び出されません)。

私はかなり長い間これで遊んでいて、100%正しくなってきましたが、まだ完全ではありません。たとえば、背景を(HBRUSH)NULL_BRUSHに設定しようとし、WM_ERASEBKGNDを処理しようとしました。

ウィンドウの下のコンテンツが変更されたときにウィンドウを再描画するにはどうすればよいですか?

44
adoss

このシリーズのパート1とパート2のコードを使用することで、私が望んでいたことを正確に行うことができました。 http://code.logos.com/blog/2008/09/displaying_a_splash_screen_with_c_introduction.html

これらのブログ投稿は、Win32 C++でスプラッシュスクリーンを表示することについて述べていますが、それは私がする必要があるものとほとんど同じでした。私が行方不明になった部分は、GDI +を使用してPNGをウィンドウにペイントする代わりに、適切なUpdateLayeredWindowBLENDFUNCTION 関数を使用する必要があることだと思いますパラメータ。上記のリンクのパート2にあるSetSplashImageメソッドを以下に貼り付けます。

void SetSplashImage(HWND hwndSplash, HBITMAP hbmpSplash)
{
  // get the size of the bitmap
  BITMAP bm;
  GetObject(hbmpSplash, sizeof(bm), &bm);
  SIZE sizeSplash = { bm.bmWidth, bm.bmHeight };

  // get the primary monitor's info
  POINT ptZero = { 0 };
  HMONITOR hmonPrimary = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
  MONITORINFO monitorinfo = { 0 };
  monitorinfo.cbSize = sizeof(monitorinfo);
  GetMonitorInfo(hmonPrimary, &monitorinfo);

  // center the splash screen in the middle of the primary work area
  const RECT & rcWork = monitorinfo.rcWork;
  POINT ptOrigin;
  ptOrigin.x = 0;
  ptOrigin.y = rcWork.top + (rcWork.bottom - rcWork.top - sizeSplash.cy) / 2;

  // create a memory DC holding the splash bitmap
  HDC hdcScreen = GetDC(NULL);
  HDC hdcMem = CreateCompatibleDC(hdcScreen);
  HBITMAP hbmpOld = (HBITMAP) SelectObject(hdcMem, hbmpSplash);

  // use the source image's alpha channel for blending
  BLENDFUNCTION blend = { 0 };
  blend.BlendOp = AC_SRC_OVER;
  blend.SourceConstantAlpha = 255;
  blend.AlphaFormat = AC_SRC_ALPHA;

  // Paint the window (in the right location) with the alpha-blended bitmap
  UpdateLayeredWindow(hwndSplash, hdcScreen, &ptOrigin, &sizeSplash,
      hdcMem, &ptZero, RGB(0, 0, 0), &blend, ULW_ALPHA);

  // delete temporary objects
  SelectObject(hdcMem, hbmpOld);
  DeleteDC(hdcMem);
  ReleaseDC(NULL, hdcScreen);
}
38
adoss

SetLayeredWindowAttributesを使用します。これにより、透明になるマスクの色を設定して、背景を透過させることができます。

http://msdn.Microsoft.com/en-us/library/ms633540(VS.85).aspx

また、レイヤー化フラグを使用してウィンドウを構成する必要があります。

SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);

その後はかなり簡単です:

// Make red pixels transparent:
SetLayeredWindowAttributes(hwnd, RGB(255,0,0), 0, LWA_COLORKEY);

PNGに背景とブレンドしたい半透明のピクセルが含まれている場合、これはより複雑になります。このCodeProject記事のアプローチを見てみてください:

Windows 2000以降の標準コントロールを備えたクール、半透明、および定形ダイアログ

18
Simon Steele