How D2D implement scroll screen function? - c++

I tried to use Direct2D in our map application. But it is much more slower than GDI because D2D draw the whole screen.
When user scroll the window, GDI scroll the video buffer and ONLY draw a small portion of screen. That is why GDI is faster than D2D.
My questions are:
How D2D scroll screen? (Similar to ScrollWindow())
How I only draw a small portion of screen in D2D? (Similar to InvalidateRect() and GetClipBox())
I found a solution in
How to scroll window contents using Direct2D api?
Does anyone have a better solution?
My example is:
D2D1::Matrix3x2F matrix;
CPoint org(GetDeviceScrollPosition());
FLOAT scaleW=(FLOAT)m_totalLog.cx/m_totalDev.cx;
FLOAT scaleH=(FLOAT)m_totalLog.cy/m_totalDev.cy;
if(scaleW > scaleH) scaleW = scaleH;
matrix.SetProduct(D2D1::Matrix3x2F::Scale(scaleW, scaleH, D2D1::Point2F(0.0f, 0.0f)), D2D1::Matrix3x2F::Translation((FLOAT)-org.x, (FLOAT)-org.y));
//"Scale Device Screen" * "Scroll Device Screen"
pRenderTarget->SetTransform(matrix);
in CScrollView. But the performance is very poor.

I found one solution: Using "CDCRenderTarget". Then we can use the scroll/GetClipBox() functions in CScrollView.
void CMFCGdi_D2D2View::OnDraw(CDC* pDC)
{
CMFCGdi_D2D2Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc) return;
// TODO: add draw code for native data here
#define LINE_WIDTH 3
CRect rcInvalid;
pDC->GetClipBox(rcInvalid);
if(rcInvalid.left==0 && rcInvalid.top==0) pDC->FillSolidRect(rcInvalid, RGB(255,0,0));
else pDC->FillSolidRect(rcInvalid, RGB(0, 255, 0)); //visible redraw area
CRect rcLogical;
GetClientRect(rcLogical);
rcLogical.bottom+=GetScrollPosition().y;
rcLogical.right+=GetScrollPosition().x;
m_RenderTarget.BindDC(*pDC, rcLogical);
m_RenderTarget.BeginDraw();
m_RenderTarget.SetTransform(D2D1::Matrix3x2F::Identity());
m_RenderTarget.DrawEllipse(CD2DEllipse(CD2DPointF(150.0f, 150.0f), CD2DSizeF(100., 100.)), m_pBlackBrush, LINE_WIDTH);
m_RenderTarget.DrawLine(CD2DPointF(150.0f, 150.0f), CD2DPointF(150.0f + 100.0f * 0.15425f, 150.0f - 100.0f * 0.988f),
m_pBlackBrush, LINE_WIDTH);
m_RenderTarget.DrawLine(CD2DPointF(150.0f, 150.0f), CD2DPointF(150.0f + 100.0f * 0.525f, 150.0f + 100.0f * 0.8509f),
m_pBlackBrush, LINE_WIDTH);
m_RenderTarget.DrawLine(CD2DPointF(150.0f, 150.0f), CD2DPointF(150.0f - 100.0f * 0.988f, 150.0f - 100.0f * 0.15425f),
m_pBlackBrush, LINE_WIDTH);
HRESULT hr = m_RenderTarget.EndDraw();
if (SUCCEEDED(hr))
{
// Draw the pie chart with GDI.
CPen penBlack(PS_SOLID, LINE_WIDTH, RGB(255, 0, 0));
CPen* pOldPen = pDC->SelectObject(&penBlack);
pDC->Ellipse(300, 50, 500, 250);
POINT pntArray1[2];
pntArray1[0].x = 400;
pntArray1[0].y = 150;
pntArray1[1].x = static_cast<LONG>(400 + 100 * 0.15425);
pntArray1[1].y = static_cast<LONG>(150 - 100 * 0.9885);
POINT pntArray2[2];
pntArray2[0].x = 400;
pntArray2[0].y = 150;
pntArray2[1].x = static_cast<LONG>(400 + 100 * 0.525);
pntArray2[1].y = static_cast<LONG>(150 + 100 * 0.8509);
POINT pntArray3[2];
pntArray3[0].x = 400;
pntArray3[0].y = 150;
pntArray3[1].x = static_cast<LONG>(400 - 100 * 0.988);
pntArray3[1].y = static_cast<LONG>(150 - 100 * 0.15425);
pDC->Polyline(pntArray1, 2);
pDC->Polyline(pntArray2, 2);
pDC->Polyline(pntArray3, 2);
pDC->SelectObject(&pOldPen);
}
}

