Using BitBlt

So,it looks like BitBlt captures an image from a specific place in a window, which allows you to duplicate the image somewhere else without redrawing. But, it looks like it is not really cached and BitBlt captures the image each time. Is there a function to capture an image into memory and do the same thing? I guess I could still use BitBlt, but it seems inefficient to have to capture the image each time.
bitblit is the graphics version of memcpy...
it has no knowledge of any 'image'. you pass it a place to draw(inherent in the call, '*this' is where it will draw) (the device context) and what to draw (a DC with the 'image' data in it) and off it goes. It runs PDQ. You will be hard pressed to find something faster.

its up to YOU to cache the image that it will copy.

it tends to look something like
myscreendc.bitblt( where (offset in myscreendc), sourceDC and dimensions, option(SRCCOPY usually);

you may need to get device contexts frequently if the program is resizing or moving around across different monitors etc. Its best to just get them on demand, otherwise you can have bugs and crashes if some user drags to a screen with a different resolution. This is also very fast -- its really just a glorified request to a pointer to existing stuff. As long as you have fast access to the source data that will be copied onto the device, it will run at crazy rates, 100+ FPS type speeds. Drawing a 2d image on a modern computer with a decent graphics card is a trivial operation.

Maybe post a sample of what is not performing well for you?
we have a couple of windows gurus here. I am not one of them, but I make do fairly well. The concept of the device context is critical to getting anything to work right in graphics under the win32 tools. You may as well take a couple of days to study them.
Last edited on
BitBlt is used to transfer data from one device context, usually an offscreen DC, to the app's DC. That way you can draw all you want onto the offscreen DC and when done drawing blit the entire offscreen image. Using an offscreen DC reduces flickering since there is only one blit operation done instead multiple operations.

Using an offscreen DC is AKA "double-buffering."

GDI games, for instance. that don't use GDI+ or DirectX.

What you are trying to do is doable, but HOW you are doing it is IMO grossly inefficient and a PITA.

So you want to move a static image around in the app's client area. Easy peasy with double-buffering.

Another issue can be the timing of the drawing, the frame rate. Too high a frame rate and you overwhelm the app's message pump with constant messaging.

A frame rate on average of 15-20 frames per second coupled with double-buffering gives smooth animation. Moving an image around the client area is animation.

"Beginning Game Programming" - https://www.amazon.com/gp/product/0672326590/

The book may be old, but it teaches good techniques for creating Win GDI games from the ground up. It certainly wouldn't hurt if you got the book and reviewed the "drawing graphics" sections.
Thanks for the replies. I got BitBlt to work, but it was still pretty slow and inefficient. In fact, it was basically the same speed as if I drew the entire image over and over across the window. When I mean slow, I have a 1ms timer under WM_TIMER which calls InvalidateRect() which calls the image update routine under WM_PAINT. It still takes many seconds to draw the image spanning about 725 horizontal pixels (staying on the same vertical). So, not sure why it doesn't just race across the screen. Which is why I was wondering if some sort of buffering would work better. Still with a 1ms timer, not sure what is taking it so long.

The book is a good request. Thanks.
Fair warning, if'n you do get the book buy new so you get the CD that should come with the book. The source code for the games has lots of extras that aren't part of the book text. Like the graphics, etc.

Another warning, the WinAPI/C++ code is outdated. Especially the WinAPI code. It requires some tweaks to get it to work in my experience.

The C++ code could/should be updated to better reflect the changes to the C++ standard since the book was released.

I learned lots just trying to get the code to work on Win10 using Visual Studio 2017/2019.

The games the book creates, using a custom rudimentary game engine, can be addicting even for windowed games. Since the games use GDI full screen isn't an option.

A book that is a couple of years older than the book I linked earlier is "Teach Yourself Game Programming with DirectX in 21 Days" - https://www.amazon.com/Sams-Teach-Yourself-Programming-DirectX/dp/0672324199/

I haven't really done much playing around with that book's source code. DX has radically altered since 2002, and using Direct3D to create a 2D game IMO seems rather heavy-handed.

Just keeping up with self-teaching all of the changes to C++ can be hard to do.

Some day I might set aside a couple of weeks/months for prolonged frustration as I try to get old code to work with a modern Win API. :)

