Flicker-free text scrolling with Double buffering in Windows
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