Related

MFC CScrollView does not clear background

My English is not perfect. I am using Visual C++ 2019 and MFC. Example program: an SDI program, the base of the view is CScrollView, draws 128*128 0s in a matrix. But MFC does not clear the background at scrolling with the scrollbar. Have you an idea? Thank you.
In settings of Windows, I am using 96 dpi * 3 = 288 dpi.
I tried: 96 dpi mode is affected so.
How can I upload the example program to this?
void CsdView::OnDraw(CDC *pDC) {
CsdDoc *pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
CRect rect;
GetClientRect(&rect);
pDC->FillSolidRect(rect, 0xFFFFFF);
CPoint pos = GetDeviceScrollPosition();
TRACE(L"OnDraw: %4u.%4u - %4u.%4u, %4u.%4u\n", rect.right, rect.bottom, pos.x, pos.y, rect.right + pos.x, rect.bottom+pos.y);
for (int i = 0; i < 128; ++ i)
for (int j = 0; j < 128; ++ j)
pDC->TextOutW(j*20 - pos.x, i*54 - pos.y, L"0", 1);
}
void CsdView::OnInitialUpdate() {
CScrollView::OnInitialUpdate();
CSize sizeTotal;
sizeTotal.cx = 20*128;
sizeTotal.cy = 54*128;
SetScrollSizes(MM_TEXT, sizeTotal);
}
BOOL CsdView::OnEraseBkgnd(CDC *pDC) {
CBrush brush(0xFFFFFF);
FillOutsideRect(pDC, &brush);
return TRUE;
// return CScrollView::OnEraseBkgnd(pDC);
}
I can not upload picture and code as a comment, so I must edit the original question.
A little bug is remained. The orginal code (MDI MFC):
void CIDEView::OnDraw(CDC *pDC) {
CIDEDoc *const d = GetDocument();
ASSERT_VALID(d);
if (! d)
return;
CPoint const pos = GetDeviceScrollPosition();
CRect rect;
GetClientRect(&rect);
OffsetRect(&rect, pos.x, pos.y);
pDC->FillSolidRect(rect, bkcolor);
auto oldfont = pDC->SelectObject(&font);
pDC->SetBkColor(bkcolor);
pDC->SetTextColor(textcolor);
pDC->SetBkMode(TRANSPARENT);
const int cxs = pos.x / mincw, cys = pos.y / lineheight;
const int cxe = (rect.right + mincw-1) / mincw,
cye = (rect.bottom + 41) / lineheight;
for (int i = cys; i <= cye; ++ i)
for (int j = cxs; j <= cxe; ++ j)
pDC->TextOutW(textmargin+j*mincw, i*lineheight, L"0", 1);
pDC->SelectObject(oldfont);
}
void CIDEView::OnInitialUpdate() {
CScrollView::OnInitialUpdate();
SetScrollSizes(MM_TEXT, {linewidth, 128*lineheight});
}
BOOL CIDEView::OnEraseBkgnd(CDC *pDC) {
return TRUE;
}
The CScrollView class is a view with scrolling capabilities. You use it almost like a CView (ie drawing in the OnDraw() member), only you have to take into account the possible scrolling.
The GetClientRect() function returns the visible client area, and the coordinates returned are not relative to the view origin, but to the window origin, ie the left and top members are always 0. The CDC parameter in the OnDraw() function though are relative to the logical view origin, so an adjustment is needed.
As for your code, you don't need to use the OnEraseBkgnd() function, because you do so in OnDraw(). You fill only the visible part of the window, but that's very much OK. So it would best to remove it. Also, the coordinates passed to the TextOutW() function must be relative to the view origin, so the -pos.x and -pos.y adjustments are wrong. Instead, it's the rectanlge passed to the FillSolidRect() function that needs to be adjusted. So, your code would become:
void CsdView::OnDraw(CDC *pDC)
{
CsdDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
CPoint pos = GetScrollPosition();
CRect rect;
GetClientRect(&rect);
// Adjust client rect to device coordinates
OffsetRect(&rect, pos.x, pos.y);
pDC->FillSolidRect(rect, 0xFFFFFF);
for (int i = 0; i < 128; ++i)
for (int j = 0; j < 128; ++j)
pDC->TextOutW(j * 20, i * 54, L"0", 1);
}
However this code is "wasteful", as it paints the whole view. It can be optimized to paint only the visible part. It will draw only the 0s for which even one pixel lies in the visible part (didn't #define anything, just used your hard-coded 20 and 54 values). Also changed the color to yellow, so you can test it better (white is the default background color).
void CsdView::OnDraw(CDC *pDC)
{
CsdDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
CPoint pos = GetScrollPosition();
CRect rect;
GetClientRect(&rect);
// Adjust client rect to device coordinates
OffsetRect(&rect, pos.x, pos.y);
pDC->FillSolidRect(rect, 0x00FFFF);
// Paint only the items in the visible part
int xL = rect.left / 20,
xR = (rect.right + 19) / 20,
yT = rect.top / 54,
yB = (rect.bottom + 53) / 54;
for (int i = yT; i < yB; ++i)
for (int j = xL; j < xR; ++j)
pDC->TextOutW(j * 20, i * 54, L"0", 1);
}
EDIT:
In the revised code, why are the doc, pos, cxs etc variables const? There are also quite a few bugs in your logic:
You set your view size in OnInitialUpdate(), rather assuming that linewidth equals to textmargin + 128*mincw. If not, revise your code again.
The rect is already adjusted (using OffsetRect()), so it is wrong to add pos.x again.
Since you have the cell sizes in variables, don't use hard-coded numbers. For example the code for cxe should become cxe = (rect.right + mincw - 1) / mincw; Update the cye code similarly.
Also you paint at an offset of textmargin. The code should then become cxe = (rect.right - textmargin + mincw - 1) / mincw;
The code I posted works OK with the < condition in the loops, you don't need <=. Do the math and you will find that this is the correct one.

D2D coordinates not matching screen coordinates

I am using D2D with D3D11. I have some code that uses GetCursorpos() from the windows API which is then converted to client coordinates and then draws a small circle at this position using D2D FillEllipse(). The screen to client coordinates work perfectly but for some reason D2D draws the circle a small distance from the expected location (tens of pixels) as if the coordinate had been scaled by a small factor so that the error gets larger as the circle is drawn further from (0, 0).
I noticed changing the dpi for the D2D1_RENDER_TARGET_PROPERTIES affects this 'scaling' so I suspect the problem has something to do with dpi. This is the code for creating the D2D render target from the DXGI surface I obtained from the swapchain in my D3D11 code.
// Create render target
float dpiX, dpiY;
this->factory->GetDesktopDpi(&dpiX, &dpiY);
D2D1_RENDER_TARGET_PROPERTIES rtDesc = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_HARDWARE,
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
dpiX,
dpiY
);
AssertHResult(this->factory->CreateDxgiSurfaceRenderTarget(
surface.Get(),
&rtDesc,
&this->renderTarget
), "Failed to create D2D render target");
Here, dpiX and dpiY become 96 which I notice is also the constant that GetDpiForWindow() from the windows API returns when it is not dpi aware.
I want to know how I can fix my code so that it will draw the circle at the position given by GetCursorPos().
More relevant code:
Driver code
Vector3f cursPos = input.GetCursorPos();
DrawCircle(Colour::Green, cursPos.x, cursPos.y, 3/*radius*/);
Input
POINT pt{};
::GetCursorPos(&pt);
// Convert from screen pixels to client pixels
return ConvertPixelSpace(this->hWnd, (float)pt.x, (float)pt.x, PixelSpace::Screen, PixelSpace::Client);
Direct2D
void DrawCircle(const Colour& c, float centreX, float centreY, float radius, PixelSpace ps)
{
Vector3f centre = ConvertPixelSpace(this->gfx.hWnd, centreX, centreY, ps, PixelSpace::Client);
centreX = centre.x;
centreY = centre.y;
D2D1_ELLIPSE el{};
el.point.x = centreX;
el.point.y = centreY;
el.radiusX = radius;
el.radiusY = radius;
auto brush = this->CreateBrush(c);
this->renderTarget->FillEllipse(
&el,
brush.Get()
);
}
PixelSpace Conversion
Vector3f ConvertPixelSpace(HWND hWnd, float x, float y, PixelSpace curSpace, PixelSpace newSpace)
{
RECT rc = GetClientRectOfWindow(hWnd);
struct
{
float top, left, width, height;
} rectf;
rectf.top = static_cast<float>(rc.top);
rectf.left = static_cast<float>(rc.left);
rectf.width = static_cast<float>(rc.right - rc.left);
rectf.height = static_cast<float>(rc.bottom - rc.top);
// Convert to client space
if (curSpace == PixelSpace::Screen)
{
x -= rectf.left;
y -= rectf.top;
}
// Convert to new space
if (newSpace == PixelSpace::Screen)
{
x += rectf.left;
y += rectf.top;
}
return Vector3f(x, y);
}
RECT GetClientRectOfWindow(HWND hWnd)
{
RECT rc;
::GetClientRect(hWnd, &rc);
// Pretty sure these are valid casts.
// rc.top is stored directly after rc.left and this forms a POINT struct
ClientToScreen(hWnd, reinterpret_cast<POINT*>(&rc.left));
ClientToScreen(hWnd, reinterpret_cast<POINT*>(&rc.right));
return rc;
}
The problem was I was creating the D3D11 swapchain with the window area instead of the client area.
RECT rect{};
GetWindowRect(hWnd, &rect); // !!! This should be GetClientRect()
this->width = rect.right - rect.left;
this->height = rect.bottom - rect.top;
DXGI_SWAP_CHAIN_DESC scDesc{};
scDesc.BufferDesc.Width = width;
scDesc.BufferDesc.Height = height;
//...

