从FillRectangle略微不受欢迎的透明度

时间:2013-10-06 22:54:31

标签: c++ winapi graphics gdi+

我有一个使用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);
}

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

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

2 个答案:

答案 0 :(得分:3)

FillRectangle似乎有一些qwerks。当我们观察到FillEllipse使用其{alpha}为255的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%不透明的形状,这可以解决我的问题。

答案 1 :(得分:1)

UpdateLayeredWindow()需要预先乘以alpha的位图:

  

请注意,API使用预乘alpha,这意味着红色,   位图中的绿色和蓝色通道值必须预乘   alpha通道值。例如,如果alpha通道值为x,   红色,绿色和蓝色通道必须乘以x并分开   在通话之前通过0xff。

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