在扩展框架中绘制会产生奇怪的颜色
Drawing in the extended frame gives strange colors
我有一个 window 的加长框架是这样制作的:
但是在扩展框中绘制的任何东西都有非常奇怪的颜色(除了白色,唯一保持不变的颜色),就像这样(忽略中间杂乱的内容和右边杂乱的工具栏。
粉色矩形 (0xFFC9FF
) 应该是 0x8000FF
。如果我将 DirectX11 内容(中心内容)放在扩展帧中,我的 FPS 计数器的 alpha 混合就会变得混乱。如果我对右侧的对话框执行相同的操作,也会发生同样的情况。
那么我怎样才能正确地做到这一点呢?我已经尝试过先绘制到内存 DC,然后再使用 BitBlt
。我正在使用 GDI+(加上 CreateCompatibleDC
、CreateCompatibleBitmap
和其他函数来处理内存 DC)。
PS:因为你问了,这里是WndProc
:
LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT ReturnValue;
if (DwmDefWindowProc(hWnd, uMsg, wParam, lParam, &ReturnValue)) return ReturnValue;
switch (uMsg)
{
case WM_CREATE:
{
// ...
RECT rcClient;
GetWindowRect(hWnd, &rcClient);
SetWindowPos(hWnd,
NULL,
rcClient.left, rcClient.top,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
SWP_FRAMECHANGED);
return 0;
}
case WM_ACTIVATE:
{
MARGINS Margins;
Margins.cxLeftWidth = LEFT_BORDER;
Margins.cxRightWidth = RIGHT_BORDER;
Margins.cyTopHeight = TOP_BORDER;
Margins.cyBottomHeight = BOTTOM_BORDER;
if (DwmExtendFrameIntoClientArea(hWnd, &Margins) != S_OK)
{
MessageBox(hWnd, L"Erro ao configurar janela.", NULL, MB_ICONERROR);
PostQuitMessage(WM_QUIT);
}
if (LOWORD(wParam))
{
fActive = true;
}
else
{
fActive = false;
}
InvalidateRect(hWnd, NULL, false);
return 0;
}
case WM_SIZE:
/* ... */
case WM_NCCALCSIZE:
return 0;
case WM_NCHITTEST:
/* ... */
case WM_GETMINMAXINFO:
((LPMINMAXINFO)lParam)->ptMinTrackSize = { 640, 400 };
return 0;
case WM_PAINT:
{
using namespace Gdiplus;
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hWnd, &ps);
RECT rcWindow;
GetWindowRect(hWnd, &rcWindow);
POINT ptSize = { rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top };
HDC hBuffer = CreateCompatibleDC(hDC);
HBITMAP hBitmap = CreateCompatibleBitmap(hDC, ptSize.x, ptSize.y);
SelectObject(hBuffer, hBitmap);
Graphics graphics(hBuffer);
Pen Outline(Color(128, 128, 128));
SolidBrush Selected(Color(128, 0, 255));
Rect Tab1(10, 10, 200, 50);
graphics.FillRectangle(&Selected, Tab1);
graphics.DrawRectangle(&Outline, Tab1);
/* ... */
BitBlt(hDC, 0, 0, ptSize.x, ptSize.y, hBuffer, 0, 0, SRCCOPY);
EndPaint(hWnd, &ps);
return 0;
}
/* ... */
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
正如我所提到的,您几乎已经掌握了 AlphaBlend。我没能做到 remember/realize 的是您需要使用包含紫色矩形的 32 位 DibSection。您还需要确保使用 32 位感知绘图函数。这意味着您要么必须直接写入 Dib 的位,在调用 GetDIBits 检索它们之后(或在从某些位创建 DIB 之前) 或 您需要使用 GDI+ 来绘制紫色矩形。您可以在 WM_INITDIALOG 处理程序中看到我注释掉的代码。这给出了第二张图片中显示的结果。
在尝试了我在已删除的答案中给出的一些建议后,我发现了一个熟悉的问题。紫色矩形的颜色根据我的 window 遮挡的 window 的颜色而变化。
这是另一个代码示例,已经过测试,可以在 DWM 的 Win7s 实现下运行。我不确定 Glass 是否会有所不同,或者 Win 8 是否会有类似的表现。
这是图片:(绘制时颜色正确,使图片成为 8 位索引的图片略有更改)
请注意,编辑框中的文本有点古怪,随着 window 旁边的背景从黑色变为白色,从正常变为不正常。这与我使用 GDI 绘制紫色矩形时显示的效果相同。当我改用 GDI+ 时,问题就消失了。快速拖动 window 会使紫色框的边缘看起来有点奇怪。我认为这是 Windows7.
中 DWM 实施的众多失败之一
这是 window 的完整代码:
#define _WIN32_IE 0x0501
#define _WIN32_WINNT 0x0501
#define WINVER 0x0510
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include "resource.h"
#include <dwmapi.h>
#include <gdiplus.h>
#include <wingdi.h>
using namespace Gdiplus;
HINSTANCE hInst;
// BMP, GIF, JPEG, PNG, TIFF, Exif, WMF, and EMF
HBITMAP mLoadImageFile(wchar_t *filename)
{
HBITMAP result = NULL;
Bitmap bitmap(filename, false);
Color colBkg(0,0,0,0);
bitmap.GetHBITMAP(colBkg, &result);
return result;
}
HBITMAP zCreateDibSection(HDC hdc, int width, int height, int bitCount)
{
BITMAPINFO bi;
ZeroMemory(&bi, sizeof(bi));
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
bi.bmiHeader.biWidth = width;
bi.bmiHeader.biHeight = height;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = bitCount;
bi.bmiHeader.biCompression = BI_RGB;
return CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, 0,0,0);
}
HRESULT ExtendGlassIntoClient(HWND hwnd, int left, int right, int top, int bottom)
{
MARGINS margins = {left,right,top,bottom};
HRESULT hr = S_OK;
hr = DwmExtendFrameIntoClientArea(hwnd,&margins);
if (SUCCEEDED(hr))
{
// ...
}
return hr;
}
HBITMAP mImg, mWhosebugBitmap;
HDC memDC, memDC2;
HBITMAP oldBmp, oldBmp2;
LRESULT CALLBACK DlgMain(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
{
ExtendGlassIntoClient(hwndDlg, 0,0,50,0);
mImg = mLoadImageFile(L"girl.png");
mWhosebugBitmap = zCreateDibSection(NULL, 200, 50, 32);
memDC = CreateCompatibleDC(NULL);
memDC2 = CreateCompatibleDC(NULL);
oldBmp = (HBITMAP)SelectObject(memDC, mImg);
oldBmp2 = (HBITMAP)SelectObject(memDC2, mWhosebugBitmap);
// ** DOESNT WORK ** - produces a washed-out pink rectangle *****
// HBRUSH mBrush = CreateSolidBrush( RGB(128,0,255) );
// RECT mRect = {0,0,200,50};
// FillRect(memDC2, &mRect, mBrush);
// DeleteObject(mBrush);
Color mCol(255,128,0,255);
SolidBrush mBrush(mCol);
Graphics graphics(memDC2);
graphics.FillRectangle(&mBrush, (int)0, (int)0, 200, 50);
}
return TRUE;
case WM_ERASEBKGND:
{
HDC hdc;
RECT mRect, topRect;
hdc = (HDC)wParam;
GetClientRect(hwndDlg, &mRect);
topRect = mRect;
topRect.bottom = 50;
FillRect(hdc, &topRect, (HBRUSH)GetStockObject(BLACK_BRUSH));
mRect.top += 50;
FillRect(hdc, &mRect, (HBRUSH)GetStockObject(WHITE_BRUSH));
BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
AlphaBlend(hdc, 0,0, 55,96, memDC, 0, 0, 55,96, bf);
AlphaBlend(hdc, 100,32,200,50, memDC2, 0,0,200,50, bf);
return 1;
}
case WM_CLOSE:
{
EndDialog(hwndDlg, 0);
}
return TRUE;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
}
}
return TRUE;
}
return FALSE;
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
hInst=hInstance;
InitCommonControls();
int retVal = DialogBox(hInst, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)DlgMain);
GdiplusShutdown(gdiplusToken);
return retVal;
}
我有一个 window 的加长框架是这样制作的:
但是在扩展框中绘制的任何东西都有非常奇怪的颜色(除了白色,唯一保持不变的颜色),就像这样(忽略中间杂乱的内容和右边杂乱的工具栏。
粉色矩形 (0xFFC9FF
) 应该是 0x8000FF
。如果我将 DirectX11 内容(中心内容)放在扩展帧中,我的 FPS 计数器的 alpha 混合就会变得混乱。如果我对右侧的对话框执行相同的操作,也会发生同样的情况。
那么我怎样才能正确地做到这一点呢?我已经尝试过先绘制到内存 DC,然后再使用 BitBlt
。我正在使用 GDI+(加上 CreateCompatibleDC
、CreateCompatibleBitmap
和其他函数来处理内存 DC)。
PS:因为你问了,这里是WndProc
:
LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT ReturnValue;
if (DwmDefWindowProc(hWnd, uMsg, wParam, lParam, &ReturnValue)) return ReturnValue;
switch (uMsg)
{
case WM_CREATE:
{
// ...
RECT rcClient;
GetWindowRect(hWnd, &rcClient);
SetWindowPos(hWnd,
NULL,
rcClient.left, rcClient.top,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
SWP_FRAMECHANGED);
return 0;
}
case WM_ACTIVATE:
{
MARGINS Margins;
Margins.cxLeftWidth = LEFT_BORDER;
Margins.cxRightWidth = RIGHT_BORDER;
Margins.cyTopHeight = TOP_BORDER;
Margins.cyBottomHeight = BOTTOM_BORDER;
if (DwmExtendFrameIntoClientArea(hWnd, &Margins) != S_OK)
{
MessageBox(hWnd, L"Erro ao configurar janela.", NULL, MB_ICONERROR);
PostQuitMessage(WM_QUIT);
}
if (LOWORD(wParam))
{
fActive = true;
}
else
{
fActive = false;
}
InvalidateRect(hWnd, NULL, false);
return 0;
}
case WM_SIZE:
/* ... */
case WM_NCCALCSIZE:
return 0;
case WM_NCHITTEST:
/* ... */
case WM_GETMINMAXINFO:
((LPMINMAXINFO)lParam)->ptMinTrackSize = { 640, 400 };
return 0;
case WM_PAINT:
{
using namespace Gdiplus;
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hWnd, &ps);
RECT rcWindow;
GetWindowRect(hWnd, &rcWindow);
POINT ptSize = { rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top };
HDC hBuffer = CreateCompatibleDC(hDC);
HBITMAP hBitmap = CreateCompatibleBitmap(hDC, ptSize.x, ptSize.y);
SelectObject(hBuffer, hBitmap);
Graphics graphics(hBuffer);
Pen Outline(Color(128, 128, 128));
SolidBrush Selected(Color(128, 0, 255));
Rect Tab1(10, 10, 200, 50);
graphics.FillRectangle(&Selected, Tab1);
graphics.DrawRectangle(&Outline, Tab1);
/* ... */
BitBlt(hDC, 0, 0, ptSize.x, ptSize.y, hBuffer, 0, 0, SRCCOPY);
EndPaint(hWnd, &ps);
return 0;
}
/* ... */
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
正如我所提到的,您几乎已经掌握了 AlphaBlend。我没能做到 remember/realize 的是您需要使用包含紫色矩形的 32 位 DibSection。您还需要确保使用 32 位感知绘图函数。这意味着您要么必须直接写入 Dib 的位,在调用 GetDIBits 检索它们之后(或在从某些位创建 DIB 之前) 或 您需要使用 GDI+ 来绘制紫色矩形。您可以在 WM_INITDIALOG 处理程序中看到我注释掉的代码。这给出了第二张图片中显示的结果。
在尝试了我在已删除的答案中给出的一些建议后,我发现了一个熟悉的问题。紫色矩形的颜色根据我的 window 遮挡的 window 的颜色而变化。
这是另一个代码示例,已经过测试,可以在 DWM 的 Win7s 实现下运行。我不确定 Glass 是否会有所不同,或者 Win 8 是否会有类似的表现。
这是图片:(绘制时颜色正确,使图片成为 8 位索引的图片略有更改)
请注意,编辑框中的文本有点古怪,随着 window 旁边的背景从黑色变为白色,从正常变为不正常。这与我使用 GDI 绘制紫色矩形时显示的效果相同。当我改用 GDI+ 时,问题就消失了。快速拖动 window 会使紫色框的边缘看起来有点奇怪。我认为这是 Windows7.
中 DWM 实施的众多失败之一这是 window 的完整代码:
#define _WIN32_IE 0x0501
#define _WIN32_WINNT 0x0501
#define WINVER 0x0510
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include "resource.h"
#include <dwmapi.h>
#include <gdiplus.h>
#include <wingdi.h>
using namespace Gdiplus;
HINSTANCE hInst;
// BMP, GIF, JPEG, PNG, TIFF, Exif, WMF, and EMF
HBITMAP mLoadImageFile(wchar_t *filename)
{
HBITMAP result = NULL;
Bitmap bitmap(filename, false);
Color colBkg(0,0,0,0);
bitmap.GetHBITMAP(colBkg, &result);
return result;
}
HBITMAP zCreateDibSection(HDC hdc, int width, int height, int bitCount)
{
BITMAPINFO bi;
ZeroMemory(&bi, sizeof(bi));
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
bi.bmiHeader.biWidth = width;
bi.bmiHeader.biHeight = height;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = bitCount;
bi.bmiHeader.biCompression = BI_RGB;
return CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, 0,0,0);
}
HRESULT ExtendGlassIntoClient(HWND hwnd, int left, int right, int top, int bottom)
{
MARGINS margins = {left,right,top,bottom};
HRESULT hr = S_OK;
hr = DwmExtendFrameIntoClientArea(hwnd,&margins);
if (SUCCEEDED(hr))
{
// ...
}
return hr;
}
HBITMAP mImg, mWhosebugBitmap;
HDC memDC, memDC2;
HBITMAP oldBmp, oldBmp2;
LRESULT CALLBACK DlgMain(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
{
ExtendGlassIntoClient(hwndDlg, 0,0,50,0);
mImg = mLoadImageFile(L"girl.png");
mWhosebugBitmap = zCreateDibSection(NULL, 200, 50, 32);
memDC = CreateCompatibleDC(NULL);
memDC2 = CreateCompatibleDC(NULL);
oldBmp = (HBITMAP)SelectObject(memDC, mImg);
oldBmp2 = (HBITMAP)SelectObject(memDC2, mWhosebugBitmap);
// ** DOESNT WORK ** - produces a washed-out pink rectangle *****
// HBRUSH mBrush = CreateSolidBrush( RGB(128,0,255) );
// RECT mRect = {0,0,200,50};
// FillRect(memDC2, &mRect, mBrush);
// DeleteObject(mBrush);
Color mCol(255,128,0,255);
SolidBrush mBrush(mCol);
Graphics graphics(memDC2);
graphics.FillRectangle(&mBrush, (int)0, (int)0, 200, 50);
}
return TRUE;
case WM_ERASEBKGND:
{
HDC hdc;
RECT mRect, topRect;
hdc = (HDC)wParam;
GetClientRect(hwndDlg, &mRect);
topRect = mRect;
topRect.bottom = 50;
FillRect(hdc, &topRect, (HBRUSH)GetStockObject(BLACK_BRUSH));
mRect.top += 50;
FillRect(hdc, &mRect, (HBRUSH)GetStockObject(WHITE_BRUSH));
BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
AlphaBlend(hdc, 0,0, 55,96, memDC, 0, 0, 55,96, bf);
AlphaBlend(hdc, 100,32,200,50, memDC2, 0,0,200,50, bf);
return 1;
}
case WM_CLOSE:
{
EndDialog(hwndDlg, 0);
}
return TRUE;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
}
}
return TRUE;
}
return FALSE;
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
hInst=hInstance;
InitCommonControls();
int retVal = DialogBox(hInst, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)DlgMain);
GdiplusShutdown(gdiplusToken);
return retVal;
}