Loading a bmp from resource - c++

I'm trying to load a bitmap from resource instead of a file location. I'm using visual stuidos and I have imported the same bitmap into the rc. The LoadImage returns a NULL when I try with the resource version. Is my syntax wrong? or am I missing additional steps? Please point me in the right direction I'm trying to learn. Thanks in advance.
HBITMAP mhbitmap;
// Loading from a file works
mhbitmap = (HBITMAP)LoadImage(NULL, L"sblue.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
mpiccontrol.SetBitmap(mhbitmap);
if (mhbitmap == NULL) {
MessageBox(L"null", L"from file", NULL);
}
The load from file version shows the image, but the resource version does not show the image.
mpiccontrol.SetBitmap(mhbitmap);
// My attempt at loading from resource
mhbitmap = (HBITMAP)LoadImage(NULL, MAKEINTRESOURCE("IDB_BITMAP1"), IMAGE_BITMAP, 0, 0, 0);
if (mhbitmap == NULL) {
MessageBox(L"null", L"from rc", NULL);
}
mpiccontrol.SetBitmap(mhbitmap);

MAKEINTRESOURCE macro accepts integer resource id so it should be MAKEINTRESOURCE(IDB_BITMAP1) where IDB_BITMAP1 is a resource identifier macro (probably from resource.h). You should also call GetLastError to figure out failure reason.

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.

SelectObject returns NULL with hbitmap created in constructor

I have a bitmap class that has a load function for loading the bitmap from either file path or resource ID. This part works fine.
void GtBitmap::Load()
{
LPTSTR szFileName;
szFileName = (LPTSTR)m_strPath.c_str();
// Check for valid .BMP file path
if (m_strPath.size() > 0)
{
// Open .BMP file
m_pFile = fopen(m_strPath.c_str(), ("rb"));
if (m_pFile != NULL)
{
m_hBitmap = (HBITMAP)LoadImage (GetModuleHandle(NULL), szFileName, IMAGE_BITMAP, 0, 0, LR_SHARED | LR_LOADFROMFILE);
GetObject(m_hBitmap, sizeof(m_bmap), &m_bmap);
int i = 1;
}
}
else if (m_intResourceID != 0)
{
m_hBitmap = (HBITMAP)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(m_intResourceID), IMAGE_BITMAP, 0, 0, LR_SHARED);
GetObject(m_hBitmap, sizeof(m_bmap), &m_bmap);
int i = 1;
}
}
However, when I try to render it in my code block, the SelectObject returns null. Here is the code for that section of the painter class.
void GtPainterGDI::GtDrawBitmap(GtRectI & target, GtBitmap & bitmap, bool blnOffset)
{
GtCanvas topCv = m_arrCanvas.back();
HDC hdcMem = CreateCompatibleDC(topCv.m_hdcParent);
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, bitmap.m_hBitmap);
DWORD lastError = GetLastError();
bool success = BitBlt(hdcMem, target.GetLeft(), target.GetTop(),
target.Width(), target.Height(), hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, bitmap.m_hBitmap);
DeleteDC(hdcMem);
};
The SelectObject() returns null and the image is not drawn. I can only get the image to show up if I use a LoadImage() in that paint function. However I don't want to load the image every time I want to paint. I should be able to load the image once in the Load function or constructor of the bitmap, then use the handle in the paint function.
If anyone could please provide an example of loading an image in a constructor and then painting it elsewhere in the codes WM_PAINT or equivalent painting function I would appreciate it. The code is a new version of the GT graphical user interface library. I plan on posting a new version on codeproject in the next few days or so. I have to do some cleanup first...
Thanks in advance.
HINSTANCE parameter in LoadImage should be NULL when loading the image from file. Use GetModuleHandle(NULL) only when loading from resource.
m_hBitmap = (HBITMAP)LoadImage(NULL, m_strPath.c_str(),
IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (!m_hBitmap)
{
//report error
}
Also LR_SHARED is not necessary here.
When testing for file's exist, you can use std::ifstream. Example:
#include <fstream>
...
bool test = std::ifstream(m_strPath).good();
This will test for file and close the file handle right away.
Make sure to select hbmOld before deleting hdcMem:
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, bitmap.m_hBitmap);
BitBlt(...)
//SelectObject(hdcMem, bitmap.m_hBitmap); <<= remove this
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);

Win32 application. HBITMAP LoadImage fails to load anything

