My intention is to create a toolbar in Win32 containing a transparent icon. I have tried the following code to create a simple toolbar with one button having a custom image:
// 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, // <-this is the HINSTANCE of the application
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_COLOR24 | ILC_MASK, 1, 0);
HBITMAP bitmap = static_cast<HBITMAP>(LoadImage(ghInstance,
/* ID_IMG_SPAWN is my custom resource -> */ MAKEINTRESOURCE(ID_IMG_SPAWN),
IMAGE_BITMAP,
32, 32,
NULL));
ImageList_AddMasked(hImagelist,
bitmap,
RGB(255,255,255) /* white is the transparent color */);
SendMessage(hToolbar,
TB_SETIMAGELIST,
static_cast<WPARAM>(0),
(LPARAM)hImagelist);
ImageList_Create only supports 24-bit bitmaps, which means there is no alpha channel for transparency. However, I can simulate a transparency effect by using a mask color via ImageList_AddMasked. (Here, I am setting white (RGB(255, 255, 255)) to the mask color.)
This worked fine, but the image displayed this way is extremely sharp/jagged because of the lack of granularity in the alpha channel (each pixel is either transparent or fully opaque).
I understand that the PNG format can solve this, since it provides a true alpha channel. I know that the PNG format is supported by Win32 ImageLists, but I don't know how to use it properly. (PNG resources can be added to Visual Studio resources, but I don't know how to use them from code.)
I couldn't find any way to make LoadImage load a PNG. The only supported types are IMAGE_BITMAP IMAGE_CURSOR and IMAGE_ICON. I changed the resource (ID_IMG_SPAWN) to a PNG file and tried each of those three types one by one, but all resulted in merely a blank display like this:
Can anyone help me out? How can I use LoadImage to load a transparent PNG and use it as a toolbar image?
ImageList_Create only supports 24-bit bitmaps, which means there is no alpha channel for transparency.
No, that's wrong. ImageList_Create supports 32-bit bitmaps as well.
Since you intend to create a toolbar in Win32 containing a transparent icon, you do NOT need to load a PNG at all. If you desire PNG you may have to work around with GdiPlus as #barmak says.
32-bit bitmap has 8 bits for ALPHA. Using 32-bit bitmaps can make the same effect as PNG does.
You say your button image was showing blank when you did these:
changed ILC_COLOR24 to ILC_COLOR32
changed the resource of ID_IMG_SPAWN to a 32-bit bitmap
IN FACT To show a 32-bit bitmap properly, you have to:
change ILC_COLOR24 to 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.
For more information about DIB bitmaps and Premultiplied Alpha, see the links.
LoadImage will return NULL when trying to load PNG resource.
You can add your PNG resource as ICON. Otherwise use Windows Imaging Component, or Gdiplus+ to load the png resource.
Read PNG resource as follows:
HBITMAP loadimage(HINSTANCE hinst, const wchar_t* name)
{
HBITMAP hbitmap = NULL;
ULONG_PTR token;
Gdiplus::GdiplusStartupInput tmp;
Gdiplus::GdiplusStartup(&token, &tmp, NULL);
if(auto hres = FindResource(hinst, name, RT_RCDATA))
if(auto size = SizeofResource(hinst, hres))
if(auto data = LockResource(LoadResource(hinst, hres)))
if(auto stream = SHCreateMemStream((BYTE*)data, size))
{
Gdiplus::Bitmap bmp(stream);
stream->Release();
bmp.GetHBITMAP(Gdiplus::Color::Transparent, &hbitmap);
}
Gdiplus::GdiplusShutdown(token);
return hbitmap;
}
...
auto hbitmap = loadimage(ghinst, MAKEINTRESOURCE(ID_PNG1 ));
if(hbitmap)
{
ImageList_AddMasked(himage, hbitmap, 0);
DeleteObject(hbitmap);
}
Resource definition should look like this:
ID_PNG1 RCDATA "file.png"
I am developing an MFC application, where I need to capture the desktop and save as a vector image.
I can capture screen shot as .BMP and write to clipboard with following code.
int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
HDC hDesktopDC = GetDC(NULL);
HDC hCaptureDC = CreateCompatibleDC(hDesktopDC);
HBITMAP hCaptureBitmap =CreateCompatibleBitmap(hDesktopDC,
nScreenWidth, nScreenHeight);
HGDIOBJ old_obj = SelectObject(hCaptureDC,hCaptureBitmap);
BitBlt(hCaptureDC,0,0,nScreenWidth,nScreenHeight,
hDesktopDC,0,0,SRCCOPY|CAPTUREBLT);
// save bitmap to clipboard
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hCaptureBitmap);
CloseClipboard();
// clean up
SelectObject(hCaptureDC, old_obj);
DeleteDC(hCaptureDC);
ReleaseDC(NULL, hDesktopDC);
DeleteObject(hCaptureBitmap);
But I am looking something similar code which would allow me to capture screenshot as vector image and place into clipboard; so, I can read as EMF from clipboard.
I tried searching for MFC functions to convert raster images to vector images from clipboard, but I have not found anything useful.
You can't. A screenshot gathers pixel data only. All information on how those pixels were generated (which is essentially what an EMF would store) is lost. It's not possible to recover the GDI calls issued to construct the final image.
Even if that were possible, EMF wouldn't be capable of representing rendering information for image data that is not produced by the GDI (e.g. Qt applications with an OpenGL/Direct2D rasterizer, WPF applications, or UWP applications).
I am building a GUI using WINApi C++ Unicode. My ultimate goal is not to load the .bmp image from a file because i will be passing around the GUI as a .exe file.
Is it possible to load the .bmp image into the GUI's resource and load the image from there every time I want to display on my GUI?
Sure. There's a resource type specially for bitmaps. Just use "Add resource" in the Visual Studio resource view. Example code on how to use it should be available in the MSDN.
Put this in the resource (.rc) file
IDI_NORMAL BITMAP "Normal.bmp"
Then this in main colde (.cpp) file
HBITMAP hBMP = (HBITMAP)LoadBitmapW(hInst, MAKEINTRESOURCEW(IDI_NORMAL)); //test bitmap
HDC hMemDC = CreateCompatibleDC(hdc);
::SelectObject(hMemDC, hBMP);
BitBlt(hdc, 0, 0, 1000, 1000, hMemDC, 0, 0, SRCCOPY);
::DeleteDC(hMemDC);
Works perfectly
I'm pretty new to Windows programming and have been following theForger's Win32 API Programming Tutorial. I've been trying to draw an image inside a window.
Having looked at similar problems, this code seems to be correct for loading a bitmap:
HBITMAP testImage == NULL;
case WM_CREATE:
testImage = (HBITMAP)LoadImage(NULL, L"C:\\ScreenSnip.bmp", IMAGE_BITMAP, 498, 304, LR_LOADFROMFILE);
if(testImage == NULL) {
MessageBox(NULL, L"NO IMAGE LOADED!", L"Error!", MB_ICONEXCLAMATION | MB_OK);
}
break;
I have an image called ScreenSnip.bmp at the location above, and its dimensions are 498*304. However, LoadImage doesn't work and the value of testImage is always null.
I have tried loading an image as a resource using LoadBitmap() and that works, which is why I haven't included the rest of my code. It seems to be LoadImage() above that's causing the problem, but I can't figure out why.
Anyone have any ideas? I'm running this using VC++ on Windows 7 64bit.
use testImage = (HBITMAP)LoadImage(NULL, L"C:\\ScreenSnip.bmp", IMAGE_BITMAP, 498, 304, LR_LOADFROMFILE);
you use wide wiht L"C:.." so you have to use LoadImageW
I've read something about GetDIBits or BitBlt but i do not understand them.
That could be because i don't understand how Windows actually handles graphics on windows. It would be perfect if someone could refer me to a page where i could learn about these things! :)
I solved the problem using this code in the windows WM_PAINT. It now shows the exact same content as the target window.
PAINTSTRUCT ps;
HDC hdc = BeginPaint(MainWindow, &ps);
HDC TargetDC = GetDC(TargetWindow);
RECT rect;
GetWindowRect(TargetWindow, &rect);
BitBlt(hdc,0,0,rect.right-rect.left,rect.bottom-rect.top,TargetDC,0,0,SRCCOPY);
EndPaint(MainWindow, &ps);
You might have some luck with sending the window a WM_PRINTCLIENT message. This may not work well for windows that use DirectX or OpenGL.
You may have some issues using WM_PRINTCLIENT on systems that have Aero enabled (i.e. when the DWM is active). If the system DOES have DWM active then it may offer ways of getting at the window backing store but I have not looked into doing that in any great depth before.
What you want is to:
Get a HWND to the window of which you want the pixels.
Create a memory DC of the correct size (check this out).
Send a WM_PRINTCLIENT or WM_PAINT to the window whilst supplying your memory DC (not all controls/windows implement this though)
Copy the contents of your memory DC to screen
Alternatively for step 3 you can use the DWM or get hacky using the clipboard:
void CopyWndToClipboard(CWnd *pWnd)
{
CBitmap bitmap;
CClientDC dc(pWnd);
CDC memDC;
CRect rect;
memDC.CreateCompatibleDC(&dc);
pWnd->GetWindowRect(rect);
bitmap.CreateCompatibleBitmap(&dc, rect.Width(),rect.Height());
CBitmap* pOldBitmap = memDC.SelectObject(&bitmap);
memDC.BitBlt(0, 0, rect.Width(),rect.Height(), &dc, 0, 0, SRCCOPY);
pWnd->OpenClipboard() ;
EmptyClipboard() ;
SetClipboardData(CF_BITMAP, bitmap.GetSafeHandle()) ;
CloseClipboard() ;
memDC.SelectObject(pOldBitmap);
bitmap.Detach();
}