2

我有一个使用WS_EX_LAYERED窗口样式创建的窗口。我目前正在使用 GDI+ 绘制内存位图,并UpdateLayeredWindow用于更新分层窗口的图形内容。

这是我的代码片段:

void Redraw(HWND hWnd, int width, int height) {
    static bool floppy = true;

    floppy = !floppy;

    HDC hScreenDC = GetDC(HWND_DESKTOP);
    HDC hMemDC = CreateCompatibleDC(hScreenDC);
    HBITMAP hBmp = CreateCompatibleBitmap(hScreenDC, width, height);
    HGDIOBJ hObj = SelectObject(hMemDC, hBmp);

    Graphics gfx(hMemDC);

    SolidBrush b(Color(254, (floppy ? 255 : 0), (floppy ? 0 : 255), 0));
    gfx.FillRectangle(&b, Rect(0, 0, width, height));

    BLENDFUNCTION blend;
    blend.BlendOp = AC_SRC_OVER;
    blend.BlendFlags = 0;
    blend.SourceConstantAlpha = 255;
    blend.AlphaFormat = AC_SRC_ALPHA;

    POINT src = { 0, 0 };

    SIZE size;
    size.cx = width;
    size.cy = height;

    Assert(UpdateLayeredWindow(
        hWnd,
        hScreenDC,
        NULL,
        &size,
        hMemDC,
        &src,
        RGB(0, 0, 0),
        &blend,
        ULW_ALPHA
    ));

    SelectObject(hMemDC, hObj);
    DeleteObject(hBmp);
    DeleteDC(hMemDC);
    ReleaseDC(HWND_DESKTOP, hScreenDC);
}

创建 my 时SolidBrush,我为 alpha 分量指定了 254 的值。这会导致 99.6% 的不透明填充,这不是我想要的。

当我将 255 指定为 alpha 分量时,似乎没有填充;我的窗户变得完全透明。这是一个问题,因为我希望绘制 100% 不透明的形状,但我也希望绘制一些不透明的形状。

4

2 回答 2

3

似乎有一些 qwerks FillRectangleFillEllipse当我们观察到使用 alpha 分量为 255 的a时,这一点变得很明显SolidBrush,导致形状被完美渲染(不透明)。

这是我想出的两种解决方法,每种方法都为我解决了问题:

  • 调用 FillRectangle 两次

    SolidBrush b(Color(254, 255, 0, 0));
    gfx.FillRectangle(&b, Rect(0, 0, width, height));
    gfx.FillRectangle(&b, Rect(0, 0, width, height));
    

    由于同一区域被填充两次,它们将混合并创建 RGB(255, 0, 0),而不管窗口后面的内容如何(它现在是 100% 不透明的)。我不喜欢这种方法,因为它需要每个矩形绘制两次。

  • 改为使用FillPolygon

    就像FillEllipse,FillPolygon似乎没有颜色问题,除非你这样称呼它

    SolidBrush b(Color(255, 255, 0, 0));
    Point points[4];
    points[0] = Point(0, 0);
    points[1] = Point(width, 0);
    points[2] = Point(width, height);
    points[4] = Point(0, height);
    gfx.FillPolygon(&b, points, 4); //don't copy and paste - this won't work
    

    上面的代码将产生一个 100% 透明的窗口。我猜这可能是由于某种形式的优化将调用传递给了FillRectangle。或者 - 很可能 - 有一些问题FillPolygon,由FillRectangle. 但是,如果您在数组中添加额外Point的内容,则可以绕过它:

    SolidBrush b(Color(255, 255, 0, 0));
    Point points[5];
    points[0] = Point(0, 0);
    points[1] = Point(0, 0); //<-
    points[2] = Point(width, 0);
    points[3] = Point(width, height);
    points[4] = Point(0, height);
    gfx.FillPolygon(&b, points, 5);
    

    上面的代码确实会绘制一个 100% 不透明的形状,这解决了我的问题。

于 2013-10-07T02:47:07.960 回答
1

UpdateLayeredWindow()需要一个带有预乘 alpha 的位图:

请注意,API 使用预乘 Alpha,这意味着位图中的红色、绿色和蓝色通道值必须与 Alpha 通道值进行预乘。例如,如果 alpha 通道值为 x,则在调用之前,红色、绿色和蓝色通道必须乘以 x 并除以 0xff。

您可以使用Bitmap::ConvertFormat()将位图转换为预乘(格式为PixelFormat32bppPARGB)。

于 2013-10-06T23:14:48.597 回答