Load a CBitmap dynamically - mfc

I have a Bitmap image that i want to load dynamically. But I am unable to load it.
CBitmap bmp;
bmp.LoadBitmap("c:\\aeimg");
it does not seem to be working.
Can someone please help me.
Thanks.

You can also try something like this:
CImage image;
image.Load(_T("C:\\image.png"));
CBitmap bitmap;
bitmap.Attach(image.Detach());

According to CBitmap documentation: LoadBitmap() function takes resource identifier of the bitmap or resource id of the bitmap.
You can't specify the path of the bitmap file.
E.g.
MyProject.rc
------------
MYBMP BITMAP "res\myimage.bmp"
and make sure that resource.h does not have any entry of MYBMP otherwise during preprocessing its replaced by id and ultimately LoadBitmap() will fail since application can't locate the resource as FindResource() fails.
Now do this :
CBitmap bmp;
bmp.LoadBitmap(L"MYBMP");
It will definitely load the bitmap.

To load a bitmap from a file, you want to use LoadImage with the LR_LOADFROMFILE flag.

CBitmap doesn't support directly reading from a .bmp file. You can instead make use of CImage class as suggested in other answers. You'll need to include atlimage.h in your code to make CImage work:
#include <atlimage.h>
:
CImage img;
img.Load (_T("C:\\image.bmp"));
CBitmap bitmap;
bitmap.Attach(img.Detach());
Another way is to load the image using LoadImage Win32 API and then attaching CBitmap to that:
HBITMAP hBitmap = (HBITMAP)LoadImage(NULL,"c:\\image.bmp",
IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (hBitmap != NULL)
bitmap.Attach(hBitmap);

CImage doesn't work with png last time I tried / checked. Have a look at CxImage - http://www.codeproject.com/KB/graphics/cximage.aspx .

It could be as simple as you forgetting to escape the backslash.
Instead of
bmp.LoadBitmap("c:\aeimg");
use
bmp.LoadBitmap("c:\\aeimg");
Otherwise you're passing an invalid path to the LoadBitmap method.

CString filename;
TCHAR szFilter[] = _T("Bitmap (*.bmp)|*.bmp|PNG (*.png)|*.png||");
CFileDialog selDlg(TRUE, NULL, NULL, OFN_OVERWRITEPROMPT | OFN_EXTENSIONDIFFERENT, szFilter, this);
if (selDlg.DoModal() == IDOK)
{
filename = selDlg.GetPathName();
CImage image;
HBITMAP hBitmap = (HBITMAP)LoadImage(NULL,filename, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (hBitmap)
{
// Delete the current bitmap
if (m_bmpBitmap.DeleteObject())
m_bmpBitmap.Detach(); // If there was a bitmap, detach it
// Attach the currently loaded bitmap to the bitmap object
m_bmpBitmap.Attach(hBitmap);
Invalidate();
}
}

When using the solutions mentioned to date, with a member variable of CBitmap I kept getting memory leaking every time I loaded the CImage onto the CBitmap. I solved this with the following code:
CString _fileName(/*Path to image*/);
CImage _image;
HRESULT hr = _image.Load(_fileName);
if (SUCCEEDED(hr)) {
if (m_Display.m_bmpImage.DeleteObject())
m_Display.m_bmpImage.Detach();
m_bmpImage.Attach(_image->Detach());
}

Related

Win32 Unable to add custom toolbar icon having transparency

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.

PNG/JPG file to HBITMAP with GDI+

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;
}

C++ HBITMAP from binary data

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.

CBitmap and CImage interchangeable?

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());

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 );