#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;
}
最后会返回可操作的hBitmap句柄
另附上异曲同工的代码:
Gdiplus::Bitmap *loadimage(HINSTANCE hinst, const wchar_t* name)
{
auto hres = FindResource(hinst, name, RT_RCDATA);
if(!hres)
return nullptr;//resource not found
if(auto size = SizeofResource(hinst, hres))
if(auto data = LockResource(LoadResource(hinst, hres)))
if(auto stream = SHCreateMemStream((const BYTE*)data, size))
{
auto *bmp = new Gdiplus::Bitmap(stream);
if(!bmp)
{
//wrong input, or gdiplus was not initialized
}
stream->Release();
return bmp;
}
return nullptr;
}
...
auto gimg = loadimage(hinst, MAKEINTRESOURCE(ID_PNG1));
Gdiplus::Color clr = Gdiplus::Color::Transparent;
gimg->GetHBITMAP(Gdiplus::Color::Transparent, &bitmap);
ImageList_AddMasked(himage, bitmap, 0));
DeleteObject(bitmap);
//资源定义应如下所示:
ID_PNG1 RCDATA "file.png"
//确保初始化Gdiplus:
struct gdiplus_init
{
Gdiplus::GdiplusStartupInput tmp;
ULONG_PTR token;
gdiplus_init(){ Gdiplus::GdiplusStartup(&token, &tmp, NULL); }
~gdiplus_init(){ Gdiplus::GdiplusShutdown(token); }
} gdipls_init;
附上代码链接:https://stackoverflow.com/questions/58751417/how-can-i-add-a-transparent-png-as-a-toolbar-icon
补充:也可以使用WIC来加载PNG文件,参考:用C ++显示启动画面
因为是外网资源,所以我将摘取部分内容以防吞链接。
为了在屏幕上显示图像,我们需要将其作为HBITMAP提供。在Windows图像处理组件允许我们PNG图像解码成32位,每像素的位图(带alpha通道),并提取其像素到一个DIB。
在此代码中,我将假定图像嵌入在初始屏幕EXE的资源中(使用与.RC文件类似的语句):
IDI_SPLASHIMAGE PNG splash.png
第一步是在资源数据上创建一个IStream。这涉及加载资源,将其数据复制到内存缓冲区中,然后在该缓冲区上创建流。(请注意,以下代码已出于教学目的进行了更改;可以通过对COM接口和Windows句柄使用智能指针以及使用异常处理错误条件来改进生产代码。)COM应该已经初始化(通过调用 CoInitialize或 CoInitializeEx),然后再调用此方法。
// Creates a stream object initialized with the data from an executable resource.
IStream * CreateStreamOnResource(LPCTSTR lpName, LPCTSTR lpType)
{
// initialize return value
IStream * ipStream = NULL;
// find the resource
HRSRC hrsrc = FindResource(NULL, lpName, lpType);
if (hrsrc == NULL)
goto Return;
// load the resource
DWORD dwResourceSize = SizeofResource(NULL, hrsrc);
HGLOBAL hglbImage = LoadResource(NULL, hrsrc);
if (hglbImage == NULL)
goto Return;
// lock the resource, getting a pointer to its data
LPVOID pvSourceResourceData = LockResource(hglbImage);
if (pvSourceResourceData == NULL)
goto Return;
// allocate memory to hold the resource data
HGLOBAL hgblResourceData = GlobalAlloc(GMEM_MOVEABLE, dwResourceSize);
if (hgblResourceData == NULL)
goto Return;
// get a pointer to the allocated memory
LPVOID pvResourceData = GlobalLock(hgblResourceData);
if (pvResourceData == NULL)
goto FreeData;
// copy the data from the resource to the new memory block
CopyMemory(pvResourceData, pvSourceResourceData, dwResourceSize);
GlobalUnlock(hgblResourceData);
// create a stream on the HGLOBAL containing the data
if (SUCCEEDED(CreateStreamOnHGlobal(hgblResourceData, TRUE, &ipStream)))
goto Return;
FreeData:
// couldn't create stream; free the memory
GlobalFree(hgblResourceData);
Return:
// no need to unlock or free the resource
return ipStream;
}
现在我们有了一个指向图像数据的IStream指针,我们可以使用WIC加载该图像了。此过程中的一个重要步骤是使用 WICConvertBitmapSource来确保图像采用32bpp格式,适合直接转换为DIB。此方法假定输入图像为PNG格式;对于启动屏幕,这是一个绝佳的选择,因为它允许使用alpha通道以及对源图像进行无损压缩。(为使初始屏幕图像尽可能小,我强烈建议使用PNGOUT压缩实用程序。)
// Loads a PNG image from the specified stream (using Windows Imaging Component).
IWICBitmapSource * LoadBitmapFromStream(IStream * ipImageStream)
{
// initialize return value
IWICBitmapSource * ipBitmap = NULL;
// load WIC's PNG decoder
IWICBitmapDecoder * ipDecoder = NULL;
if (FAILED(CoCreateInstance(CLSID_WICPngDecoder, NULL, CLSCTX_INPROC_SERVER, __uuidof(ipDecoder), reinterpret_cast<void**>(&ipDecoder))))
goto Return;
// load the PNG
if (FAILED(ipDecoder->Initialize(ipImageStream, WICDecodeMetadataCacheOnLoad)))
goto ReleaseDecoder;
// check for the presence of the first frame in the bitmap
UINT nFrameCount = 0;
if (FAILED(ipDecoder->GetFrameCount(&nFrameCount)) || nFrameCount != 1)
goto ReleaseDecoder;
// load the first frame (i.e., the image)
IWICBitmapFrameDecode * ipFrame = NULL;
if (FAILED(ipDecoder->GetFrame(0, &ipFrame)))
goto ReleaseDecoder;
// convert the image to 32bpp BGRA format with pre-multiplied alpha
// (it may not be stored in that format natively in the PNG resource,
// but we need this format to create the DIB to use on-screen)
WICConvertBitmapSource(GUID_WICPixelFormat32bppPBGRA, ipFrame, &ipBitmap);
ipFrame->Release();
ReleaseDecoder:
ipDecoder->Release();
Return:
return ipBitmap;
}
接下来是使用CreateDIBSection分配可直接写入的DIB。通过使用正确的值设置BITMAPINFO结构,DIB的格式将与WIC加载的32bpp BGRA图像相同,并且可以将像素直接从WIC位图复制到DIB。
//Creates a 32-bit DIB from the specified WIC bitmap.
HBITMAP CreateHBITMAP(IWICBitmapSource * ipBitmap)
{
// initialize return value
HBITMAP hbmp = NULL;
// get image attributes and check for valid image
UINT width = 0;
UINT height = 0;
if (FAILED(ipBitmap->GetSize(&width, &height)) || width == 0 || height == 0)
goto Return;
// prepare structure giving bitmap information (negative height indicates a top-down DIB)
BITMAPINFO bminfo;
ZeroMemory(&bminfo, sizeof(bminfo));
bminfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bminfo.bmiHeader.biWidth = width;
bminfo.bmiHeader.biHeight = -((LONG) height);
bminfo.bmiHeader.biPlanes = 1;
bminfo.bmiHeader.biBitCount = 32;
bminfo.bmiHeader.biCompression = BI_RGB;
// create a DIB section that can hold the image
void * pvImageBits = NULL;
HDC hdcScreen = GetDC(NULL);
hbmp = CreateDIBSection(hdcScreen, &bminfo, DIB_RGB_COLORS, &pvImageBits, NULL, 0);
ReleaseDC(NULL, hdcScreen);
if (hbmp == NULL)
goto Return;
// extract the image into the HBITMAP
const UINT cbStride = width * 4;
const UINT cbImage = cbStride * height;
if (FAILED(ipBitmap->CopyPixels(NULL, cbStride, cbImage, static_cast<BYTE *>(pvImageBits))))
{
// couldn't extract image; delete HBITMAP
DeleteObject(hbmp);
hbmp = NULL;
}
Return:
return hbmp;
}
最后,可以将这三个函数放在一起以从EXE的资源加载PNG图像并将其转换为HBITMAP:
// Loads the PNG containing the splash image into a HBITMAP.
HBITMAP LoadSplashImage()
{
HBITMAP hbmpSplash = NULL;
// load the PNG image data into a stream
IStream * ipImageStream = CreateStreamOnResource(MAKEINTRESOURCE(IDI_SPLASHIMAGE), _T("PNG"));
if (ipImageStream == NULL)
goto Return;
// load the bitmap with WIC
IWICBitmapSource * ipBitmap = LoadBitmapFromStream(ipImageStream);
if (ipBitmap == NULL)
goto ReleaseStream;
// create a HBITMAP containing the image
hbmpSplash = CreateHBITMAP(ipBitmap);
ipBitmap->Release();
ReleaseStream:
ipImageStream->Release();
Return:
return hbmpSplash;
}