【问题标题】:How to round the corners of an image inside of a hdc?如何在 hdc 内圆角图像?
【发布时间】:2022-02-25 03:48:12
【问题描述】:

我正在使用 winapi PrintWindow 捕获一个窗口,并使用 BitBlt 仅裁剪所需的区域,但我试图捕获的区域是一个圆角矩形,我如何可以圆角图像在hdc里面?

我知道Graphics::FromHDC(HDC) 的方法,但我不确定如何“圆”从图形中获取的图像。

RECT rc;
HWND hwnd = FindWindow(TEXT("Test"), NULL);
if (hwnd == NULL)
{
    cout << "Can't find the given window" << endl;
    return 0;
}
GetClientRect(hwnd, &rc);

// Capture the window screen.
HDC hdcScreen = GetDC(NULL);
HDC hdcSrc = CreateCompatibleDC(hdcScreen);
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen, 
    rc.right - rc.left, rc.bottom - rc.top);
SelectObject(hdcSrc , hbmp);

//Print to memory hdc
PrintWindow(hwnd, hdcSrc, PW_CLIENTONLY);


// Copy only the desired area.
HDC hdcScreen2 = GetDC(NULL);
HDC hdcDest = CreateCompatibleDC(hdcScreen2);
HBITMAP hbmp2 = CreateCompatibleBitmap(hdcScreen2, 
    300, 200);
SelectObject(hdcDest, hbmp2);

BitBlt(hdcDest, X, Y, W, H, hdcSrc, 0, 0, SRCCOPY);

【问题讨论】:

  • 如果我是对的,您可以指定具有所需形状的区域,并且只会修改该区域。
  • 指定区域在哪个函数?
  • 不知道,不好意思,看看GDI函数。查找剪辑。
  • SelectClipRgnCreateRoundedRectRgn。裁剪是二元操作。这不会执行任何类型的抗锯齿并产生锯齿状边缘。
  • 必须在调用 BitBlc 之前或之后完成? @IInspectable

标签: c++ winapi gdi+


【解决方案1】:

(我写了这个答案,并认为 OP 出于某种原因想要 Gdiplus ...常规 GDI 几乎是一样的,尽管在这种情况下实际上有一个 CreatRoundRectRgn(...))

您可以使用带有 Region 的 SetClip 成员函数设置 Gdiplus::Graphics 对象的剪辑区域。您可以从Path 创建一个Region,但在Gdi+ 中显然没有开箱即用的方法来创建圆角矩形形状的路径,因此您必须手动完成。 StackOverflow 上有一个关于 doing this in C# 的答案,另一个将其移植到 C++/Win32 here

代码如下:

#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>
namespace gdi = Gdiplus;
#pragma comment (lib,"Gdiplus.lib")

void GetRoundRectPath(gdi::GraphicsPath* pPath, gdi::Rect r, int dia)
{
    // diameter can't exceed width or height
    if (dia > r.Width)    dia = r.Width;
    if (dia > r.Height)    dia = r.Height;

    // define a corner 
    gdi::Rect Corner(r.X, r.Y, dia, dia);

    // begin path
    pPath->Reset();

    // top left
    pPath->AddArc(Corner, 180, 90);

    // tweak needed for radius of 10 (dia of 20)
    if (dia == 20)
    {
        Corner.Width += 1;
        Corner.Height += 1;
        r.Width -= 1; r.Height -= 1;
    }

    // top right
    Corner.X += (r.Width - dia - 1);
    pPath->AddArc(Corner, 270, 90);

    // bottom right
    Corner.Y += (r.Height - dia - 1);
    pPath->AddArc(Corner, 0, 90);

    // bottom left
    Corner.X -= (r.Width - dia - 1);
    pPath->AddArc(Corner, 90, 90);

    // end path
    pPath->CloseFigure();
}

VOID OnPaint(HDC hdc, gdi::Image* img)
{
    gdi::Graphics g(hdc);
    gdi::GraphicsPath path;
    GetRoundRectPath(&path, { 10,10,512,512 }, 50);
    gdi::Region rgn(&path);
    g.SetClip(&rgn);
    g.DrawImage(img, 10, 10);
}

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, INT iCmdShow)
{
    HWND                hWnd;
    MSG                 msg;
    WNDCLASS            wndClass;
    gdi::GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR           gdiplusToken;

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

    {
        // nest everything in a scope so that the image doesnt
        // get destroyed after Gdiplus has been shut down.

        wndClass.style = CS_HREDRAW | CS_VREDRAW;
        wndClass.lpfnWndProc = WndProc;
        wndClass.cbClsExtra = 0;
        wndClass.cbWndExtra = 0;
        wndClass.hInstance = hInstance;
        wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
        wndClass.lpszMenuName = NULL;
        wndClass.lpszClassName = TEXT("cliptorrect");

        RegisterClass(&wndClass);

        gdi::Image img(TEXT("C:\\test\\lenna.png"));

        hWnd = CreateWindow(
            TEXT("cliptorrect"),  
            TEXT("clip to round rect"),  
            WS_OVERLAPPEDWINDOW,     
            CW_USEDEFAULT,           
            CW_USEDEFAULT,           
            548,
            572,
            NULL,                    
            NULL,                    
            hInstance,               
            &img
        );                    

        ShowWindow(hWnd, iCmdShow);
        UpdateWindow(hWnd);

        while (GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    gdi::GdiplusShutdown(gdiplusToken);
    return msg.wParam;
}  // WinMain

LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
    WPARAM wParam, LPARAM lParam)
{
    HDC          hdc;
    PAINTSTRUCT  ps;

    switch (message)
    {
    case WM_CREATE: {
        CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lParam);
        SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG>(cs->lpCreateParams));
    } return 0;

    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        OnPaint(hdc, reinterpret_cast<gdi::Image*>(GetWindowLongPtr(hWnd, GWLP_USERDATA)));
        EndPaint(hWnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
}

上面的输出看起来像

不过,我不确定 GDi+ 中是否有办法获得抗锯齿效果。

【讨论】:

  • 我不认为你可以在这里使用抗锯齿。抗锯齿用于绘制线条。画完图像后,你可以尝试设置g.SetSmoothingMode,然后选择白笔和角落处的DrawArc。还有DrawRectangle
  • 是的,或者如果图像总是被绘制成相同的圆形矩形,您可以将抗锯齿功能刻录为白色/bkgd 颜色。
  • 实际上,这应该可以用于抗锯齿:OnPaint() { ... g.DrawImage... g.SetSmoothingMode(gdi::SmoothingModeAntiAlias); gdi::Pen pen(gdi::Color::White, 1.0F); g.DrawPath(&amp;pen, &amp;path);}(它将擦除图像周围的 1 个像素)
  • 该问题被标记为“gdi+”,因此可以合理地假设该问题与gdi plus有关。
猜你喜欢
  • 2012-04-06
  • 2021-04-03
  • 1970-01-01
  • 1970-01-01
  • 2019-01-01
  • 2011-11-28
  • 2011-08-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多