问题
I have a window created with the WS_EX_LAYERED
window style. I am currently drawing onto a memory bitmap using GDI+, and using UpdateLayeredWindow to update the graphical content of my layered window.
Here's a snippet of my code:
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);
}
When creating my SolidBrush
, I specified the value of 254 for the alpha component. This results in a 99.6% opaque fill, which is not what I want.
When I specify 255 as the alpha component, there appears to be no fill; my window becomes completely transparent. This is an issue because I wish to draw shapes that are 100% opaque, but I also wish to draw some that aren't.
回答1:
There seems to be some qwerks with FillRectangle
. This becomes apparent when we observe that using FillEllipse
with a SolidBrush
whose alpha component is 255, results in the shape being rendered perfectly (opaque).
Here are two work-arounds that I came up with, which each solve the issue for me:
Call
FillRectangle
twiceSolidBrush b(Color(254, 255, 0, 0)); gfx.FillRectangle(&b, Rect(0, 0, width, height)); gfx.FillRectangle(&b, Rect(0, 0, width, height));
Since the same area is being filled twice, they will blend and create RGB(255, 0, 0) regardless of the content behind the window (it's now 100% opaque). I do not prefer this method, as it requires every rectangle to be drawn twice.
Use FillPolygon instead
Just as with
FillEllipse
,FillPolygon
doesn't seem to have the colour issue, unless you call it like so: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
The above code will result in a 100% transparent window. I am guessing that this is either due to some form of optimisation that passes the call to
FillRectangle
instead. Or - most likely - there is some problem withFillPolygon
, which is called byFillRectangle
. However, if you add an extraPoint
to the array, you can get around it: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);
The above code will indeed draw a 100% opaque shape, which fixes my problem.
回答2:
UpdateLayeredWindow()
requires a bitmap with pre-multiplied alpha:
Note that the APIs use premultiplied alpha, which means that the red, green and blue channel values in the bitmap must be premultiplied with the alpha channel value. For example, if the alpha channel value is x, the red, green and blue channels must be multiplied by x and divided by 0xff prior to the call.
You can use Bitmap::ConvertFormat() to convert a bitmap to pre-multiplied (the format is PixelFormat32bppPARGB
).
来源:https://stackoverflow.com/questions/19214921/slight-undesired-transparency-from-fillrectangle