Thread: DrawDibDraw - Draw into back buffer instead of on screen

  1. #1
    Math wizard
    Join Date
    Dec 2006
    Location
    USA
    Posts
    582

    DrawDibDraw - Draw into back buffer instead of on screen

    I use DrawDibDraw to display graphics. Beforehand, I used my own piece of code that copies contents of an image to get smoother scrolling, but even just a 800x600 image and a 512x384 image on top of it. It was using a monstrous 45% of the CPU and going much beyond 3 more images of the 512x384 size would would be too much for the CPU. In my other tool, I had 40 times more detail than I currently do and it only uses 3% or so of the CPU (if I recall correctly). DrawDibDraw seems to use no CPU whatsoever (it barely registers any CPU usage in Windows Task Manager, about 0.2% on average). What DrawDib-related function do I use to get it to "draw" into a buffer without the high CPU usage? I'm just saying so so I can have it render into a back buffer and BitBlt the two buffers for each frame.

  2. #2
    Math wizard
    Join Date
    Dec 2006
    Location
    USA
    Posts
    582
    I figured I should just post my code:

    Code:
    void DrawScenery()
    {
    	DrawDibBegin(ImageHandle, HDCBack, 0, 0, BackBufferInfoPointer, 0, 0, DDF_BUFFER); // initiate to draw into back buffer
    	DrawDibDraw(ImageHandle, HDCBack, 0, 0, BackgroundInfo.biWidth, BackgroundInfo.biHeight, BackgroundInfoPointer, BackgroundDataPointer, 0, 0, BackgroundInfo.biWidth, BackgroundInfo.biHeight, 0);
    	DrawDibDraw(ImageHandle, HDCBack, Logo.XMain, Logo.YMain, LogoInfo.biWidth, LogoInfo.biHeight, LogoInfoPointer, LogoDataPointer, 0, 0, LogoInfo.biWidth, LogoInfo.biHeight, 0);
    	DrawDibEnd(ImageHandle); // clear the flags
    	
    	if (DebugTest[0] == 0) // used for debugging
    	{
    		DebugTest[0] = 1; // so this test is processed only once
    		DebugTest[1] = (int)BackBuffer[0]; // record the values, 255 means a fault with BitBlt, 0 means the back buffer remains unchanged - this gives 0
    		DebugTest[2] = (int)BackBuffer[1439999]; // opposite corner to be sure on this - also gives 0
    		// DebugTest[1] = HDCBack; // what is the value of the back buffer's HDC?  It shows a nonzero value - it's good
    		// DebugTest[2] = BackBufferPointer; // What is the value of the back buffer's pointer?  It shows a nonzero value
    		
    		sprintf(DebugDetails, "The resulting values are %d and %d.", DebugTest[1], DebugTest[2]); // for debugging
    		MessageBox(hwnd, DebugDetails, "Debug Results", MB_OK);
    	}
    	
    	BitBlt(HDCScreen, 0, 0, WindowSizeBase.x, WindowSizeBase.y, HDCBack, 0, 0, SRCCOPY); // swaps the back and front buffers for displaying
    	DrawDibBegin(ImageHandle, HDCScreen, 0, 0, ScreenBufferInfoPointer, 0, 0, 0); // initiate to draw into front buffer
    	DrawDibDraw(ImageHandle, HDCScreen, 0, 0, ScreenBufferInfo.biWidth, ScreenBufferInfo.biHeight, ScreenBufferInfoPointer, ScreenBufferPointer, 0, 0, ScreenBufferInfo.biWidth, ScreenBufferInfo.biHeight, 0);
    	DrawDibEnd(ImageHandle); // clear the flags
    }
    The pointers are good as they show a nonzero value, but what's happening is that the back buffer isn't being drawn into at all. The "background" one is an all-white image and the "logo" one is colorful. What shows up is an all-black display and from the debugging tests, this is because nothing is being drawn into the back buffer at all. Why is this? I suspect it has something to do with the flags or the improper use of DrawDibBegin. Any ideas?

    The DrawDibDraw functions are good (returning 1), but the DrawDibBegin function, however, is failing, returning 0. Why is it failing?
    Last edited by ulillillia; 04-11-2007 at 09:55 PM. Reason: More clues found

  3. #3
    Math wizard
    Join Date
    Dec 2006
    Location
    USA
    Posts
    582
    I've tried everything I can come up with: swapping DrawDibDC's, using the DDF_BUFFER flag in the DrawDibDraw function itself, changed and updated the pointers, made sure the pointers are valid, everything. DrawDibBegin still fails for some reason and nothing draws into the back buffer (causing it to remain all black), both instances fail which causes DrawDibDraw to not work properly. Commenting out the DrawDibBegin and DrawDibEnd functions causes very strange artifacts to occur because the display is showing each image at different times (sometimes all black, then the bottom half of my test image, the top 1/4 next, all black, and so on). This is because two images are being drawn on screen at the same time. If I could get it to draw into a back buffer, it would display much more nicely. How do you get it to draw into the back buffer? At the top of my code, I have the buffers and things defined as follows, if that helps:

    Code:
    HDRAWDIB ImageHandle; // handle for the DrawDib DC
    HDC HDCScreen; // pointer for front buffer, for rendering
    BITMAPINFOHEADER ScreenBufferInfo;
    LPBITMAPINFOHEADER ScreenBufferInfoPointer;
    LPVOID ScreenBufferPointer;
    unsigned char ScreenBuffer[1440000]; // front buffer, of which is to be displayed
    HDC HDCBack; // pointer for buffer DC, for drawing into
    BITMAPINFOHEADER BackBufferInfo;
    LPBITMAPINFOHEADER BackBufferInfoPointer;
    LPVOID BackBufferPointer;
    unsigned char BackBuffer[1440000]; // back buffer, of which is to be drawn into
    I checked the SDK documentation and I matched everything it stated, but nothing is working at all. Can anyone give any assistance to this?

    Edit: Here's more code related to it:

    Code:
    void DrawScenery()
    {
    	DebugTest[1] = DrawDibBegin(ImageHandle, HDCBack, 0, 0, BackBufferInfoPointer, 0, 0, DDF_BUFFER); // initiate to draw into back buffer // it fails, returning 0
    	DrawDibDraw(ImageHandle, HDCBack, 0, 0, BackgroundInfo.biWidth, BackgroundInfo.biHeight, BackgroundInfoPointer, BackgroundDataPointer, 0, 0, BackgroundInfo.biWidth, BackgroundInfo.biHeight, 0);
    	DrawDibDraw(ImageHandle, HDCBack, Logo.XMain, Logo.YMain, LogoInfo.biWidth, LogoInfo.biHeight, LogoInfoPointer, LogoDataPointer, 0, 0, LogoInfo.biWidth, LogoInfo.biHeight, 0);
    	DrawDibEnd(ImageHandle); // clear the flags
    	
    	BitBlt(HDCScreen, 0, 0, WindowSizeBase.x, WindowSizeBase.y, HDCBack, 0, 0, SRCCOPY); // swaps the back and front buffers for displaying
    	DebugTest[2] = DrawDibBegin(ImageHandle, HDCScreen, 0, 0, ScreenBufferInfoPointer, 0, 0, 0); // initiate to draw into front buffer // it also fails, returning 0
    	DrawDibDraw(ImageHandle, HDCScreen, 0, 0, ScreenBufferInfo.biWidth, ScreenBufferInfo.biHeight, ScreenBufferInfoPointer, ScreenBufferPointer, 0, 0, ScreenBufferInfo.biWidth, ScreenBufferInfo.biHeight, 0);
    	DrawDibEnd(ImageHandle); // clear the flags
    	
    	if (DebugTest[0] == 0)
    	{
    		DebugTest[0] = 1; // so this test is processed only once
    		// DebugTest[1] = (int)BackBuffer[0]; // record the values, 255 means a fault with BitBlt, 0 means the back buffer remains unchanged // these report 0 and 0
    		// DebugTest[2] = (int)BackBuffer[1439999]; // opposite corner to be sure on this
    		// DebugTest[1] = HDCBack; // what is the value of the back buffer's HDC?  It shows a nonzero value - it's good
    		// DebugTest[2] = BackBufferPointer; // What is the value of the back buffer's pointer?  It shows a nonzero value
    		// DebugTest[1] = HDCScreen; // shows a nonzero value
    		// DebugTest[2] = ScreenBufferPointer; // nonzero again
    		
    		sprintf(DebugDetails, "The resulting values are %d and %d.", DebugTest[1], DebugTest[2]); // for debugging
    		MessageBox(hwnd, DebugDetails, "Debug Results", MB_OK);
    	}
    }
    
    void InitializeDrawing()
    {
    	// this initializes handles and headers
    	ImageHandle = DrawDibOpen(); // set the DrawDib DC handle
    	
    	ScreenBufferInfo.biSize = 40; // the size of the struct, always 40 in my case (due to true color being used)
    	ScreenBufferInfo.biWidth = WindowSizeBase.x; // display matches window interior size // 800 pixels
    	ScreenBufferInfo.biHeight = WindowSizeBase.y; // 600 pixels
    	ScreenBufferInfo.biPlanes = 1; // always 1
    	ScreenBufferInfo.biBitCount = 24; // number of bits per pixel, always to be 24 for the display
    	ScreenBufferInfo.biCompression = BI_RGB; // no compression used
    	ScreenBufferInfo.biSizeImage = ScreenBufferInfo.biWidth*ScreenBufferInfo.biHeight*3; // width times height times bitcount divided by 8 gives the image data size
    	ScreenBufferInfo.biXPelsPerMeter = 2835; // resolution in pixels per meter
    	ScreenBufferInfo.biYPelsPerMeter = 2835;
    	ScreenBufferInfo.biClrUsed = 0; // always 0 in my case
    	ScreenBufferInfo.biClrImportant = 0; // same here
    	ScreenBufferInfoPointer = &ScreenBufferInfo; // set the pointers
    	ScreenBufferPointer = &ScreenBuffer;
    	
    	// Back buffer is identical in every way
    	BackBufferInfo.biSize = 40; // the size of the struct, always 40 in my case (due to true color being used)
    	BackBufferInfo.biWidth = WindowSizeBase.x; // display matches window interior size // 800 pixels
    	BackBufferInfo.biHeight = WindowSizeBase.y; // 600 pixels
    	BackBufferInfo.biPlanes = 1; // always 1
    	BackBufferInfo.biBitCount = 24; // number of bits per pixel, always to be 24 for the display
    	BackBufferInfo.biCompression = BI_RGB; // no compression used
    	BackBufferInfo.biSizeImage = BackBufferInfo.biWidth*BackBufferInfo.biHeight*3; // width times height times bitcount divided by 8 gives the image data size
    	BackBufferInfo.biXPelsPerMeter = 2835; // resolution in pixels per meter
    	BackBufferInfo.biYPelsPerMeter = 2835;
    	BackBufferInfo.biClrUsed = 0; // always 0 in my case
    	BackBufferInfo.biClrImportant = 0; // same here
    	BackBufferInfoPointer = &BackBufferInfo; // set the pointers
    	BackBufferPointer = &BackBuffer;
    }
    These two functions are called from within the main function.
    Last edited by ulillillia; 04-14-2007 at 04:14 AM. Reason: Provided more code

  4. #4
    Registered User pronecracker's Avatar
    Join Date
    Oct 2006
    Location
    netherlands
    Posts
    158
    Well it seems no one here has enough knowledge of the in my point of view very weird library you are using. Are you sure you can't just use native Win32 functions? You know DIBs are slower to display

  5. #5
    Math wizard
    Join Date
    Dec 2006
    Location
    USA
    Posts
    582
    What do you mean by "native Win32 functions"? DrawDibDraw is the fastest of the image drawing functions that I've seen with only DirectX being faster, of which I want to avoid using. The SDK mentions it is possible to draw into a back buffer, but it doesn't explain at all how you do it. DrawDibBegin has the DDF_BUFFER flag that allows DrawDib to draw into a back buffer, but it fails no matter how I set it up and it's this that prevents it from working. I've searched Google and that's not getting me anywhere (the formatting I have is correct, the pointers are valid (nonzero), everything I can come up with. Just recently, I've spent 4 hours messing around with various configurations and settings but nothing works at all. DrawDibDraw uses so little CPU that it hardly shows anything in Windows Task Manager.

    All I'm after is trying to get DrawDibDraw to draw into a back buffer. That's all I'm asking and this has been getting on my nerves for 3 or 4 days by now. It's either I use DrawDibDraw to draw the scenes, or, I use my custom-made function that really strains the CPU (40% usage for just 800x600 24-bit color and 512x384 24-bit color images being drawn at 60 fps). My 2D game that I made (and released in August of 2006), The Supernatural Olympics, has far, far more detail than my test program as it is (over 30 times as many pixels to draw, and even then, it still only uses 3% of the CPU and it's poorly designed) and it runs very smoothly (it was created with another tool, but that tool is full of limitations and annoyances, of which lead to the poor programming design). By drawing into a back buffer, I can BitBlt this to the screen buffer to be drawn then redraw the scene in the back buffer. I only need to get it to draw into the back buffer. It's all I'm asking.

    Edit: DrawDibDraw and DrawDibBegin are explained here, but it doesn't explain how to use these to draw into a back buffer (and why DrawDibBegin is failing to begin with).
    Last edited by ulillillia; 04-14-2007 at 07:25 AM. Reason: Links for further details

  6. #6
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    First in Windows GDI you are certainly going to strain the CPU. It was not designed with speed in mind - rather robustness.

    To perform double buffering I setup two DC's and two bitmap's. This is really odd on the half of MS but a DC is NOT a drawing surface. A DC is a device context. In order to do anything with it you must select a bitmap object into it to use as a scratchpad. Now you can bitblt a bitmap with graphic information into a DC and everything works. In other words you cannot perform any type of drawing on a DC without a bitmap being selected into it. However you can bitblt a pre-loaded bitmap into the DC and it will display correctly. After this point you can also draw to the bitmap via the DC.

    What you need to do is setup a memory DC with the same params as your OnPaint() DC. Instead of using the Window DC in your graphic functions you would use the memory DC. Then at the end of the drawing you would use a BitBlt from the memory DC to the Window DC and use SRC_COPY.

    I have no idea what your functions do or how they operate but I can show you code (albeit MFC code) that does double buffering.

    Here is a very small snippet from my setup code for my GDI based tile render class.

    Code:
      m_pMemoryDC=new CDC();
      m_pMemoryDC->CreateCompatibleDC(GetDC());
      
      
      m_pMemoryBitmap=new CBitmap();
      m_pMemoryBitmap->CreateCompatibleBitmap(GetDC(),rect.Width(),rect.Height());
    The memory bitmap is selected into the memory DC later in the code.
    This is MFC code, but you should be able to port quite easily.
    Last edited by VirtualAce; 04-14-2007 at 01:35 PM.

  7. #7
    Math wizard
    Join Date
    Dec 2006
    Location
    USA
    Posts
    582
    Yes! It's working now, after spending an hour fixing a dumb bug I had. It's now working exactly as expected. Thanks.

    I have an 800x600 image being displayed, and a 512x384 image also being displayed and even with that, there's no CPU usage at all. I took a screenshot to help explain the lack of CPU usage (Windows Task Manager is using more CPU than it).

    Here's the screenshot. Sure it shows 3% CPU usage, it's on 0% most of the time (2 seconds it shows 0 then the next half second shows 3%, then its 2 seconds of 0% and so on, thus about 0.6%, hardly anything measurable).

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Largest screen buffer size?
    By Ash1981 in forum C Programming
    Replies: 2
    Last Post: 01-30-2006, 04:31 AM
  2. buffer contents swapping
    By daluu in forum C++ Programming
    Replies: 7
    Last Post: 10-14-2004, 02:34 PM
  3. FAQ: MSVC++ allegro tutorial
    By bobish in forum FAQ Board
    Replies: 3
    Last Post: 02-09-2002, 07:48 PM
  4. Allegro tutorials
    By valar_king in forum Game Programming
    Replies: 6
    Last Post: 12-27-2001, 12:44 PM
  5. need some help with this engine
    By DavidP in forum A Brief History of Cprogramming.com
    Replies: 6
    Last Post: 09-01-2001, 01:27 PM