winapi: from HDC to an HBITMAP - c++

I would like to do something which I believe is fairly simple but since I am new to the winapi I am finding a lot of problems. Basically I have an HDC (which I am BitBlitting from a loaded Bitmap) and I am drawing a rectangle on it. Then I would like to BitBlt that HDC onto a new HBITMAP Object, but alas for now to no avail.
Here is my code which I have been trying to get to work for a couple of hours now
BITMAPINFO info;
Bitmap *tempbmp = Bitmap::FromFile(L"C:\\Users\\abelajc\\Pictures\\BackgroundImage.png", false);
HBITMAP loadedbackground;
tempbmp->GetHBITMAP(NULL, &loadedbackground);
HBRUSH hRed = CreateSolidBrush(RGB(255, 0, 0));
HDC pDC = GetDC(0);
HDC TmpDC = CreateCompatibleDC(pDC); //main DC on which we will paint on
HDC dcBmp = CreateCompatibleDC(TmpDC); //DC for the loadedbackground HBitmap
HGDIOBJ TmpObj2 = SelectObject(dcBmp , tempbmp); //Selecting Bitmap in DC
BitBlt(TmpDC, 0, 0, 512, 512, dcBmp, 0, 0, SRCCOPY);
SelectObject(dcBmp, TmpObj2); //Deselecting Bitmap from DC
DeleteDC(dcBmp);
RECT rectangle;
SetRect(&rectangle, 5, 5, 20, 20);
FillRect(TmpDC, &rectangle, hRed);
HDC hCompDC = CreateCompatibleDC(TmpDC);
HBITMAP hBmp = CreateCompatibleBitmap(TmpDC, 512, 512);
HBITMAP hOld = (HBITMAP)SelectObject(hCompDC, hBmp);
BitBlt(hCompDC, 0, 0, 512, 512, TmpDC, 0, 0, SRCCOPY);
SelectObject(hCompDC, hOld);
DeleteDC(hCompDC);
Bitmap *image = new Bitmap(hBmp, NULL);

I think you just need some clarification about GDI.
A DC is exactly what its name imply : a device context. It's just a context, nothing concrete. Some DCs are context to a real graphic device, some others (memory DCs) are context to a virtual graphic surface in memory. The DCs you create with CreateCompatibleDC are memory DC, but creating the DC only create the context, not the memory surface. As the MSDN documentation says :
Before an application can use a memory DC for drawing operations, it must select a bitmap of the correct width and height into the DC.
You need to associate a HBITMAP with the DC. After doing that, you can consider that drawing to the DC is essentially drawing to the bitmap. The memory DC is the 'window' to the bitmap.
Once you understand that, you will see that your program can be greatly shortened. Feel free to comment if you still have problems.

Related

Create a GDI Rectangle image

