Windows api get client dc bitmap size - c++

I use the following code to get the size of the bitmap bound with Windows MFC View window's client area DC:
void CView::OnDraw(CDC* )
{
CDC *pDc = GetDC();
BITMAP bmpHeader;
memset( &bmpHeader, 0, sizeof(BITMAP));
HGDIOBJ hbmp = GetCurrentObject(pDc->m_hDC, OBJ_BITMAP);
GetObject(hbmp,sizeof(BITMAP), &bmpHeader);
int bmpWidth = bmpHeader.bmWidth;
int bmpHeight = bmpHeader.bmHeight;
...
}
According to MSDN GetDC() gets the client area dc:
Retrieves a pointer to a common, class, or private device context for the client area depending on the class style specified for the CWnd
So I suppose the bmpWidth and bmpHeight should be the same size as client area rect. But it isn't. It appears to be the size of entire window including toolbar area and menu area. Am I doing something wrong here?

Use GetClientRect to find with and height of client area. This is the area which does not include the titlebar and borders. Instead of calling GetDC(), use the CDC* parameter which is already provided, or else use CClientDC dc(this) which has automatic cleanup. In this case drawing should be something like this:
void CMyView::OnDraw(CDC* dc)
{
CRect rc;
GetClientRect(&rc);
dc->FillSolidRect(rc, RGB(0, 0, 255));
}
Use Window Functions to get information about Windows.
Most Window functions have equivalent in MFC. For example,
In WinApi: GetClientRect(HWND hwnd, LPRECT rc);
In MFC: CWnd::GetClientRect(LPRECT rc);

Related

Create CBitmap from CDC?

Using C++/MFC and GDI (not GDI+), the overall goal is to create an patterned HBRUSH, which will be used in OnCtlColor to outline an edit control in red, with the ability to turn the outline on and off. To do this, you attach a bitmap to an HBRUSH using CreatePatternBrush. Here is the code for doing that, using a stored bitmap resource:
CDialog::OnInitDialog();
BOOL ok = redBoxBitmap.LoadBitmap(MAKEINTRESOURCE(IDB_mespe_EditBox_Red));
ok = redBoxBrush.CreatePatternBrush(&redBoxBitmap);
and in OnCtlColor
HBRUSH CModelEditorSpecies::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr;
int ctrlID=pWnd->GetDlgCtrlID();
if(ctrlID==IDC_MyEditControl)
hbr=(HBRUSH) redBoxBrush;
else
hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
return hbr;
}
The above code all works as desired. However, it depends on the bitmap being sized to the edit control. What I need now is the ability to create the bitmap within the C++ program, sized to the client area of the control, which depends on both the design size of the control (in the dialog editor), and the user's setting of text size in Windows 10 settings.
I cannot find a straightforward way of either constructing a bitmap, or, better, creating an empty one of the proper size (can do), selecting it into a CDC (can do), drawing the red box into it (can do), then extracting the update bitmap from the CDC (how to do?).
Can anyone suggest either how to create the bitmap programmatically, or suggest a better method of getting an edit control boxed in red when the program calls for that?
Added in response to #Constantine Georgiou's answer of 3/9:
New code:
CBitmap redBoxBitmap; // member variables of class CModelEditorSpecies
CBrush redBoxBrush;
BOOL CModelEditorSpecies::OnInitDialog()
{
CDialog::OnInitDialog();
BOOL ok;
CRect r; defaultSpecies1Ctrl.GetClientRect(&r);
xx(r.Width(), r.Height()/*, redBoxBrush*/);
ok = redBoxBrush.CreatePatternBrush(&redBoxBitmap);
//...
}
void CModelEditorSpecies::xx(const int w, const int h)
{
CDC *pDC=GetDC();
redBoxBitmap.CreateCompatibleBitmap(pDC, w, h);
// Create a red pen
CPen redPen;
redPen.CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
// Draw the bitmap - red pen & default background brush
CBitmap *pOldBitmap=pDC->SelectObject(&redBoxBitmap);
pDC->SelectObject(&redPen);
CBrush editBoxBrush;
editBoxBrush.CreateSysColorBrush(COLOR_WINDOW);
pDC->SelectObject(&editBoxBrush);
pDC->Rectangle(0, 0, w, h);
pDC->SelectObject(pOldBitmap);
// Create the edit-control custom brush
redBoxBrush.CreatePatternBrush(&redBoxBitmap);
return;
}
This code produces an all-black edit control, as if the bitmap being used were monochrome. That would be expected if drawing in the dc does not affect the bitmap, or, if drawing in a dc-compatible bitmap does not use the colors in redPen and editBoxBrush, as suggested by #IInspectable.
Here's how to create the brush - used the Win32 functions instead of their MFC wrappers, but you can figure it out.
BOOL CModelEditorSpecies::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Get edit-control's size
RECT rc;
GetDlgItem(IDC_MyEditControl)->GetClientRect(&rc);
// Create the bitmap and a memory-DC
HDC hDC = ::GetDC(HWND_DESKTOP);
HDC mDC = CreateCompatibleDC(hDC);
HBITMAP hBmp = CreateCompatibleBitmap(hDC, rc.right, rc.bottom);
::ReleaseDC(HWND_DESKTOP, hDC);
// Create a red pen
HPEN hPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
// Draw the bitmap - red pen & default background brush
HBITMAP hOldBmp = (HBITMAP) SelectObject(mDC, hBmp);
HPEN hOldPen = (HPEN) SelectObject(mDC, hPen);
HBRUSH hOldBr = (HBRUSH) SelectObject(mDC, GetSysColorBrush(COLOR_WINDOW));
Rectangle(mDC, rc.left, rc.top, rc.right, rc.bottom);
SelectObject(mDC, hOldBr);
SelectObject(mDC, hOldPen);
SelectObject(mDC, hOldBmp);
// Create the edit-control custom brush
redBoxBrush = CreatePatternBrush(hBmp);
// Clean-up - the SysColorBrush doesn't need to be deleted
DeleteObject(hPen);
DeleteObject(hBmp);
DeleteDC(mDC);
return TRUE;
}
HBRUSH CModelEditorSpecies::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
return (nCtlColor == CTLCOLOR_EDIT && pWnd->GetDlgCtrlID() == IDC_MyEditControl) ?
redBoxBrush : CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
}
Also check the alternatives I suggested in the comments. This one will be drawing into the edit-control's client area.
EDIT:
The code I posted above does work, and after it runs all left is the HBRUSH handle (pattern-brush for your edit box). I can't see why you insist using the MFC wrappers instead. They are "higher-level" thin wrappers, so... "thin" actually, that you still have to perform almost exactly the same operations (create, select into a DC, perfrom some drawing operation, select out of the DC) as the GDI ones. The only operation you don't need to perform is delete the resource (the object's destructor will call DeleteObject() for you).
Anyways, if you prefer MFC over GDI, let's see what's wrong with your code. It has quite a few issues:
First and foremost, you need a memory DC to draw on a bitmap, the window DC you get by calling GetDC() draws on a window's surface.
The DC you get by calling GetDC() must be returned to the system (ReleaseDC()), because pDC is just a pointer, and the compiler won't call the destructor.
The objects you select into a DC must be selected out before it is destroyed, otherwise memory-leaks may be occurring.
So your code should be changed as shown below:
void CModelEditorSpecies::xx()
{
CRect r;
GetDlgItem(IDC_MyEditControl)->GetClientRect(&r);
// Create the bitmap and a memory-DC
CBitmap redBoxBitmap;
CDC mDC, *pDC = GetDC();
redBoxBitmap.CreateCompatibleBitmap(pDC, r.Width(), r.Height());
mDC.CreateCompatibleDC(pDC);
ReleaseDC(pDC);
// Create a red pen and get the default background brush
CPen redPen;
redPen.CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
CBrush editBoxBrush;
editBoxBrush.CreateSysColorBrush(COLOR_WINDOW);
// Draw the bitmap - red pen & default background brush
CBitmap *pOldBitmap = mDC.SelectObject(&redBoxBitmap);
CPen *pOldPen = mDC.SelectObject(&redPen);
CBrush *pOldBrush =mDC.SelectObject(&editBoxBrush);
mDC.Rectangle(r);
mDC.SelectObject(pOldBrush);
mDC.SelectObject(pOldPen);
mDC.SelectObject(pOldBitmap);
// Create the edit-control custom brush
redBoxBrush.CreatePatternBrush(&redBoxBitmap);
}
It's basically the same as the GDI version.

Device context size is getting reduced during drawing sometimes

We have an application in MFC.
We have come across an issue with device context.
The situation is like - we have an info display window, which is variable in size and posiion.
During some size and position changing scenarios, only part of the window is drawn, like a portion of the window is cut.
We doubted there is difference between the rect in device context and the rect returned from GetWindowRect function.
So we have logged and checked the size of the window rect which is being drawn from the device context and also
the window rect of memory DC which is used for drawing during the issue scenario.
But both returned the small window rect size.
That is device context has only the partial information of the rect at that time.
We didn't called UpdateWindow() or Invalidate().
When we focused the window using WinSpy, the whole window is present, but only that small portion is drawn.
We placed and then removed another window above this window to check whether any repainting would happen. But still the issue persists.
Can anyone please help rectify this problem?
hi, our code is like this.
BOOL InfoDisplayWindow::OnEraseBkgnd(CDC* pDC)
{
CBitmap m_bitmap; // Offscreen bitmap
CBitmap* m_oldBitmap; // bitmap originally found
CRect m_rect; // Rectangle of drawing area.
HDC hDC = CreateCompatibleDC(pDC->m_hDC);
CDC* pTmpDC = CDC::FromHandle(hDC);
pDC->GetClipBox(&m_rect);
m_bitmap.CreateCompatibleBitmap(pDC, m_rect.Width(), m_rect.Height());
m_oldBitmap = pTmpDC->SelectObject(&m_bitmap);
pTmpDC->SetWindowOrg(m_rect.left, m_rect.top);
CRect rc;
GetClientRect(&rc);
pTmpDC->FillSolidRect(&rc, COLOR_KEY);
DrawFunction();// Text is displayed in this function
CPen pen(PS_SOLID, SOLID_BORDER_WIDTH, BORDER_COLOR);
CPen *old_pen = pTmpDC->SelectObject(&pen);
// Drawing the 4 boarders of the window here.
pTmpDC->MoveTo(rc.left, rc.bottom - 1);
pTmpDC->LineTo(rc.left, rc.top);
pTmpDC->LineTo(rc.right - 1, rc.top);
pTmpDC->LineTo(rc.right - 1, rc.bottom - 1);
pTmpDC->LineTo(rc.left, rc.bottom - 1);
pTmpDC->SelectObject(old_pen);
// Copy the offscreen bitmap onto the screen.
pDC->BitBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(),
pTmpDC, m_rect.left, m_rect.top, SRCCOPY);
//Swap back the original bitmap.
pTmpDC->SelectObject(m_oldBitmap);
return TRUE;
}
I assume you get your device context (DC) either from BeginPaint (or using MFC using a CPaintDC) or from GetDC. All these variants give you the DC for your window's client area, which doesn't include the border and title bar. The corresponding rect is returned by GetClientRect.
Corresponding to GetWindowRect is GetWindowDC, which allows to draw in the full area. Be aware that GetWindowRect gives you screen coordinates, so you should transform them by ScreenToClient before applying them to your DC.

How to find CStatic (text) bounding rect of actual content?

When I have a mfc CStatic control, I can read the control's window rect by
CStatic textStatic = GetDlgItem(IDC_TEXT_STATIC);
CRect rect;
textStatic.GetWindowRect(&rect);
ScreenToClient(rect);
However, this gives me the bounding rect of the control independent from the actual displayed text. If the text is longer than this, then the text is truncated, and if the text is shorter than this, it does not give me the 'right border' of the text.
For some dynamic resizing of dialogs, I would like to find the bounding rect (or at least the width) of the actually drawn text, not the control itself.
How do I get to this (programatically)?
You can call CDC::DrawTextEx with the DT_CALCRECT formatting option (see DrawTextEx). An appropriate device context is returned from a call to CWnd::GetDC:
CRect GetTextSize(int ctrlId) {
CWnd* pControl = GetDlgItem(ctrlId);
CString text;
pControl->GetWindowText(text);
CDC* pDC = pControl->GetDC();
CRect textRect;
pDC->DrawTextEx(text, &textRect, DT_CALCRECT, NULL);
return textRect;
}
IInspectable's answer above got me started, but was missing the bit to select the appropriate font (to deal with system font DPI setting). The following method now does what I need:
CRect GetControlTextRect(CWnd *pWnd)
{
CString text;
pWnd->GetWindowText(text);
CDC* pDC = pWnd->GetDC();
CFont* pFont = pWnd->GetParent()->GetFont();
pDC->SelectObject( pFont );
CRect textRect;
pWnd->GetWindowRect(&textRect);
pDC->DrawTextEx(text, &textRect, DT_CALCRECT, NULL);
return textRect;
}
Called f.e. like here:
CWnd * txtCtrl = GetDlgItem(IDC_STATIC); // IDC_STATIC is the resource ID of the control
CRect rect = GetControlTextRect( txtCtrl );
ScreenToClient(rect);
Or alternatively like:
CStatic txtCtrl = GetDlgItem(IDC_STATIC); // IDC_STATIC is the resource ID of the control
CRect rect = GetControlTextRect( &txtCtrl );
ScreenToClient(rect);

