2

我有一个分层窗口,我自己创建了WS_EX_LAYERED扩展样式和UpdateLayeredWindow功能。

然后我使用 GDI+ 库Graphics::DrawString方法在其中绘制一些文本。

结果是这样 的: 分层窗口的屏幕截图

如您所见,日文、韩文和中文字符是完全透明的。他们甚至使窗口的白色背景透明,根本不透明。

只有在禁用桌面组合(Aero 主题)时,该问题才会出现在 Windows Vista 和 Windows 7 上。
在 Windows 10 上它工作正常,因为桌面组合总是在那里启用。

为什么这种奇怪的效果只发生在东亚字符身上?
以及如何解决这个问题?

4

1 回答 1

0

我没有要测试的 Windows 7 机器,所以我不知道 alpha 通道是否是真正的问题,但假设它是,您可以通过在编写后将 alpha 通道设置回正确状态来解决它错误的文字:

enum { WIDTH = 255 * 3, HEIGHT = 25 };
#define CalcStride(w, bpp) ( ((((w) * (bpp)) + 31) & ~31) >> 3 )
#define PMC(c, a) ( (c) = ((int)(c) * (a) / 255) )
#define PM(q) PMC( (q).rgbRed, (q).rgbReserved), PMC( (q).rgbGreen, (q).rgbReserved), PMC( (q).rgbBlue, (q).rgbReserved)

RGBQUAD* GetPxPtr32(void*pBits, UINT x, UINT y)
{
    return ((RGBQUAD*) ( ((char*)pBits) + (y * CalcStride(WIDTH, 32)) )) + x;
}

void SaveAlpha32(void*pBits, BYTE*buf)
{
    for (UINT x = 0; x < WIDTH; ++x)
        for (UINT y = 0; y < HEIGHT; ++y)
            buf[(y * WIDTH) + x] = GetPxPtr32(pBits, x, y)->rgbReserved;
}

void RestoreAlpha32(void*pBits, const BYTE*buf)
{
    for (UINT x = 0; x < WIDTH; ++x)
        for (UINT y = 0; y < HEIGHT; ++y)
            GetPxPtr32(pBits, x, y)->rgbReserved = buf[(y * WIDTH) + x];
}

void Draw(HDC hDC, HBITMAP hBM, void*pBits, UINT w, UINT h, bool isDwmActive)
{
    // Fill with white and a silly gradient alpha channel:
    for (UINT y = 0; y < h; ++y)
        for (UINT x = 0; x < w; ++x)
            (*(UINT32*)GetPxPtr32(pBits, x, y)) = 0xffffffff, GetPxPtr32(pBits, x, y)->rgbReserved = max(42, x % 255);

    BYTE *alphas = isDwmActive ? 0 : (BYTE*) LocalAlloc(LPTR, sizeof(BYTE) * w * h), fillWithRed = true;
    if (!isDwmActive) SaveAlpha32(pBits, alphas);
    HGDIOBJ hBmOld = SelectObject(hDC, hBM);
    RECT r = { 0, 0, WIDTH, HEIGHT };
    int cbk = SetBkColor(hDC, RGB(255, 0, 0));
    if (fillWithRed) ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &r, NULL, 0, NULL);
    int ctx = SetTextColor(hDC, RGB(0, 0, 0));
    int mode = SetBkMode(hDC, TRANSPARENT);
    DrawText(hDC, TEXT("Hello World Hello World Hello World Hello World Hello World"), -1, &r, DT_SINGLELINE|DT_VCENTER|DT_CENTER); // Plain GDI always destroys the alpha
    SetBkMode(hDC, mode), SetBkColor(hDC, cbk), SetTextColor(hDC, ctx);
    SelectObject(hDC, hBmOld), GdiFlush();
    if (!isDwmActive) RestoreAlpha32(pBits, alphas), LocalFree(alphas);
    for (UINT y = 0; y < h; ++y) for (UINT x = 0; x < w; ++x) PM(*GetPxPtr32(pBits, x, y));
}

int main()
{
    const INT w = WIDTH, h = HEIGHT, bpp = 32, x = 222, y = 222;
    HWND hWnd = CreateWindowEx(WS_EX_LAYERED|WS_EX_TOPMOST, WC_STATIC, 0, WS_VISIBLE|WS_POPUP, x, y, WIDTH, HEIGHT, 0, 0, 0, 0);
    SetWindowLong(hWnd, GWLP_WNDPROC, (LONG_PTR) DefWindowProc); // HACK
    BITMAPINFO bi;
    ZeroMemory(&bi, sizeof(bi));
    BITMAPINFOHEADER&bih = bi.bmiHeader;
    bih.biSize = sizeof(BITMAPINFOHEADER);
    bih.biWidth = w, bih.biHeight = -h;
    bih.biPlanes = 1, bih.biBitCount = bpp;
    bih.biCompression = BI_RGB;
    void*bits;
    HBITMAP hBmp = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, &bits, NULL, 0);
    HDC hDCScreen = GetDC(NULL), hDC = CreateCompatibleDC(hDCScreen);
    Draw(hDC, hBmp, bits, w, h, false);
    HGDIOBJ hBmOld = SelectObject(hDC, hBmp);
    BLENDFUNCTION blend = { 0 };
    blend.BlendOp = AC_SRC_OVER, blend.AlphaFormat = AC_SRC_ALPHA, blend.SourceConstantAlpha = 255;
    POINT location = { x, y }, srcpt = { 0, 0 };
    SIZE szWnd = { w, h };
    UpdateLayeredWindow(hWnd, hDCScreen, &location, &szWnd, hDC, &srcpt, 0, &blend, ULW_ALPHA);
    SelectObject(hDC, hBmOld), DeleteObject(hBmp);
    DeleteDC(hDC), ReleaseDC(NULL, hDCScreen);
    struct Closer { Closer(HWND h) { SetTimer(h, 1, 1000 * 11, TP); } static void CALLBACK TP(HWND h,UINT,UINT_PTR,DWORD) { ExitProcess(666); } } closer(hWnd); // HACK
    for (MSG msg; GetMessage(&msg, 0, 0, 0); ) DispatchMessage(&msg);
    return 666;
}

如果您不关心没有平台更新的 Vista,您可以尝试使用Direct2D而不是 GDI+。

于 2019-06-10T14:00:23.160 回答