double buffering issue...
-
Hi, In my project,I have to draw icon over a bitmap every 250msec. My problem is that sometimes the drawing over the bitmap is flickering in a very annoying way. I know that I need to draw over the bitmap using double buffering, and looked over some articles about it,but failed to understand how to do it. I can't use the classes of those articles (Safety issues...:~ ). Can anyone help me understand how to improve my drawing technique??? I use the following piece of code:
void Frm_WkAtol::OnTimer(UINT_PTR nIDEvent)
{
// TODO: Add your message handler code here and/or call default
m_AtolCorridorBitmap.Invalidate(); // cause the bitmap to redraw itself
CDialog::OnTimer(nIDEvent);
}void AtolCorridorBitmapClass::OnPaint()
{
CStatic::OnPaint();CRect CorridorRect; CDC \*pDC = GetDC(); static int x = 0; // Set attributes of the DC pDC->SetTextColor(RED\_COLOR); pDC->SetBkMode(TRANSPARENT); GetClientRect(&CorridorRect); szCaption.Format(\_T("Test %d") , x++); pDC->ExtTextOut(CorridorRect.left, CorridorRect.top + 30 , ETO\_OPAQUE , NULL , szCaption , NULL); DrawIcon(pDC->m\_hDC, CorridorRect.left , CorridorRect.top , m\_hUavInsideAtolCorridorIcon); ReleaseDC(pDC);
}
With best regards, Eli;P
-
Hi, In my project,I have to draw icon over a bitmap every 250msec. My problem is that sometimes the drawing over the bitmap is flickering in a very annoying way. I know that I need to draw over the bitmap using double buffering, and looked over some articles about it,but failed to understand how to do it. I can't use the classes of those articles (Safety issues...:~ ). Can anyone help me understand how to improve my drawing technique??? I use the following piece of code:
void Frm_WkAtol::OnTimer(UINT_PTR nIDEvent)
{
// TODO: Add your message handler code here and/or call default
m_AtolCorridorBitmap.Invalidate(); // cause the bitmap to redraw itself
CDialog::OnTimer(nIDEvent);
}void AtolCorridorBitmapClass::OnPaint()
{
CStatic::OnPaint();CRect CorridorRect; CDC \*pDC = GetDC(); static int x = 0; // Set attributes of the DC pDC->SetTextColor(RED\_COLOR); pDC->SetBkMode(TRANSPARENT); GetClientRect(&CorridorRect); szCaption.Format(\_T("Test %d") , x++); pDC->ExtTextOut(CorridorRect.left, CorridorRect.top + 30 , ETO\_OPAQUE , NULL , szCaption , NULL); DrawIcon(pDC->m\_hDC, CorridorRect.left , CorridorRect.top , m\_hUavInsideAtolCorridorIcon); ReleaseDC(pDC);
}
With best regards, Eli;P
How about only invalidating only the rectangle that changes, then the only bit that needs redrawing is the icon, rather than the whole bitmap. I'd consider a separate overlaid windowless control if that doesn't work. Otherwise you need to incorporate the icon directly into the bitmap data so each change only redraws that area of the screen once. Remember to catch the erase background message and tell windows not to erase or you'll get really bad flicker whatever you do. :)
Nothing is exactly what it seems but everything with seems can be unpicked.
-
Hi, In my project,I have to draw icon over a bitmap every 250msec. My problem is that sometimes the drawing over the bitmap is flickering in a very annoying way. I know that I need to draw over the bitmap using double buffering, and looked over some articles about it,but failed to understand how to do it. I can't use the classes of those articles (Safety issues...:~ ). Can anyone help me understand how to improve my drawing technique??? I use the following piece of code:
void Frm_WkAtol::OnTimer(UINT_PTR nIDEvent)
{
// TODO: Add your message handler code here and/or call default
m_AtolCorridorBitmap.Invalidate(); // cause the bitmap to redraw itself
CDialog::OnTimer(nIDEvent);
}void AtolCorridorBitmapClass::OnPaint()
{
CStatic::OnPaint();CRect CorridorRect; CDC \*pDC = GetDC(); static int x = 0; // Set attributes of the DC pDC->SetTextColor(RED\_COLOR); pDC->SetBkMode(TRANSPARENT); GetClientRect(&CorridorRect); szCaption.Format(\_T("Test %d") , x++); pDC->ExtTextOut(CorridorRect.left, CorridorRect.top + 30 , ETO\_OPAQUE , NULL , szCaption , NULL); DrawIcon(pDC->m\_hDC, CorridorRect.left , CorridorRect.top , m\_hUavInsideAtolCorridorIcon); ReleaseDC(pDC);
}
With best regards, Eli;P
Here's an example of a double-buffered bitmap static control class...
//------------------------------
// DblBufferedStatic.h#pragma once
// CDblBufferedStatic
class CDblBufferedStatic : public CStatic
{
DECLARE_DYNAMIC(CDblBufferedStatic)protected:
HBITMAP hStaticBitmap;
CSize BackBufferSize;
CDC BackBufferDC;
CBitmap BackBufferBitmap;void InitBackBuffer();
public:
void RedrawBackBuffer();public:
CDblBufferedStatic();
virtual ~CDblBufferedStatic();protected:
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnPaint();
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
};//------------------------------
// DblBufferedStatic.cpp// DblBufferedStatic.cpp : implementation file
//#include "stdafx.h"
#include "MyApp.h"
#include "DblBufferedStatic.h"
#include ".\dblbufferedstatic.h"// CDblBufferedStatic
IMPLEMENT_DYNAMIC(CDblBufferedStatic, CStatic)
CDblBufferedStatic::CDblBufferedStatic()
{
hStaticBitmap = 0;
}CDblBufferedStatic::~CDblBufferedStatic()
{
}BEGIN_MESSAGE_MAP(CDblBufferedStatic, CStatic)
ON_WM_PAINT()
ON_WM_ERASEBKGND()
END_MESSAGE_MAP()void CDblBufferedStatic::InitBackBuffer()
{
// Get a handle to the bitmap associated with this control
hStaticBitmap = GetBitmap();if (hStaticBitmap)
{
// Get the bitmap's dimensions
BITMAP bitmapstruct;
::GetObject(hStaticBitmap, sizeof(BITMAP), &bitmapstruct);
BackBufferSize.cx = bitmapstruct.bmWidth;
BackBufferSize.cy = bitmapstruct.bmHeight;// Create the background buffer (DC and bitmap) CWindowDC WindowDC(this); BackBufferDC.CreateCompatibleDC(&WindowDC); BackBufferBitmap.CreateCompatibleBitmap(&WindowDC, BackBufferSize.cx, BackBufferSize.cy); `//*edit* removed BackBufferBitmap.GetObject(sizeof(BITMAP), &bitmapstruct);` BackBufferDC.SelectObject(&BackBufferBitmap);
}
}void CDblBufferedStatic::RedrawBackBuffer()
{
// Refresh (redraw) contents of background bufferif (hStaticBitmap)
{
// Draw original bitmap to offscreen buffer
CDC TempDC;
TempDC.CreateCompatibleDC(&BackBufferDC);
CBitmap SrcBitmap;
SrcBitmap.Attach(hStaticBitmap);
CBitmap *pOldBitmap = TempDC.SelectObject(&SrcBitmap);
BackBufferDC.BitBlt(0, 0, BackBufferSize.cx, BackBufferSize.cy, &TempDC, 0, 0, SRCCOPY);
TempDC.SelectObject(pOldBitmap);
SrcBitmap.Detach(); -
Here's an example of a double-buffered bitmap static control class...
//------------------------------
// DblBufferedStatic.h#pragma once
// CDblBufferedStatic
class CDblBufferedStatic : public CStatic
{
DECLARE_DYNAMIC(CDblBufferedStatic)protected:
HBITMAP hStaticBitmap;
CSize BackBufferSize;
CDC BackBufferDC;
CBitmap BackBufferBitmap;void InitBackBuffer();
public:
void RedrawBackBuffer();public:
CDblBufferedStatic();
virtual ~CDblBufferedStatic();protected:
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnPaint();
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
};//------------------------------
// DblBufferedStatic.cpp// DblBufferedStatic.cpp : implementation file
//#include "stdafx.h"
#include "MyApp.h"
#include "DblBufferedStatic.h"
#include ".\dblbufferedstatic.h"// CDblBufferedStatic
IMPLEMENT_DYNAMIC(CDblBufferedStatic, CStatic)
CDblBufferedStatic::CDblBufferedStatic()
{
hStaticBitmap = 0;
}CDblBufferedStatic::~CDblBufferedStatic()
{
}BEGIN_MESSAGE_MAP(CDblBufferedStatic, CStatic)
ON_WM_PAINT()
ON_WM_ERASEBKGND()
END_MESSAGE_MAP()void CDblBufferedStatic::InitBackBuffer()
{
// Get a handle to the bitmap associated with this control
hStaticBitmap = GetBitmap();if (hStaticBitmap)
{
// Get the bitmap's dimensions
BITMAP bitmapstruct;
::GetObject(hStaticBitmap, sizeof(BITMAP), &bitmapstruct);
BackBufferSize.cx = bitmapstruct.bmWidth;
BackBufferSize.cy = bitmapstruct.bmHeight;// Create the background buffer (DC and bitmap) CWindowDC WindowDC(this); BackBufferDC.CreateCompatibleDC(&WindowDC); BackBufferBitmap.CreateCompatibleBitmap(&WindowDC, BackBufferSize.cx, BackBufferSize.cy); `//*edit* removed BackBufferBitmap.GetObject(sizeof(BITMAP), &bitmapstruct);` BackBufferDC.SelectObject(&BackBufferBitmap);
}
}void CDblBufferedStatic::RedrawBackBuffer()
{
// Refresh (redraw) contents of background bufferif (hStaticBitmap)
{
// Draw original bitmap to offscreen buffer
CDC TempDC;
TempDC.CreateCompatibleDC(&BackBufferDC);
CBitmap SrcBitmap;
SrcBitmap.Attach(hStaticBitmap);
CBitmap *pOldBitmap = TempDC.SelectObject(&SrcBitmap);
BackBufferDC.BitBlt(0, 0, BackBufferSize.cx, BackBufferSize.cy, &TempDC, 0, 0, SRCCOPY);
TempDC.SelectObject(pOldBitmap);
SrcBitmap.Detach();Hi Mark, I couldn't ask for a better answer. It is working perfect. After reading your code - it seems to be so simple...:doh: THANK YOU VERY MUCH:-D:-D:-D With best regards, Eli
-
Here's an example of a double-buffered bitmap static control class...
//------------------------------
// DblBufferedStatic.h#pragma once
// CDblBufferedStatic
class CDblBufferedStatic : public CStatic
{
DECLARE_DYNAMIC(CDblBufferedStatic)protected:
HBITMAP hStaticBitmap;
CSize BackBufferSize;
CDC BackBufferDC;
CBitmap BackBufferBitmap;void InitBackBuffer();
public:
void RedrawBackBuffer();public:
CDblBufferedStatic();
virtual ~CDblBufferedStatic();protected:
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnPaint();
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
};//------------------------------
// DblBufferedStatic.cpp// DblBufferedStatic.cpp : implementation file
//#include "stdafx.h"
#include "MyApp.h"
#include "DblBufferedStatic.h"
#include ".\dblbufferedstatic.h"// CDblBufferedStatic
IMPLEMENT_DYNAMIC(CDblBufferedStatic, CStatic)
CDblBufferedStatic::CDblBufferedStatic()
{
hStaticBitmap = 0;
}CDblBufferedStatic::~CDblBufferedStatic()
{
}BEGIN_MESSAGE_MAP(CDblBufferedStatic, CStatic)
ON_WM_PAINT()
ON_WM_ERASEBKGND()
END_MESSAGE_MAP()void CDblBufferedStatic::InitBackBuffer()
{
// Get a handle to the bitmap associated with this control
hStaticBitmap = GetBitmap();if (hStaticBitmap)
{
// Get the bitmap's dimensions
BITMAP bitmapstruct;
::GetObject(hStaticBitmap, sizeof(BITMAP), &bitmapstruct);
BackBufferSize.cx = bitmapstruct.bmWidth;
BackBufferSize.cy = bitmapstruct.bmHeight;// Create the background buffer (DC and bitmap) CWindowDC WindowDC(this); BackBufferDC.CreateCompatibleDC(&WindowDC); BackBufferBitmap.CreateCompatibleBitmap(&WindowDC, BackBufferSize.cx, BackBufferSize.cy); `//*edit* removed BackBufferBitmap.GetObject(sizeof(BITMAP), &bitmapstruct);` BackBufferDC.SelectObject(&BackBufferBitmap);
}
}void CDblBufferedStatic::RedrawBackBuffer()
{
// Refresh (redraw) contents of background bufferif (hStaticBitmap)
{
// Draw original bitmap to offscreen buffer
CDC TempDC;
TempDC.CreateCompatibleDC(&BackBufferDC);
CBitmap SrcBitmap;
SrcBitmap.Attach(hStaticBitmap);
CBitmap *pOldBitmap = TempDC.SelectObject(&SrcBitmap);
BackBufferDC.BitBlt(0, 0, BackBufferSize.cx, BackBufferSize.cy, &TempDC, 0, 0, SRCCOPY);
TempDC.SelectObject(pOldBitmap);
SrcBitmap.Detach();Hi Mark, Just one more question.... I'm using the http://www.codeproject.com/listctrl/ReportControl.asp[^] in order to display a list control with different rows color and icons. The problem is that I need to update the list every 250msec, and guess what......the list is not flicker free... I know that updating a list control is very expensive, but I was wondering if I can use the double buffering technique also for the list control(which is owner drawn). With best regards, Eli
-
Hi Mark, Just one more question.... I'm using the http://www.codeproject.com/listctrl/ReportControl.asp[^] in order to display a list control with different rows color and icons. The problem is that I need to update the list every 250msec, and guess what......the list is not flicker free... I know that updating a list control is very expensive, but I was wondering if I can use the double buffering technique also for the list control(which is owner drawn). With best regards, Eli
What happens if you use WM_SETREDRAW... m_ListCtl.SendMessage(WM_SETREDRAW, (WPARAM)FALSE, 0); ... ...do stuff with the listview contents... ... m_ListCtl.SendMessage(WM_SETREDRAW, (WPARAM)TRUE, 0); m_ListCtl.Invalidate(); m_ListCtl.UpdateWindow();
"Posting a VB.NET question in the C++ forum will end in tears." Chris Maunder
-
What happens if you use WM_SETREDRAW... m_ListCtl.SendMessage(WM_SETREDRAW, (WPARAM)FALSE, 0); ... ...do stuff with the listview contents... ... m_ListCtl.SendMessage(WM_SETREDRAW, (WPARAM)TRUE, 0); m_ListCtl.Invalidate(); m_ListCtl.UpdateWindow();
"Posting a VB.NET question in the C++ forum will end in tears." Chris Maunder
Hi Mark, Instead of sending WM_SETREDRAW message,I'm using m_ListCtrl.SetRedraw() and m_ListCtrl.SetRedraw(FALSE). But still,the list control is blinking. I thought that double buffering drawing will solve the problem,but since the control is owner drawn,I don't have access to the drawing functions.... Maybe it is not possible???:sigh: Thanks, Eli
-
Hi Mark, Instead of sending WM_SETREDRAW message,I'm using m_ListCtrl.SetRedraw() and m_ListCtrl.SetRedraw(FALSE). But still,the list control is blinking. I thought that double buffering drawing will solve the problem,but since the control is owner drawn,I don't have access to the drawing functions.... Maybe it is not possible???:sigh: Thanks, Eli
It should be possible - You may have to do most of the drawing yourself. In the source you linked to, the author is only providing custom colors although the framework is there to do the drawing. What parts flicker specifically? Is it the text on the background? Mark
"Posting a VB.NET question in the C++ forum will end in tears." Chris Maunder
-
It should be possible - You may have to do most of the drawing yourself. In the source you linked to, the author is only providing custom colors although the framework is there to do the drawing. What parts flicker specifically? Is it the text on the background? Mark
"Posting a VB.NET question in the C++ forum will end in tears." Chris Maunder
Hi Mark, Thanks for your patience:-D
Mark Salsbery wrote:
What parts flicker specifically? Is it the text on the background?
No,Only the background is flickering(and not allways). I don't know if this could help,but the following function is being called every time that the list needs to be updated(i.e every 250msec).
void Frm_WKFailureList::UpdateList(iGendevBit::BitMatrixResultListType &p_FailuresListRef)
{
iGendevBit::BitMatrixResultClass *CurrentElement;
dDbsrvNotif::notificationTextType NotifText;
COLORREF SeverityColor;
UINT uiCurrentRow = 0;
UINT uiCurrentErrorsCounter = 0;m\_FailureList.SetRedraw(FALSE); // Adjust the list's last column according to // the right scroll bar existent if (p\_FailuresListRef.GetElementNum() > MAX\_VISIBLE\_ITEM\_WITH\_NO\_SCROLL) { m\_FailureList.SetColumnWidth(2 , 30); } else { m\_FailureList.SetColumnWidth(2 , 48); } uiCurrentErrorsCounter = p\_FailuresListRef.GetElementNum(); p\_FailuresListRef.ScanInit(bTypesCnt::FROM\_TAIL); while ((CurrentElement = p\_FailuresListRef.ScanNext()) != NULL) { // Get failure severity switch(CurrentElement->m\_Severity) { case iGendevBit::SLIGHT: // in the future - do not show "Slight" severity in the list. // For now - show with with background SeverityColor = RGB(255 , 255 , 0); break; case iGendevBit::MEDIUM: SeverityColor = RGB(255 , 255 , 0); break; case iGendevBit::SEVERE: SeverityColor = RGB(255 , 0 , 0); break; } // Get failure text dDbsrvNotif::GetNotifText(NotifText , CurrentElement->m\_NotiffId); CString szError , szTime; szError = NotifText; szTime = CurrentElement->m\_FailureTime.GetFormattedTod(bTimeTod::HH\_MM\_SS); if (m\_FailureList.GetItemCount() < (uiCurrentRow + 1)) { m\_FailureList.InsertItem(uiCurrentRow , \_T("")); } static int x = 0; szTime.Format(\_T("%d") , x++); m\_FailureList.SetItemText(uiCurrentRow , 0 , szTime); m\_FailureList.SetItemText(uiCurrentRow , 1 , szError); // Get failure check list existents CHECK\_LIST\_ENUM IsCheckListExist = NO\_CHECK\_LIST; SetLineBkColorAndIcon(uiCurrentRow , SeverityColor , IsCheckListExist); // Set the icon and background color for the current row uiCurrentRow++; m\_bListIsEmpty = FALSE; } if (uiCurrentRow < m\_LastErrorsCounter) { for (UINT uiIndex = uiCur