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.
dodge55,

Would you please tell me how you are using this to create the effect of, "I took out the timer and it does go like 'wildfire' across the screen." I am trying to create an animation with bitmaps and bitblt, but I am struggling with the animation logic. I can bitblt some. Help please. Just a simple example that works please. I am also using C++11.

I found the following and I adjusted it to work within my understanding:

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
38
void Draw_Test_Image_to_New_Position(HDC hdc, RECT* prc)
    {

        // hdcWorkingBackBuffer is a back buffer to blt to.

        //HBITMAP is An object handle that manages bitmap data.
        HBITMAP hbmBuffer = CreateCompatibleBitmap(hdc_MainWindow, prc->right, prc->bottom);


        SelectObject(hdcWorkingBackBuffer, hbmBuffer);


        DeleteObject(hbmBuffer);

        // This puts the animation in place. EACH TIME.

            // This creates a buffer for the animation bitmap to be blitted from.
            HDC hdcAnimationBuffer_001 = CreateCompatibleDC(hdc_MainWindow);


            // BitBlt the mask of the image to hdcWorkingBackBuffer .
            SelectObject(hdcAnimationBuffer_001, Moving_Test_Image_MASK);
            BitBlt(hdcWorkingBackBuffer, TestImage_Info.x, TestImage_Info.y, TestImage_Info.width, TestImage_Info.height, hdcAnimationBuffer_001, 0, 0, SRCAND);


            // BitBlt the color image of the image to hdcWorkingBackBuffer .
            SelectObject(hdcAnimationBuffer_001, Moving_Test_Image);
            BitBlt(hdcWorkingBackBuffer, TestImage_Info.x, TestImage_Info.y, TestImage_Info.width, TestImage_Info.height, hdcAnimationBuffer_001, 0, 0, SRCPAINT);



        // This blits the working (composted) buffer (hdcWorkingBackBuffer) to the front (screen) buffer.
            BitBlt(hdc_MainWindow, 0, 0, prc->right, prc->bottom, hdcWorkingBackBuffer, 0, 0, SRCCOPY);

        DeleteDC(hdcAnimationBuffer_001);

    }


I studied and am using double buffering but it is difficult for me to keep up with the logic.

I call the previous from inside of

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
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {

        switch(msg)
            {

//etcetra other stuff

//Then I use a timer like this:

                case WM_TIMER:
                    {
                        RECT rcClient;

                        GetClientRect(hwnd, &rcClient);

                        Update_X_and_Y_Position_of_Test_Image(&rcClient);
                        Draw_Test_Image_to_New_Position(hdc_MainWindow, &rcClient);

                    }
                    break;


//etcetra other stuff

            }
    }


This is not so fast.

How do you animate your blitting so fast? If I could see how you do it then maybe I can adapt to your logic.

Please cut some out of your code and directly show me how you do this.

Thank you.


Thank you very much.

lose the timer. just go as fast as you can up front, and if that is too fast, then you can throttle it.
again a cheesy way to animate is to have onpaint invalidate() (triggers onpaint again).
if its a graphics themed program you may want to use onpaint as a direct handler instead of a case in the generic handler.

here is a stupid GDI rotating rectangle (I had to make this recently to relearn how to do the rotation matrix stuff) showing how to use the paint handler to animate without a timer, using sleep (which is the worst possible way to do it) as a mild throttle. It does not matter that you use bitblt etc or if you use something smarter than sleep to control it... the point is that the ontimer IS very likely the problem. If you take the sleep out it will animate stupidly fast and if you change the erase to false it will make a sickening kalideoscope effect...

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

   case WM_PAINT:
        {
            static double ang{};
            const double cang{cos(ang)}, sang{sin(ang)};
            ang += 0.175;
            ang = fmod(ang, 6.28);
            PAINTSTRUCT ps;            
            HDC hdc = BeginPaint(hWnd, &ps); 
            //Rectangle(hdc, 100, 100, 200, 200);
            XFORM xForm;
            SetGraphicsMode(hdc, GM_ADVANCED);
            
            SetViewportOrgEx(hdc,
              (150 - (cang * 150)) + ((sang * 150)),
              (150 - (sang * 150)) - ((cang * 150)), nullptr);

            xForm.eM11 = cang; 
            xForm.eM12 = sang;
            xForm.eM21 = -sang;
            xForm.eM22 = cang;
            xForm.eDx = 0;// (theCenterPt.x - (cos_ang * theCenterPt.x)) + ((sin_ang * theCenterPt.y));
            xForm.eDy = 0;
            SetWorldTransform(hdc, &xForm);

            Rectangle(hdc, 100, 100, 200, 200);
            
            EndPaint(hWnd, &ps);   
            //Sleep(30);
            Sleep(5);
            InvalidateRect(hWnd,0, 1);
        }
        break;
Last edited on
jonnin,

I tried your suggestion and took out the sleep timer and it drew those rectangles so fast that they seemed to be almost instantly in all positions at the same time continuously. Thank you.

I am working on how to do this with blitting an animation.

I can take an avi apart frame by frame and save the frames as individual bitmaps. Like b001.bmp, b002.bmp, b003.bmp. And have a timer or something to make it maybe 1 frame per second.

I am trying to use something like my
1
2
void Draw_Test_Image_to_New_Position(HDC hdc, RECT* prc)
   etc.


and put in different frames to remake the animation. This is taking me a while to put together. But, I think that I might be able to make it work.

at some point the invalidate rect trick may fail you, if you get into a big project. If it does, you can try double buffering (drawing to one buffer then display that one while drawing the next one, swapping each time and some parallelism). The invalidate rect will work for quite a lot with modern computers, actually -- a perk of an old tool vs new hardware, but if it fails you and you need help ask again.
Topic archived. No new replies allowed.