Подтвердить что ты не робот

Создание прозрачного окна в С++ Win32

Я создаю то, что должно быть очень простым приложением Win32 С++, единственной целью которого является ТОЛЬКО отображать полупрозрачный PNG. В окне не должно быть хрома, и вся непрозрачность должна контролироваться в самой 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;
 }

Следует отметить, что приложение всегда состыковано слева от экрана и не перемещается. Но, что под приложением может измениться по мере того, как пользователь открывает, закрывает или перемещает окна под ним.

Когда приложение запускается, оно выглядит идеально. Прозрачные (и аналогично-прозрачные) части PNG показывают отлично. НО, когда фон под приложением меняется, фон НЕ обновляется, он остается неизменным с момента запуска приложения. Фактически, WM_PAINT (или WM_ERASEBKGND не вызывается при изменении фона).

Я играю с этим довольно долгое время и вплотную приблизился к 100% прав, но не совсем там. Например, я попытался установить фон (HBRUSH) NULL_BRUSH, и я попытался обработать WM_ERASEBKGND.

Что можно сделать, чтобы окно перерисовывало, когда содержимое под ним изменяется?

4b9b3361

Ответ 1

Я смог сделать именно то, что хотел, используя код из Части 1 и Части 2 этой серии: http://code.logos.com/blog/2008/09/displaying_a_splash_screen_with_c_introduction.html

Эти сообщения в блоге говорят о отображении заставки в Win32 C++, но это было почти идентично тому, что мне нужно было сделать. Я считаю, что часть, которую мне не хватало, заключалась в том, что вместо того, чтобы просто рисовать PNG в окне с помощью GDI+, мне нужно было использовать функцию UpdateLayeredWindow с соответствующим параметром BLENDFUNCTION. Я вставлю метод SetSplashImage ниже, который можно найти в части 2 в приведенной выше ссылке:

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 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 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);
}

Ответ 2

Используйте 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 и выше