windows gdi怎么加载png

老刘

Windows GDI怎么加载PNG图片?超详细教程适合小白

很多做Windows桌面程序的朋友,一碰到画图就会用到GDI。BMP图片随便LoadImage就能显示,可一换成PNG就懵了。PNG体积小、支持透明,现在到处都是,偏偏原生GDI对它不友好。今天就把Windows GDI怎么加载PNG这件事彻底讲清楚,从原理到代码,一步一步带着走,就算你是刚接触WinAPI的新手也能跟着做出来。

为什么GDI直接加载PNG会失败

GDI是Windows最早的图形接口,只认几种简单格式,最常见的就是未压缩的BMP。PNG是压缩格式,还带Alpha通道,GDI本身没有解码器,所以LoadImage、BitBlt这些函数直接喂PNG文件路径,基本都会返回失败或者显示黑块。

微软后来出了GDI+,专门补上了这个短板。GDI+能直接读PNG、JPEG、GIF,还能处理透明度。现在几乎所有需要加载PNG的GDI程序,都是靠GDI+完成的。另外Windows Imaging Component(WIC)也能干这事,不过写法更绕一点。下面先讲最常用、最稳的GDI+方案。

准备工作:把环境搭好

先确认你的项目能链接GDI+。

  1. 打开Visual Studio,右键项目 → 属性 → 链接器 → 输入 → 附加依赖项,加上gdiplus.lib
  2. 如果是纯C++代码,记得在源文件顶部写:
    #include <windows.h>
    #include <gdiplus.h>
    #pragma comment(lib, "gdiplus.lib")
    using namespace Gdiplus;
  3. 程序入口处(比如WinMain或者主对话框初始化)必须先启动GDI+,退出时再关掉。这个步骤漏了,后面所有加载都会崩。

启动代码就这么几行:

GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
// 程序结束前调用
// GdiplusShutdown(gdiplusToken);

方法一:GDI+直接加载PNG并画到窗口(推荐新手先掌握)

这是最简单也最常用的写法。假设你有个窗口句柄hwnd,设备上下文hdc

第一步:从文件加载PNG

Bitmap* pBitmap = new Bitmap(L"C:\\test\\image.png");
if (pBitmap->GetLastStatus() != Ok)
{
    // 加载失败,检查路径、文件是否损坏、权限
    delete pBitmap;
    return;
}

注意路径前面的L,必须是宽字符。路径里有中文也没问题,GDI+支持Unicode。

第二步:画到窗口

Graphics graphics(hdc);
// 画在坐标(50,50),保持原图大小
graphics.DrawImage(pBitmap, 50, 50);
// 也可以指定宽高缩放
// graphics.DrawImage(pBitmap, 50, 50, 200, 150);

Graphics对象用完会自动释放,不用手动delete。

第三步:释放资源

delete pBitmap;

一定要配对释放,不然内存会慢慢涨。

完整小例子(放在WM_PAINT里):

case WM_PAINT:
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);

    Bitmap* pBitmap = new Bitmap(L"C:\\test\\image.png");
    if (pBitmap->GetLastStatus() == Ok)
    {
        Graphics g(hdc);
        g.DrawImage(pBitmap, 10, 10);
    }
    delete pBitmap;

    EndPaint(hwnd, &ps);
    break;
}

方法二:把PNG转成HBITMAP,继续用传统GDI函数

有时候你已经写了一大堆BitBlt、SelectObject代码,不想全部改成GDI+。那就先用GDI+解码,再拿到HBITMAP。

Bitmap* pBitmap = new Bitmap(L"C:\\test\\image.png");
HBITMAP hBitmap = NULL;
pBitmap->GetHBITMAP(Color(0,0,0), &hBitmap);  // 背景色可改
delete pBitmap;

// 现在hBitmap就能像普通位图一样用了
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hOld = (HBITMAP)SelectObject(hdcMem, hBitmap);
BitBlt(hdc, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hOld);
DeleteDC(hdcMem);
DeleteObject(hBitmap);  // 记得删

GetHBITMAP拿到的位图默认带Alpha,但传统BitBlt不会自动混合透明,如果需要半透明效果,得自己写AlphaBlend或者继续用GDI+的DrawImage。

方法三:从资源加载PNG(打包进exe更方便)

把PNG加到.rc资源文件,类型写成RCDATA或者自定义类型,比如PNG。

// 假设资源ID是IDR_PNG1
HRSRC hResource = FindResource(hInstance, MAKEINTRESOURCE(IDR_PNG1), L"PNG");
DWORD imageSize = SizeofResource(hInstance, hResource);
HGLOBAL hGlobal = LoadResource(hInstance, hResource);
void* pData = LockResource(hGlobal);

// 用IStream包装
IStream* pStream = NULL;
CreateStreamOnHGlobal(NULL, TRUE, &pStream);
pStream->Write(pData, imageSize, NULL);

Bitmap* pBitmap = Bitmap::FromStream(pStream);
pStream->Release();
// 后面画图和释放跟前面一样

这样用户拿到的只有一个exe,不用再带图片文件。

处理PNG透明通道的关键点

PNG最大的优势就是Alpha透明。GDI+的DrawImage默认会正确处理半透明,背景会透出来。如果你转成HBITMAP后再用BitBlt,透明就丢了,画面会变成黑底或者白底。解决办法有两个:

  1. 继续用Graphics.DrawImage,最省事。
  2. 自己实现Alpha混合,或者用UpdateLayeredWindow做分层窗口。

颜色格式也要注意。有些PNG是32位ARGB,有些是24位。GDI+会自动转换,但你如果手动操作像素,记得用LockBits检查PixelFormat。

常见坑和避坑技巧

  1. 路径写错最常见。用绝对路径测试,确认文件真的存在。中文路径没问题,但别用相对路径,程序工作目录一变就找不到。
  2. GdiplusStartup只调用一次。多线程里共用同一个Token即可,不要每个线程都Startup。
  3. 内存泄漏。Bitmap对象一定要delete,HBITMAP一定要DeleteObject。用智能指针或者包一层类更安全。
  4. 高DPI屏幕。GDI+画出来的图在150%缩放时可能模糊,需要自己根据GetDpiForWindow计算缩放比例。
  5. 性能。频繁加载同一张PNG很浪费。加载一次后缓存Bitmap指针,或者转成HBITMAP缓存起来。大图建议先缩放到需要的尺寸再画。
  6. 错误检查。每次操作后调用GetLastStatus(),Ok以外的值都能在MSDN查到具体原因。

进阶替代方案:Windows Imaging Component

如果你已经用上Windows 7以上系统,并且想要更现代的接口,可以用WIC。它支持更多格式,解码速度也更快,还能直接输出到Direct2D。不过代码量比GDI+多一截,新手先把GDI+吃透再去碰。

简单对比:

  • GDI+:写起来快,兼容性好,适合传统GDI程序。
  • WIC:功能更强,适合新项目或者需要大量图片处理的场景。
  • 第三方库(libpng + zlib):完全自己控制,但打包体积和跨平台才考虑。

实际项目里的小建议

做工具软件时,把常用图标都做成PNG,用资源方式加载,界面一下子就精致很多。游戏或者动画如果帧数高,建议把PNG预先转成DIB或者纹理,避免每帧解码。调试阶段可以先用文件加载,确认显示正常后再改成资源。

加载成功后记得测试几种情况:带透明的图标、大尺寸壁纸、带ICC色彩配置的PNG、渐进式PNG。这些边界情况最容易暴露问题。

掌握了上面这些,Windows GDI怎么加载PNG基本就通了。GDI+把解码的脏活累活都干了,你只需要关心怎么画和怎么释放。多写几个小demo练练手,后面再复杂的界面也能轻松应对。

文章版权声明:文章内容均来源于各大短视频平台搜集以及修改和删减新增,如有侵权或者违规,请联系站长进行删除,如需转载或复制请以超链接形式并注明出处。

发表评论

快捷回复: 表情:
AddoilApplauseBadlaughBombCoffeeFabulousFacepalmFecesFrownHeyhaInsidiousKeepFightingNoProbPigHeadShockedSinistersmileSlapSocialSweatTolaughWatermelonWittyWowYeahYellowdog
验证码
评论列表 (暂无评论,1人围观)

还没有评论,来说两句吧...

目录[+]