Search code examples
c++bitmapresizewindowgdi

Resizing the window results in bitmaps being overwritten


I've been messing around with bitmaps and rendering in Visual C++ and hit a weird problem.

I'm trying to get the drawing area to resize when the user resizes the window. Right now, not only does enlarging the window not enlarge the visible drawing area (white borders appear outside of where the original window bounds were), but changing the window size seems to be messing with my bitmaps.

Here's what the game looks like on start-up:

enter image description here

After changing the width or height of the window, the purple character has now been replace by the orange one.

enter image description here

And finally, all three become blue.

enter image description here

If I keep going, all the characters will be overwritten by the green grass background bitmap.

enter image description here

It seems to happen in the reverse order that the bitmaps are rendered in. Adding a second row of characters confirmed it's a not a problem in the rendering code - the actual bitmaps in memory are getting changed somehow. Removing the resize code causes the bitmap error to stop repro-ing.

It's almost certainly a mistake in my resize code, when I try to reinitialize the device contexts, but I'm not sure what it is.

Here's the relevant set-up code:

HBITMAP player_blue = (HBITMAP) LoadImage (NULL, L"Images/test_player_blue.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
HBITMAP player_orange = (HBITMAP) LoadImage (NULL, L"Images/test_player_orange.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
HBITMAP player_purple = (HBITMAP) LoadImage (NULL, L"Images/test_player_purple.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
HBITMAP background_bitmap = (HBITMAP) LoadImage (NULL, L"Images/test_background.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

  HDC hdc = GetDC (Window);
  HDC hdc_temp = CreateCompatibleDC (hdc);
  HBITMAP bitmap_buffer = CreateCompatibleBitmap (hdc, resize.screen_width, resize.screen_height);
  SelectObject (hdc, bitmap_buffer);

The main loop from wWinMain():

  while (!Exit)
    {
    while (PeekMessage (&msg, nullptr, 0, 0, PM_REMOVE))
      {
      DispatchMessage(&msg);
      }
    Render ();
    }

The callback function where the resizing is done:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  {
  switch (message)
    {
    case WM_SIZE:

      RECT window_rect;
      if (GetWindowRect (Window, &window_rect))
        {
        hdc = GetDC (Window);
        hdc_temp = CreateCompatibleDC (hdc);
        bitmap_buffer = CreateCompatibleBitmap (hdc, resize.screen_width, resize.screen_height);
        SelectObject (hdc, bitmap_buffer);

        screen_width = window_rect.right - window_rect.left;
        screen_height = window_rect.bottom - window_rect.top;
        }
      break;

    default:
      return DefWindowProc(hWnd, message, wParam, lParam);
    }
  return 0;
  }

The actual rendering via BitBlt():

void Render ()
  {
  SelectObject (hdc_temp, background_bitmap);
  BitBlt (hdc, 0, 0, 800, 600, hdc_temp, 0, 0, SRCCOPY);

  SelectObject (hdc_temp, player_blue);
  BitBlt (hdc, 0, 0, 126, 126, hdc_temp, 0, 0, SRCCOPY);
  SelectObject (hdc_temp, player_orange);
  BitBlt (hdc, 126, 0, 126, 126, hdc_temp, 0, 0, SRCCOPY);
  SelectObject (hdc_temp, player_purple);
  BitBlt (hdc, 252, 0, 126, 126, hdc_temp, 0, 0, SRCCOPY);
  }

Solution

  • As Ken commented, you are leaking precious GDI resources. A few more resizes and your window might go all black :)

    Please see https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createcompatibledc :

    When you no longer need the memory DC, call the DeleteDC function. You probably don't need to create that DC on every resize.

    Same goes for CreateCompatibleBitmap.