I have a function in which a bunch of stuff is drawn into an offscreen buffer. At the end of the function, it calls InvalidateRect. For some reason, sometimes it redraws halfway through the function, causing a flicker. Here's the code for the function:
// Side Info
HBITMAP side = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_SIDEINFO));
hdcold = (HBITMAP)SelectObject(hbcmem, side);
BitBlt(hdcmem, 339, 26, 154, 300, hbcmem, 0, 0, SRCCOPY);
DrawLevelNumber(game.levelnumber);
if (color)
sprites = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_COLOR_SPRITES));
else sprites = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BLACKWHITE_SPRITES));
hdcold = (HBITMAP)SelectObject(hbcmem, sprites);
// Find x and y coordinate for the top left of the visible screen
int x = game.Player_x, y = game.Player_y, ypos = 0;
if (x < 4) x = 4;
if (x > 27) x = 27;
if (y < 4) y = 4;
if (y > 27) y = 27;
x -= 4;
y -= 4;
// Draw lower layer
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if (game.Layer_Two[x + i][y + j] != 0)
{
int xpos = game.get_pos(game.Layer_Two[x + i][y + j], ypos, false);
BitBlt(hdcmem, (i * 32) + 32, (j * 32) + 32, 32, 32, hbcmem, xpos, ypos, SRCCOPY);
}
}
}
// Draw upper layer
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if ((game.Layer_Two[x + i][y + j] != 0 && game.Layer_One[x + i][y + j] >= 64 && game.Layer_One[x + i][y + j] <= 111))
{
int xpos = game.get_pos(game.Layer_One[x + i][y + j], ypos, true);
BitBlt(hdcmem, (i * 32) + 32, (j * 32) + 32, 32, 32, hbcmem, xpos + 96, ypos, SRCPAINT);
BitBlt(hdcmem, (i * 32) + 32, (j * 32) + 32, 32, 32, hbcmem, xpos, ypos, SRCAND);
} else {
int xpos = game.get_pos(game.Layer_One[x + i][y + j], ypos, false);
BitBlt(hdcmem, (i * 32) + 32, (j * 32) + 32, 32, 32, hbcmem, xpos, ypos, SRCCOPY);
}
}
}
// If it isn't started, show title
if (!game.started)
{
HDC tmphdc = CreateCompatibleDC(hdcmem);
HDC tmp = CreateCompatibleDC(tmphdc);
RECT rc;
GetClientRect(hWnd, &rc);
string str = game.leveltitle.substr(0, game.leveltitle.length() - 1) + "\nPassword: " + game.password;
TCHAR* tch = new TCHAR[str.length()];
mbstowcs_s(NULL, tch, _tcslen(tch), str.c_str(), str.length());
HFONT font = CreateFont(25, 0, 0, 0, FW_BOLD, false, false, false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, NULL);
SelectObject(tmp, font);
DrawText(tmp, tch, str.length(), &rc, DT_CALCRECT);
rc.right += 16;
HBITMAP tmpbm = CreateCompatibleBitmap(hdcmem, rc.right, rc.bottom);
HBITMAP tmpold = (HBITMAP)SelectObject(tmphdc, tmpbm);
HBRUSH hbr = CreateSolidBrush(RGB(255, 255, 255));
HPEN pen = CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
SelectObject(hdcmem, pen);
SelectObject(hdcmem, hbr);
Rectangle(hdcmem, 176 - (rc.right / 2), 243, 177 + (rc.right / 2), 248);
hbr = CreateSolidBrush(RGB(128, 128, 128));
pen = CreatePen(PS_SOLID, 1, RGB(128, 128, 128));
SelectObject(hdcmem, pen);
SelectObject(hdcmem, hbr);
Rectangle(hdcmem, 176 - (rc.right / 2), 294, 177 + (rc.right / 2), 299);
HBITMAP left = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_LEFT));
hdcold = (HBITMAP)SelectObject(hbcmem, left);
BitBlt(hdcmem, 176 - (rc.right / 2) - 4, 243, 4, 56, hbcmem, 0, 0, SRCCOPY);
HBITMAP right = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_RIGHT));
hdcold = (HBITMAP)SelectObject(hbcmem, right);
BitBlt(hdcmem, 176 + (rc.right / 2) + 1, 243, 4, 56, hbcmem, 0, 0, SRCCOPY);
SelectObject(tmphdc, font);
SetTextColor(tmphdc, RGB(255, 255, 0));
SetBkColor(tmphdc, RGB(0, 0, 0));
DrawText(tmphdc, tch, str.length(), &rc, DT_CENTER);
BITMAP structBitmapHeader;
memset( &structBitmapHeader, 0, sizeof(BITMAP) );
HGDIOBJ hBitmap = GetCurrentObject(tmphdc, OBJ_BITMAP);
GetObject(hBitmap, sizeof(BITMAP), &structBitmapHeader);
BitBlt(hdcmem, 176 - (rc.right / 2), 247, structBitmapHeader.bmWidth, structBitmapHeader.bmHeight, tmphdc, 0, 0, SRCCOPY);
}
// If paused
if (game.paused)
{
RECT rc;
rc.top = 32;
rc.left = 32;
rc.bottom = 330;
rc.right = 330;
BitBlt(hdcmem, 32, 32, 288, 288, NULL, 0, 0, BLACKNESS);
HFONT font = CreateFont(50, 0, 0, 0, FW_NORMAL, false, false, false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, NULL);
SelectObject(hdcmem, font);
SetTextColor(hdcmem, RGB(255, 0, 0));
SetBkColor(hdcmem, RGB(0, 0, 0));
DrawText(hdcmem, L"PAUSED", 6, &rc, (DT_CENTER + DT_SINGLELINE + DT_VCENTER));
}
nums = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_NUMBERS));
hdcold = (HBITMAP)SelectObject(hbcmem, nums);
for (int i = 100; i > 0; i /= 10) // Coins left
{
int tmp;
if (i == 100)
tmp = game.coinsleft / 100;
if (i == 10)
tmp = ((game.coinsleft % 100) - (game.coinsleft % 10)) / 10;
if (i == 1)
tmp = game.coinsleft % 10;
if (game.coinsleft < i && i > 1)
tmp = 10;
int ypos = game.get_num_pos(tmp, (game.coinsleft == 0));
BitBlt(hdcmem, 369 + ((3 - log10((double)i)) * 17), 215, 17, 23, hbcmem, 0, ypos, SRCCOPY);
if (i == 100)
tmp = game.timeleft / 100;
if (i == 10)
tmp = ((game.timeleft % 100) - (game.timeleft % 10)) / 10;
if (i == 1)
tmp = game.timeleft % 10;
if (game.timeleft < i && i > 1)
tmp = 10;
if (game.timelimit == 0)
tmp = 11;
ypos = game.get_num_pos(tmp, (game.timeleft < 16 || game.timelimit == 0));
BitBlt(hdcmem, 369 + ((3 - log10((double)i)) * 17), 125, 17, 23, hbcmem, 0, ypos, SRCCOPY);
}
if (game.onhint)
{
HBITMAP sidebg = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_SIDEBG));
hdcold = (HBITMAP)SelectObject(hbcmem, sidebg);
BitBlt(hdcmem, 353, 165, 127, 146, hbcmem, 0, 0, SRCCOPY);
} else {
hdcold = (HBITMAP)SelectObject(hbcmem, sprites); // LOWER SIDE INFO
if (game.key1 > 0)
BitBlt(hdcmem, 352, 247, 32, 32, hbcmem, 192, 160, SRCCOPY);
else BitBlt(hdcmem, 352, 247, 32, 32, hbcmem, 0, 0, SRCCOPY);
if (game.key2 > 0)
BitBlt(hdcmem, 384, 247, 32, 32, hbcmem, 192, 128, SRCCOPY);
else BitBlt(hdcmem, 384, 247, 32, 32, hbcmem, 0, 0, SRCCOPY);
if (game.key3 > 0)
BitBlt(hdcmem, 416, 247, 32, 32, hbcmem, 192, 224, SRCCOPY);
else BitBlt(hdcmem, 416, 247, 32, 32, hbcmem, 0, 0, SRCCOPY);
if (game.key4)
BitBlt(hdcmem, 448, 247, 32, 32, hbcmem, 192, 192, SRCCOPY);
else BitBlt(hdcmem, 448, 247, 32, 32, hbcmem, 0, 0, SRCCOPY);
if (game.water)
BitBlt(hdcmem, 352, 279, 32, 32, hbcmem, 192, 256, SRCCOPY);
else BitBlt(hdcmem, 352, 279, 32, 32, hbcmem, 0, 0, SRCCOPY);
if (game.fire)
BitBlt(hdcmem, 384, 279, 32, 32, hbcmem, 192, 288, SRCCOPY);
else BitBlt(hdcmem, 384, 279, 32, 32, hbcmem, 0, 0, SRCCOPY);
if (game.ice)
BitBlt(hdcmem, 416, 279, 32, 32, hbcmem, 192, 320, SRCCOPY);
else BitBlt(hdcmem, 416, 279, 32, 32, hbcmem, 0, 0, SRCCOPY);
if (game.suction)
BitBlt(hdcmem, 448, 279, 32, 32, hbcmem, 192, 352, SRCCOPY);
else BitBlt(hdcmem, 448, 279, 32, 32, hbcmem, 0, 0, SRCCOPY);
}
RECT rc;
rc.left = 0;
rc.top = 0;
rc.right = 518;
rc.bottom = 401;
InvalidateRect(hWnd, &rc, false);
What is causing the flicker?
Ensure that you handle the WM_ERASEBKGND message and return TRUE, this will prevent the window base class from drawing the background.
Instead of calling InvalidateRect at the end of your drawing routine, place your drawing code inside a WM_PAINT message handler, this ensures your drawing code is called whenever the window is being repainted.
I'm trying to use an off-screen buffer so that I can keep track of changes to the screen before/after WM_PAINT and just copy them through one line in WM_PAINT. Here's some code I have to set up the graphics:
hdc = GetDC(hWnd);
hdcmem = CreateCompatibleDC(hdc);
hbcmem = CreateCompatibleDC(hdcmem);
// Load bitmaps
bg = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BACKGROUND));
side = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_SIDEINFO));
mainCont = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_GAME_CONT));
if(bg == NULL || side == NULL || mainCont == NULL)
ThrowError("A bitmap failed to load.");
// Background
hdcold = (HBITMAP)SelectObject(hbcmem, bg);
BitBlt(hdcmem, 0, 0, 237, 196, hbcmem, 0, 0, SRCCOPY);
BitBlt(hdcmem, 237, 0, 237, 196, hbcmem, 0, 0, SRCCOPY);
BitBlt(hdcmem, 237 * 2, 0, 237, 196, hbcmem, 0, 0, SRCCOPY);
BitBlt(hdcmem, 0, 196, 237, 196, hbcmem, 0, 0, SRCCOPY);
BitBlt(hdcmem, 237, 196, 237, 196, hbcmem, 0, 0, SRCCOPY);
BitBlt(hdcmem, 237 * 2, 196, 237, 196, hbcmem, 0, 0, SRCCOPY);
// Side Info
hdcold = (HBITMAP)SelectObject(hbcmem, side);
BitBlt(hdcmem, 339, 26, 154, 300, hbcmem, 0, 0, SRCCOPY);
// Main Game Container
hdcold = (HBITMAP)SelectObject(hbcmem, mainCont);
BitBlt(hdcmem, 26, 26, 300, 300, hbcmem, 0, 0, SRCCOPY);
hdc, hdcmem, hbcmem, hdcold, bg, side, and mainCont are declared previously. Their scope includes everything in this file (including this code and the code in WM_PAINT).
Here's the code in WM_PAINT:
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
BitBlt(hdc, 0, 0, 518, 401, hdcmem, 0, 0, SRCCOPY);
EndPaint(hWnd, &ps);
For some reason, nothing is being drawn to the screen. I'm racking my brain trying to figure it out. A pointer in the right direction would be much appreciated.
Create a compatible bitmap for your memory device context first, then select that bitmap to the memory dc and it should work !
hdc = GetDC(hWnd); // used only to create compatibles.
hdcmem = CreateCompatibleDC(hdc);
hbcmem = CreateCompatibleDC(hdc);
// Create client-area-sized compatible bitmap.
RECT rc;
GetClientRect(hWnd, &rc);
HBITMAP hbm_memdc = CreateComptibleBitmap(hdc, rc.right, rc.bottom);
HBITMAP hbm_memdc_old = (HBITMAP)SelectObject(hdcmem, hbm_memdc)
ReleaseDC(hdc); // this no longer needed
// now start rendering into hdcmem...
You'll want to keep the old bitmap handle selected out to put it back before destroying your custom one on shutdown. How you manage that is entirely up to you.