Trying to create Flicker free CStatic with Bitmap background

microwhat
microwhat used Ask the Experts™
on
I have a transparent background CStatic text class(CStatic subclass). The CStatic txt is located in a dialog with a bitmap for a background. I'm trying to implement flicker free with this. Currently the code below is flickering less, but it's not flicker free. Also, i can't seem to get it to display the background as transparent with this flicker free code, this is something it did with no issue without the flicker free code. Any input is welcome. I've read/searched a lot and i'm just about stuck at this point.
BOOL CTransparentStatic::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
void CTransparentStatic::OnPaint()
{
	CPaintDC dc(this); // device context for painting
	CDC      memDC;
	CBitmap  memBM;
	CBrush   hbrBkGnd;

	// Where to draw text
	CRect client_rect;
	GetClientRect(client_rect);

	// Get the caption
	CString szText;
	GetWindowText(szText);

	// Get the font
	CFont *pFont, *pOldFont;
	pFont = GetFont();


	
		// Create a compatible DC.
		memDC.CreateCompatibleDC(&dc);

		// Create a bitmap big enough for our client rectangle.
		memBM.CreateCompatibleBitmap(&dc,
										client_rect.right-client_rect.left,
										client_rect.bottom-client_rect.top);

		// Select the bitmap into the off-screen memory DC.
		CBitmap *hbmOld = memDC.SelectObject(&memBM);

		// Erase the background.
		hbrBkGnd.CreateSolidBrush(GetSysColor(COLOR_WINDOW));
		memDC.FillRect( &client_rect, &hbrBkGnd);
		hbrBkGnd.DeleteObject();

		// Select the font.
		if(pFont){
			pOldFont = memDC.SelectObject(pFont);
		}

		// Render the image into the offscreen DC.
		memDC.SetBkMode(TRANSPARENT);
		//memDC.SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
		memDC.SetTextColor ( RGB(54,54,112) );
		memDC.DrawText(szText, client_rect, DT_LEFT);

		if(pOldFont){
			memDC.SelectObject(pOldFont);
		}

		// Blt the changes to the screen DC.
		dc.BitBlt(client_rect.left, client_rect.top,
			   client_rect.right-client_rect.left,
			   client_rect.bottom-client_rect.top,
			   &memDC,
			   0, 0,
			   SRCCOPY);

		// Done with off-screen bitmap and DC, clean up.
		memDC.SelectObject(hbmOld);
		memBM.DeleteObject();
		memDC.DeleteDC();
}

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®

Commented:
"Remove" everything and leave only one BitBlt.

You GetWindowText to retrieve a text. Probably, you set this text in another place of your program. At this moment you can prepare a memory DC with the selected bitmap and draw text on it.

Or, in this OnPaint method, make all that only once and  save the memory DC with the selected bitmap in a member-variable. When you change the text, just delete this member-variable.
Something like that:
 
void MyDialog::OnPaint()
{
   CPaintDC dc(this);
   if ( m_MemDC == NULL)
      CreateThePicture(&dc);
   dc.BitBlt(...);
 }

Hi microwhat,

IMO the flickering you describe isn't caused by that function since all drawing is made in the memory DC which is once drawn to the control's DC.

I think you have to handle WM_ERASEBKGND message too for your control (and most probably for the dialog where you use this control too) and return a none-zero value there to avoid the system itself erases the control's/dialog's background in case of a needed redrawing action ...

ZOPPO
AndyAinscowFreelance programmer / Consultant

Commented:
Your code looks OK as a simple flicker free attempt.  Handling the ERASEBKGND message as you do is correct (failure to do so is the usual cause of lickering).

>>Also, i can't seem to get it to display the background as transparent with this flicker free code

Erm, what should be transparent?  The dialog or the control?  What are you doing to try to make things transparent - that might be conflicting with what you are doing to remove flickering.
CompTIA Network+

Prepare for the CompTIA Network+ exam by learning how to troubleshoot, configure, and manage both wired and wireless networks.

Author

Commented:
pgnatyuk:
"Remove" everything and leave only one BitBlt.

Im not sure what you mean. I can't removed most of the code or BitBlt will have nothing as a source.

You GetWindowText to retrieve a text. Probably, you set this text in another place of your program. At this moment you can prepare a memory DC with the selected bitmap and draw text on it.

Yes it is already set as this is a subclass of CStatic. The memory Dc you are referring to, Is that not what im already doing?

Or, in this OnPaint method, make all that only once and  save the memory DC with the selected bitmap in a member-variable. When you change the text, just delete this member-variable.
Something like that:
 
