author
Tejashwi Kalp Taru
Engineer, Tinkerer, Blogger
Reading time about 7 minutes

Flicker-free text scrolling with Double buffering in Windows


cover

If you are new to Windows native programming and ever tried to scroll a text on UI, you know the issue of flickering, that is, the text is not properly drawn over the UI or image. This problem is due to improperly handled Windows dialog paint messages and procedures and can be avoided with the double buffering technique.

Double buffering technique

The double buffering technique uses a memory buffer instead of directly drawing on the surface screen, and hence all the pain operations are done in memory and finally the buffer is drawn. This will reduce the graphic operation and only one graphic operation is performed on screen, it will eliminate the flicker.

What we will do is:

  • create a buffer
  • draw the image in the memory buffer
  • draw our scrolling text in the memory buffer
  • finally draw the buffer to screen and free any resources which are acquired

To do this, we need an array of text which will be displayed in the scroll. Then we will calculate the size taken by the total text, paint the image in the dialog box using the WM_PAINT message and use the DrawText() function.

Calculating the size required for the text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Counts the number of lines in scroll text
int TextLen = lstrlen(Text);
int nLines = 0;
for(int i=0;i<TextLen;i++) {
  if(Text[i]=='\n')
    nLines++;
}

// Gets the size of total scroll texts
SIZE sizeAboutText;
HDC hDC = GetDC(hWnd);
HDC hDCMem = CreateCompatibleDC(hDC);
SelectObject(hDCMem, hFont);
GetTextExtentPoint32A(hDCMem, Text, TextLen, &sizeAboutText);

// Calculates the needed size for given scroll text
GetClientRect(hWnd, &rcClient);
rcText.bottom += (sizeAboutText.cy * (nLines+3))+rcClient.bottom;
rcText.top = rcClient.bottom;
rcText.right = rcClient.right;
rcText.left = rcClient.left;
Getting the size of the image

Now when we have the RECT size needed for the text scroll in the dialog box, we will need the height and width of the image which we are going to paint in the background of the dialog box. We will need them when we will use double buffer later on. To find the height and width of the BMP image, we are going to use a simple set of functions.

1
2
3
4
5
6
// Loads the bitmap, get its CX,CY and get compatible device context 
hSkin   = LoadBitmap(hIns, MAKEINTRESOURCE(IDB_BITMAP1));
BITMAP bm = {0};
GetObject( hSkin, sizeof(bm), &bm );
cx = bm.bmWidth;
cy = bm.bmHeight;
The double buffer painting

Now we have all the required knowledge in our hand, let’s draw then

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
case WM_PAINT:
{
	PAINTSTRUCT ps;
	if(BeginPaint(hWnd,&ps)) {
		//Creating double buffer
		HDC hdcMem = CreateCompatibleDC(ps.hdc);
		int ndcmem = SaveDC(hdcMem);
		HBITMAP hbmMem = CreateCompatibleBitmap(ps.hdc, cx, cy);
		SelectObject(hdcMem, hbmMem);
		//-------------------------------------------------------

		// Copy background bitmap into double buffer
		BitBlt(hdcMem, 0, 0, cx, cy, hdcBackground, 0, 0, SRCCOPY);
		//---------------------------------------------------------

		// Draw the text
		SelectObject(hdcMem, hFont);
		SetTextColor(hdcMem, CLR);
		SetBkMode(hdcMem, TRANSPARENT);
		DrawText(hdcMem, Text, -1, &rcText, DT_CENTER | DT_TOP | DT_NOPREFIX | DT_NOCLIP);
		//-----------------------------------------------------------------------------

		// Copy double buffer to screen
		BitBlt(ps.hdc, 0, 0, cx, cy, hdcMem, 0, 0, SRCCOPY);
		//--------------------------------------------------

		// Clean up
		RestoreDC(hdcMem, ndcmem);
		DeleteObject(hbmMem);
		DeleteDC(hdcMem);
		EndPaint(hWnd, &ps);
		//--------------------
	} else {
		MessageBox(hWnd, "Can not start painting!", "Error", MB_ICONERROR);
	}
}
return TRUE;

Now let me explain the code provided above. In our WM_PAINT message, we started the painting using the BeginPaint function, and if that failed we will show an error message. When the painting started, we created a context for memory device by calling CreateCompatibleDC function and saved that for later use.

We created a bitmap on which we will be drawing all our text and image and then we will draw this bitmap to screen, for this we used the function CreateCompatibleBitmap and selected this bitmap for drawing using SelectObject function.

Once we have selected our object, our double buffer is ready for some drawing. Because we need to draw an image to the screen over which the text will be scrolled, we copy that bitmap (image) to our buffer using BitBlt function, and then we draw our text over it.

Finally, when text is drawn over the image in memory (double buffer), we copy the double buffer to screen and clean any resource acquired during this process.

You can download the complete source code from here

comments powered by Disqus