I must be doing something wrong or have missed something because all I actually want to is render a rectangle into a bitmap, so that I can CreateWindowEx() on it. Does anyone know what I'm missing?
HDC hdc = GetDC(hWnd);
// Create Pen and brush for the rectangle
HPEN pn = CreatePen(style, stroke, pen);
HBRUSH br = CreateSolidBrush(brush);
// Create a compatible bitmap and DC from the window DC with the correct dimensions
HDC bm_hdc = CreateCompatibleDC(hdc);
HBITMAP hImage = CreateCompatibleBitmap(bm_hdc, sz.x, sz.y);
// Select the bitmap, pen, brush into the DC
HGDIOBJ bm_obj = SelectObject(bm_hdc, hImage);
HGDIOBJ pn_obj = SelectObject(bm_hdc, pn);
HGDIOBJ br_obj = SelectObject(bm_hdc, br);
// Draw the rectangle into the compatible DC with the bitmap selected
::Rectangle(bm_hdc, xPos, yPos, xPos + xSize, yPos + ySize);
// Restore the old selections
SelectObject(bm_hdc, br_obj);
SelectObject(bm_hdc, pn_obj);
SelectObject(bm_hdc, bm_obj);
// Delete the not needed DC, pen and brush
DeleteDC(bm_hdc);
DeleteObject(br);
DeleteObject(pn);
ReleaseDC(hWnd, hdc);
// Create the window and send a message to set the static image
HWND win = CreateWindow(TEXT("STATIC"), NULL, WS_CHILD | SS_BITMAP | WS_VISIBLE, pos.x, pos.y, sz.x, sz.y, hWnd, NULL, hInst, NULL)));
HGDIOBJ obj = (HGDIOBJ)SendMessage(win, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImage);
// Delete the old image
if (obj)
DeleteObject(hImage);
Hummmm... but this doesn't work... All I get is a completely black area and not the rectangle that I have drawn. Any ideas why? Do I need to create another DC and BitBlt() between device contexts?
Thanks for all the help everyone, but I've actually solved it myself and it was SUCH a silly mistake too... Consider this line...:-
::Rectangle(bm_hdc, xPos, yPos, xPos + xSize, yPos + ySize);
Nothing wrong with that at first glance, right? WRONG! If you look at my code, I create a compatible bitmap of the required size to contain my rectangle and try to render the rectangle into this bitmap (which is selected into the DC).
But... WHERE in the bitmap am I rendering? xPos and yPos are window positions of the rectangle, but I'm not rendering to the Window DC am I?!? d'oh! That's right, xPos and yPos should both be 0 because I'm rendering into a bitmap of the correct size and it's when the Window is displayed that xPos and yPos should contain screen coordinates!
Wow... what a dumb mistake and thanks for the nice spot on the HDC from the Window rather than from the compatible DC. I did know that a memory DC has a 1bit depth, but still made that classic blunder. Thanks everyone.
Try changing this line HBITMAP hImage = CreateCompatibleBitmap(bm_hdc, sz.x, sz.y); into this :
HBITMAP hImage = CreateCompatibleBitmap(hdc, sz.x, sz.y);
Paul Watt wrote excellent articles for GDI and image composition with MsImage32.dll.
I am reffering you to this article because it addresses your problem, and here are the relevant quotes and code snippets:
The memory DC is initialized with a mono-chromatic 1x1 pixel bitmap by default.
Avoid a Common Mistake
Before we get too far away from code where I showed you what you need to start running, I want to make sure you are holding this new pair of scissors safely. Do not use a Memory DC with a call to CreateCompatibleBitmap.
...
// You may be tempted to do this; DON'T:
HDC hMemDC = ::CreateCompatibleDC(hDC);
// DON'T DO THIS
// |
// V
HBITMAP hBmp = ::CreateCompatibleBitmap(hMemDC, width, height);
...
// TIP: Try to use the same DC to create
// the Bitmap which you used to create the memory DC.
Remember the part about how The memory DC is initialized with a mono-chromatic 1x1 pixel bitmap by default?!
As for the remarks of member Raymond Chen I believe he is also right, but since you said that your actual code is different this is the only thing I can see as a mistake.
Hopefully this helps.
Best regards.

Why does GetObject return an BITMAP with null bmBits?

