(I wrote this answer and thought the OP wanted Gdiplus for some reason ... regular GDI is pretty much the same thing although there actually is a CreatRoundRectRgn(...) in that case)
You can set the clipping region of a Gdiplus::Graphics
object with its SetClip
member function which takes a Region. You can create a Region
from a Path
but there is apparently no out-of-the-box way in Gdi+ of creating a round rectangle shaped path so you have to do it manually. There is an answer on StackOverflow about doing this in C#, and another one that ports it to C++/Win32 here.
Code below:
#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);
}
}
The output of the above looks like

I'm not sure if there is a way in GDi+ to get that anti-aliased or not, however.