首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C++ Win32 API GDI :矩形AntiAliasing在透明背景下不能正常工作

C++ Win32 API GDI :矩形AntiAliasing在透明背景下不能正常工作
EN

Stack Overflow用户
提问于 2020-09-06 16:05:25
回答 1查看 1.6K关注 0票数 0

我使用扩展框架来呈现自定义标题和窗口边框。

代码语言:javascript
运行
复制
hr = DwmExtendFrameIntoClientArea(hWnd, &margins);

我还使用分层窗口使背景透明。我的COLORKEY是RGB(0,0,0)

代码语言:javascript
运行
复制
SetLayeredWindowAttributes(hWnd, RGB(0, 0, 0), 255, LWA_COLORKEY);

我使用分层窗口的原因是,我想使窗口的底部边框圆角。

问题是,我想做一些美好的事情,我试图用GDI在客户端区域呈现一个反别名窗口边框。然而,它作为反别名与油漆背景(实心),但不是透明的背景。

图片:https://ibb.co/fD9GsFg

我该怎么做才能解决这个问题?

如果您尝试它,请使用VS调试器,因为我没有设置功能窗口按钮。

代码语言:javascript
运行
复制
#include <windows.h>
#include <tchar.h>
#include <windowsx.h>
#include <objidl.h>
#include <gdiplus.h>

#include <dwmapi.h>
#pragma comment(lib,"Dwmapi")

using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")

HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int nWidth = 600, nHeight = 400;

#define RECTWIDTH(rc)            (rc.right - rc.left)
#define RECTHEIGHT(rc)            (rc.bottom - rc.top)

//CHANGE THEM TO 0 when thats maximized!
const int TOPEXTENDWIDTH = 48;
const int LEFTEXTENDWIDTH = 8;
const int RIGHTEXTENDWIDTH = 8;
const int BOTTOMEXTENDWIDTH = 8;

HBRUSH RED_BRUSH = CreateSolidBrush(RGB(237, 28, 36));
HBRUSH DARKBLUE_BRUSH = CreateSolidBrush(RGB(26, 31, 96));
HBRUSH PURPLE_BRUSH = CreateSolidBrush(RGB(163, 73, 164));
HBRUSH LIGHTPURPLE_BRUSH_1 = CreateSolidBrush(RGB(189, 106, 189));
HBRUSH LIGHTPURPLE_BRUSH_2 = CreateSolidBrush(RGB(255, 174, 201));
HBRUSH DARKEST_BRUSH = CreateSolidBrush(RGB(0, 0, 0));

LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam);

int WINAPI wWinMain( _In_opt_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_opt_ LPTSTR lpCmdLine, _In_opt_ int nCmdShow)
{
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR           gdiplusToken;

    // Initialize GDI+.
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    hInst = hInstance;
    WNDCLASSEX wcex =
    {
        sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInst, LoadIcon(NULL, IDI_APPLICATION),
        LoadCursor(NULL, IDC_ARROW), (HBRUSH)GetStockObject(BLACK_BRUSH), NULL, TEXT("WindowClass"), NULL,
    };
    if (!RegisterClassEx(&wcex))
        return MessageBox(NULL, L"Cannot register class !", L"Error", MB_ICONERROR | MB_OK);
    int nX = (GetSystemMetrics(SM_CXSCREEN) - nWidth) / 2, nY = (GetSystemMetrics(SM_CYSCREEN) - nHeight) / 2;
    HWND hWnd = CreateWindowEx(0, wcex.lpszClassName, TEXT("Test"), WS_OVERLAPPEDWINDOW, nX, nY, nWidth, nHeight, NULL, NULL, hInst, NULL);
    
    if (!hWnd) return MessageBox(NULL, L"Cannot create window !", L"Error", MB_ICONERROR | MB_OK);

    //NO SHADOW
    SystemParametersInfoA(SPI_SETDROPSHADOW,0,(const PVOID) false,SPIF_SENDWININICHANGE);

    ShowWindow(hWnd, SW_SHOWNORMAL);
    UpdateWindow(hWnd);
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}
void FillRoundRectangle(Gdiplus::Graphics* g, Brush* p, Gdiplus::Rect& rect, UINT8 radius[4])
{
    if (g == NULL) return;
    GraphicsPath path;
    //TOP RIGHT
    path.AddLine(rect.X + radius[0], rect.Y, rect.X + rect.Width - (radius[0] * 2), rect.Y);
    path.AddArc(rect.X + rect.Width - (radius[0] * 2), rect.Y, radius[0] * 2, radius[0] * 2, 270, 90);

    //BOTTOM RIGHT
    path.AddLine(rect.X + rect.Width, rect.Y + radius[1], rect.X + rect.Width, rect.Y + rect.Height - (radius[1] * 2));
    path.AddArc(rect.X + rect.Width - (radius[1] * 2), rect.Y + rect.Height - (radius[1] * 2), radius[1] * 2, radius[1] * 2, 0, 90);

    //BOTTOM LEFT
    path.AddLine(rect.X + rect.Width - (radius[2] * 2), rect.Y + rect.Height, rect.X + radius[2], rect.Y + rect.Height);
    path.AddArc(rect.X, rect.Y + rect.Height - (radius[2] * 2), radius[2] * 2, radius[2] * 2, 90, 90);

    //TOP LEFT
    path.AddLine(rect.X, rect.Y + rect.Height - (radius[3] * 2), rect.X, rect.Y + radius[3]);
    path.AddArc(rect.X, rect.Y, radius[3] * 2, radius[3] * 2, 180, 90);
    path.CloseFigure();

    g->FillPath(p, &path);
}
VOID OnPaint(HDC hdc,int width, int height)
{
    Graphics graphics(hdc);

    graphics.SetSmoothingMode(SmoothingMode::SmoothingModeHighQuality);

    graphics.SetCompositingQuality(CompositingQuality::CompositingQualityInvalid);
    graphics.SetPixelOffsetMode(PixelOffsetMode::PixelOffsetModeHighQuality);
    SolidBrush mySolidBrush(Color(255, 255, 0, 0)); ;
    Gdiplus::Rect rect1;
    rect1.X = 0;
    rect1.Y = TOPEXTENDWIDTH;
    rect1.Width = width;
    rect1.Height = height- TOPEXTENDWIDTH-111;
    UINT8 rad[4]{ 0,12,12,0 };
    FillRoundRectangle(&graphics, &mySolidBrush, rect1, rad);
    SolidBrush DarkSolidBrush(Color(255, 0, 1, 0)); ;

    Gdiplus::Rect rectX = {0,455,55,55};
    graphics.FillEllipse(&DarkSolidBrush,rectX);
}


LRESULT CustomCaptionProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool* pfCallDWP)
{
    LRESULT lRet = 0;
    HRESULT hr = S_OK;
    bool fCallDWP = true; // Pass on to DefWindowProc?
    static HICON hIcon = NULL;

    fCallDWP = !DwmDefWindowProc(hWnd, message, wParam, lParam, &lRet);
    if (message == WM_CREATE)
    {

        RECT rcClient;
        GetWindowRect(hWnd, &rcClient);
        // Inform the application of the frame change.
        SetWindowPos(hWnd, NULL, rcClient.left, rcClient.top, RECTWIDTH(rcClient), RECTHEIGHT(rcClient), SWP_FRAMECHANGED);

        HMODULE hDLL = LoadLibrary(L"Setupapi.dll");
        if (hDLL)
        {
            hIcon = (HICON)LoadImage(hDLL, MAKEINTRESOURCE(2), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR | LR_SHARED);
        }
        SetWindowLong(hWnd, GWL_EXSTYLE,
            GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
        /*Use pointer to function*/
        SetLayeredWindowAttributes(hWnd, 0,
            (255 * 70) / 100, LWA_ALPHA);
        fCallDWP = true;
        lRet = 0;
    }

    // Handle window activation.
    if (message == WM_ACTIVATE)
    {
        // Extend the frame into the client area.
        MARGINS margins;
        margins.cxLeftWidth = 0;
        margins.cxRightWidth = 0;
        margins.cyBottomHeight = 0;
        margins.cyTopHeight = 0;
        hr = DwmExtendFrameIntoClientArea(hWnd, &margins);

        if (!SUCCEEDED(hr))
        {
            // Handle error.
        }

        fCallDWP = true;
        lRet = 0;
    }

    if (message == WM_PAINT)
    {
        PAINTSTRUCT ps;
        BITMAP bm;
        RECT rect, rectCaptionButtonBounds, rectText,myRect,ContentRect,ClientRect,CaptionBorderBottom,  rect_EXIT_BTN, rect_RESTORE_BTN, rect_MINIMIZE_BTN;

        HFONT windowTitleText;

        GetClientRect(hWnd,&ClientRect);
        BeginPaint(hWnd, &ps);

        SetGraphicsMode(ps.hdc, GM_ADVANCED);

        SetLayeredWindowAttributes(hWnd, RGB(0, 0, 0), 255, LWA_COLORKEY);
        SetBkMode(ps.hdc, TRANSPARENT);
        if (SUCCEEDED(DwmGetWindowAttribute(hWnd, DWMWA_CAPTION_BUTTON_BOUNDS, &rectCaptionButtonBounds, sizeof(rectCaptionButtonBounds))))
        {
            GetClientRect(hWnd, &rect);
            
            //HRGN hrgn_cptBtmBrdrRND = CreateRoundRectRgn(0, 0, RECTWIDTH(ClientRect), RECTHEIGHT(ClientRect), 16, 16);

            //FillRgn(ps.hdc, hrgn_cptBtmBrdrRND, DARKBLUE_BRUSH);

            HRGN hrgn = CreateRectRgn(0, 0, RECTWIDTH(ClientRect), TOPEXTENDWIDTH);

            FillRgn(ps.hdc, hrgn, PURPLE_BRUSH);

            DrawIconEx(ps.hdc, rect.right - (rectCaptionButtonBounds.right - rectCaptionButtonBounds.left) - 32, 0, hIcon, 32, 32, 0, NULL, DI_NORMAL);
            SetRect(&myRect, LEFTEXTENDWIDTH, 10, RECTWIDTH(rect)-200, TOPEXTENDWIDTH);
            SetTextColor(ps.hdc, RGB(1, 0, 0));
            DrawText(ps.hdc,L"test",-1,&myRect, DT_SINGLELINE | DT_RIGHT);


            SetTextColor(ps.hdc, RGB(255, 255, 255));
            WCHAR wsText[255] = L"ARMNET";
            SetRect(&rectText, LEFTEXTENDWIDTH, 0, RECTWIDTH(rect), TOPEXTENDWIDTH);


                windowTitleText =
                CreateFontA
                (
                  32,
                  0,
                  GM_ADVANCED,
                  0,
                  FW_DONTCARE,
                  false,
                  false,
                  false,
                  DEFAULT_CHARSET,
                  OUT_OUTLINE_PRECIS,
                  CLIP_DEFAULT_PRECIS,
                  CLEARTYPE_QUALITY, //BETTER BLENDING THAN ANTIALIASED
                  VARIABLE_PITCH,
                  "RETRO COMPUTER");

                SelectObject(ps.hdc, windowTitleText);
            DrawText(ps.hdc, wsText, -1, &rectText, DT_SINGLELINE | DT_VCENTER);
            DeleteObject(windowTitleText);
            DeleteObject(hrgn);

        }

        //CONTENT AREA
        //SetRect(&ContentRect, 0, TOPEXTENDWIDTH, RECTWIDTH(ClientRect) - 0, RECTHEIGHT(ClientRect) - 0);
        //FillRect(ps.hdc, &ContentRect, DARKBLUE_BRUSH);

        HRGN hrgn_cptBtmBrdr = CreateRectRgn(0, TOPEXTENDWIDTH-1, RECTWIDTH(ClientRect), TOPEXTENDWIDTH);
        FillRgn(ps.hdc, hrgn_cptBtmBrdr, CreateSolidBrush(RGB(132, 68, 133)));

        hrgn_cptBtmBrdr = CreateRectRgn(0, TOPEXTENDWIDTH - 2, RECTWIDTH(ClientRect), TOPEXTENDWIDTH-1);
        FillRgn(ps.hdc, hrgn_cptBtmBrdr,CreateSolidBrush(RGB(185, 91, 186)));

        //BUTTONS
        hrgn_cptBtmBrdr = CreateRectRgn(RECTWIDTH(ClientRect)-32, 0, RECTWIDTH(ClientRect), 32);
        FillRgn(ps.hdc, hrgn_cptBtmBrdr, CreateSolidBrush(RGB(11, 11, 111)));
        hrgn_cptBtmBrdr = CreateRectRgn(RECTWIDTH(ClientRect) - 64, 0, RECTWIDTH(ClientRect)-32, 32);
        FillRgn(ps.hdc, hrgn_cptBtmBrdr, CreateSolidBrush(RGB(111, 11, 111)));
        hrgn_cptBtmBrdr = CreateRectRgn(RECTWIDTH(ClientRect) - 96, 0, RECTWIDTH(ClientRect)-64, 32);
        FillRgn(ps.hdc, hrgn_cptBtmBrdr, CreateSolidBrush(RGB(11, 111, 11)));
        
        OnPaint(ps.hdc, RECTWIDTH(ClientRect), RECTHEIGHT(ClientRect));

        DeleteObject(hrgn_cptBtmBrdr);
        EndPaint(hWnd, &ps);



        fCallDWP = true;
        lRet = 0;
    }

    // Handle the non-client size message.
    if ((message == WM_NCCALCSIZE) && (wParam == TRUE))
    {
        // Calculate new NCCALCSIZE_PARAMS based on custom NCA inset.
        NCCALCSIZE_PARAMS *pncsp = reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam);

        pncsp->rgrc[0].left = pncsp->rgrc[0].left + 1;
        pncsp->rgrc[0].top = pncsp->rgrc[0].top + 0;
        pncsp->rgrc[0].right = pncsp->rgrc[0].right - 1;
        pncsp->rgrc[0].bottom = pncsp->rgrc[0].bottom - 1;

        lRet = 0;
        // No need to pass the message on to the DefWindowProc.
        fCallDWP = false;
    }

    // Handle hit testing in the NCA if not handled by DwmDefWindowProc.
    if ((message == WM_NCHITTEST) && (lRet == 0))
    {
        lRet = HitTestNCA(hWnd, wParam, lParam);

        if (lRet != HTNOWHERE)
        {
            fCallDWP = false;
        }
    }

    if (message == WM_SIZE)
    {
        if (unsigned int(wParam) == SIZE_MAXIMIZED) {

        }
        else
        {

        }
    }

    if (message == WM_GETMINMAXINFO)
    {
        LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam;
        lpMMI->ptMinTrackSize.x = 800;
        lpMMI->ptMinTrackSize.y = 600;
    }

    if (message == WM_DESTROY)
    PostQuitMessage(0);
    *pfCallDWP = fCallDWP;

    return lRet;
}