void MyDialog::OnPaint()
{
   CPaintDC dc(this);
   if ( m_MemDC == NULL)
      CreateThePicture(&dc);
   dc.BitBlt(...);
 }
Sorry i dont not follow what you are suggesting here

See underlined text.


Hi microwhat,

IMO the flickering you describe isn't caused by that function since all drawing is made in the memory DC which is once drawn to the control's DC.

I think you have to handle WM_ERASEBKGND message too for your control (and most probably for the dialog where you use this control too) and return a none-zero value there to avoid the system itself erases the control's/dialog's background in case of a needed redrawing action ...

ZOPPO

Its flickering less with this code. Without this code the flicker is very obvious(Due to bad painting implementation in MFC).
 I am currently handling the erasebckgrnd for this subclass control and i had to also return TRUE for the dialogs erasebckgrnd call to get the flicker to a minimum.


AndyAinscow:
Your code looks OK as a simple flicker free attempt.  Handling the ERASEBKGND message as you do is correct (failure to do so is the usual cause of lickering).

By just returning TRUE from ERASEBKGND, will that cause a memory leak of any kind?

>>Also, i can't seem to get it to display the background as transparent with this flicker free code

Erm, what should be transparent?  The dialog or the control?  What are you doing to try to make things transparent - that might be conflicting with what you are doing to remove flickering.

The class CTransparentStatic is a subclass of CStatic, which is just a text control. The text control is located in a dialog which uses a bitmap for the entire background. Without the flicker free code, the text control has a transparent background so there is no ugly box around the text, only the background bitmap of the dialog.


Also one of the other methods that i tried which works flawlessly in its test application is a CPaintDC replacement. It does not work perfect for me though when i implement it in my application. The only need changes are replacing CPaintDC dc(this); with CBufferDC dc(this); and returning true for erasebckgrnd like all other flicker free implementations. It flickers slightly and has a black background for the text control. Here is the code for it.

CBufferDC::CBufferDC(CWnd* pWnd) : CPaintDC(pWnd)
{
	if (pWnd != NULL && CPaintDC::m_hDC != NULL)
	{
		m_hOutputDC    = CPaintDC::m_hDC;
		m_hAttributeDC = CPaintDC::m_hAttribDC;

		pWnd->GetClientRect(&m_ClientRect);

		m_hMemoryDC = ::CreateCompatibleDC(m_hOutputDC);

		m_hPaintBitmap =
			::CreateCompatibleBitmap(
					m_hOutputDC,
					m_ClientRect.right  - m_ClientRect.left,
					m_ClientRect.bottom - m_ClientRect.top);

		m_hOldBitmap = (HBITMAP)::SelectObject(m_hMemoryDC, m_hPaintBitmap);

		CPaintDC::m_hDC       = m_hMemoryDC;
		CPaintDC::m_hAttribDC = m_hMemoryDC;
	}

	m_bBoundsUpdated = FALSE;
}

CBufferDC::~CBufferDC(void)
{
	Flush();

	::SelectObject(m_hMemoryDC, m_hOldBitmap);
	::DeleteObject(m_hPaintBitmap);

	CPaintDC::m_hDC		  = m_hOutputDC;
	CPaintDC::m_hAttribDC = m_hAttributeDC;

	::DeleteDC(m_hMemoryDC);
}

void CBufferDC::Flush()
{
	::BitBlt(
		m_hOutputDC,
		m_ClientRect.left, m_ClientRect.top,
		m_ClientRect.right  - m_ClientRect.left, 
		m_ClientRect.bottom - m_ClientRect.top,
		m_hMemoryDC,
		0, 0,
		SRCCOPY);
}

Open in new window


I've found that if i use my old transparent background code and return true from both the CStatic subclass onpaint() and the dialog onpaint(), the flickering is greatly reduced and the transparent background works like it should. So the only real change im making then is disabling the erase background in both classes. Is this a safe thing to do? IE. Blocking erase background will not cause a memory leak or any sort?
To simply 'return TRUE' from 'OnEraseBkgnd' is absolutely legal and doesn't cause any harm like memory or GDI leak.

One general problem with the transparency IMO is that using a memory DC created from a CPaintDC and selecting a bitmap into it doesn't mean the background of the control is automatically copied to the bitmap so bit-blitting back the bitmap 'overdraws' the background, in your case black.

I'm not sure if it's enough to just bit-blit from the window's DC to the memory DC before you start drawing in the memory DC, but I don't see why it shouldn't work so I think it's worth a try ... but, beside this I think from this code it's not the drawing within your control which flickers - more probably the flickering exists because the parent window's background is drawn over your control before your control is drawn.

