How can I create a device context compatible bitmap and then associating the obtained handle to a BITMAP struct?
If I write:
...
HBITMAP hbitmap = CreateCompatibleBitmap(hdc, width, height); // these three arguments are initialized somewhere else
hbitmap = CreateBitmapIndirect(bitmap); // argument already initialized and properly filled
...
A HBITMAP handle compatible with hdc is created, and then a new HBITMAP (filled with bitmap data) is initialized, though without keeping its compatibility. Is there a function which allows not to create a HBITMAP from a BITMAP, but rather fills an initialized HBITMAP with an already existing BITMAP source?
CopyImage function
Creates a new image (icon, cursor, or bitmap) and copies the attributes of the specified image to the new one. If necessary, the function stretches the bits to fit the desired size of the new image.
HANDLE WINAPI CopyImage(
HANDLE hImage,
UINT uType,
int cxDesired,
int cyDesired,
UINT fuFlags
);
hImage A handle to the image to be copied.
uType The type of image to be copied. This parameter can be one of the following values.
IMAGE_BITMAP 0 Copies a bitmap.
IMAGE_ICON 1 Copies an icon.
IMAGE_CURSOR 2 Copies a cursor.
cxDesired The desired width, in pixels, of the image. If this is zero, then the returned image will have the same width as the original hImage.
cyDesired The desired height, in pixels, of the image. If this is zero, then the returned image will have the same height as the original hImage.
fuFlags
CreateBitmapIndirect takes BITMAP on its input. And you can get it via GetObject from HBITMAP:
BITMAP Bitmap;
INT nResult = GetObject((HGDIOBJ) hBitmap, sizeof Bitmap, &Bitmap);
CreateBitmapIndirect will be able create a bitmap from this struct. Or you can use CreateCompatibleBitmap to create compatible bitmap providing width/height from obtained Bitmap.
Related
I have an HBITMAP handle to a device-dependent bitmap, resulting from this code:
// copy screen to bitmap
HDC hScreen = GetDC(NULL);//don't use hwnd, it doesn't work for non native windows
HDC hDC = CreateCompatibleDC(hScreen);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, w, h);
HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
BOOL bRet = BitBlt(hDC, 0, 0, w, h, hScreen, x1, y1, SRCCOPY);
I want to access (read-only) the bits of the bitmap, read some values, do some calculations, then discard it.
According to this answer, I shouldn't use GetDIBits() or GetBitmapBits() since they copy the data, but instead I should use:
BITMAP bitmap;
GetObject(hBitmap, sizeof(bitmap), (LPVOID)&bitmap);
But according to GetObject's documentation:
If hgdiobj is a handle to a bitmap created by calling CreateDIBSection, and the specified buffer is large enough, the GetObject function returns a DIBSECTION structure. In addition, the bmBits member of the BITMAP structure contained within the DIBSECTION will contain a pointer to the bitmap's bit values.
If hgdiobj is a handle to a bitmap created by any other means, GetObject returns only the width, height, and color format information of the bitmap. You can obtain the bitmap's bit values by calling the GetDIBits or GetBitmapBits function.
Which is pointed to in a comment on the above answer:
According to this https://msdn.microsoft.com/en-us/library/dd144904(v=vs.85).aspx if bitmap is not created by CreateDIBSection bitmap.bmBit pointer will be null.
So, is there anyway to access the bits of the bitmap (RGB values), without copying them?
If it helps, the device on which the bitmap is dependent is always a screen, so I guess it's always a 24bit or 32bit bitmap.
In this article, regarding how to save an HBITMAP to a file, there's this section:
// Allocate memory for the BITMAPINFO structure. (This structure
// contains a BITMAPINFOHEADER structure and an array of RGBQUAD
// data structures.)
if (cClrBits < 24) pbmi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << cClrBits));
// There is no RGBQUAD array for these formats: 24-bit-per-pixel or 32-bit-per-pixel
else pbmi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));
So, I don't really understand what's happening here, and which is right?
I have some existing code that uses a CImage which has an alpha channel, and I need to rotate it.
I have found the following suggestion which converts the CImage to a GDI+ Bitmap and then rotates it, and the rotated result ends up back in the CImage.
Bitmap* gdiPlusBitmap=Bitmap::FromHBITMAP(atlBitmap.Detach());
gdiPlusBitmap->RotateFlip(Rotate90FlipNone);
HBITMAP hbmp;
gdiPlusBitmap->GetHBITMAP(Color::White, &hbmp);
atlBitmap.Attach(hbmp);
Apparently it works without actually copying the bitmap bytes, which is great, but the problem is that if you create a Bitmap object from a HBITMAP it throws away the alpha channel.
Apparently to preserve the alpha channel you must instead create the Bitmap using the constructor
Bitmap(
[in] INT width,
[in] INT height,
[in] INT stride,
[in] PixelFormat format,
[in] BYTE *scan0
);
So I'm trying to adapt the above to use this constructor, but the interaction between CImage and Bitmap is a bit confusing. I think I need to create the Bitmap like this
Bitmap* gdiPlusBitmap = new Bitmap(
pCImage->GetWidth(),
pCImage->GetHeight(),
pCImage->GetPitch(),
PixelFormat32bppARGB,
(BYTE *)pCImage->GetBits());
nGDIStatus = gdiPlusBitmap->RotateFlip(Rotate90FlipNone);
but I'm not sure how to make the CImage pick up the changes (so that I end up with the original CImage rotated), or where to delete the Bitmap object.
Does anyone know the correct way to do this, preserving the alpha channel ?
Ideally I'd like to avoid copying the bitmap data, but it's not mandatory.
You can use Gdiplus::Graphics to draw bitmap on CImage.
Note, hard coding PixelFormat32bppARGB can cause problems if image doesn't support alpha channel. I added some basic error check.
CImage image;
if (S_OK != image.Load(L"c:\\test\\test.png"))
{
AfxMessageBox(L"can't open");
return 0;
}
int bpp = image.GetBPP();
//get pixel format:
HBITMAP hbmp = image.Detach();
Gdiplus::Bitmap* bmpTemp = Gdiplus::Bitmap::FromHBITMAP(hbmp, 0);
Gdiplus::PixelFormat pixel_format = bmpTemp->GetPixelFormat();
if (bpp == 32)
pixel_format = PixelFormat32bppARGB;
image.Attach(hbmp);
//rotate:
Gdiplus::Bitmap bmp(image.GetWidth(), image.GetHeight(), image.GetPitch(), pixel_format, static_cast<BYTE*>(image.GetBits()));
bmp.RotateFlip(Gdiplus::Rotate90FlipNone);
//convert back to image:
image.Destroy();
if (image.Create(bmp.GetWidth(), bmp.GetHeight(), 32, CImage::createAlphaChannel))
{
Gdiplus::Bitmap dst(image.GetWidth(), image.GetHeight(), image.GetPitch(), PixelFormat32bppARGB, static_cast<BYTE*>(image.GetBits()));
Gdiplus::Graphics graphics(&dst);
graphics.DrawImage(&bmp, 0, 0);
}
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.
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?
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