C++ MFC: Draw a bitmap into a CFrame

I am trying to draw a bitmap (stored as an array of bytes) to a window.
Typical procedure follows:
Get the device context in the OnPaint() handler.
CPaintDC dc(this);
Create a compatible device context from it,
CDC pMemDC->CreateCompatibleDC(&dc);
Create a compatible bitmap that is the size of the client area (GetClientRect(&WinRect)).
CBitmap pNewBitmap->CreateCompatibleBitmap(&dc, WinRect.Width(), WinRect.Height());
Now, if the window client size is exactly the same size as the bitmap, I can simply make a call to pNewBitmap->SetBitmapBits to "feed" my array into the bitmap.
Follow this with a BitBlt and the bitmap appears on the window.
dc->BitBlt(0, 0, WinRect.Width(), WinRect.Height(), pMemDC, 0, 0, SRCCOPY);
If you want the window size to vary independent of the image size, then first you must make sure pNewBitmap is the right size (the client rectangle), but now you cannot simply shove the array into the bitmap.
In this situation, I have found a solution by repeating the above procedure to create a bitmap the exact size of the image so I can "shove" my bits into it, and then BitBlt that into the larger, client-sized bitmap, which then gets BitBlt'ed into the window.
Is there another way to do this? Actual code follows.
void CAnimateWnd::OnPaint()
{
CPaintDC dc(this); // device context for painting
DrawScene(&dc);
// Do not call CWnd::OnPaint() for painting messages
}
void CAnimateWnd::DrawScene(CDC *pDrawDC)
{
CRect WinRect;
GetClientRect(&WinRect);
if (pNewBitmap == NULL)
{
pMemDC = new CDC();
pMemDC->CreateCompatibleDC(pDrawDC);
pNewBitmap = new CBitmap();
pNewBitmap->CreateCompatibleBitmap(pDrawDC, WinRect.Width(), WinRect.Height());
pMemDC->SelectObject(pNewBitmap);
}
CRect BMPRect;
GetBitmapDrawSize(&BMPRect);
if (BMPRect != NULL)
{
if (!(BMPRect.Width() >= WinRect.Width() && BMPRect.Height() >= WinRect.Height()))
{
//The bitmap is smaller than the window, so fill the window with the background color.
CBrush BackBrush(BackGndColor);
CPen BackPen(PS_SOLID, 1, BackGndColor);
pMemDC->SelectObject(&BackBrush);
pMemDC->SelectObject(&BackPen);
pMemDC->Rectangle(&WinRect);
BackBrush.DeleteObject();
BackPen.DeleteObject();
}
}
OverrideAndDrawInHere(pMemDC, resize);
pDrawDC->BitBlt(0,0,WinRect.right,WinRect.bottom,pMemDC,0,0,SRCCOPY);
}
template <class T>
void ImageWindow<T>::OverrideAndDrawInHere(CDC *pDC, int resize)
{
if (this->sourceImage == NULL) return;
CRect clientRect;
GetClientRect(&clientRect);
if (this->dispBMP == NULL)
{
this->dispDC = new CDC();
this->dispDC->CreateCompatibleDC(pDC);
this->dispBMP = new CBitmap();
this->dispBMP->CreateCompatibleBitmap(pDC, this->sourceImage->GetWidth(), this->sourceImage->GetHeight());
this->dispDC->SelectObject(this->dispBMP);
}
this->dispBMP->SetBitmapBits(this->sourceImage->GetArea() * 4, this->translatedImage);
pDC->BitBlt(0, 0, this->sourceImage->GetWidth(), this->sourceImage->GetHeight(), this->dispDC, 0, 0, SRCCOPY);
}
Instead of using BitBlt try StretchBlt if you want to paint a bitmap to a window that isn't the same size as source image:
From MSDN:
Copies a bitmap from a source rectangle into a destination rectangle,
stretching or compressing the bitmap if necessary to fit the
dimensions of the destination rectangle.
If you want the same results as you've shown then you're probably doing it the best way. You could use something like FillRect to draw directly to the Frame (or handle OnEraseBkgnd) and then BitBlt the original sized image but you'd get probably get some flickering, which your double-buffered solution solves.

