I am using this code to add a toolbar to the window with one button having a custom image:
HWND hToolbar = CreateWindow(TOOLBARCLASSNAME, NULL,WS_CHILD |
TBSTYLE_FLAT|TBSTYLE_AUTOSIZE |TBSTYLE_LIST|CCS_BOTTOM, 0, 0, 0, 0,
hwnd, NULL, ghInstance, NULL); //create the toolbar
SendMessage(hToolbar, WM_SETFONT, (WPARAM)hFontBold,
static_cast<LPARAM>(MAKELONG(TRUE, 0))); //set the font. there cannot be the problem
//↓↓↓↓↓**ILC_COLOR32 is specificied here but 32bit bmp is still not showing**//
auto hImagelist = ImageList_Create(32, 32,ILC_COLOR32|ILC_MASK,1, 0);
//↑↑↑↑↑**ILC_COLOR32 is specificied here but 32bit bmp is still not showing**//
HBITMAP bitmap = static_cast<HBITMAP>(ghInstance,
MAKEINTRESOURCE(ID_IMG_SPAWN),// <- ID_IMG_SPAWN is my custom resource
IMAGE_BITMAP, 32, 32, NULL));
ImageList_Add(hImagelist, bitmap, nullptr);
SendMessage(hToolbar, TB_SETIMAGELIST,static_cast<WPARAM>(0),(LPARAM)hImagelist);
In the code above, ID_IMG_SPAWN is the resource bmp image I imported to Visual Studio. However, Visual Studio threw an error saying it didn't recognize my bmp, and the bmp showed up blank when running the application.
Visual Studio told me my bmp wasn't recognized, see the below error:
When the app runs it looks as below:
I learned that Visual Studio hereby recognizes ONLY 24bit bmp.
So, I converted the bmp to 24bit and imported it again, changed ILC_COLOR32 to ILC_COLOR24, that really worked. No error and my image were shown on the button.
However, I lost my alpha channel. Because 24bit bitmap does not support an alpha channel, my image ended up having an ugly square background.
Try something like this (Note I used a "BINARY" resource.. maybe it will work with others too but not sure.. This code works with 32-bit bitmaps, alpha-transparent PNGs, and JPEG):
#include <windows.h>
#include <gdiplus.h>
HBITMAP LoadBitmapFromResource(DWORD ResourceID, bool transparent = true)
{
HANDLE hGlobal = NULL;
ULONG_PTR GDIToken = 0;
Gdiplus::Image* Img = NULL;
Gdiplus::GdiplusStartupInput GDIStartInput = NULL;
Gdiplus::GdiplusStartup(&GDIToken, &GDIStartInput, NULL);
HRSRC hResource = FindResource(NULL, MAKEINTRESOURCE(ResourceID), "BINARY");
if (!hResource) {return NULL;}
HGLOBAL hFileResource = LoadResource(NULL, hResource);
if (!hFileResource) {return NULL;}
LPVOID lpFile = LockResource(hFileResource);
if (!lpFile) {return NULL;}
DWORD dwSize = SizeofResource(NULL, hResource);
if (!dwSize) {return NULL;}
void* data = LockResource(hFileResource);
if (!data) {return NULL;}
IStream* pStream = NULL;
hGlobal = GlobalAlloc(GMEM_FIXED, dwSize);
memcpy(hGlobal, data, dwSize);
UnlockResource(hFileResource);
FreeResource(hFileResource);
if (CreateStreamOnHGlobal(hGlobal, true, &pStream) == S_OK)
{
Img = new Gdiplus::Image(pStream, false);
pStream->Release();
GlobalFree(hGlobal);
hGlobal = NULL;
HBITMAP hBitmap = NULL;
static_cast<Gdiplus::Bitmap*>(Img)->GetHBITMAP(transparent ? Gdiplus::Color::Transparent : Gdiplus::Color(0, 0, 0), &hBitmap);
delete Img;
Gdiplus::GdiplusShutdown(GdiImage::GDIToken);
GDIStartInput = NULL;
GDIToken = 0;
return hBitmap;
}
GlobalFree(hGlobal);
hGlobal = NULL;
Gdiplus::GdiplusShutdown(GdiImage::GDIToken);
GDIStartInput = NULL;
GDIToken = 0;
return NULL;
}
You will have to link against GDIPlus. It's not very nice to constantly startup and shutdown GDIPlus for every image you load.. so it's best to move that somewhere.. but other than that, everything should work just fine.
There are other ways without GDIPlus but it makes for a long post..
I learned that Visual Studio hereby recognizes ONLY 24bit bmp.
No, that's wrong. Visual Studio supports alpha-premultiplied 32-bit bitmaps.
32-bit bitmap has 8 bits for ALPHA, it's capable to make a smooth effect.
To show a 32-bit bitmap properly, you have to:
specify ILC_COLOR32
change the resource of ID_IMG_SPAWN to a 32-bit bitmap with premultiplied alpha.
create a DIB section to your bitmap when loading
(Win32's format requirement is very strict)
Q: How to create a DIB section to the bitmap?
A: Specify LR_CREATEDIBSECTION in the last parameter of LoadImage.
Explanation:
LoadImage((HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE),MAKEINTRESOURCE(ID_IMG_SPAWN), IMAGE_BITMAP,32, 32,NULL)
This is your code of the LoadImage function. See the MSDN document of LoadImage, to create the DIB section, all you need is to specify LR_CREATEDIBSECTION in the last parameter of LoadImage.
Q: How to get a BMP with premultiplied alpha?
A: Pixelformer can help you convert your alpha-channeled file to a premultiplied-alpha BMP.
The steps are
Open your image (any format) in Pixelformer and choose Export from the menu
Select A8:R8:G8: B8 (32bpp) and Premultiplied Alpha, then click Ok.
Then you can save your BMP file! Import this BMP file to Visual Studio resources, replacing your previous 24-bit BMP.
Then, you don't need to use the ImageList_AddMasked (which makes the image sharp) anymore, because you already have a recognizable ALPHA in your 32-bit BMP. So, straight use ImageList_Add.
Okay, after the manipulations explained above your code should be this:
// Create the toolbar
HWND hToolbar = CreateWindow(TOOLBARCLASSNAME,NULL,
WS_CHILD | TBSTYLE_FLAT | TBSTYLE_AUTOSIZE | TBSTYLE_LIST | CCS_BOTTOM,
0, 0, 0, 0, hwnd, NULL, ghInstance, NULL);
// Set the font (this cannot be the problem)
SendMessage(hToolbar, WM_SETFONT, (WPARAM)hFontBold,
static_cast<LPARAM>(MAKELONG(TRUE, 0)));
auto hImagelist =
ImageList_Create(32, 32,ILC_COLOR32 /*DON'T NEED THE MASK. CHANGED TO ILC_COLOR32.*/, 1, 0);
HBITMAP bitmap = static_cast<HBITMAP>(LoadImage((HINSTANCE)GetWindowLong(hwnd,
GWL_HINSTANCE), MAKEINTRESOURCE(ID_IMG_SPAWN), IMAGE_BITMAP,
32, 32, LR_CREATEDIBSECTION /*THIS IS IMPORTANT*/ ));
ImageList_Add(hImagelist, bitmap, NULL);
SendMessage(hToolbar, TB_SETIMAGELIST, static_cast<WPARAM>(0), (LPARAM)hImagelist);
This worked fine as below.
These I answered above is well enough to solve this problem. You may wonder "What means DIB bitmaps?" "What is Premultiplied Alpha?". Those are the deep topics.
To learn DIB bitmaps and Premultiplied Alpha, see the links.
If you are using Visual Studio 2010 or higher (i guess), you can use PNG files. As stated here.
I need to load an image as a HBITMAP in C++ so I can send it to a printer.
I've tried using Gdiplus::Bitmap::FromFileand then Gdiplus::Bitmap->GetHBITMAP however the HBITMAP returned is still empty.
I can load .bmp files fine with LoadImage() but this doesn't work with PNG files.
Could anyone point me to the correct way of implementing this to work with .png (and more, if possible) file types?
EDIT: CODE
ZeroMemory(&hBitmap, sizeof(HBITMAP));
Gdiplus::Bitmap* gpBitmap = Gdiplus::Bitmap::FromFile(L"img.png");
gpBitmap->GetHBITMAP(0, &hBitmap);
EDIT 2: Solution found
HBITMAP GetHBITMAPFromImageFile(WCHAR* pFilePath)
{
GdiplusStartupInput gpStartupInput;
ULONG_PTR gpToken;
GdiplusStartup(&gpToken, &gpStartupInput, NULL);
HBITMAP result = NULL;
Gdiplus::Bitmap* bitmap = Gdiplus::Bitmap::FromFile(pFilePath, false);
if (bitmap)
{
bitmap->GetHBITMAP(Color(255, 255, 255), &result);
delete bitmap;
}
GdiplusShutdown(gpToken);
return result;
}
I'm using Winapi and I'm stuck. Normally to load image into SS_BITMAP on Dialog I'm using
HBITMAP hImage = ( HBITMAP ) LoadImage( hInstance, L"D:\\foo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
HWND hFrame = GetDlgItem( hWnd, IDC_IMAGE );
SendMessage(hFrame, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImage);
but now I have different task. I need to put there image from buffer which contains binary data of image. I've used some code but I know its wrong SetBitmapBits( hImage, sizeof(image),image);. image is char array with data.
Then you can use
CreateDIBSection to create a bitmap memory structure
SetDIBits to fill it with your pixel data
This SO answer looks good to me.
I'd like to know how something like this can be done
CBitmap bmp;
CImage img;
///
bmp=img;
//use bmp here
In my program I have to use CBitmap at some point but from the start I am only around with CImage.
Sort of CImage is GdiPlus and CBitmap is MFC. You can do something like this:
CBitmap bmp;
CImage img;
bmp = CBitmap::FromHandle(img.Detach());
Edit: Actually CBitmap has an attach so might be more efficient to do this:
bmp.Attach(img.Detach());
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 );