绘制一个标准框架和透明内容的窗口

时间:2015-03-13 17:03:46

标签: windows winapi

对于xtow,我想绘制一个顶级窗口,其中标准的非客户区域和客户区域填充了一个具有alpha通道的位图。

我现在发现我在Windows 7上实现此功能的方式,但在Windows 8.1上无法正确呈现,在移动或最大化时会留下窗口内容的图像。

为了调查,我制作了一个简单的测试程序alpha-test,其中

  • 使用DwmEnableBlurBehindWindow()设置非交叉模糊区域,以便窗口中的alpha值得到尊重,而不会模糊。
  • 使用BitBlt()将带有alpha的位图复制到其中。
//
// g++ alpha-test.cc -o alpha-test -mwindows -lgdiplus -ldwmapi
//

#define  _WIN32_WINNT 0x0600

#include <assert.h>
#include <stdio.h>
#include <windows.h>
#include <gdiplus.h>
#include <dwmapi.h>

int width = 360;
int height = 360;
HBITMAP hBitmap;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch (message)
    {
    case WM_PAINT:
      {
        PAINTSTRUCT ps;
        HDC hdcUpdate = BeginPaint(hWnd, &ps);

        RECT rc;
        GetClientRect(hWnd, &rc);
        HBRUSH hbrush = CreateSolidBrush(RGB(0,0,0));
        FillRect(hdcUpdate, &rc, hbrush);
        DeleteObject(hbrush);

        HDC hdcMem = CreateCompatibleDC(hdcUpdate);
        HBITMAP hbmpold = (HBITMAP)SelectObject(hdcMem, hBitmap);

        if (!BitBlt(hdcUpdate, 0, 0, ps.rcPaint.right, ps.rcPaint.bottom, hdcMem, 0, 0, SRCCOPY))
          {
            printf("BitBlt failed: 0x%08x\n", (int)GetLastError());
          }

        SelectObject(hdcMem, hbmpold);
        DeleteDC(hdcMem);

        EndPaint(hWnd, &ps);
      }
      return 0;

    case WM_DESTROY:
      PostQuitMessage(0);
      return 0;

    default:
      return DefWindowProc(hWnd, message, wParam, lParam);
    }
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
  ULONG_PTR gdiplusToken;
  Gdiplus::GdiplusStartupInput gdiplusStartupInput;
  GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

  LPCTSTR szWindowClass = "TransparentClass";

  // Register class
  WNDCLASSEX wcex = {0};
  wcex.cbSize = sizeof(WNDCLASSEX);
  wcex.style          = CS_HREDRAW | CS_VREDRAW; // | CS_OWNDC;
  wcex.lpfnWndProc    = WndProc;
  wcex.cbClsExtra     = 0;
  wcex.cbWndExtra     = 0;
  wcex.hInstance      = hInstance;
  wcex.hIcon          = NULL;
  wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
  wcex.lpszClassName  = szWindowClass;
  wcex.hIconSm        = NULL;
  wcex.hbrBackground  = (HBRUSH)CreateSolidBrush(0x00000000);
  RegisterClassEx(&wcex);

  // Create window
  HWND hWnd = CreateWindowEx(WS_EX_APPWINDOW,
                             szWindowClass,
                             "Transparent Window",
                             WS_OVERLAPPED | WS_SYSMENU,
                             CW_USEDEFAULT, CW_USEDEFAULT, width, height,
                             NULL, NULL, hInstance, NULL);

  Gdiplus::Bitmap *m_pImage = Gdiplus::Bitmap::FromFile(L"sample.png", FALSE);
  Gdiplus::Color bg(0,0,0,0);
  m_pImage->GetHBITMAP(bg, &hBitmap);
  assert(hBitmap);

  DWM_BLURBEHIND blurBehind = { 0 };
  blurBehind.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
  blurBehind.hRgnBlur = CreateRectRgn(0, 0, -1, -1);
  blurBehind.fEnable = TRUE;
  blurBehind.fTransitionOnMaximized = FALSE;
  DwmEnableBlurBehindWindow(hWnd, &blurBehind);
  DeleteObject(blurBehind.hRgnBlur);

  ShowWindow(hWnd, SW_SHOW);

  // Main message loop
  MSG msg;
  while (GetMessage(&msg, NULL, 0, 0))
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }

  return (int)msg.wParam;
}

这真的坏了吗?我该如何修复我的代码?这是Windows错误/限制吗?

有没有其他方法可以实现我的目标,即将带有alpha的位图绘制到带边框的窗口中?

更新 我做了一些测试,使用Direct2D和Direct3D来填充客户区 位图,但它们以同样的方式错误渲染。

1 个答案:

答案 0 :(得分:1)

DWM不再模糊(此功能被认为太耗电而且在Windows 8中被删除),所以我猜它没有正确合成背景区域你的窗口了 - 因此你没有得到自动&#34;它在Windows 7中为您提供的alpha效果。

这是一种不透明的方式来绘制透明窗口。使用UpdateLayeredWindow是&#34;官方&#34;方式,并将有利于在Windows 8和Windows 7上工作。