MFC GDI+ Text rendering issue - c++

I'm currently using 5MP Camera, so I convert BYTE* to GDI+ Bitmap object and uses Graphics object to draw on picture control (all GDI+ objects)
and I want to draw a string on it and when I do so, resolution (quality or whatsoever) gets strange. here're the images.
this is the original image
this is the image with the text on it
And here's my code. it uses MFC's WM_MOUSEMOVE. and when mouse pointer gets on CRect(dispRC[array]), it renders string "aa" on the Bitmap object.
and when I do so, quality of image gets lower or I don't exactly know it changes the IMAGE. (You might not notice because those are captured images, but latter image's quality gets lower.)
void CSmall_StudioDlg::OnMouseMove(UINT nFlags, CPoint point)
{
CPoint insidePoint;
// MAXCAM is the number of bitmap objects.
for (int i = 0; i < MAXCAM; i++)
{
// m_pBitmap[MAXCAM] is array of Bitmap* which contains address of Gdiplus::Bitmap objects.
if (m_pBitmap[i] != NULL)
{
// m_rcDisp[MAXCAM] are CRect objects which has information of picture control.
// i.e. GetDlgItem(IDC_BIN_DISP)->GetWindowRect(m_rcDisp[BINARY_VID]);
if (point.x > m_rcDisp[i].TopLeft().x && point.y > m_rcDisp[i].TopLeft().y)
{
if (point.x < m_rcDisp[i].BottomRight().x && point.y < m_rcDisp[i].BottomRight().y)
{
StringFormat SF;
insidePoint.x = point.x - m_rcDisp[i].TopLeft().x;
insidePoint.y = point.y - m_rcDisp[i].TopLeft().y;
Graphics textG(m_pBitmap[i]);
textG.SetTextRenderingHint(TextRenderingHintSingleBitPerPixel);
Gdiplus::Font F(L"Palatino Linotype Bold", 10, FontStyleBold, UnitPixel);
RectF R(insidePoint.x, insidePoint.y, 20, 100);
SF.SetAlignment(StringAlignmentCenter);
SF.SetLineAlignment(StringAlignmentCenter);
SolidBrush B(Color(0, 0, 0));
textG.DrawString(_T("aa"), -1, &F, R, &SF, &B);
// m_pGraphics[MAXCAM] is made like this
// i.e.
// static CClientDC roiDc(GetDlgItem(IDC_ROI_DISP));
// m_hDC[ROI_VID] = roiDc.GetSafeHdc();
// m_pGraphics[ROI_VID] = Graphics::FromHDC(m_hDC[ROI_VID]);
m_pGraphics[i]->DrawImage(m_pBitmap[i], 0, 0, m_vidwidth[i], m_vidheight[i]);
}
}
}
}
CDialogEx::OnMouseMove(nFlags, point);
}
Hope I get a helpful answer.
Thanks!

I had a similar problem and solved it by creating the GDI+ font from a Windows font like this:
Gdiplus::Font font(hDc, hFont);
where hDc is a DC handle and hFont is a font handle.
Can you try if this helps?

Related

How to drag a rectangle using GDI+?

I am trying to implement a dragging of a simple rectangle using GDI+. My code is simply working by translating the graphics object to the new position and to draw a new Rectangle.
TransllateRectangle is called frequently whenever OnMouseMove event occur.
void RectangleContainer::TranslateRectangle(CDC *pDC, CRect newR, CRect oldR)
{
Graphics graphics(pDC->m_hDC);
DWORD ole = m_FillColor;
BYTE rr = ((BYTE)(ole));
BYTE gg = ((BYTE)(((WORD)(ole)) >> 8));
BYTE bb = ((BYTE)((ole) >> 16));
SolidBrush linGrBrush(
Color(125, rr, gg, bb)
);
Rect newRectangle = Rect(newR.left, newR.top, newR.Width(), newR.Height());
Matrix matrix;
matrix.Translate(newR.left - oldR.left, 0);
graphics.SetTransform(&matrix);
graphics.FillRectangle(&linGrBrush, newRectangle);
graphics.~Graphics();
}
My problem is that I could not remove old rectangle drawn in the old position properly. How can I achieve this please?

