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

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

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


person adoss    schedule 19.10.2010    source источник
comment
SetBKMode и SetBKColor - это API-интерфейсы, которые я использовал для создания прозрачного родительского элемента управления.   -  person    schedule 19.10.2010


Ответы (2)


Я смог сделать именно то, что хотел, используя код из Части 1 и Части 2 этой серии:

Отображение экрана-заставки с C ++


В этих сообщениях в блоге говорится об отображении заставки в Win32 C ++, но это было почти идентично тому, что мне нужно было сделать. Я считаю, что мне не хватало того, что вместо того, чтобы просто рисовать PNG в окне с помощью GDI +, мне нужно было использовать _ 1_ с правильным параметром 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'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);
}
person adoss    schedule 19.10.2010
comment
лучший ответ, также поддерживает автоматическое альфа-смешение PNG при использовании с WIC (Windows Imaging Component) через интерфейс COM. - person dns; 05.07.2015
comment
В итоге с AlphaBlend пришлось потратить кучу времени. Это решение, вероятно, является одним из самых простых способов добиться попиксельного альфа-смешивания, особенно когда вы обновляете окно. - person TheBlueNotebook; 11.01.2016

Используйте SetLayeredWindowAttributes архив, это позволяет вам установить цвет маски, который станет прозрачным, позволяя просвечивать фон.

Вам также потребуется настроить окно с многоуровневым флагом, например:

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 и более поздних версий

person Simon Steele    schedule 19.10.2010
comment
Я пробовал это, и это ПОЧТИ работает. Проблема в том, что есть некоторые области PNG, которые являются simi-прозрачными, и метод цветового ключа делает прозрачными только те пиксели, которые имеют 100% непрозрачность значения цвета ключевого цвета. Если все остальное не поможет, моим запасным вариантом будет удаление всех полупрозрачных областей, но я бы предпочел этого не делать. - person adoss; 19.10.2010
comment
Вы можете попробовать более сложные приемы, подобные тому, который приведен в статье, на которую я ссылаюсь, но, по сути, как только вы захотите совместить уже существующее изображение с содержимым рабочего стола, все начинает усложняться. - person Simon Steele; 19.10.2010
comment
Посмотрите ответ, который я опубликовал. Мне этот код нравится намного больше, чем то, что я видел в этой статье CodeProject. Не только это, но я считаю, что то, что я нашел, является правильным решением вместо взлома. Спасибо за помощь! - person adoss; 19.10.2010
comment
Ах, хорошо, я раньше не видел BLENDFUNCTION - пожалуйста! - person Simon Steele; 20.10.2010
comment
почему бы вам не установить четвертый параметр SetLayeredWindowAttributes для LWA_ALPHA - person Valen; 18.04.2021