Небольшая нежелательная прозрачность от FillRectangle

У меня есть окно, созданное с 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 Я указал значение 254 для альфа-компонента. Это приводит к непрозрачной заливке 99,6%, а это не то, что я хочу.

Когда я указываю 255 в качестве альфа-компонента, кажется, что нет заполнения; мое окно становится полностью прозрачным. Это проблема, потому что я хочу рисовать фигуры, которые на 100% непрозрачны, но я также хочу рисовать и те, которые не являются прозрачными.

2 ответа

Решение

Кажется, есть некоторые qwerks с FillRectangle, Это становится очевидным, когда мы наблюдаем, что с помощью FillEllipse с SolidBrush чей альфа-компонент равен 255, в результате фигура отображается идеально (непрозрачно).

Вот два обходных пути, которые я придумал, каждый из которых решает проблему для меня:

  • Вызов 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% непрозрачную форму, что решает мою проблему.

UpdateLayeredWindow() требуется растровое изображение с предварительно умноженным альфа:

Обратите внимание, что API используют предварительно умноженный альфа-канал, что означает, что значения красного, зеленого и синего каналов в растровом изображении должны быть предварительно умножены на значение альфа-канала. Например, если значение альфа-канала равно x, красный, зеленый и синий каналы должны быть умножены на x и разделены на 0xff до вызова.

Ты можешь использовать Bitmap::ConvertFormat() преобразовать растровое изображение в предварительно умноженное (формат PixelFormat32bppPARGB).

Другие вопросы по тегам