- 下载演示项目 - 121 Kb
- 下载源 - 1 Kb
介绍
最近我需要显示一些JPG和PNG文件。我有一个旧版的LeadTools和这两种格式的开源库,但希望我的可执行文件尽可能的小。所以我决定给GDI +一个尝试。我很快发现GDI +设计不好,非常古怪,但它对我的目的很好,直到我发现我的恐惧,GDI +无法加载存储为资源的JPG或PNG图像!
像我相信,其他开发人员面临这个问题,我不相信文档,试图Bitmap::FromResource
无济于事。在浏览可用的Bitmap方法的同时,我碰到过Bitmap::FromStream
。
经过一些测试和几个错误,主要是由于可怕的GDI +文档,我想出了工作代码。休息一晚后,我决定将代码封装在一个简单的类中,以确保内存被释放。其结果是两类:CGdiPlusBitmap
和CGdiPlusBitmapResource
。
Gotcha
在讨论代码本身之前,有一个需要解决的GDI +的警告。使用JPG,某些TIFF和其他格式,原始图像信息必须始终可用。换句话说,如果您打开位图Bitmap::FromFile
,则在图像打开时您不能删除或以其他方式更改该文件。同样的限制适用于CGdiPlusBitmapResource
。(我的测试发现,PNG和BMP文件似乎没有遵循这一概括,虽然我不知道这是标准行为还是与我的文件只是一个侥幸。)
GDI +初始化
在进行任何GDI +调用之前,需要初始化GDI +。我建议将以下数据成员添加到派生自CWinApp
:
ULONG_PTR m_gdiplusToken;
在InitInstance()
,添加以下调用:
Gdiplus::GdiplusStartupInput gdiplusStartupInput; Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
在ExitInstance()
,添加以下内容:
Gdiplus::GdiplusShutdown(m_gdiplusToken);
类
我创建了两个类,基类是非常简单的封装 Bitmap
和封装全局内存的派生类。我想,如果我有耐心和愿望,我可以延长这个类,但我没有必要这样做。(如果你好奇为什么我不是简单地从ATL类派生出来的CImage
,那是因为代码被用在一个没有使用MFC或ATL的程序中,但代码很简单,很容易被修改为使用CImage
作为基类。)
我不会干扰CGdiPlusBitmap 类
,除非说它有一个公共的数据成员Bitmap* m_pBitmap
。(在类中,我使用Gdiplus
命名空间来开发GDI +对象,以防开发人员不想声明using namespace Gdiplus;
。)
本CGdiPlusBitmapResource
类有几个构造函数和几个重载Load
功能。重载功能简单地允许像我一样的惰性程序员不需要键入MAKEINTRESOURCE
。主Load
函数将资源名称和类型作为字符串,并且是类的关键。本代码全部如下:
inline bool CGdiPlusBitmapResource::Load(LPCTSTR pName, LPCTSTR pType, HMODULE hInst) { Empty(); HRSRC hResource = ::FindResource(hInst, pName, pType); if (!hResource) return false; DWORD imageSize = ::SizeofResource(hInst, hResource); if (!imageSize) return false; const void* pResourceData = ::LockResource(::LoadResource(hInst, hResource)); if (!pResourceData) return false; m_hBuffer = ::GlobalAlloc(GMEM_MOVEABLE, imageSize); if (m_hBuffer) { void* pBuffer = ::GlobalLock(m_hBuffer); if (pBuffer) { CopyMemory(pBuffer, pResourceData, imageSize); IStream* pStream = NULL; if (::CreateStreamOnHGlobal(m_hBuffer, FALSE, &pStream) == S_OK) { m_pBitmap = Gdiplus::Bitmap::FromStream(pStream); pStream->Release(); if (m_pBitmap) { if (m_pBitmap->GetLastStatus() == Gdiplus::Ok) return true; delete m_pBitmap; m_pBitmap = NULL; } } m_pBitmap = NULL; ::GlobalUnlock(m_hBuffer); } ::GlobalFree(m_hBuffer); m_hBuffer = NULL; } return false; }
我发现代码自作聪明,虽然知道返回值的::LoadResource
是一个HGLOBAL
可能会发现明显的双重复制使用CopyMemory
混乱。简而言之,CreateStreamOnHGlobal
需要使用标志HGLOBAL
分配的句柄。通过GlobalAlloc
使用
GMEM_MOVEABLE
标志