Win32 MFC OnPaint Resizing and Redrawing handling

how to handle this problem everytime i resize my drawing seems to not draw right.
i think i need to call Invalidate() everytime i resize the window but doesnt WM_PAINT automatically called every time i move or resize the window?
CPaintDC dc(this); // device context for painting
CRect rect;
GetClientRect(rect);
if (IsIconic())
{
//CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
for(int ndx(0); ndx < rect.Width() - 150; ndx += 10)
{
dc.MoveTo( rect.left + 50, rect.bottom / 2 );
dc.LineTo( rect.left + 50 + ndx, rect.bottom / 2 );
}
CBrush mybrush(RGB(30,30,30));
dc.FillRect( CRect(rect.left + 10, rect.top + 10, rect.Width() / 4, rect.Height() / 4),&mybrush );
CDialogEx::OnPaint();
}
Before Resizing:
After Resizing:
When you start painting and use CPaintDC it doesn't make sense to call the base class that tries this again and may again erase the background...
What happens.
Your CPaintDC ist created
BeginPaint is called and WM_ERASEBKGND is sent.
You paint your stuff.
You call the base class anbd a new CPaintDC calles BeginPaint.
Because EndPaint isn't called the paint area isn't validated. So BeginPaint is executed and the WM_ERASEBKGND is called again.
Finally the CPaintDC's destructors are called and the client area is validated.
Never call the OnPaint baseclass function if you start using CPaintDC/BeginPaint!
I do not know if this is the optimal solution.
In your paint code, before painting, fill the entire client area with the background color:
dc.FillSolidRect(&rect, ...);
Remove CDialogEx::OnPaint().
Add an OnEraseBkgnd handler:
BEGIN_MESSAGE_MAP(CMFCApplication1Dlg, CDialogEx)
//...
ON_WM_ERASEBKGND()
END_MESSAGE_MAP()
//...
BOOL OnEraseBkgnd(CDC * pDC)
{
RedrawWindow();
return TRUE;
}
If the dialog is flickering, use MFC's CMemDC (undocumented).
this is fixes my problem
step 1: BeginPaint()
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CTestDrawDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect rect;
GetClientRect(rect);
// draw background manually
EraseBkgnd(&dc);
if (IsIconic())
{
//CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
for(int ndx(0); ndx < rect.Width() - 150; ndx += 10)
{
dc.MoveTo( rect.left + 50, rect.bottom / 2 );
dc.LineTo( rect.left + 50 + ndx, rect.bottom / 2 );
}
CBrush mybrush(RGB(30,30,30));
dc.FillRect( CRect(rect.left + 10, rect.top + 10, rect.Width() / 4, rect.Height() / 4),&mybrush );
}
}
STEP: 2 RedrawWindow() on WM_ERASEBACKGND
BOOL CTestDrawDlg::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
RedrawWindow();
return TRUE;
}
and Manually draw the background
void CTestDrawDlg::EraseBkgnd( CDC * pDC )
{
CRect rect;
GetClientRect(rect);
pDC->FillSolidRect(rect, RGB(255,255,255));
// paint whatever you want in the background
}
STEP 3: EndPaint()