CClientDC and DC not Drawing on ChtmlEditCtrl

Hi all I am working with a CHtmlEditCtrl in MFC. I want to draw some random rectangles and lines inside a function handling right click event.
The ChtmlEditCtrl control is created from static using this snippet:
bool CHtmlEditCtrlEx::CreateFromStatic( UINT nID, CWnd* pParent ) {
CStatic wndStatic;
if ( !wndStatic.SubclassDlgItem(nID, pParent)) {
return false;
}
CRect rc;
wndStatic.GetWindowRect( &rc );
pParent->ScreenToClient( &rc );
if (Create( 0, (WS_CHILD | WS_VISIBLE), rc, pParent, nID, 0 )) {
...
}
Then I override the CWnd::pretranslate() function as thus:
CClientDC dcc(this);
switch (pMsg->message) {
case WM_RBUTTONUP: // Right-click
// Just some dummy values
DrawSquigly(dcc, 600, 240, 20);
break;
}
the DrawSquigly() function is defined as thus:
void CHtmlEditCtrlEx::DrawSquigly(CDC &dcc, int iLeftX, int iWidth, int iY)
{
CAMTrace trace;
trace.Trace("Drawing Squiggly");
//dcc.TextOut(10, 10, CString(_T("I used a client DC!")));
CPen * oldPen;
CBrush * oldBrush;
oldPen = (CPen *) dc.SelectStockObject(WHITE_PEN);
dcc.MoveTo(5,10);
dcc.LineTo(80, 10);
dcc.SelectObject(oldPen);
//GDI 002_2: Create custom pen with different Line thickness.
CPen thick_pen(PS_SOLID, 3, RGB(0,255,0));
oldPen = dc.SelectObject(&thick_pen);
dcc.MoveTo(5, 20);
dcc.LineTo(80,20);
dcc.SelectObject(oldPen);
//GDI 002_3: Create a Rectangle now
dcc.Draw3dRect(5,30,80,70, RGB(25,25,255), RGB(120,120,120));
//GDI 002_4: Create a Brush that we can use for filling the
// closed surfaces
CBrush brush(RGB(255,0,255));
oldBrush = dc.SelectObject(&brush);
dcc.Rectangle(5,110,80,140);
dcc.SelectObject(oldBrush);
//GDI 002_5: Hatch Brush is useful to apply a pattern in stead
//of solid fill color
CBrush* hatBrush = new CBrush();
hatBrush->CreateHatchBrush(HS_CROSS, RGB(255,0,255));
oldBrush = dc.SelectObject(hatBrush);
dcc.FillRect(new CRect(5,160,80,190), hatBrush);
dcc.SelectObject(oldBrush);
}
but no drawing happens when I right click. I think I am missing something especially because I am new to MFC.
I have added a trace to the top of the event handler to be sure that the function is getting called and it is.
Can anyone please point me the right direction?
There are actually 2 device context in your code: one you pass as parameter in the call (we don't know where it comes from) and the other created locally in the drawing function.
Normally, when the systems gives you a DC it expects you draw something in it, not that you draw into something else.
If the window you are working on is layered, the system gives you a memory context you draw in that is - upon clearing - blit-ted onto the window itself with some window manager effect.
My suspect is that -by allocating a second dc- your drawing are ovewritten when the first one (you left blank) is cleared upon returning from the message handler.

MFC: flickering issue with GDI+

I create a sample dialog application which has a circle drawn. Also on mouse move the circle will be re-drawn. I am providing my code below. Its also compilable.
I tried using double buffering and erasebackground, i was not getting the flickering issue, but i observed that the drawining is not erased properly. So to erase, in OnPaint i wrote the erasing code. Again i am facing the flickering issue.
void CPOCDlg::OnPaint()
{
CPaintDC dc(this);
GetClientRect(&clientRect);
circle = clientRect;
circle.DeflateRect(100,100);
dc.SelectStockObject(NULL_BRUSH);
dc.SelectStockObject(NULL_PEN);
dc.FillSolidRect(circle, ::GetSysColor(COLOR_BTNFACE));
Bitmap buffer(circle.right, circle.bottom);
Graphics graphicsbuf(&buffer);
Graphics graphics(dc.m_hDC);
graphicsbuf.SetSmoothingMode(SmoothingModeHighQuality);
SolidBrush brush(Color(255,71,71,71));
Pen bluePen(Color(255, 0, 0, 255),1);
graphicsbuf.DrawEllipse(&bluePen,Rect(circle.left,circle.top,circle.Width(),circle.Height()));
graphicsbuf.SetSmoothingMode(SmoothingModeHighQuality);
graphics.DrawImage(&buffer, 0, 0);
}
void CPOCDlg::OnMouseMove(UINT nFlags, CPoint point)
{
m_point = point;
InvalidateRect(circle,FALSE);
CDialogEx::OnMouseMove(nFlags, point);
}
BOOL CPOCDlg::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
Please let me know if i am doing any mistake.
You need to use so called double buffer technique to prevent flickering:
// create Mem DC
dcMemory = new CDC;
dcMemory->CreateCompatibleDC(pDC);
pDC->SetMapMode(MM_TEXT);
dcMemory->SetMapMode(MM_TEXT);
// TODO: draw to memDC here
//switch back to paint dc
pDC->BitBlt(rectDirty.left, rectDirty.top,
rectDirty.Width(), rectDirty.Height(),
dcMemory,
rectDirty.left,rectDirty.top,SRCCOPY);
dcMemory->DeleteDC();
delete dcMemory;
dcMemory = NULL;

Displaying image in MFC method that is not ONPaint

I am trying to display an image in a dialog dynamically, it works no problem if I put the code in the on paint method and use the dc from there, I can't do this though I need to display after the window is shown, the code I am using is as follows, I am getting the dc of the client window creating the bitmap from a resource and "trying" to display it in the window but nothing displays, Any suggestions what might be wrong?
void CProcessSteps::OnShowWindow(BOOL bShow, UINT nStatus)
{
CDialog::OnShowWindow(bShow, nStatus);
SetupInstructions();<-----------------Call To Method
}
void CProcessSteps::OnPaint()
{
CPaintDC dc(this);
}
void CProcessSteps::SetupInstructions()
{
CDC *pDC = new CDC();<------------------------------Problem starts here
CFontUtil cfu;
cfu.SetFont(&LineFont,30);
CDC memDC;
memDC.CreateCompatibleDC(pDC);
int stepTop = 10;
int stepEnd = 230;
int imageLeft = 30;
STEP_STRUCT* step;
CBitmap iconImage;
iconImage.LoadBitmap ( IDB_TID_CHECK );
memDC.SelectObject(&iconImage);
CRect iconRect;
BITMAP bmInfo;
iconImage.GetObject ( sizeof ( bmInfo ), &bmInfo );
iconRect.SetRect ( imageLeft, stepTop, imageLeft+bmInfo.bmWidth, stepTop+bmInfo.bmHeight );
pDC = this->GetDC();
pDC->BitBlt(imageLeft, stepTop, imageLeft+bmInfo.bmWidth, stepTop+bmInfo.bmHeight, &memDC, 0, 0, SRCCOPY);
//RedrawWindow();<-------- tried this here no luck
int stepCount = m_pageStructure->PageSteps.GetCount();<----------------------------Bellow this works correctly
POSITION pos = m_pageStructure->PageSteps.GetHeadPosition();
while (pos)
{
step = m_pageStructure->PageSteps.GetNext(pos);
CStatic *label = new CStatic;
label->Create(_T( step->StepInstruction ),WS_CHILD | WS_VISIBLE, CRect(80, stepTop, 480, stepEnd), this);
label->SetFont(&LineFont, true);
label->GetWindowRect(rect);
ScreenToClient(rect);
pDC = label->GetDC();
pDC->SelectObject(&LineFont);
pDC->DrawText(step->StepInstruction, &rect, DT_CALCRECT|DT_WORDBREAK);
label->ReleaseDC(pDC);
label->MoveWindow(rect);
stepTop += rect.Height();
stepTop += 30;
stepEnd += rect.Height();
}
}
Reasons why you can't use OnPaint() are not clear.
The usual strategy when one needs to redraw all or part of a window upon some event is to call InvalidateRect().
Windows will in turn send WM_PAINT (handled by your OnPaint() method) to your app, specifying which part of the window should be redrawn.
I think there's more in the BeginPaint-function than just giving you the CDC. And BeginPaint can only be called from the OnPaint-method.
To solve your problem, use the Invalidate-functions to force a repaint from your "SetupInstructions" method. Then do the drawing inside the OnPaint function.
I suppose CProcessSteps derives from CWnd, perhaps a CDialog?
If you want to draw in the client area of a CWnd derived class you have to get the DC using the CWnd GetDC method. I don't understand why you create your own CDC, you should get the CWnd DC at the beginning of SetupInstructions and use this DC everywhere, also to create your memDC.
Also you should be careful when you allocate memory (new CStatic) if you don't call delete for this variables you will have memory leaks. If you really need to create this CStatics dynamically you will have to keep a pointer to all of them in order to delete them before closing the dialog/view.
As people suggested, I don't think you are following the right way by drawing using OnShowWindow. You should use OnPaint to make your drawing stuff, if you don't want to draw the image until the window is fully initialized you should use a member variable of the window (for instance a bool) initialized to false in the constructor and set it to true when you are ready to draw the image. Then calling Invalidate will draw the image. Something like:
In the .h:
class CProcessSteps : CDialog
{
...
private:
bool m_bReadyToDraw;
};
In the .cpp:
CProcessSteps::CProcessSteps() : CDialog()
{
m_bReadyToDraw = false;
}
BOOL CProcessSteps::OnInitDialog()
{
CDialog:OnInitDialog();
m_bReadyToDraw = true;
return TRUE;
}
void CProcessSteps::OnPaint()
{
CPaintDC dc(this);
if(m_bReadyToDraw)
{
CFontUtil cfu;
cfu.SetFont(&LineFont,30);
CDC memDC;
memDC.CreateCompatibleDC(&dc);
...
}
}
Hope it helps.
Javier

How do I prevent flickering on CListCtrl?

I'm using a CListCtrl/CListView report view (LVS_REPORT) in virtual mode (LVS_OWNERDATA) with LVS_EX_DOUBLEBUFFER enabled and I encounter ugly flickering. Double buffer have a real effect but it doesn't stop all flickering (without it very slow).
I'm not looking for switching to other controls that would require a high amount of rework (like ObjectListView)
How does the flickering behaves:
on column resize - the background is first clean using lightgray and after this is displayed the text (background is white)
on mouse scroll (animated) - for a very short time there is lightgray-bar displayed in the area where new lines are to be displayed.
It looks like it does clean the background using the default window background color (lightgray) for the area where it has to redraw.
How do I solve the flickering problem?
Try to do the following:
- Set Clip Children and Clip Sibling for paremt dialog of List Control.
- Make dirived from CListCtrl class. In this class overwrite OnEraseBkgnd. In the OnEraseBkgnd fill with background color area around of visible items of the list.
The OnEraseBkgnd can look like:
BOOL CListCtrlEx::OnEraseBkgnd(CDC* pDC)
{
CBrush br;
CRect rcCli;
CRect rcItemsRect(0, 0, 0, 0);
int nHeadHeight = 0;
int nItems = GetItemCount();
GetClientRect(&rcCli);
CHeaderCtrl* pHeadCtrl = GetHeaderCtrl();
if (pHeadCtrl)
{
CRect rcHead;
pHeadCtrl->GetWindowRect(&rcHead);
nHeadHeight = rcHead.Height();
}
rcCli.top += nHeadHeight;
if (nItems > 0)
{
CPoint ptItem;
CRect rcItem;
GetItemRect(nItems - 1, &rcItem, LVIR_BOUNDS);
GetItemPosition(nItems - 1, &ptItem);
rcItemsRect.top = rcCli.top;
rcItemsRect.left = ptItem.x;
rcItemsRect.right = rcItem.right;
rcItemsRect.bottom = rcItem.bottom;
if (GetExtendedStyle() & LVS_EX_CHECKBOXES)
rcItemsRect.left -= GetSystemMetrics(SM_CXEDGE) + 16;
}
br.CreateSolidBrush(GetBkColor());
if (rcItemsRect.IsRectEmpty())
pDC->FillRect(rcCli, &br);
else
{
if (rcItemsRect.left > rcCli.left) // fill left rectangle
pDC->FillRect(
CRect(0, rcCli.top, rcItemsRect.left, rcCli.bottom), &br);
if (rcItemsRect.bottom < rcCli.bottom) // fill bottom rectangle
pDC->FillRect(
CRect(0, rcItemsRect.bottom, rcCli.right, rcCli.bottom), &br);
if (rcItemsRect.right < rcCli.right) // fill right rectangle
pDC->FillRect(
CRect(rcItemsRect.right, rcCli.top, rcCli.right, rcCli.bottom), &br);
}
return TRUE;
}
I know only way to have flicker free is using double buffering or MemDC.
have found this article: Flicker-free-drawing-of-any-control
This article explains it well how to quickly perform Non Flickering drawing on your CListCtrl.
And it works excellent.
PS: VS 2005 doesn't have CMemDC class you will need to implement it your self, or use the following code:
//
// CMemDC.h header file
//
#pragma once
class CMemDC
{
public:
CMemDC(CDC& dc, CWnd* pWnd);
CMemDC(CDC& dc, const CRect& rect);
virtual ~CMemDC();
CDC& GetDC() { return m_bMemDC ? m_dcMem : m_dc; }
BOOL IsMemDC() const { return m_bMemDC; }
BOOL IsVistaDC() const { return m_hBufferedPaint != NULL; }
void EraseBkClip();
protected:
CDC& m_dc;
BOOL m_bMemDC;
HANDLE m_hBufferedPaint;
CDC m_dcMem;
CBitmap m_bmp;
CBitmap* m_pOldBmp;
CRect m_rect;
};
//
// CMemDC.cpp source file
//
#include "CMemDC.h"
CMemDC::CMemDC(CDC& dc, CWnd* pWnd) :
m_dc(dc), m_bMemDC(FALSE), m_hBufferedPaint(NULL), m_pOldBmp(NULL)
{
ASSERT_VALID(pWnd);
pWnd->GetClientRect(m_rect);
m_rect.right += pWnd->GetScrollPos(SB_HORZ);
m_rect.bottom += pWnd->GetScrollPos(SB_VERT);
if (m_dcMem.CreateCompatibleDC(&m_dc) &&
m_bmp.CreateCompatibleBitmap(&m_dc, m_rect.Width(), m_rect.Height()))
{
m_bMemDC = TRUE;
m_pOldBmp = m_dcMem.SelectObject(&m_bmp);
}
}
CMemDC::CMemDC(CDC& dc, const CRect& rect) :
m_dc(dc), m_bMemDC(FALSE), m_hBufferedPaint(NULL), m_pOldBmp(NULL), m_rect(rect)
{
ASSERT(!m_rect.IsRectEmpty());
if (m_dcMem.CreateCompatibleDC(&m_dc) &&
m_bmp.CreateCompatibleBitmap(&m_dc, m_rect.Width(), m_rect.Height()))
{
m_bMemDC = TRUE;
m_pOldBmp = m_dcMem.SelectObject(&m_bmp);
}
}
CMemDC::~CMemDC()
{
if (m_bMemDC)
{
CRect rectClip;
int nClipType = m_dc.GetClipBox(rectClip);
if (nClipType != NULLREGION)
{
if (nClipType != SIMPLEREGION)
{
rectClip = m_rect;
}
m_dc.BitBlt(rectClip.left, rectClip.top, rectClip.Width(), rectClip.Height(), &m_dcMem, rectClip.left, rectClip.top, SRCCOPY);
}
m_dcMem.SelectObject(m_pOldBmp);
}
}
void CMemDC::EraseBkClip()
{
CRect clip;
m_dcMem.GetClipBox(&clip);
m_dcMem.FillSolidRect(clip, GetSysColor(COLOR_WINDOW));
}
There is an ultra simple way I found that worked for me:
Turn off redraw with m_List1.SetRedraw(false)
Reset contents with m_List1.ResetContents()
Add new strings in loop with m_List1.AddString()
Then finalize by turning back on redraw and a m_List1.UpdateWindow().