Context: I'm trying to take a screenshot of another window to feed it into OpenCV. I found some code on the web that should be able to convert a BITMAP to something OpenCV can work with. Unfortunately I ran into some trouble.
Question: Why is the bmBits attribute/member always null? (I also tried with PrintWindow instead of BitBlt the result was the same)
#include <iostream>
#include <string>
#include <Windows.h>
int main(int argc, char* argv[])
{
std::wstring windowName = L"Calculator";
RECT rect;
HWND hwnd = FindWindow(NULL, windowName.c_str());
if (hwnd == NULL)
{
return 0;
}
GetClientRect(hwnd, &rect);
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen,
rect.right - rect.left, rect.bottom - rect.top);
SelectObject(hdc, hbmp);
PrintWindow(hwnd, hdc, PW_CLIENTONLY);
BITMAP bmp;
GetObject(hbmp, sizeof(BITMAP), &bmp);
return 0;
}
The bmBits member is non-null for DIB sections. For device-dependent bitmaps (such as the one you're creating), the bmBits is not set because the pixels are on the video card, not in main memory.
In your example, you need to change CreateCompatibleBitmap to CreateDIBSection if you want direct access to the bits.
Just for information.
When loading bitmap from file and want to use BITMAP .bmBits (for glTexImage2D, glDrawPixels):
LoadImage(NULL, "path_to.bmp", IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE);
u must specify flag LR_CREATEDIBSECTION
HBITMAP hBmp = NULL;
BITMAP BMp;
hBmp = (HBITMAP) LoadImage(NULL, "bitmap.bmp", IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION);
GetObject(hBmp, sizeof(BMp), &BMp);
//BMp.bmBits now points to data
From GetObject documentation on MSDN. Please note the second paragraph.
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.
One thing which you could do is to look at the return value of GetObject. If 0 you know something has gone wrong. Something wrong with the parameters of the call.

GDI+ DC in memory always monochrome

Working in mingw, having a terrible time creating a color DC in memory. For instance, in the following code snippet, as written, "foo_scratch.bmp" is a monochrome version of the
image (from an EMR_STRETCHDIBITS record). If instead aDC is omitted and srcDC uses the CreateDC directly, then that file has a color image.
Gdiplus::Bitmap *pbmp = NULL;
BITMAPINFO *pbitmapinfo = (BITMAPINFO *)((char *)lpEMFR + pEmr->offBmiSrc);
void *pBitsInMem = (char *)lpEMFR + pEmr->offBitsSrc;
HBITMAP hbmsrc;
HDC aDC = CreateDC("DISPLAY", "", NULL, NULL);
HDC srcDC = CreateCompatibleDC(aDC);
hbmsrc = CreateDIBitmap(
srcDC,
&(pbitmapinfo->bmiHeader),
CBM_INIT,
pBitsInMem,
pbitmapinfo,
DIB_RGB_COLORS);
if(hbmsrc){
CLSID pngClsid;
GetEncoderClsid(L"image/bmp", &pngClsid);
pbmp = Gdiplus::Bitmap::FromHBITMAP(hbmsrc,NULL);
pbmp->Save(L"C:\\Temp\\foo_scratch.bmp",&pngClsid, NULL);
This all comes to a head later when two images (hbmdst, hbmsrc) need to be put together with a bitblt operation. At present the best I have managed is monochrome. At worst the image is solid black. In this snippet the ROP has been hard coded to SRCCOPY, and I still have not succeeded in just copying the image from one HBITMAP to another. Very frustrating!
HDC dstDC = CreateCompatibleDC(aDC);
HBITMAP hbmdOld = (HBITMAP) SelectObject(dstDC, hbmdst);
HBITMAP hbmsOld = (HBITMAP) SelectObject(srcDC, hbmsrc);
GetObject(hbmsrc, sizeof(bm), &bm);
BitBlt(dstDC, 0, 0, bm.bmWidth, bm.bmHeight, srcDC, 0, 0, SRCCOPY);
SelectObject(srcDC, hbmsOld);
SelectObject(dstDC, hbmdOld);
(void) DeleteDC(dstDC);
pbmp = Gdiplus::Bitmap::FromHBITMAP(hbmdst,NULL);
pbmp->Save(L"C:\\Temp\\scratch.bmp",&pngClsid, NULL);
What am I doing wrong?
Thanks
Figured it out - wherever a bitmap is created have to use the DC directly associated with the display (or other device), not the "compatible" DC derived from the first DC.

How to convert HICON to HBITMAP in VC++?

How to convert HICON to HBITMAP in VC++?
I know this is an FAQ but all the solutions I've found on Google don't work. What I need is a function which takes a parameter HICON and returns HBITMAP.
Greatest if possible to make conversion to 32-bit bitmap even the icon is 24-bit, 16-bit or 8-bit.
This is the code, I don't know where it goes wrong:
HBITMAP icon_to_bitmap(HICON Icon_Handle) {
HDC Screen_Handle = GetDC(NULL);
HDC Device_Handle = CreateCompatibleDC(Screen_Handle);
HBITMAP Bitmap_Handle =
CreateCompatibleBitmap(Device_Handle,GetSystemMetrics(SM_CXICON),
GetSystemMetrics(SM_CYICON));
HBITMAP Old_Bitmap = (HBITMAP)SelectObject(Device_Handle,Bitmap_Handle);
DrawIcon(Device_Handle, 0,0, Icon_Handle);
SelectObject(Device_Handle,Old_Bitmap);
DeleteDC(Device_Handle);
ReleaseDC(NULL,Screen_Handle);
return Bitmap_Handle;
}
this code do it:
HICON hIcon = (HICON)LoadImage(instance, MAKEINTRESOURCEW(IDI_ICON), IMAGE_ICON, width, height, 0);
ICONINFO iconinfo;
GetIconInfo(hIcon, &iconinfo);
HBITMAP hBitmap = iconinfo.hbmColor;
and this is the code in the *.rc file:
IDI_ICON ICON "example.ico"
and this is the code in the *.h file:
#define IDI_ICON 4000
HDC hDC = GetDC(NULL);
HDC hMemDC = CreateCompatibleDC(hDC);
HBITMAP hMemBmp = CreateCompatibleBitmap(hDC, x, y);
HBITMAP hResultBmp = NULL;
HGDIOBJ hOrgBMP = SelectObject(hMemDC, hMemBmp);
DrawIconEx(hMemDC, 0, 0, hIcon, x, y, 0, NULL, DI_NORMAL);
hResultBmp = hMemBmp;
hMemBmp = NULL;
SelectObject(hMemDC, hOrgBMP);
DeleteDC(hMemDC);
ReleaseDC(NULL, hDC);
DestroyIcon(hIcon);
return hResultBmp;
I don't have code readily available to share, but I think this is pretty easy. You have to create the HBITMAP, create a device context, select the bitmap into the DC (this will make the bitmap the drawing area for this DC). Finally call the DrawIcon() function to draw your icon on this DC. After that detach the bitmap from the DC and destroy the DC. Your bitmap now should be ready to go.
Update after looking at your code:
I believe the problem is in the createCompatibleBitmap call. You are asking for a bitmap compatible with the memory DC, but memory DCs start with a 1 bit/pixel bitmap selected into them. Try asking for a bitmap compatible with the screen DC instead.
Update 2: you may want to look at this question as it seems related to your problem.
I found this(similar code works for me - 32x32 icons with or without alpha data):
used CopyImage (msdn link)
HICON hICON = /*your code here*/
HBITMAP hBITMAPcopy;
ICONINFOEX IconInfo;
BITMAP BM_32_bit_color;
BITMAP BM_1_bit_mask;
// 1. From HICON to HBITMAP for color and mask separately
//.cbSize required
//memset((void*)&IconInfo, 0, sizeof(ICONINFOEX));
IconInfo.cbSize = sizeof(ICONINFOEX);
GetIconInfoEx( hICON , &IconInfo);
//HBITMAP IconInfo.hbmColor is 32bit per pxl, however alpha bytes can be zeroed or can be not.
//HBITMAP IconInfo.hbmMask is 1bit per pxl
// 2. From HBITMAP to BITMAP for color
// (HBITMAP without raw data -> HBITMAP with raw data)
// LR_CREATEDIBSECTION - DIB section will be created,
// so .bmBits pointer will not be null
hBITMAPcopy = (HBITMAP)CopyImage(IconInfo.hbmColor, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
// (HBITMAP to BITMAP)
GetObject(hBITMAPcopy, sizeof(BITMAP), &BM_32_bit_color);
//Now: BM_32_bit_color.bmBits pointing to BGRA data.(.bmWidth * .bmHeight * (.bmBitsPixel/8))
// 3. From HBITMAP to BITMAP for mask
hBITMAPcopy = (HBITMAP)CopyImage(IconInfo.hbmMask, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
GetObject(hBITMAPcopy, sizeof(BITMAP), &BM_1_bit_mask);
//Now: BM_1_bit_mask.bmBits pointing to mask data (.bmWidth * .bmHeight Bits!)
BM_32_bit_color bitmap may be have Alpha *channel*(each 4th byte) already set! So - check for it before u add mask bit to color data.

Drawing On GDI+ Graphics Object a Bitmap using StretchDIBits for Scaling

I am drawing bitmap images on graphics object using DrawImage method But the Images are in large number so it is taking too much time for Drawing. I have read in this forum that using StretchDIBits takes less time for Drawing.
I am scaling the image by calling Drawimage but i want any other efficent method.
I have a Vector of Bitmap* & i want to draw each Bitmap on graphics.
HDC orghDC = graphics.GetHDC();
CDC *dc = CDC::FromHandle(orghDC);
m_vImgFrames is image vector containg Bitmap*. I have taken HBITMAP from Bitmap*.
HBITMAP hBitmap;
m_vImgFrames[0]->GetHBITMAP(Color(255,0,0),&hBitmap);
Using this HBITMAP i want to draw on orghDC & finally on graphics. So I want to know how StretchDIBits can be used for scaling the Bitmap and finally draw on Graphics Object.
I am new to this forum.Any ideas or code can be helpful
Instead of using StretchDIBits, why not use the GDI+ API directly to scale the bitmap?:
CRect rc( 0, 0, 20, 30 );
graphics.DrawImage( (Image*)m_vImgFrames[0],
rc.left, rc.top, rc.Width(), rc.Height() );
To use StretchDIBits with Gdiplus::Bitmap you could do the following:
// get HBITMAP
HBITMAP hBitmap;
m_vImgFrames[0]->GetHBITMAP( Gdiplus::Color(), &hBitmap );
// get bits and additional info
BITMAP bmp = {};
::GetObject( hBitmap, sizeof(bmp), &bmp );
// prepare BITMAPINFO
BITMAPINFO bminfo = {};
bminfo.bmiHeader.biSize = sizeof( BITMAPINFO );
bminfo.bmiHeader.biWidth = bmp.bmWidth;
bminfo.bmiHeader.biHeight = bmp.bmHeight;
bminfo.bmiHeader.biBitCount = bmp.bmBitsPixel;
bminfo.bmiHeader.biCompression = BI_RGB;
bminfo.bmiHeader.biPlanes = bmp.bmPlanes;
bminfo.bmiHeader.biSizeImage = bmp.bmWidthBytes*bmp.bmHeight*4; // 4 stands for 32bpp
// select stretch mode
::SetStretchBltMode( HALFTONE );
// draw
::StretchDIBits( hDC, 0, 0, new_cx, new_cy, 0, 0,
m_vImgFrames[0]->GetWidth(), m_vImgFrames[0]->GetHeight(),
bmp.bmBits, &bminfo, DIB_RGB_COLORS, SRCCOPY );
But this doesn't looks much faster on my machine than simple Graphics::DrawImage.