How to find out DC's dimensions?

Let's say I have a handle to device context (naturally, in Windows environment):
HDC hdc;
How can I get the width and height of it?
A device context (DC) is a structure that defines a set of graphic objects and their associated attributes, and the graphic modes that affect output.
By width and height I'm guessing you are referring to the bitmap painted ?
If so then i guess you can try the following :
BITMAP structBitmapHeader;
memset( &structBitmapHeader, 0, sizeof(BITMAP) );
HGDIOBJ hBitmap = GetCurrentObject(hDC, OBJ_BITMAP);
GetObject(hBitmap, sizeof(BITMAP), &structBitmapHeader);
//structBitmapHeader.bmWidth
//structBitmapHeader.bmHeight
I also know little about GDI, but it seems GetDeviceCaps might do the trick.
This simple piece of code I use always to get the dimensions of the rendering area, when I have only the HDC.
First, you must get a HWND from the HDC - is simple, then you can get the client rect of this HWND:
RECT rcCli;
GetClientRect(WindowFromDC(hdc), &rcCli);
// then you might have:
nWidth = rcCli.right-rcCli.left;
nHeight = rcCli.bottom-rcCli.top;
As a disclaimer, I know nothing about GDI or what you have to work with in your application. I'm just trying to be helpful if possible.
That said, I found a link which seems to suggest that it's appropriate to use GetClientRect to get the size of the drawing area:
RECT clientRect;
GetClientRect(hWnd,&clientRect);
http://www.toymaker.info/Games/html/gdi.html#winsize
You could WindowFromDC(...) to get the DC's window if it's associated with a window. You could then use #KevinK's answer to get the client rect from this.
but if get Calculator' window_dc dimension, it will failed at “GetCurrentObject” or "GetObject", i think maybe the window attribute include "ws_ex_noredirectionbitmap", how to get dismension in this case?
HDC win_dc = ::GetWindowDC(hwnd);
BITMAP bm = { 0 };
HGDIOBJ hBitmap = GetCurrentObject(win_dc, OBJ_BITMAP);
if (hBitmap)
{
if (GetObject(hBitmap, sizeof(BITMAP), &bm))
{
windc_dimension.cx = bm.bmWidth;
windc_dimension.cy = bm.bmHeight;
}
}