What kind of parent window do you have? A dialog, or maybe a CFormView? If it's CFormView usually you'll have to add some code to unset the window's class style flags 'CS_HREDRAW' and 'CS_VREDRAW' using 'SetClassLong'.

And another question: When does this flickering occur? Is it by any kind of internal update mechanism? Something like 'RedrawWindow' after some data update? Or is it when re-sizing the parent window?


BTW: If you want to just change the color of the text of a CStatic IMO what you're doing here is overkill (sorry ;o) - you can easily solve this by ha dling WM_CTLCOLOR message within CTransparentStatic by message reflection somehow like this:

Declare this in header:
> afx_msg HBRUSH CtlColor( CDC* pDC, UINT nCtlColor );

Add this to message map:
> ON_WM_CTLCOLOR_REFLECT()

Add this:
HBRUSH CTransparentStatic::CtlColor( CDC* pDC, UINT nCtlColor )
{
      pDC->SetTextColor( RGB( 54,54,112 ) );
      pDC->SetBkMode( TRANSPARENT );
      return (HBRUSH)GetStockObject( NULL_BRUSH );
}


If it's still flickering then I'm sure it has to do something with the parent window and the way its background bitmap is drawn.

ZOPPO
To simply 'return TRUE' from 'OnEraseBkgnd' is absolutely legal and doesn't cause any harm like memory or GDI leak.

Thanks for the verification. Everything i've read on this subject recommends changing the return to false or true and never mentions if their is any repercussions  or not.

One general problem with the transparency IMO is that using a memory DC created from a CPaintDC and selecting a bitmap into it doesn't mean the background of the control is automatically copied to the bitmap so bit-blitting back the bitmap 'overdraws' the background, in your case black.

I'm not sure if it's enough to just bit-blit from the window's DC to the memory DC before you start drawing in the memory DC, but I don't see why it shouldn't work so I think it's worth a try ... but, beside this I think from this code it's not the drawing within your control which flickers - more probably the flickering exists because the parent window's background is drawn over your control before your control is drawn.

Yes doing a bitblt into the memdc of the dc work perfectly. No more black background in the text control.

What kind of parent window do you have? A dialog, or maybe a CFormView? If it's CFormView usually you'll have to add some code to unset the window's class style flags 'CS_HREDRAW' and 'CS_VREDRAW' using 'SetClassLong'.

just a dialog with static text and buttons.

And another question: When does this flickering occur? Is it by any kind of internal update mechanism? Something like 'RedrawWindow' after some data update? Or is it when re-sizing the parent window?

The slight flicker occurs when the text control is updated with SetWindowText, the text control is a count down display. By using the code in my OP with the extra bitblt, i get slightly more of the minor flicker. This is updating the control at about 10 times a second. If i only use my transparent background code(Not show in this question) i does it much less. The flicker im talking about though is not like the normal flicker where the whole background of what you are updating shows white or some other color. Its more like any text in the control does draw properly or even doesnt show up right away. Its a very minor flicker in comparison to the normal flicker the MFC will cause without the fix. For the time being this is good enough as the control doesnt need to update with such a high frequency normally.


BTW: If you want to just change the color of the text of a CStatic IMO what you're doing here is overkill (sorry ;o) - you can easily solve this by ha dling WM_CTLCOLOR message within CTransparentStatic by message reflection somehow like this:

Declare this in header:
> afx_msg HBRUSH CtlColor( CDC* pDC, UINT nCtlColor );

Add this to message map:
> ON_WM_CTLCOLOR_REFLECT()

Add this:
HBRUSH CTransparentStatic::CtlColor( CDC* pDC, UINT nCtlColor )
{
      pDC->SetTextColor( RGB( 54,54,112 ) );
      pDC->SetBkMode( TRANSPARENT );
      return (HBRUSH)GetStockObject( NULL_BRUSH );
}

Thanks for the tip. Is that something you came up with, or did you read about that from a book/tutorial?

If it's still flickering then I'm sure it has to do something with the parent window and the way its background bitmap is drawn.

Maybe, Ill have to go over that code again.

ZOPPO

Thanks for the Solution(s)!

Author

Commented:
Accepted myself also as Zoppo verified code i wrote.
You're welcome - I'm glad I could help ...

> Is that something you came up with, or did you read about that from a book/tutorial?
I know about this since at least 10 years but cannot really remember where I saw it first, but I guess I found it at www.codeguru.com, there I found a lot of helpful hints.

Best regards,

ZOPPO

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial