Can I create more than one bitmaps for compatible DC? - c++

Is it right to create two bitmaps for one DC?
My situation is I have
dc = CreateCompatibleDC(NULL);
bitmap = CreateDIBSection(dc, pbmi1, DIB_RGB_COLORS, (void **) &bmpBits, NULL, 0);
extra_bitmap = CreateDIBSection(dc, pbmi2, DIB_RGB_COLORS, (void **) &extraBmpBits, NULL, 0);
The difference is: bitmap is width*height , extra_bitmap is width/2*height*2
I need to show both images on the screen according to requirements from users.
I tried these code above, it can run (based only on my limited test). But I am not sure if it is the right way to do it. Any suggestions and/or comments are welcome. Thanks.
By the way, if I can have two bitmaps associated with one DC, what is the right way to release them? Can I just call DeleteDC(dc)? Thanks.

Bitmaps are only associated with a DC when they're selected into them via SelectObject. The DC you pass to the CreateDIBSection is only used to define the layout/palette of the newly created bitmap, and only in some circumstances, but it doesn't irrevocably associate that bitmap with the DC or anything like that.
To draw a bitmap to a window, it needs to be selected into a DC (the "source DC"), and this is where you can only have one at a time.
For example:
HDC hdcTarget = GetDC(hWnd); // or hdcTarget = BeginPaint(...)
HDC hdcSource = CreateCompatibleDC(hdcTarget); // create a "source DC")
HGDIOBJ hOldBmp = SelectObject(dc, bitmap); // select bitmap 1 in
BitBlt(hdcTarget, x, y, w, h, hdcSource, 0, 0, SRCCOPY); // draw bitmap 1
SelectObject(hdcSource, extra_bitmap); // select bitmap 2 in
BitBlt(hdcTarget, x1, y1, w1, h1, hdcSource, 0, 0, SRCCOPY); // draw bitmap 2
SelectObject(hdcSource, hOldBmp); // restore previous bitmap
DeleteDC(hdcSource); // no longer needed
ReleaseDC(hWnd, hdcTarget); // or EndPaint(...)

After search around for a few hours, I get
http://www.ucancode.net/Visual_C_MFC_Samples/CreateDIBSection-BITMAPINFOHEADER-CreateCompatibleDC-BITMAPINFO.htm
In this example:
// Create HDCs to hold our surfaces.
hdcSrc1 = CreateCompatibleDC(hdcDest);
if (!hdcSrc1) goto HANDLEERROR;
hdcSrc2 = CreateCompatibleDC(hdcDest);
if (!hdcSrc2) goto HANDLEERROR;
For delete:
DeleteDC(hdcSrc1);
DeleteDC(hdcSrc2);
DeleteObject(hbmSrc1); // hbmSrc1 and hbmSrc2 are created using hbm = CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS,(void **)&pBits, NULL, 0);
DeleteObject(hbmSrc2);
Hope this can be helpful for others. thanks.

You may create as many in-memory bitmaps as you like.
You may create as many device-dependent DCs as you like.
A bitmap may be selected into only one DC at a time. If you try to select the same bitmap into multiple DCs, then you will get unpredictable behavior.
A DC will never have more that one bitmap selected into it at a time, because the act of selecting one in selects the previous one out.
Don't delete a memory DC while your bitmap is still selected into it. You should select the original bitmap (which is a monochrome 1x1 bitmap) into the DC to get your bitmap out before you delete the DC.
Don't delete an in-memory bitmap while it's still selected into a DC.

Related

How do I get a rectangle (or &rc) from a memory hdc without using a handle or HWND

I am using Microsoft Windows (32 bit). Please, no Visual Studio anything, or .net.
I have the following that works if I have a windows handle to use for it:
// I have a handle to a window.
// HWND Handle_Of_SomeWindow
// I previously assigned a handle for that and use it.
// I have some Unicode text that I am using.
wstring SomeWideStringText = L"C++ stole my lunch money.";
// I convert that wstring to LPWSTR using
LPWSTR Text_Being_Added = const_cast<wchar_t*>(SomeWideStringText.c_str());
//I create a rectangle to use in my DrawTextExW
RECT rc;
// If I have a handle to a window then I can do this.
GetClientRect(Handle_Of_SomeWindow, & rc);
//But, if I do not have a handle to a window and if I only have a hdc, then that does not work.
When I have an HDC without a window handle (I think that this is a memory dc but I do not yet understand that so well) which I am using in double buffering, then there is no handle to use. And, I can not get a handle for it. Get handle from DC does not work.
So, my question is How do I get a rectangle or &rc for that to use in my command of :
DrawTextExW(HDC_of_FRONT_BUFFER_001, Text_Being_Added, -1, & rc, DT_WORDBREAK, nullptr);
?
Maybe there might be something else other than a rectangle &rc that I could use, but I have not found it.
I have been studying this and I do not understand how to get a rectangle or a &rc to use.
You're misunderstanding what the rectangle you provide to DrawTextEx is, it's not the size of your bitmap, it's the size you want your text to occupy. It should obviously be less than or equal to the size of your backing bitmap, but it has no other relation to it.
then there is no handle to use. And, I can not get a handle for it. Get handle from DC does not work.
Literally no idea what you're trying to express here.
I do not understand how to get a rectangle or a &rc to use
Again, you aren't handed this, you provide it.
A memory DC has a bitmap selected into it, and that bitmap has a size. In most cases your code created that bitmap, so it should already know the size.
But if it doesn't, you have a couple options.
Option 1: You can select the bitmap out of the DC, get its size, and then select it back in. This is kind of kludgy and I've omitted error checking:
// assuming you're given hdcMem...
HBITMAP hbmpTemp = CreateCompatibleDC(hdcMem, 1, 1);
HBITMAP hbmpActual = SelectObject(hdcMem, hdcTemp);
BITMAP bm = {0};
GetObject(hbmpActual, sizeof(bm), &bm);
// now your size is in bm.bmWidth and bm.bmHeight,
RECT rc = {0, 0, bm.bmWidth, bm.bmHeight};
SelectObject(hdcMem, hdcActual); // put the memory DC back
DeleteObject(hbmpTemp);
Option 2: [untested] You could try to query the DC for its "resolution" caps. I know this works for device DCs, like a monitor or printer. I don't know whether it works for a memory DC.
int width = GetDeviceCaps(hdcMem, HORZRES);
int height = GetDeviceCaps(hdcMem, VERTRES);

Create a HBITMAP which points to an existing memory buffer

I am using the FreeImage library to store and manipulate bitmap data. Part of my code requires me to take a screenshot of a window (in Windows), and store it as a FBITMAP* (FreeImage's bitmap data structure). The method I have worked out of doing this involves two steps: capture the image, then convert it to a FBITMAP*.
To capture the image, I do something like this:
HWND window; // Assume this is a valid handle to my window
int width; // width of window client area
int height; // height of window client area
HDC windowDC = GetDC(window);
HDC captureDC = CreateCompatibleDC(windowDC);
HBITMAP screenshot = CreateCompatibleBitmap(windowDC, width, height);
SelectObject(captureDC, screenshot);
BitBlt(captureDC, 0, 0, width, height,
captureDC, 0, 0, SRCCOPY|CAPTUREBLT);
ReleaseDC(window, windowDC);
DeleteDC(captureDC);
FreeImage provides a function which returns a raw pointer to the pixel data:
BYTE* FreeImage_GetBits(FBITMAP*)
The FAQ explains that a HBITMAP (WinAPI handle to a bitmap) can be converted to a FBITMAP* using GetDIBits, which takes a source HBITMAP and a destination raw pointer as arguments, and copies the pixel data from one to the other.
The problem with this approach is that I have copied the data twice - once in the BitBlt from the window DC to the HBITMAP selected in the memory DC, and then again from the HBITMAP to the FreeImage memory buffer. I wish to remove this inefficiency and copy the data directly to my raw pointer in the BitBlt operation. For this to work, I need a memory DC which has a HBITMAP selected into it, and where that HBITMAP points to my memory buffer instead of to memory that Windows allocated for it.
How can I achieve this?

Drawing multiple bitmaps on one Device Context in MFC - winapi

I want to use a single CDC, inwhich to draw 3 bitmaps, positioned in the CDC, and then pass it into UpdateLayeredWindow. My problem is that I can't get the SIZE* psize parameter of the UpdateLayeredWindow function right! Can Anyone help? What do I do?
BLENDFUNCTION bBlendFnc = {
AC_SRC_OVER,
0,
255,
AC_SRC_ALPHA
};
CBitmap btCdcBuff;
CBitmap* cache;
BITMAP hbCdcBuff;
btCdcBuff.CreateCompatibleBitmap(pCdcMain, szWndSize.cx, szWndSize.cy); btCdcBuff.GetBitmap(&hbCdcBuff);
cache = pCdcMain->SelectObject(&btCdcBuff); // pCdcMain is a compatible CDC with the screen (pCdcScreen)
Blend(&btIcon); // This function just creates a compatible CDC from a CPaintDC, selects the passed in CBitmap via SelectObject and AlphaBlends it to the pCdcMain.
pCdcMain->SelectObject(cache);
UpdateLayeredWindow(pCdcScreen, NULL, new CSize(hbCdcBuff.bmWidth, hbCdcBuff.bmHeight), pCdcMain, new CPoint(0, 0), 0, &bBlendFnc, ULW_ALPHA) // This fails and returns false
When using CreateCompatibleBitmap with UpdateLayeredWindow, make sure you pass in the CDC for the screen (ie, pCdcScreen not pCdcMain), so that the Bitmap created is of the correct format. See this thread: http://social.msdn.microsoft.com/Forums/en/windowsuidevelopment/thread/1fbcf5e4-b9eb-4537-bf0b-d330aa333fea

Unable to implement DIB printing with GDI (MFC)

MFC doc/view architecture, GDI drawing/printing. I have a DIB backbuffer I need to display and print.
After the long and painful road I came to the conclusion than I need to use DIB created with CreateDIBSection (rather than DDB created with CreateCompatibleBitmap), and I have to blit it onto printer dc with StretchDIBits (rather than StretchBlt).
But i'm not able to get this thing to work.
Here is what I do:
In my initialization routine, I setup the backbuffer, like this:
// Prepare device context:
CClientDC aDC(this);
OnPrepareDC(&aDC);
// Setup proper backbuffer:
_pMemDc = new CDC;
_pMemDc->CreateCompatibleDC(&aDC);
memset(&_bitmapInfo, 0, sizeof(BITMAPINFO));
_bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
_bitmapInfo.bmiHeader.biWidth = _sizeBackBuffer.cx;
_bitmapInfo.bmiHeader.biHeight = -_sizeBackBuffer.cy; // top-down
_bitmapInfo.bmiHeader.biPlanes = 1;
_bitmapInfo.bmiHeader.biBitCount = 24; // Maybe 32?
_bitmapInfo.bmiHeader.biCompression = BI_RGB;
HANDLE hMemBitmap = CreateDIBSection(aDC.GetSafeHdc(), &_bitmapInfo, DIB_RGB_COLORS, (void**)&_pBitmapRawBits, 0, 0);
_hOldSelBitmap = (HBITMAP)_pMemDc->SelectObject(hMemBitmap);
Variables with underscores are (private) member variables, declared like this:
CDC *_pMemDc; // Backbuffer memory dc
HBITMAP _hOldSelBitmap;
BITMAPINFO _bitmapInfo; // Backbuffer DIB (header-only)
unsigned char *_pBitmapRawBits; // Pointer to pixel data of DIB
SIZE _sizeBackBuffer; // Size of backbuffer, i.e. size of that DIB
Now below is what I do in my OnDraw override:
Firstly I get the area to be drawn like this (simplified code):
CRect rectClipBoxPlayground;
if (pDC->IsPrinting())
{
rectClipBoxPlayground = _printingParams.pPrintInfo->m_rectDraw;
}
else
{
pDC->GetClipBox(&rectClipBoxPlayground);
}
Then I calculate the corresponding rect coordinates in my backbuffer, which is usually (much) larger than the DC. Details of this calculation are irrelevant here, I just say that
CRect rectClipBoxBackBuffer;
represents the corresponding rect of backbuffer (in pixel coordinates of backbuffer).
Then I draw onto my backbuffer, using the _pMemDc memory dc.
And finally comes the part where I have troubles: blitting my DIB onto target dc (screen or printer). Here is what I do:
// Copy back buffer to screen/printer dc:
pDC->SetStretchBltMode(HALFTONE);
SetBrushOrgEx(pDC->GetSafeHdc(), 0, 0, 0);
// BELOW COMMENTED CODE OF StretchBlt WORKS(!) INSTEAD OF StretchDIBits.
//
//BOOL bSuccess = pDC->StretchBlt(rectClipBoxPlayground.left, rectClipBoxPlayground.top,
// rectClipBoxPlayground.Width(), rectClipBoxPlayground.Height(),
// _pMemDc, rectClipBoxBackBuffer.left, rectClipBoxBackBuffer.top,
// rectClipBoxBackBuffer.Width(), rectClipBoxBackBuffer.Height(), SRCCOPY);
HBITMAP hMemBitmap = (HBITMAP)_pMemDc->SelectObject(_hOldSelBitmap);
DWORD dwLines = StretchDIBits(pDC->GetSafeHdc(),
rectClipBoxPlayground.left, rectClipBoxPlayground.top, rectClipBoxPlayground.Width(), rectClipBoxPlayground.Height(),
rectClipBoxBackBuffer.left, rectClipBoxBackBuffer.top, rectClipBoxBackBuffer.Width(), rectClipBoxBackBuffer.Height(),
_pBitmapRawBits, &_bitmapInfo, DIB_RGB_COLORS, SRCCOPY);
_pMemDc->SelectObject(hMemBitmap);
The problem is, the commented-out code of StretchBlt works flawlessly (except printing on some printers), but I can't use it because some printers have troubles with it. So I have to use StretchDIBits. Note that I firstly unselect the DIB from its memory dc temporarily, so that it is not associated with any dc. Then I use StretchDIBits but things just don't work! Output is messed up, like I give incorrect coordinates, areas are drawn off-set from where they should be drawn, and sometimes totally black. So I must be missing something (maybe something very trivial). Help! I tried changing signs of rectClipBoxBackBuffer.top and bitmapInfo.bmiHeader.biHeight, the results change, but nothing works like it should.
What am I doing wrong??
Dont't select the DIB into/out DC if you are going to use StretchDIBits.
DC can only contain DDB bitmap, if you supply a DIB, DC will convert it.
Microsoft's documentation about StretchDIBits is totally wrong. I found out that direction of Y axis has to be changed. Now following code works:
// Copy back buffer to screen dc:
pDC->SetStretchBltMode(HALFTONE);
SetBrushOrgEx(pDC->GetSafeHdc(), 0, 0, 0);
HBITMAP hMemBitmap = (HBITMAP)_pMemDc->SelectObject(_hOldSelBitmap);
DWORD dwLines = StretchDIBits(pDC->GetSafeHdc(),
rectClipBoxPlayground.left, rectClipBoxPlayground.top, rectClipBoxPlayground.Width(), rectClipBoxPlayground.Height(),
rectClipBoxBackBuffer.left, _sizeBackBuffer.cy - rectClipBoxBackBuffer.top - rectClipBoxBackBuffer.Height(), rectClipBoxBackBuffer.Width(), rectClipBoxBackBuffer.Height(),
_pBitmapRawBits, &_bitmapInfo, DIB_RGB_COLORS, SRCCOPY);
_pMemDc->SelectObject(hMemBitmap);
P.S.: Now it works for screen drawing and print preview, but fails for actual printing, which was my original problem discussed here: How to print DIB backbuffer on printer - GDI, MFC

Win32 Device Context without Window

In my application i need to create HBITMAP objects to which I render and from where I copy the result.
I use the function "CreateDIBSection" to create these bitmaps, however this function required a DC (Device Context) as first parameter. Currently I get this by calling GetDC(hWnd) on the main windows handle (hWnd). But I would like to be able to create HBITMAPS without the requirement of having an application window, without some kind of in memory DC, Is this possible?
CreateCompatibleDC(NULL) will create you a device context that is compatible with the screen - which sounds like it would be ideal in the situation.
You can get one with CreateDC for the display:
HDC hDc = CreateDC(L"DISPLAY", NULL, NULL, NULL);
Cleanup with DeleteDC(). It is only used to initialize the palette for bitmaps with indexed format. NULL might work if you don't use such a format, never tried it.
Then there's GDI+, #include <gdiplus.h> and the Bitmap class...
try this. it worked.
HDC hdcScreen = ::GetDC( NULL );
HDC hdcMemDC = ::CreateCompatibleDC(hdcScreen);
HBITMAP hbmScreen = ::CreateCompatibleBitmap(hdcScreen, cx, cy);
HBITMAP hOldBitmap = (HBITMAP) ::SelectObject(hdcMemDC, hbmScreen);
MyImageDraw(hdcMemDC, ...);
// The drawing image is held in hBitmap. You can save it
HBITMAP hBitmap = (HBITMAP)::SelectObject(hdcMemDC, hOldBitmap);
// save The trend image into c:\test.bmp
PBITMAPINFO pbi = CreateBitmapInfoStruct(hBitmap);
CreateBMPFile("C:\\Temp\\test.bmp", pbi, hBitmap, hdcMemDC);
//Clean up
::DeleteObject(hbmScreen);
::DeleteObject(hdcMemDC);
::ReleaseDC( NULL, hdcScreen );