首页 > 代码库 > 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中,之前,我们看到的图片,都是本人使用画图工具截图下来的,这次,我们可以看看原貌了。