How do I scale a rectangle to another rectangle (such as a picture to a window) preserving aspect ratio with the option to fill?

I'm putting this here because the algorithm for doing this is more difficult to find than it should be. Hopefully Google will cache this.
The problem is: you have a bitmap and a window. You want to draw the bitmap inside a window, filling the window, keeping the aspect ratio, as the window resizes.
You may also want to be able to fit it the other way, so that you can draw the image "over" the window, and all the area in the window will be filled. This will clip out some of the image. I present in the answer a simple algorithm for doing so.
Here's an implementation that uses integer math only.
The algorithm first stretches both dimensions, preserving aspect ratio. The new size is calculated, assuming that the respective other dimension occupies the entire space. Of these new dimensions, the one that overshoots the available area is set to the maximum possible value, while the other is scaled back, preserving aspect ratio. (For pan and scan (bScale is set to true) mode, the dimension that doesn't overshoot the available space is set to occupy the entire range.)
(Note: If sizePicture is an empty rectangle, this function returns a rectangle that stretches one pixel to the left and one pixel up, either from the origin, or the center.)
RECT size_rect( RECT& rcScreen,
RECT& sizePicture,
bool bCenter/*,
bool bScale*/ ) {
int clientWidth = rcScreen.right - rcScreen.left;
int clientHeight = rcScreen.bottom - rcScreen.top;
int picWidth = sizePicture.right - sizePicture.left;
int picHeight = sizePicture.bottom - sizePicture.top;
// Calculate new content size
int contentWidth = ::MulDiv( clientHeight, picWidth, picHeight );
int contentHeight = ::MulDiv( clientWidth, picHeight, picWidth );
// Adjust dimensions to fit inside client area
if ( contentWidth > clientWidth ) {
// To use the bScale parameter that allows the image to fill the entire
// client area, use the following if-clause instead.
//if ( ( bScale && ( contentWidth < clientWidth ) )
// || ( !bScale && ( contentWidth > clientWidth ) ) ) {
contentWidth = clientWidth;
contentHeight = ::MulDiv( contentWidth, picHeight, picWidth );
} else {
contentHeight = clientHeight;
contentWidth = ::MulDiv( contentHeight, picWidth, picHeight );
}
RECT rect = { 0 };
::SetRect( &rect, 0, 0, contentWidth, contentHeight );
if ( bCenter ) {
// Calculate offsets to center content
int offsetX = ( clientWidth - contentWidth ) / 2;
int offsetY = ( clientHeight - contentHeight ) / 2;
::OffsetRect( &rect, offsetX, offsetY );
}
return rect;
}
Make two RECT. One is the window you wish to fit to (passed into rcScreen), and the other holds the dimensions of the picture:
(pseudo-code)
RECT window;
GetClientRect(hwnd,&window)
RECT bitmap_rect;
BITMAP bitmap;
bitmap_rect.left = bitmap_rect.top = 0;
bitmap_rect.right = bitmap.bmWidth;
bitmap_rect.bottom = bitmap.bmHeight;
RECT draw_rect = size_rect(window,bitmap_rect,true,true);
Then StretchBlt it:
StretchBlt(toDC, draw_rect.left, draw_rect.top, draw_rect.right, draw_rect.bottom, fromDC, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY);
This is the function: (note there is no case for bCenter = false and Scale = true). **bCenter is flag for "center picture in window." Scale is flag for "pan and scan mode" instead of "letterbox," useful if you are using an image as a window background that you want resized but don't want to have letterboxes. **
RECT size_rect(RECT& rcScreen,
RECT& sizePicture,
bool bCenter,
bool Scale)
{
RECT rect = rcScreen;
double dWidth = rcScreen.right - rcScreen.left;
double dHeight = rcScreen.bottom - rcScreen.top;
double dAspectRatio = dWidth / dHeight;
double dPictureWidth = sizePicture.right - sizePicture.left;
double dPictureHeight = sizePicture.bottom - sizePicture.top;
double dPictureAspectRatio = dPictureWidth / dPictureHeight;
double nNewHeight = dHeight;
double nNewWidth = dWidth;
double nHeightCenteringFactor = 0;
double nWidthCenteringFactor = 0;
double xstart = rcScreen.left;
double ystart = rcScreen.top;
if (dPictureAspectRatio > dAspectRatio)
{
if (bCenter && Scale) {
nNewWidth = dPictureWidth*(1 / (dPictureHeight / dHeight));
xstart = rcScreen.left - ((nNewWidth / 2) - (dWidth / 2));
}
else {
nNewHeight = (int)(dWidth / dPictureWidth*dPictureHeight);
if (bCenter)
ystart = ((dHeight - nNewHeight) / 2) + rcScreen.top;
}
}
else if (dPictureAspectRatio < dAspectRatio)
{
if (bCenter && Scale) {
nNewHeight = dPictureHeight*(1 / (dPictureWidth / dWidth));
ystart = rcScreen.top - ((nNewHeight / 2) - (dHeight / 2));
}
else{
nNewWidth = (dHeight / dPictureHeight*dPictureWidth);
if (bCenter)
xstart = ((dWidth - nNewWidth) / 2) + rcScreen.left;
}
}
SetRect(&rect, xstart, ystart, nNewWidth, nNewHeight);
return rect;
}

MFC: Draw dialog border using GDI+

I modified my dialog to a polygon region dialog. Then i am trying to frame/draw the border.Using device context the CRgn::FrameRgn, i am bale to draw the border around the dialog. But i want to achieve this using Gdi+. I did as below, but the border is appearing only on left and top of the dialog.
Can someone please help on this.
CPoint vertex[4];
BOOL CPolygonDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
ModifyStyle(WS_CAPTION,0);
ModifyStyle(WS_BORDER,0);
CRect rect(400,200,900,700);
CRect wr = rect;
AdjustWindowRect( wr, 0, FALSE );
MoveWindow(wr);
GetClientRect( rect );
CRect csr = rect;
ClientToScreen( csr );
vertex[0] = CPoint(rect.left,rect.top);
vertex[1] = CPoint(rect.right,rect.top);
vertex[2] = CPoint(rect.right,rect.bottom);
vertex[3] = CPoint(rect.left,rect.bottom);
m_rgnShape.CreatePolygonRgn( vertex, 4, ALTERNATE );
m_rgnShape.OffsetRgn( CPoint( csr.TopLeft() - wr.TopLeft() ) );
SetWindowRgn( (HRGN)m_rgnShape.Detach(), TRUE );
m_rgnShape.CreatePolygonRgn( vertex, 4, ALTERNATE );
return TRUE; // return TRUE unless you set the focus to a control
}
void CPolygonDlg::OnPaint()
{
Graphics graphics(dc.m_hDC);
CRect rect;
GetClientRect(rect);
GraphicsPath gp;
Point point[4];
point[0] = Point(rect.left,rect.top);
point[1] = Point(rect.right,rect.top);
point[2] = Point(rect.right,rect.bottom);
point[3] = Point(rect.left,rect.bottom);
gp.AddPolygon(point,4);
Pen pen(Color(255, 255, 0, 0));
graphics.DrawPath(&pen, &gp);
}
Thanks
When you call GetClientRect(), it returns the size of the client area of the window - the part you can easily draw on, and the part that is controlled by the device context when you do CPaintDC dc(this); in your OnPaint() method.
The problem you are facing is that your dialog window has a border and you need to handle WM_NCPAINT in order to draw on border area.