您的位置:首页 > 其它

使用GDI +加载JPG和PNG资源的CGdiPlusBitmap类

2017-06-13 19:11 946 查看
翻译来源:https://www.codeproject.com/Articles/3537/Loading-JPG-PNG-resources-using-GDI

作者:Joe Woodbury

GDI +无法加载存储为资源的JPG或PNG图像,使用此类就可以加载资源了。

同样效果的文章:GDI+从资源文件中加载图片

下载demo,S2015打开stdafx.h请加上

#ifndef WINVER				// Allow use of features specific to Windows 95 and Windows NT 4 or later.
#define WINVER 0x0501		// Change this to the appropriate value to target Windows 98 and Windows 2000 or later.
#endif

#ifndef _WIN32_WINNT		// Allow use of features specific to Windows NT 4 or later.
#define _WIN32_WINNT 0x0501		// Change this to the appropriate value to target Windows 98 and Windows 2000 or later.
#endif
#pragma warning(disable:4996)




一个类,以方便从资源使用GDI +加载JPG和PNG文件

下载演示项目 - 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
标志



演示

该演示是具有ANSI和UNICODE构建的Visual Studio .NET 2003项目。它允许您加载重新采样的JPG或PNG资源(好奇的是,我在瓦胡岛,夏威夷拍摄了两张照片,同时拍摄了多媒体产品的内容,一个是莱湾,另一个是从威基基观看的夕阳。


CGdiPlusBitmapResource* pBitmap = new CGdiPlusBitmapResource;
if (pBitmap->Load(_T("IDB_BAY"), _T("PNG")))
//如果资源ID没有双引号,直接Load(IDB_BAY,_T("PNG"));这里我找了很久才知道原因在这里
{
CClientDC dc(this);
Gdiplus::Graphics graphics(dc);
graphics.DrawImage(*pBitmap, 0, 0);
}
else
{
AfxMessageBox("Failure loading image");
}
delete pBitmap;



GDI +免责声明

我不是一个GDI +专家,也不是一个大粉丝,即使我发现GDI +偶尔非常有用。请不要问我有关的问题。


为什么不是图片?

我被问到为什么我没有使用
IPicture
。答案是三重的 首先,
IPicture
不支持PNG图像。其次,
IPicture
 几乎只是一个图像加载程序,比标准的GDI位图调用功能更多。第三,
IPicture
立即对图像数据进行解码。JPG和GIF图像将使用比这个类更多的内存。


更新

2004年4月22日

CreateStreamOnHGlobal
现在使用
FALSE
第二个参数,因为
Bitmap
要求保存至少JPG图像的内存,我决定在谨慎的一边。有趣的是,我的测试显示这个标志经常被忽略,但是我收到的报告说,这并不总是这样,在技术上是错误的。
另外,在修复这个bug的时候,我意识到我没有在失败时清除全局内存。这导致代码正在重新排列。

2004年6月15日

如果
Gdiplus::Bitmap::FromStream
失败,我补充说,我认为是多余的,但更正确的代码在处理
m_pBitmap
。(不幸的是,关于是否在失败时始终返回NULL的情况下,文档中没有提到这个主题。)

2004年9月3日

添加了一个GDI +初始化部分。
修复了示例应用程序中图像负载故障时的潜在内存泄漏。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: