使用GDI+(WIC)加载32位的位图或者PNG图片(具有透明通道)

#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

       https://stackoverflow.com/questions/58749796/win32-unable-to-add-custom-toolbar-icon-having-transparency

补充:也可以使用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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值