I'm writing a function that quickly draws an image of a menu for a game I'm making. I'm able to draw the background and text blocks just fine but I'm having trouble creating a bitmap image on the screen
bool menu::drawMenu(PAINTSTRUCT ps)
{
HWND hWnd = GetActiveWindow();
HDC hdc = GetDC(hWnd), hdcMem;
//Draw a new background
HPEN blackPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0));
SelectObject(hdc, blackPen);
SelectObject(hdc, blackBrush);
Rectangle(hdc, 0, 0, 1080, 720);
//insert selection text
TextOut(hdc, 30, 0, L"New Game", 8);
TextOut(hdc, 30, 30, L"Exit Game", 9);
//draw arrow sprite
HBITMAP arrow = (HBITMAP)LoadImage(NULL, L"C:\\Users\\Tim\documents\\visual studio 2013\\Projects\\BoulderBisque\\BoulderBisque\\arrow.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
DWORD lastError = GetLastError();
if (arrow == NULL)
{
TextOut(hdc, 0, 60, L"Image Load Failed", 17);
return false;
}
hdcMem = CreateCompatibleDC(hdc);
if (hdcMem == NULL)
{
TextOut(hdc, 0, 90, L"Memory Creation Failed", 22);
return false;
}
SelectObject(hdcMem, arrow);
BitBlt(hdc, 0, choice * 30, 16, 16, hdcMem, 0, 0, SRCCOPY);
//cleanup
ReleaseDC(hWnd, hdc);
DeleteDC(hdcMem);
return true;
}
As of now arrow is NULL and I get the Load Image Failed textbox.
I am using the relative path of arrow.bmp I also tried using a full path, that didn't work either.
You may have noticed that this function is outside the WndProc. Everything else draws fine.
I tried running it in there too everything but the arrow.bmp loads.
What am I doing wrong to cause arrow.bmp to be NULL? I have other methods I plan to run in similar ways so getting this function to run would really be a big help.
EDIT* Whenever I give the full path name it still fails to load.
Also is this not the appropriate code for SO? This is my first question...
EDIT** The aditional '/'s haven't fixed the issue.
EDIT*** Using GetLastError, I found the error code to be 2, ERROR_FILE_NOT_FOUND
Your early versions of the question checked the return value of LoadImage but then did nothing more. The documentation says this:
If the function fails, the return value is NULL. To get extended error information, call GetLastError.
So, if the function fails, call GetLastError to find out why. When you do so, you obtain the error code ERROR_FILE_NOT_FOUND. Which is pretty conclusive. The filename that you specified does not exist.
Do note that the code in the latest update to the question calls GetLastError unconditionally. That is a mistake, one that is seen all too frequently here on Stack Overflow. The documentation only tells you to call GetLastError when the call to LoadImage fails. If the call to LoadImage succeeds, then the value return by GetLastError is meaningless. Error handling in Win32 is largely handled in the same way as LoadImage does it, but not always. So you have to read the documentation very carefully.
Perhaps instead of
C:\\Users\\Tim\documents\\...
you meant
C:\\Users\\Tim\\documents\\...
OK, now you've got the path right. The call to LoadImage returns NULL, but GetLastError is no longer helpful and returns ERROR_SUCCESS. Which is weird in itself.
I believe that the problem is that your image uses a format that LoadImage does not understand. I took your .bmp file, loaded it in Paint.net and then re-saved it. Once I did that, the re-saved image was loaded successfully.
Rather than trying to load this from a file it would make far more sense to link the image as a resource and load it that way.
I found through experimenting with Gimp, that your .bmp file must have the following attributes in order for it to work:
depth:24bit
"Do not show write color space information" box checked: Gimp export options
If that still doesn't work then try to "unblock" it: File options.

Win32 LoadImage() from File Error

Sorry if this is duplicate but I can't find answer elsewhere. I am simply trying to load an image during runtime using the LoadImage() function of WINAPI. I receive the error code(8) which indicates that there is not enough storage space(error codes found here).
the file is relatively small(2.5kb) so I wonder if there is problem with my code:
void OnCreate()
{
...
HBITMAP hbmDeck = (HBITMAP)LoadImage(hInstance, L"standard.bmp", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE);
DWORD err = GetLastError();
HBITMAP hbmT = SelectBitmap(hdc, hbmDeck);
if(!hbmT)
{
MessageBox(NULL, L"Failed to LoadImage - 'hbmDeck'", L"OnCreate()", MB_OK);
PostMessage(hwnd, WM_DESTROY, NULL, NULL);
}
...
}
Yeah, some of the API return error codes are a bit cryptic and don't fit the error.
The file you are trying to load "standard.bmp" is a file on the disk NOT in the resource section right? Well to load a file from the disk, the fist parameter of LoadImage (hInst) has to be NULL and the fuLoad flag needs to include LR_LOADFROMFILE which you correctly have.

Load a CBitmap dynamically

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