// Hit test the frame for resizing and moving.
LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
    // Get the point coordinates for the hit test.
    POINT ptMouse = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };

    // Get the window rectangle.
    RECT rcWindow;
    GetWindowRect(hWnd, &rcWindow);

    // Get the frame rectangle, adjusted for the style without a caption.
    RECT rcFrame = { 0 };
    AdjustWindowRectEx(&rcFrame, WS_OVERLAPPEDWINDOW & ~WS_CAPTION, FALSE, NULL);

    // Determine if the hit test is for resizing. Default middle (1,1).
    USHORT uRow = 1;
    USHORT uCol = 1;
    bool fOnResizeBorder = false;

    // Determine if the point is at the top or bottom of the window.
    if ((ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + TOPEXTENDWIDTH)  )
    {
        if((ptMouse.x < rcWindow.right - 100) || (ptMouse.y > rcWindow.top + 32)){
        fOnResizeBorder = (ptMouse.y < (rcWindow.top - rcFrame.top));
        uRow = 0;
        }
    }

    else if (ptMouse.y < rcWindow.bottom && ptMouse.y >= rcWindow.bottom - BOTTOMEXTENDWIDTH)
    {
        uRow = 2;
    }

    // Determine if the point is at the left or right of the window.
    if (ptMouse.x >= rcWindow.left && ptMouse.x < rcWindow.left + LEFTEXTENDWIDTH)
    {
        uCol = 0; // left side
    }
    else if (ptMouse.x < rcWindow.right && ptMouse.x >= rcWindow.right - RIGHTEXTENDWIDTH)
    {
        uCol = 2; // right side
    }

    // Hit test (HTTOPLEFT, ... HTBOTTOMRIGHT)
    LRESULT hitTests[3][3] =
    {
        { fOnResizeBorder ? HTTOPLEFT : HTLEFT, fOnResizeBorder ? HTTOP : HTCAPTION, fOnResizeBorder ? HTTOPRIGHT : HTRIGHT },
        { HTLEFT,       HTNOWHERE,     HTRIGHT },
        { HTBOTTOMLEFT, HTBOTTOM, HTBOTTOMRIGHT },
    };

    return hitTests[uRow][uCol];
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    bool fCallDWP = true;
    BOOL fDwmEnabled = FALSE;
    LRESULT lRet = 0;
    HRESULT hr = S_OK;

    // Winproc worker for custom frame issues.
    hr = DwmIsCompositionEnabled(&fDwmEnabled);
    if (SUCCEEDED(hr))
    {
        lRet = CustomCaptionProc(hWnd, message, wParam, lParam, &fCallDWP);
    }

    // Winproc worker for the rest of the application.
    if (fCallDWP)
    {
        //  lRet = AppWinProc(hWnd, message, wParam, lParam);
        lRet = DefWindowProc(hWnd, message, wParam, lParam);
    }
    return lRet;
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-09-08 14:38:38

