CreateCompatibleBitmap and CreateDIBSection (Memory DC's) - c++

from what I've read here it seems that most of the Windows GDI functions are accelerated. So for instance a call to BitBlt or AlphaBlend uses hardware acceleration if available. It also mentions that the contents of a window are kept only in video memory. Now this is all good and true for a window DC, but how can I use a memory DC that resides in video card memory? And once we've accomplished that how to obtain direct access to the pixels, I think that would involve 1. temporary copying the data to system memory 2. alter the pixel data 3. copy back to video memory.
I've tried two approaches, both allocate system memory as I can see in the task manager...
CreateCompatibleBitmap
HDC hDC = GetDC(NULL);
m_hDC = CreateCompatibleDC(hDC);
m_hBmp = CreateCompatibleBitmap(hDC, cx, cy);
ReleaseDC(NULL, hDC);
m_hOldBmp = (HBITMAP)SelectObject(m_hDC, m_hBmp);
and then call to obtain the bits
GetBitmapBits(...)
according to various comments this should indeed create the compatible bitmap in video memory, but why can I still see an increase in system memory (even when I don't call GetBitmapBits)?
CreateDIBSection
HDC hDC = GetDC(NULL);
m_hDC = CreateCompatibleDC(hDC);
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = cx;
bmi.bmiHeader.biHeight = -cy; // top-down
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
m_hBmp = CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS, (void**)&m_pBits, NULL, NULL);
ReleaseDC(NULL, hDC);
m_hOldBmp = (HBITMAP)SelectObject(m_hDC, m_hBmp);
in this case we receive the pointer to the bits immediately (m_pBits) so it's obvious that these reside in system memory...
Or is it a copy that is kept in system memory for both methods? But if I change the bits in system memory a call to BitBlt would still have to check/copy from system memory again... not very optimized IMHO.
EDIT: I've also tried creating memory DC's by using the BeginBufferedPaint and GetBufferedPaintBits. It allocates system memory as well, so in that respect I suppose it's just a wrapper for the above methods but caches the DC's so a next call doesn't necessarily has to recreate a memory DC. See Raymond Chen's article.
EDIT #2: I guess the actual question is: Am I doing the memory DC creation correct in method 1 or 2 to get hardware accelerated GDI operations? To me it all seems fast, and both methods provide the same speed too, so there's not really a way to check it...

Memory DCs are not created on a device. They are designed to put GDI output into memory.
From Memory Device Contexts on MSDN:
To enable applications to place output in memory rather than sending
it to an actual device, use a special device context for bitmap
operations called a memory device context. A memory DC enables the
system to treat a portion of memory as a virtual device.
If you want hardware accelerated 2d graphics, you should consider using Direct2D.

Related

C++: Avoid flickering using WM_ERASEBKGND-Message in MFC-Application

in the OnDraw()-Method i create a Bitmap and blit it to the output every time the window size changes:
void CmbmView::OnDraw(CDC* pDC)
{
CRect WindowSize;
HDC hdc;
BITMAPINFO pbmi;
HBITMAP hbm;
CBitmap *pBitmap;
CDC MemDC;
void* ppvBits;
GetClientRect(WindowSize);
hdc = CreateDC (TEXT ("DISPLAY"), NULL, NULL, NULL) ;
memset(&pbmi, 0, sizeof(BITMAPINFO));
pbmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi.bmiHeader.biWidth = WindowSize.Width();
pbmi.bmiHeader.biHeight = -WindowSize.Height(); // top down
pbmi.bmiHeader.biPlanes = 1;
pbmi.bmiHeader.biBitCount = 32;
pbmi.bmiHeader.biCompression = BI_RGB;
hbm = CreateDIBSection(hdc, &pbmi, DIB_RGB_COLORS, &ppvBits, NULL, NULL);
pBitmap = CBitmap::FromHandle(hbm);
MemDC.CreateCompatibleDC(pDC);
MemDC.SelectObject(pBitmap);
// "Draw" into ppvBits
GetDocument()->DrawApple(pDC, ppvBits, WindowSize.Width(), WindowSize.Height(), m_MaxIter, m_MaxBetragQuadrat, m_BW);
// Blit it to the output
pDC->BitBlt(0, 0, WindowSize.Width(), WindowSize.Height(), &MemDC, 0, 0, SRCCOPY);
}
But every time the application needs to recreate the bitmap in OnDraw(), the Screen gets white until it blits the bitmap to the screen. How can i use the WM_ERASEBKGND-Message to avoid this flickering?
I don't know with MFC but with the native Windows API you have to process the WM_ERASEBKGND message and simply return TRUE;. This tells the default window procedure that the message is processed hence the window background is erased. As a result the flickering disappears.
Additionally if you're using function InvalidateRect(..) be sure to set parameter bErase to FALSE. Note that the parameter is TRUE by default if not explicitly given.
In an application where rendering the window content took nontrivial amounts of time I took the following steps:
when the screen needs a redraw, blit its content from a bitmap
if the underlying data changes, kick off a thread to render that data into a new bitmap (if that thread is already running, just set a flag)
when the rendering thread finishes, exchange the stored bitmap with the thread result and invalidate the window (if the according flag is set, restart rendering right away)
when the window is resized trigger rendering and stretch-blit the bitmap to the window
when the view is scrolled, blit those parts that are available and trigger rendering
The important benefit is not just that you don't have flickering, but also that the application remains responsive while a thread in it is busy rendering the data into a graphic. In the implementation, apart from the usual multithreading issues, there are a few important things:
Don't run multiple threads in the background at once, as that can degrade performance. If you just stretch the window with the mouse you can easily generate tens of resize messages and you neither want to waste the time nor the amount of memory for that.
Only render the visible parts, as with virtual sizes of a scrollview the bitmap can become really large. In order to make scrolling easier you can add a frame (e.g. 1/5th of the width/height) to keep some additional data available while preparing a new bitmap in the background.

CreateDibSection on Disk instead of physical memory

I am having a memory issue with an algorithm I am using to "Flatten" a page in a PDF document.
HBITMAP hbmp = CreateDibSection(...);
ThirdPartyBmpManipulation(hbmp, "C:\\file.pdf", 0); //renders page 0 in file.pdf
void * hdib = ConvertBitmap(hbmp); //copy a Dib Section to a Dib
DeleteObject(hbmp); //frees the HBitmap while the Dib is now in memory
The problem is I have a really large bitmap and in some cases I cannot keep the HBitmap in memory while I allocate the DIB to be copied to.
So it is a long shot, but can I somehow allocate the Dib Section on disk and still have an HBITMAP for it? (use the same handle for my ConvertBitmap function)
What is the failure mode? You say "physical memory": windows uses virtual memory, it will page to disk, you don't need to worry about running out of physical memory beyond the performance hit. If you're running out of address space, using the disk may not be able to help you.
However, CreateDIBSection can take a HANDLE to a file mapping (created with CreateFileMapping ). If the internals of the function and related HBITMAP functions are smart enough, it may be possible to avoid exhausting your address space by leveraging that capability. If they are "smart" they will use MapViewOfFile to map relatively small "windows" of the file as needed into your process's address space.

CreateCompatibleBitmap failing on Windows mobile 6

I'm porting an application from Windows Mobile 2003 to Windows Mobile 6, under Visual Studio 2008. The target device has a VGA resolution screen, and I was surprised to find that the following code fails;
CClientDC ClientDC(this);
CRect Rect;
GetClientRect(&Rect);
int nWidth = Rect.Width(),nHeight = Rect.Height();
CBitmap Temp;
if (!Temp.CreateCompatibleBitmap(&ClientDC,nWidth,nHeight))
{
LogError(elvl_Debug,_T("Error creating bitmap (%s)"),LastSysError());
} else
{
BITMAP bmpinfo;
Temp.GetBitmap(&bmpinfo);
}
The return code from CreateCompatibleBitmap is 8, which translates to 'Not enough memory to process command. nWidth is 350, nHeight is 400, and the display is 16 bits per pixel, so my bitmap is a whopping 280K. The device I'm using has 256mb of program memory, and I've told the linker to reserve 4mb of stack and 64mb of heap. Any ideas what I'm doing wrong, and more importantly a solution? I've been using code similar to the above on Windows CE since CE 2.1 with no problems.
Edit: As per Josh Kelly's post, I moved to device independent bitmaps which works fine on the device. Code is now something like this
CClientDC ClientDC(this);
CRect Rect;
GetClientRect(&Rect);
int nWidth = Rect.Width(),nHeight = Rect.Height();
BITMAPINFOHEADER bmi = { sizeof(bmi) };
bmi.biWidth = nWidth;
bmi.biHeight = nHeight;
bmi.biPlanes = 1;
bmi.biBitCount = 8;
HDC hdc = CreateCompatibleDC(NULL);
BYTE* pbData = 0;
HBITMAP DIB = CreateDIBSection(hdc, (BITMAPINFO*)&bmi, DIB_RGB_COLORS, (void**)&pbData, NULL, 0);
CBitmap *pTempBitmap = CBitmap::FromHandle(DIB);
I haven't done any Windows CE / Windows Mobile programming, but I have dealt with a similar problem (CreateCompatibleBitmap failing with ERROR_NOT_ENOUGH_MEMORY) in desktop Windows. Apparently, from what I've been able to tell from looking around online, Windows may enforce global limitations on the available memory for device dependent bitmaps. (For example, some video drivers may choose to store device dependent bitmaps in video RAM, in which case you're limited by how much RAM is on your video card.) See, for example, this thread. From what I can tell, these limits are determined by the individual video cards or drivers; some computers' storage may be effectively unlimited, others may have strict limits.
One solution is to use device independent bitmaps instead, even though they have a slight performance penalty.

How to draw on given bitmap handle (C++ / Win32)?

I'm writing an unmanaged Win32 C++ function that gets a handle to a bitmap, and I need to draw on it.
My problem is that to draw I need to get a device context, but when I do GetDC (NULL), it gives me a device context for the WINDOW! The parameter for GetDC () is a window handle (HWND), but I don't have a window; just a bitmap handle.
How can I draw on this bitmap? Thanks!
In addition to Pavel's answer, the "compatible with the screen" always bugged me too, but, since CreateCompatibleDC(NULL) is universally used for that purpose, I assume it is correct.
I think that the "compatible" thing is related just to DDB (the DC is set up to write on the correct DDB type for the current screen), but does not affect read/writes on DIBs.
So, to be safe, always use DIBs and not DDBs if you need to work on bitmaps that doesn't just have to go temporarily onscreen, nowadays the difference in performance is negligible. See here for more info about DIBs and DDBs.
CreateCompatibleDC() and SelectObject() your bitmap into it.
However, not every bitmap can be selected into any DC.
You might have to play with mapping mode and other options of memory DCs.
The basic win32 paradigm for drawing on a bitmap is that you select the bitmap onto a device context, after which, all drawing operations on that device context are stored in the bitmap. You then use one of the various 'blit' operations (e.g. StretchBlt) to transfer this to a display surface, which is just the device context of a window client area.
Others have provided better detail, this is just the high-level view.
Well, this is a bit outside the box.. I guess.. But I do know that Graphics can return a HDC, and Graphics take a Bitmap as an argument to its ctor . A Bitmap in turn can be created from a HBITMAP and a HPALETTE. The only problem here is that I do not know if the HPALETTE argument can be NULL.
Graphics* g;
Bitmap* bitmap;
HBITMAP _bitmap; // <- this one is yours
bitmap = Bitmap::FromHBITMAP(_bitmap, NULL);
g = new Graphics(bitmap);
HDC hdc = g->GetHDC();
// when done, call g->ReleaseHDC(hdc);
However, I would urge you to receive the HDC as an argument to your function as well.. I do not think that anyone will have a BITMAP and NOT have the DC to it.
If you're having these issues with finding a HDC to a HBITMAP, so will everyone else.

Performing full screen grab in windows

I am working on an idea that involves getting a full capture of the screen including windows and apps, analyzing it, and then drawing items back onto the screen, as an overlay.
I want to learn image processing techniques and I could get lots of data to work with if I can directly access the Windows screen. I could use this to build automation tools the likes of which have never been seen before. More on that later.
I have full screen capture working for the most part.
HWND hwind = GetDesktopWindow();
HDC hdc = GetDC(hwind);
int resx = GetSystemMetrics(SM_CXSCREEN);
int resy = GetSystemMetrics(SM_CYSCREEN);
int BitsPerPixel = GetDeviceCaps(hdc,BITSPIXEL);
HDC hdc2 = CreateCompatibleDC(hdc);
BITMAPINFO info;
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biWidth = resx;
info.bmiHeader.biHeight = resy;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = BitsPerPixel;
info.bmiHeader.biCompression = BI_RGB;
void *data;
hbitmap = CreateDIBSection(hdc2,&info,DIB_RGB_COLORS,(void**)&data,0,0);
SelectObject(hdc2,hbitmap);
Once this is done, I can call this repeatedly:
BitBlt(hdc2,0,0,resx,resy,hdc,0,0,SRCCOPY);
The cleanup code (I have no idea if this is correct):
DeleteObject(hbitmap);
ReleaseDC(hwind,hdc);
if (hdc2) {
DeleteDC(hdc2);
}
Every time BitBlt is called it grabs the screen and saves it in memory I can access thru data.
Performance is somewhat satisfactory. BitBlt executes in 50 milliseconds (sometimes as low as 33ms) at 1920x1200x32.
What surprises me is that when I switch display mode to 16 bit, 1920x1200x16, either through my graphics settings beforehand, or by using ChangeDisplaySettings, I get a massively improved screen grab time between 1ms and 2ms, which cannot be explained by the factor of two reduction in bit-depth. Using CreateDIBSection (as above) offers a significant speed up when in 16-bit mode, compared to if I set up with CreateCompatibleBitmap (6-7ms/f).
Does anybody know why dropping to 16bit causes such a speed increase? Is there any hope for me to grab 32bit at such speeds? if not for the color depth, but for not forcing a change of screen buffer modes and the awful flickering.
I solved my original question about the incredible speed up of switching to 16 bit color mode. Turns out it was causing the Aero theme to be disabled, which accounts for most of it. With Aero off in 32bit it is just about as fast.
Link to other topic with a good answer.