Как предотвратить мерцание цветовой клавиши при включении LWA_COLORKEY - PullRequest
1 голос
/ 02 июля 2019

У меня есть окно, в котором иногда должно быть прозрачное отверстие, а иногда нет. В идеале мы должны использовать SetWindowRgn, но это отключает визуальные стили, которые не только выглядят некрасиво, но и некорректно рисуются с поддержкой DPI для каждого монитора, поэтому я пытаюсь использовать многослойное окно с цветовой клавишей.

При включении цветовой клавиши я сначала вызываю SetLayeredWindowAttributes(hWnd, colorkey, 0, LWA_COLORKEY), а затем отключаю окно, чтобы оно было перерисовано. На данный момент окно не должно содержать цвет ключа. Затем через некоторое время окно получает WM_PAINT, и цвет ключа окрашивается, но в этот момент в окне должно быть установлено LWA_COLORKEY, поэтому, опять же, я ожидаю, что цвет ключа не будет виден.

При отключении цветовой клавиши я сначала перекрашиваю окно (синхронно), чтобы оно не содержало цвет клавиши, а затем отключаю WS_EX_LAYERED, поэтому, опять же, я никогда не ожидаю увидеть цвет клавиши.

Тем не менее, окно со следующей оконной процедурой постоянно мигает между зеленым, прозрачным и фоновым цветом, когда мышь перемещается по нему.

Похоже, что SetLayeredWindowAttributes не вступает в силу немедленно (и даже до следующего WM_PAINT). Как я могу убедиться, что этот атрибут вступил в силу перед перерисовкой, или как-то иначе предотвратить отображение цвета ключа?

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static auto const colorkey = RGB(0,255,0);
    static auto const hbrush = CreateSolidBrush(colorkey);
    static auto transparent = false;
    switch (message)
    {
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            if (transparent) {
                RECT rect{30,30,500,500};
                FillRect(hdc, &rect, hbrush);
            }
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_MOUSEMOVE:
        if (transparent) {
            transparent = false;
            RedrawWindow(hWnd, nullptr /* lprcUpdate */, nullptr /* hrgnUpdate */, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN);
            SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
        } else {
            SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
            SetLayeredWindowAttributes(hWnd, colorkey, 0, LWA_COLORKEY);
            transparent = true;
            InvalidateRect(hWnd, nullptr, TRUE);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

1 Ответ

0 голосов
/ 05 июля 2019

Я не думаю, что многослойные окна предназначены для включения и выключения несколько раз в секунду (Windows будет выделять / уничтожать образ 32 BPP и т. Д. При каждом переключении).

SWP_FRAMECHANGED и дополнительное стирание делает мне намного лучше, по крайней мере:

case WM_MOUSEMOVE:
    if (transparent) {
        transparent = false;
        RedrawWindow(hWnd, nullptr /* lprcUpdate */, nullptr /* hrgnUpdate */, RDW_INTERNALPAINT|RDW_INVALIDATE|RDW_ERASE|RDW_ERASENOW|RDW_UPDATENOW|RDW_ALLCHILDREN);
        SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
        SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED|SWP_NOACTIVATE);
        InvalidateRect(hWnd, nullptr, true);
    } else {
        SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
        SetLayeredWindowAttributes(hWnd, colorkey, 0, LWA_COLORKEY);
        SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED|SWP_NOACTIVATE);
        transparent = true;
        InvalidateRect(hWnd, nullptr, true);
    }
    TCHAR b[99];wsprintf(b,TEXT("%d tick=%d"), transparent, GetTickCount()), SetWindowText(hWnd, b);
    break;
...