嗯,在寻找解决方案之后,我想我终于找到了答案。

解决方案:针对反混叠问题的解决方案可能是捕获整个窗口的背景,由从捕获中提取。此时,您将不需要分层窗口,因为您正在用位图绘制背景。HDC将能够将它与背景混合。希望Windows 10 2004版本您有一个名为:

WDA_EXCLUDEFROMCAPTURE

用法:

SetWindowDisplayAffinity(hWnd,WDA_EXCLUDEFROMCAPTURE);//在创建时

来源: https://blogs.windows.com/windowsdeveloper/2019/09/16/new-ways-to-do-screen-capture/

之后,你可以用位图画背景,然后在上面画所有的东西。然而,这产生了低性能,我并没有受益。不过,这是有效的,并产生反别名的外观时,绘制。对于性能问题,可以使用Direct2D从返回的位图绘制。

示例:

代码语言:javascript
运行
复制
int DsktpBkSS(HWND hWnd) {

    HDC hdcScreen;
    HDC hdcWindow;
    HDC hdcMemDC = NULL;
    HBITMAP hbmScreen = NULL;
    BITMAP bmpScreen;
    RECT windowPos;
    SetWindowDisplayAffinity(hWnd, WDA_EXCLUDEFROMCAPTURE);
    GetWindowRect(hWnd, &windowPos);

    // Retrieve the handle to a display device context for the client 
    // area of the window. 
    hdcScreen = GetDC(NULL);
    hdcWindow = GetDC(hWnd);

    // Create a compatible DC which is used in a BitBlt from the window DC
    hdcMemDC = CreateCompatibleDC(hdcWindow);

    if (!hdcMemDC)
    {
        MessageBox(hWnd, L"CreateCompatibleDC has failed", L"Failed", MB_OK);
    }

    // Get the client area for size calculation
    RECT rcClient;
    GetClientRect(hWnd, &rcClient);

    //This is the best stretch mode
    SetStretchBltMode(hdcWindow, HALFTONE);

    //The source DC is the entire screen and the destination DC is the current window (HWND)
    if (!StretchBlt(hdcWindow,
        0, 0,
        GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
        hdcScreen,
        windowPos.left+1,windowPos.top,
        GetSystemMetrics(SM_CXSCREEN),
        GetSystemMetrics(SM_CYSCREEN),
        SRCCOPY))
    {
        MessageBox(hWnd, L"StretchBlt has failed", L"Failed", MB_OK);
    }

    // Create a compatible bitmap from the Window DC
    hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);

    if (!hbmScreen)
    {
        MessageBox(hWnd, L"CreateCompatibleBitmap Failed", L"Failed", MB_OK);
    }

    // Select the compatible bitmap into the compatible memory DC.
    if(hdcMemDC && hbmScreen){
    SelectObject(hdcMemDC, hbmScreen);
    }
    // Bit block transfer into our compatible memory DC.
    if(hdcMemDC)
    if (!BitBlt(hdcMemDC,
        0, 0,
        rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
        hdcWindow,
        0, 0,
        SRCCOPY))
    {
        MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
    }

    // Get the BITMAP from the HBITMAP
    if(hbmScreen)
    GetObjectW(hbmScreen, sizeof(BITMAP), &bmpScreen);

    if (hbmScreen)DeleteObject(hbmScreen);
    if (hdcMemDC)DeleteObject(hdcMemDC);
    ReleaseDC(NULL, hdcScreen);
    ReleaseDC(hWnd, hdcWindow);

    return 0;
}

稍后,

代码语言:javascript
运行
复制
(...){
if(message==WM_PAINT)
{
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
Graphics graphics(ps.hdc);
/*SET SMOOTHING (AA)*/
graphics.SetSmoothingMode(SmoothingMode::SmoothingModeHighQuality);
if(DsktpBkSS(hWnd))
{
/*DRAW ROUNDED RECTANGLE*/
}
}
//...
};

对于使用“"flickering”桌面窗口管理器“(Desktop window Manager)绘制的窗口来说,更重要的是,如果从左侧调整大小太快且持续太长,过了一段时间就会产生。在Microsoft上,建议使用StretchBlt(...)进行绘图,因为GDI+会导致这种情况。

闪烁提到的DWM:https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-stretchblt

“使用BitBlt或StretchBlt函数代替GDI+呈现绘图以供呈现。GDI+在软件呈现时一次呈现一条扫描线。这可能会导致应用程序中的闪烁。”

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63766173

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档