首页 > 代码库 > GDI+学习笔记(七)保存简单图像

GDI+学习笔记(七)保存简单图像

请尊重本人的工作成果,转载请留言,并说明转载地址,谢谢。地址如下:

http://blog.csdn.net/fukainankai/article/details/27710883


前几节中,我们利用GDI+在窗口中绘制了各种各样的图形、图像,这一节,我们将会将这些图像保存成简单图像。所谓简单图像,指的是bmp/jpg/png等图像或者单帧的gif图像。保存成多帧的gif图像稍微复杂一点,本节中暂时不做说明。保存成动态的tiff文件也比较简单,但这里也不做说明,下次有机会和gif一起介绍。

另,说一句扫盲的,知道的可以直接略过,那就是按图像格式来说,jpeg/png等都是单帧的,不可能是动态图,网上所谓的动态jpeg/png实际上都是gif。

ok,现在开始正题。


(一)准备工作

回顾一下,之前显示的大概流程,利用双缓冲,将内容暂存于Bitmap中,Bitmap的绘制方法,是讲其关联到一个Graphics对象上,利用Graphics进行绘制。今天,我们的保存工作,是利用这里的Bitmap实例。实际上,我们就是将这份内存中的Bitmap按我们指定的格式保存到硬盘上而已。

(二)CLSID

这个名词听来似乎有些陌生,但是如果说GUID,相信大家就比较熟悉了。CLSID就是GUID在GDI+中的别名。GUID就是操作系统中的身份证,它为应用程序,组件,OLE对象等等分配的唯一标识码。而CLSID我们这里主要是为编码器分配的一个唯一编码。我们可以理解为,jpg的编码器有个身份证,bmp也有,gif,png等等都有一个。这个编码器是保存文件必不可少的东西。我倒是很希望GDI+能够给我们提供一个获取CLSID的接口,但是,GDI+似乎偷懒了,我们不得不替它完成这项工作。代码如下:

int GetEncoderClsid(const TCHAR *szFormat, CLSID &clsid)
{
	int nRet = -1;
	UINT num = 0;		// 编码器支持的种类
	UINT size = 0;		// 编码器大小

	ImageCodecInfo *pImageCoderInfo = NULL;
	GetImageEncodersSize(&num, &size);	// 获取GDI+的所有编码器的解码种类和解码器大小
	
	if (size == 0)
	{
	<span style="white-space:pre">	</span>return nRet;
	}

	// 为编码器动态分配一块内存
	pImageCoderInfo = (ImageCodecInfo*)(malloc(size));
	if (pImageCoderInfo == NULL)
	{
		return nRet;
	}

	// 获取所有的图形编码器
	GetImageEncoders(num ,size, pImageCoderInfo);

	// 遍历并找到我们需要的编码器
	for(UINT j = 0; j < num; ++j)
	{
		// 取到我们需要的那个解码器
		if( wcscmp(pImageCoderInfo[j].MimeType, szFormat) == 0 )
		{
			clsid = pImageCoderInfo[j].Clsid;
			nRet = j;
			break;
		}   
	}

	free(pImageCoderInfo);
	return nRet;
}
以上是本系列当中最长的代码了吧,事实上,我很讨厌这种事情,我希望我的这个系列尽可能简单,但是今天似乎无法避免了,现在解释一下。


我们先单独拿出原型看一下

int GetEncoderClsid(const TCHAR *szFormat, CLSID &clsid)

这个函数的返回值,是我们需要的解码器在编码器数组中的索引。如果小于0,说明获取失败。

参数szFormat表示编码的格式,比如“image/jpeg”、"image/bmp"等等

参数clsid,刚才已经解释了,就是一组序列号。


这个函数虽然看起来挺长,但好在内容还算简单,简单介绍一下:

1. 通过GetImageEncodersSize获取到支持的编码器的个数,和所有编码器加起来的size

2. 通过GetImageEncoders获得所有的编码器

3. 遍历所有的编码器,获得我们需要的解码器CLSID


这里需要注意的是,GetImageEncoders第三个参数输出参数,存储了第一个解码器的地址,它指向了一块连续的内存空间(就是我们之前分配的那块),其中存放了所有的编码器。我们不能使用new ImageCodecInfo来为pImageCoderInfo分配内存,因为我们将要获得num个编码器。虽然,我也很不喜欢这种C和C++的混用,但是一时没有想到更好的办法,如果你有好主意,请留言。

(三)保存文件

解决了编码器CLSID的问题,保存反倒简单了很多,直接看代码吧

	if (m_bNotSaved)
	{
		CLSID clsid;
		if (GetEncoderClsid(_T("image/jpg"), clsid) >= 0)
		{
			Status s = cacheBitmap.Save(_T("D:\\test.jpg"), &clsid);
			if (s == Ok)
			{
				OutputDebugString(_T("当前图形已经被成功保存至D:\\test.jpg"));
				m_bNotSaved = false;
			}
		}
	}

这里我们需要注意一下:jpeg文件名的格式名为“image/jpeg”如果你写了jpg,恐怕什么都无法得到。


至此,我们就将我们应用程序的第一个画面保存到了D盘下的test.jpg中,之前,我们看到的图片,都是本人使用画图工具截图下来的,这次,我们可以看看原貌了。