13.4.2 图像编解码与存储转换
保存和转换图像文件,需要使用编解码器的信息。
1.获取编解码器信息
可以用GDI+的全局函数来获取系统提供的图像编解码器信息:
// 获取编码器总数和存储编码器信息所需的空间大小(字节数): Status GetImageEncodersSize(UINT *numEncoders, UINT *size); // 获取编码器信息:
Status GetImageEncoders(UINT numEncoders, UINT size, ImageCodecInfo *encoders); // 获取解码器总数和存储解码器信息所需的空间大小(字节数): Status GetImageDecodersSize(UINT *numDecoders, UINT *size); // 获取解码器信息:
Status GetImageDecoders(UINT numDecoders, UINT size, ImageCodecInfo *decoders);
其中的编解码器信息类ImageCodecInfo中只含有一些数据成员,见表13-2。
表13-2 ImageCodecInfo类的数据成员 数据成员 Clsid FormatID CodecName DllName 类型 CLSID GUID 编解码器标识符 文件格式标识符 描述 WCHAR * 编解码器名称串 WCHAR * 包含编解码器的DLL文件的路径名串 FormatDescription WCHAR * 编解码器所使用的文件格式名串 FilenameExtension WCHAR * 包含所有关联文件扩展名串(分号分隔) MimeType Flags Version SigCount SigSize SigPattern SigMask
WCHAR * 包含编解码器的MIME类型串 DWORD DWORD DWORD DWORD BYTE * BYTE * ImageCodecFlags枚举的标志位组合 表示编解码器版本的整数 与编解码器关联的文件格式使用的签名数 表示每个签名的字节大小的整数 包含每个签名之模式的字节数组的指针 包含每个签名之掩模的字节数组的指针 41
其中MIME = Multipurpose Internet Mail Extension protocol, 多用途因特网邮件扩充协议。
例如(输出系统可用的编解码器信息,参见图13-32):
图13-32 系统可用图像编解码器信息
void CImgProcView::OnCodecInfo() {
Graphics graph(GetDC()->m_hDC); RedrawWindow();
SolidBrush brush(Color::Green); Font font(L\宋体\
UINT n, size;
ImageCodecInfo *codecInfos; GetImageEncodersSize(&n, &size);
codecInfos = (ImageCodecInfo *)malloc(size); GetImageEncoders(n, size, codecInfos);
CString str = L\编解码器名称\\t\\t格式描述\\t文件扩展名\graph.DrawString(str, str.GetLength(), &font,
PointF(10, 10), &brush);
for (UINT i = 0; i < n; i++) { }
42
str.Format(L\
codecInfos[i].FormatDescription, codecInfos[i].FilenameExtension);
graph.DrawString(str, str.GetLength(), &font,
PointF(10, 40 + 30 * REAL(i)), &brush);
}
free(codecInfos);
Image类也有两个方法,可以用来获取编码器的参数列表:
// 返回参数表的大小(字节数)
UINT GetEncoderParameterListSize(const CLSID *clsidEncoder); Status GetEncoderParameterList(const CLSID *clsidEncoder, UINT size,
EncoderParameters *buffer); // 获取参数表
其中的EncoderParameters类,只包含两个数据成员:
UINT Count; // 数组中EncoderParameter对象的数目
EncoderParameter Parameter[l]; // EncoderParameter对象的数组
其中的EncoderParameter类,包含如下4个数据成员:
GUID Guid; // 参数种类标识符
ULONG NumberOfValues; // 数组中Value数据成员指向值的数目 ULONG Type; // 确定参数的数据类型 VOID *Value; // 指向值数组的指针
2.图像的保存与转换
可以在对图像数据进行若干修改后,再保存到其他图像文件中(因为共享冲突,不能保存到原图像文件)。也可以将当前图像,用另一个指定的图像文件格式来保存(即格式转换)。这些都可以用下面的Image类方法Save来完成:
Status Save(const WCHAR *filename, const CLSID *clsidEncoder,
const EncoderParameters *encoderParams = NULL); Status Save(IStream *stream, const CLSID *clsidEncoder,
const EncoderParameters *encoderParams = NULL);
其中,需要CLSID(CLaSs, IDentifer,类标识符)的指针作为输入参数(比MFC中CImage类麻烦)。为此,可以自己编写一个由格式描述符来获取类ID的函数。
例如(作者自己写的,供大家参考):
bool CImgProcDoc::GetEncoderClassID(const wchar_t *format,
CLSID *pClsid) {
43
}
UINT n, size;
ImageCodecInfo *codecInfos; GetImageEncodersSize(&n, &size);
codecInfos = (ImageCodecInfo *)malloc(size); GetImageEncoders(n, size, codecInfos); for (UINT i = 0; i < n; i++)
if (wcscmp(codecInfos[i].FormatDescription, format) == 0){ }
*pClsid = codecInfos[i].Clsid; free(codecInfos); return true;
free(codecInfos); return false;
下面是作者自己写的一个保存图像为指定格式文件的消息响应函数,供大家参考。其中的fileName为CString类变量,是在图像文件被装入时,得到的当前图像文件名。这里用作默认的保存文件名。
void CImgProcDoc::OnFileSaveAs() {
if (ok) {
wchar_t filters[] = L\图像文件(*.bmp;*.gif;*.jpg;*.png;\\
*.tif;*.emf)| *.bmp;*.gif;*.jpg;*.png;*.tif;*.emf|位图文件\\ (*.bmp)|*.bmp|图形交换格式文件(*.gif)|*.gif|联合图象专家组[JPEG]文件\\ (*.jpg)|*.jpg|可移植网络图形文件(*.png)|*.png|标记图像文件格式[TIFF]\\ 文件(*.tif)|*.tif|增强型图元文件(*.emf)|*.emf|所有文件(*.*)|*.*||\
CFileDialog fileDlg(FALSE, NULL, fileName,
OFN_HIDEREADONLY, filters);
if (fileDlg.DoModal() == IDOK) {
CString extStr = fileDlg.GetFileExt(); extStr.MakeUpper();
if (extStr.Compare(L\
44
}
}
else if (extStr.Compare(L\== 0) extStr = L\CLSID clsid;
if (GetEncoderClassID(extStr, &clsid)) {
pImg->Save(fileDlg.GetPathName(), &clsid, NULL); if (pImg->GetLastStatus() != Ok)
MessageBox(NULL, L\保存文件出错!\错误\
} else
MessageBox(NULL, L\找不到指定的编码器!\错误\
说明:这里需要自己为文档类添加“另存为”菜单项ID_FILE_SAVE_AS的消息响应函数,还可以将“保存”菜单项的消息响应也定向于该函数。例如,可以在消息映射
ON_COMMAND(ID_FILE_SAVE_AS, &CImgProcDoc::OnFileSaveAs)
之后,手工添加消息映射:
ON_COMMAND(ID_FILE_SAVE, &CImgProcDoc::OnFileSaveAs)
13.4.3 位图类Bitmap
在三个主要的图像类中,Bitmap类对应于点阵位图,Metafile类对应于矢量图形,基类Image则对应于通用操作。
1.构造函数
Bitmap类有10个构造函数,其中常用的有:
Bitmap(const WCHAR *filename, BOOL useIcm = FALSE);
Bitmap(INT width, INT height, PixelFormat format = PixelFormat32bppARGB); Bitmap(INT width, INT height, Grpaphics *target);
其中PixelFormat可取值为表13-3中所列的常量。
表13-3 图像像素格式(14个)
常量 PixelFormat1bppIndexed
说明 每像素1位,索引色 45