CreateCompatibleBitmap failing on Windows mobile 6 - c++

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.

Related

BitBlt not working for Office 2013

I'm using BitBlt to get screenshots of windows on a user's desktop. I've found that all works well for windows from most programs. However, BitBlt returns blank pixels for any Office 2013 windows (Word, Powerpoint, etc).
PrintWindow does work for these Office 2013 windows, but it has higher processor overhead. I'd rather use BitBlt if possible.
Any ideas why BitBlt would not work with Office 2013 windows?
Code snippet below.
Thanks
UPDATE: BitBlt(...) returns true. Changing code to capture entire desktop instead of appWin captures the Office 2013 window correctly, see this post. I can capture the whole desktop and extract the desired window. However, this loses one cool thing with the new DWM that allows the software to capture images from windows that are not in the foreground.
Is there a better way to do this that will allow the system to both capture Office 2013 type windows AND capture images from windows not in the foreground?
UPDATE 2: When doing the BitBlt on the appWin, it looks like the first BitBlt gives valid pixel data, but subsequent BitBlts give blank pixels. Not sure why but may be an important data point.
INT texUSize = 1024;
INT texVSize = 1024;
RECT rect;
GetClientRect(appWin, &rect);
INT appW = rect.right - rect.left;
INT appH = rect.bottom - rect.top;
// create BITMAP in memory, write to texture pixels from BITMAP
HDC hdc = GetDC(appWin);
HDC hdc_App = CreateCompatibleDC(hdc);
HDC hdc_Tex = CreateCompatibleDC(hdc);
HBITMAP hbmp_App = CreateCompatibleBitmap(hdc, appW, appH);
HBITMAP hbmp_Tex = CreateCompatibleBitmap(hdc, texUSize, texVSize);
HBITMAP hbmp_AppOld = (HBITMAP)SelectObject(hdc_App, hbmp_App);
HBITMAP hbmp_TexOld = (HBITMAP)SelectObject(hdc_Tex, hbmp_Tex);
// Due to the new Desktop Window Manager, Windows Vista and Windows 7 can simply BitBlt the app window
// Windows XP has issues with BitBlt if app window is covered by a different window
if (false)//bVista7 || (GetForegroundWindow() == appWin))
{
appOutputDebugStringf(TEXT("-- bitblting"));
BitBlt(hdc_App, 0, 0, appW, appH, hdc, 0, 0, SRCCOPY);
}
else
{
appOutputDebugStringf(TEXT("-- printwindow"));
PrintWindow(appWin, hdc_App, NULL);
}

Getting actual screen dpi/ppi under windows

I would like to get the actual screen dpi/ppi, not the dpi setting used for font in C++.
I tried with the following codes:
Version 1, reports 72 dpi, which is wrong.
SetProcessDPIAware(); //true
HDC screen = GetDC(NULL);
double hSize = GetDeviceCaps(screen, HORZSIZE);
double vSize = GetDeviceCaps(screen, VERTSIZE);
double hRes = GetDeviceCaps(screen, HORZRES);
double vRes = GetDeviceCaps(screen, VERTRES);
double hPixelsPerInch = hRes / hSize * 25.4;
double vPixelsPerInch = vRes / vSize * 25.4;
ReleaseDC(NULL, screen);
return (hPixelsPerInch + vPixelsPerInch) * 0.5;
Version 2, reports 96 dpi, which is the Windows dpi setting for font, but not the actual screen dpi.
SetProcessDPIAware(); //true
HDC screen = GetDC(NULL);
double hPixelsPerInch = GetDeviceCaps(screen,LOGPIXELSX);
double vPixelsPerInch = GetDeviceCaps(screen,LOGPIXELSY);
ReleaseDC(NULL, screen);
return (hPixelsPerInch + vPixelsPerInch) * 0.5;
I'm honestly confused by the answers here.
Microsoft has a GetDpiForMonitor method:
https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx
And monitors DO expose their physical dimensions to tools. You can read your monitors width and height, in centimeters, using the HWiNFO64 tool. So if they're getting it (DDI?), it stands to reason that you can access that information yourself.
Even a different Stack Overflow post mentions using WmiMonitorBasicDisplayParams to get the data.
How to get monitor size
So the top post is flat-out, 100%, wrong.
What you're asking for is, unfortunately, not possible in the general case.
Windows doesn't know the physical screen size. Windows might know that your screen has 1024x768 pixels, but it doesn't know how big the screen actually is. You might pull the cable out of your old 13" screen and connect it to a 19" monitor without changing the resolution. The DPI would be different, but Windows won't notice that you changed monitors.
You can get the true physical dimensions and DPI for a printer (assuming the driver isn't lying), but not for a screen. At least not reliably.
UPDATED
As others have pointed out, there are standards for two-way communication between newer monitors and the OS (EDID), that might make this information available for some devices. But I haven't yet found a monitor that provides this information.
Even if EDID were universally available, it's still not solvable in the general case, as the display could be a video projector, where the DPI would depend on the zoom, the focus, the lens type, and the throw distance. A projector is extremely unlikely to know the throw distance, so there's no way for it to report the actual DPI.
Getting DPI information is found to produce exact value using the below method.
ID2D1Factory* m_pDirect2dFactory;
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory);
FLOAT dpiX, dpiY;
m_pDirect2dFactory->GetDesktopDpi( &dpiX, &dpiY );
I think what you're after is:
GetDeviceCaps(hdcScreen, LOGPIXELSX);
GetDeviceCaps(hdcScreen, LOGPIXELSY);

CreateCompatibleBitmap and CreateDIBSection (Memory DC's)

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.

Unit testing of control drawing in GDI

I have a control written in C++ using WinAPI and I would like to automatically test that it is being drawn properly. I can either compare the drawn image with saved reference images or simply test that specific pixels have specific color. I have both types implemented.
The problem is that the tests now also run every night on a virtual machine which for some reason has only 16bit color depth. This causes the colors to be slightly off. I have tried to come up with colors that wouldn't be changed when drawn in 16bit color depth, but the rounding scheme seems to be rather complicated and I need the tests to be functional in both 32b an 16b color depths.
Another idea was to create an offscreen bitmap that would always have 32b color depth. It would have the benefit that the tests would use the same environment every time,but I couldn't get that to work. How can I create a 32b HBITMAP and HDC regardless of the screen color depth? Or do you have any other tips how to solve the general problem?
Thanks
How can I create a 32b HBITMAP and HDC
regardless of the screen color depth?
it's simple:
BITMAPINFO bmp_info = {};
bmp_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmp_info.bmiHeader.biWidth = width;
bmp_info.bmiHeader.biHeight = height;
bmp_info.bmiHeader.biPlanes = 1;
bmp_info.bmiHeader.biBitCount = 32;
bmp_info.bmiHeader.biCompression = BI_RGB;
HDC mem_dc = CreateCompatibleDC(0);
void *dummy;
HBITMAP bitmap_handle = CreateDIBSection(mem_dc, &bmp_info, DIB_RGB_COLORS, &dummy, NULL, 0);
SelectObject(mem_dc, bitmap_handle));
now draw your button over this DC. Remember to check for errors and to release resources.
Or alternatively, automatically convert your button reference view to actual desktop mode:
HWND desktop = GetDesktopWindow();
HDC desktop_dc = GetDC(desktop);
HDC mem_dc = CreateCompatibleDC(desktop_dc);
RECT rect;
GetClientRect(desktop, &rect);
HBITMAP bitmap_handle = CreateCompatibleBitmap(desktop_dc, rect.right - rect.left, rect.bottom - rect.top);
SelectObject(mem_dc, bitmap_handle);
and now BitBlt your preloaded image over mem_dc. It will be automatically converted to current desktop color mode
I've done GDI unit testing by drawing into a WMF (now EMF) file. It did replicate the resolution and DPI of a source (and the later destination) device, but I don't recall if color depth was a "sticky" attribute. Even if it was, since the fileformat allows you to capture/replay the GDI sequence, you might have a more accurate unit test in the end anyways. We'd interpret the WMF file to make sure we generated what we thought we should.
CreateEnhMetaFile is a starting point.
What about using GDI+ to create the offscreen bitmap, and then drawing to it with GDI - something like this:
int width=64; // or whatever you need
int height=100;
int stride = width*4;
BYTE buffer[stride*height];
Gdiplus::Bitmap bitmap(width, height, stride, PixelFormat32bppARGB, buffer);
Gdiplus::Graphics g (&bitmap);
HDC dc = g.GetHDC();
// drawing code, using WinAPI, to draw to dc
g.ReleaseHDC();
// Now compare the contents of your buffer
More about gdi / gdi+ interop here: http://support.microsoft.com/kb/311221
Change your test to pass if comparison against the 32 bit image or against the 16 bit image is OK. Capture both 32 bit versions and 16 bit versions (running virtual). This is really quick and easy to implement.
You should already have an automatic way to capture the reference images for a known good version of the code. If you don't, do that now, as it will save you time later when you make small changes to the appearance of the control. You now have the reference for regression testing.
Either create a 32bpp offscreen surface like you said, or perform the exact same operation with your comparison reference image so you are testing both at the same bpp. In other words don't do your own rounding; let the GDI system do the same rounding to both surfaces.

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.