Occasionally I have dabbled at taking the GDI games from the first book and adapting the GDI calls to GDI+.
while waiting for your books, try this:
get rid of invalidate rect. call, in a loop, your bitblt and then *you call* onpaint() without going through the message system at all, just bypass it and force it. strip down the work to nothing but bitblt and painting, in other words. See what that does. Get rid of the timer and everything too. Basically a simple version would be on-init-dialog would call foo() and foo is a loop across the screen of bitblt & onpaint() calls.
you may later need to add threads so you don't lock up your GUI etc, but for now, see if you can satisfy your performance with a minimalist approach.
Last edited on
I have a 1ms timer under WM_TIMER which calls InvalidateRect() which calls the image update routine under WM_PAINT
Why do you do this? That makes things certainly slow. See:

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-invalidaterect

Particularly:
MSDN wrote:
The invalidated areas accumulate in the update region until the region is processed when the next WM_PAINT message occurs or until the region is validated by using the ValidateRect or ValidateRgn function.

The system sends a WM_PAINT message to a window whenever its update region is not empty and there are no other messages in the application queue for that window.
the messaging system is great for its purpose... to get user clicks and process them, mostly.
it isnt for graphics/animation, which is why I said to bypass it and just directly call paint. The problem with calling paint directly in a tight loop is it may lock up the UI so the user can't click or do anything, which is resolved by threading the paint loop. I don't know if this is the 'right' way to do it or the 'best' way but its reasonably fast and has worked for me on relatively simple tasks.
But I think we lost the OP to a book :(
Last edited on
A normal, minimal message loop for Desktop WinAPI:
1
2
3
4
5
6
7
8
9
   MSG          msg;

   // code elided for other app creation tasks

   while (GetMessageW(&msg, NULL, 0, 0))
   {
      TranslateMessage(&msg);
      DispatchMessageW(&msg);
   }

A message loop that uses idle-time processing, with a time delay to trigger an update:
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
   MSG msg;
   int frame_delay { 50 }; // 20 FPS

   // enter the main message loop
   while (TRUE)
   {
      if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
      {
         // process the message
         if (msg.message == WM_QUIT)
         {
            break;
         }

         TranslateMessage(&msg);
         DispatchMessageW(&msg);
      }
      else
      {
         // check the tick count to see if a game cycle has elapsed
         static int iTickTrigger { };

         int iTickCount { GetTickCount() };

         if (iTickCount > iTickTrigger)
         {
            // update the trigger to the next frame
            iTickTrigger = iTickCount + frame_delay;

            // do frame based work here
         }
      }
   }

A bit more complicated than a normal message loop.

Is this the best approach? Maybe it is, maybe it isn't. It isn't a resource hog while constantly looping for the next update frame. And not a whole lot of complicated code.

*FYI* this was adapted from the "Beginning Game Programming" book.
Last edited on
I took out the timer and it does go like 'wildfire' across the screen. With the 1ms timer, it goes really slow. So, I'm trying to get something in the middle. So, I need a better timer to slow it a little.
<chrono> lets you do something like
auto trigger = chrono::high_resolution_clock::now();
if(1.0e-6*(chrono::duration_cast<chrono::microseconds>(auto trigger = chrono::high_resolution_clock::now()-trigger).count()) > 100)
{
trigger = chrono::high_resolution_clock::now(); //reset delay
draw();
}

busy waits are 'bad' (burning cpu to do nothing) but sleep-waits are also 'bad' for some code (may sleep longer than intended or for erratic intervals 'near' but not exactly what was asked for). Real time graphics / animation that needs to hit a fairly consistent frame rate etc probably work better with a busy wait. There are probably better libraries for sleeps/waits out there; I am not a game dev so what little of this I do is KISS stuff like above.

I remember when a ms was a small unit of time to a CPU. If micros are not enough, it supports nanos too; I think that is the current smallest. The multiply isnt necessary (takes you back to seconds). which means I probably goofed, its testing 100 seconds per frame :) but you get the idea
Last edited on
The sole reason why I and the book source use WinAPI tick counting is when the code was written. Before C++11 and <chrono>.

Maybe it is time to "revisit" the code and look at some more modern C++ tweaks I could muck up.

Loading bitmaps originally was seriously old school C. :|

Using LoadImage was a real useful tweak.
before chrono, I had my own assembly code to get the CPU clocks. There just was no better way, and that code got broken and fixed and broken and fixed again as hardware changed; the invention of the variable clock cpu meant we had to get the clocks off something else (was it the north bridge??). Chrono is one of the best additions to c++, regardless. It was very frustrating back in clock() days.
Sleep actually works fairly well for what I was doing. A little herky jerky at times but OK for the function I was performing. Thanks.
Sleep is a blocking function, it stops your app hard from receiving and processing any Windows messages it should receive and process or doing other processing while the function is active.

Yes, I know that.
Registered users can post here. Sign in or register to post.