# 10.5 使用wxImage编程
你可以使用wxImage对图形进行一些平台无关的调整,或者将其作为图片加载和保存的中间步骤.图片在wxImage中是按照每一个象素使用一个分别代表红色,绿色和蓝色的字节的格式保存的,如果图片包含alpha通道,则还会占用额外的一个字节.
wxImage主要的函数如下:
| wxImage | wxImage的创建方法包括:指定宽度和高度, 从另外一幅图片创建, 使用XPM数据, 图片元数据(char[]) 和可选的alpha通道数据,文件名及其类型,以及通过输入流等多种方式创建. |
|:--- |:--- |
| ConvertAlphaToMask | 将alpla通道(如果有的话)转换成一个透明遮罩. |
| ConvertToMono | 转换成一个黑白图片. |
| Copy | 返回一个不使用引用记数器的完全一样的拷贝. |
| Create | 创建一个指定大小的图片,可选的参数指明是否初始化图片数据. |
| Destroy | 如果没有人再使用的话,释放内部数据. |
| GeTData, SetData | 获取和设置内部数据指针(unsigned char*). |
| GetImageCount | 返回一个文件或者流中的图片个数. |
| GetOption, GetOptionInt, SetOption, HasOption | 获取, 设置和测试某个选项是否设置. |
| GetSubImage | 将图片的一部分返回为一个新的图像. |
| GetWidth, GetHeight | 返回图片大小. |
| Getred, GetGreen, GetBlue, SetRGB, GetAlpha, SetAlpha | 获得和指定某个象素的RGB以及Alpha通道的值. |
| HasMask, GetMaskRed, GetMaskGreen, GetMaskBlue, SetMaskColour | 用来测试图像是否有一个遮罩,以及遮罩颜色的RGB值或者整个颜色的值. |
| LoadFile, SaveFile | 各种图片格式文件的读取和保存操作. |
| Mirror | 在各种方向上产生镜像,返回一个新图片. |
| Ok | 判断图片是否已初始化. |
| Paste | 将某个图片粘贴在这个图片的指定位置. |
| Rotate, Rotate90 | 旋转图片,返回一个新图片. |
| SetMaskFromImage | 通过指定的图片和透明颜色产生一个遮罩并且设置这个遮罩. |
| Scale, Rescale | 缩放产生一个新图片或者缩放本图片. |
加载和保存图像
wxImage可以读取和保存各种各样的图片格式,并且使用图像处理过程来增加扩展的能力.其它的图像类(比如wxBitmap)在某个平台不具备处理某种图形格式的能力的时候,也通常使用的都是wxImage的图象处理过程来加载特定格式的图形.
本章第二小节中展示了wxWidgets支持的各种图形处理过程.其中wxBMPHandler是默认支持的,而要支持其它的图形格式处理,就需要使用 wxImage::AddHandler函数增加对应的图形处理过程或者使用wxInitAllImageHandlers增加所有支持的图形处理过程.
如果你只需要特定的图形格式支持,可以在OnInit函数中使用类似下面的代码:
```
#include "wx/image.h"
wxImage::AddHandler( new wxPNGHandler );
wxImage::AddHandler( new wxJPEGHandler );
wxImage::AddHandler( new wxGIFHandler );
wxImage::AddHandler( new wxXPMHandler );
```
或者,你可以简单的调用:
```
wxInitAllImageHandlers();
```
下面演示了几种从文件或者流读取图片的方式,注意在实际使用过程中,最好使用绝对路径以避免依赖于当前路径的设置:
```
// 使用构造函数指定类型来读取图像
wxImage image(wxT("image.png"), wxBITMAP_TYPE_PNG);
if (image.Ok())
{
...
}
// 不指定图像类型一般也能正常工作
wxImage image(wxT("image.png"));
// 使用两步法创建图像
wxImage image;
if (image.LoadFile(wxT("image.png")))
{
...
}
// 如果一个文件包含两副图片Two-step loading with an index into a multi-image file:
// 下面演示选择第2副加载
wxImage image;
int imageCount = wxImage::GetImageCount(wxT("image.tif"));
if (imageCount > 2)
image.LoadFile(wxT("image.tif"), wxBITMAP_TYPE_TIFF, 2);
// 从文件流加载图片
wxFileInputStream stream(wxT("image.tif"));
wxImage image;
image.LoadFile(stream, wxBITMAP_TYPE_TIF);
// 保存到一个文件
image.SaveFile(wxT("image.png")), wxBITMAP_TYPE_PNG);
// 保存到一个流
wxFileOutputStream stream(wxT("image.tif"));
image.SaveFile(stream, wxBITMAP_TYPE_TIF);
```
除了XPM和PCX格式以外,其它的图片格式都将以24位颜色深度保存(译者注:GIF格式因为版权方面的原因不支持保存到文件),这两种格式的图形处理过程将会计算实际的颜色个数从而选择相应的颜色深度.JPEG格式还拥有一个质量选项可供设置.它的值的范围为从0到100,0代表最低的图片质量和最高的压缩比,100则代表最高的图片质量和最低的压缩比.如下所示:
```
// 设置一个合理的质量压缩比
image.SetOption(wxIMAGE_OPTION_QUALITY, 80);
image.SaveFile(wxT("picture.jpg"), wxBITMAP_TYPE_JPEG);
```
另外如果以XPM格式保存到流输出中的时候,需要使用wxImage::SetOption函数设置一个名称否则,处理函数不知道该用什么名称命名对应的C变量.
```
// 保存XPM到流格式
image.SetOption(wxIMAGE_OPTION_FILENAME, wxT("myimage"));
image.SaveFile(stream, wxBITMAP_TYPE_XPM);
```
注意处理函数会自动在你设置的名称后增加"_xpm".
透明
有两种方式设置一个wxImage为透明的图像:使用颜色遮罩或者alpha通道.一种颜色可以被指定为透明颜色,通过这种方法在将wxImage转换成wxBitmap的时候可以很容易的制作一个透明遮罩.
wxImage也支持alpha通道数据,在每一个象素的RGB颜色之外来由另外一个字节用来指示alpha通道的值,0代表完全透明,255则代表完全不透明.中间的值代表半透明.
不是所有的图片都用有alpha通道数据的,因此在使用GetAlpha函数之前,应该使用HasAlpha函数来判断图像是否拥有 alpha通道数据.到目前为止,只有PNG文件或者调用SetAlpha设置了alpha通道的图像才拥有alpha通道数据.保存一个带有alpha 通道的图像目前还不被支持.绘制一个拥有alpha通道的方法是先将其转换成wxBitmap然后使用wxDC::DrawBitmap或者wxDC:: Blit函数.
下面的代码演示了怎样使用颜色掩码创建一个透明的wxImage,它是蓝色的,拥有一个透明的矩形区域:
```
// 创建一个有颜色掩码的wxBitmap
// 首先,在这个wxBitmap上绘画
wxBitmap bitmap(400, 400);
wxMemoryDC dc;
dc.SelectObject(bitmap);
dc.SetBackground(*wxBLUE_BRUSH);
dc.Clear();
dc.SetPen(*wxRED_PEN);
dc.SetBrush(*wxRED_BRUSH);
dc.DrawRectangle(50, 50, 200, 200);
dc.SelectObject(wxNullBitmap);
// 将其转换成wxImage
wxImage image = bitmap.ConvertToImage();
// 设置掩码颜色
image.SetMaskColour(255, 0, 0);
```
在下面的例子中,使用从一个图片创建颜色遮罩的方式,其中image.bmp是原始图像,而mask.bmp则是一个掩码图像,在后者中所有透明的部分都是黑色显示的.
```
// 加载一副图片和它的掩码遮罩
wxImage image(wxT("image.bmp"), wxBITMAP_TYPE_BMP);
wxImage maskImage(wxT("mask.bmp"), wxBITMAP_TYPE_BMP);
// 从后者创建一个遮罩并且设置给前者.
image.SetMaskFromImage(maskImage, 0, 0, 0);
```
如果你加载的图片本身含有透明颜色,你可以检测并且直接创建遮罩:
```
// 加载透明图片
wxImage image(wxT("image.png"), wxBITMAP_TYPE_PNG);
// 获取掩码
if (image.HasMask())
{
wxColour maskColour(image.GetMaskRed(),
image.GetMaskGreen(),
image.GetMaskBlue());
}
```
变形
wxImage支持缩放,旋转以及镜像等多种变形方式,下面各举一些例子:
```
// 把原始图片缩放到200x200,并保存在新的图片里
// 原图保持不变.
wxImage image2 = image1.Scale(200, 200);
// 将原图缩放到200x200
image1.Rescale(200, 200);
// 旋转固定角度产生新图片.
// 原图片保持不变.
wxImage image2 = image1.Rotate(0.5);
// 顺时针旋转90度产生新图片.
// 原图保持不变.
wxImage image2 = image1.Rotate90(true);
// 水平镜像产生新图片.
// 原图保持不变.
wxImage image2 = image1.Mirror(true);
```
颜色消减
如果你想对某个图像的颜色进行消减,你可以使用wxQuantize类的一些静态函数,其中最有趣的函数Quantize的参数为一个输入图片,一个输出图片,一个可选的wxPalette**指针用来存放经过消减的颜色,以及一个你希望保留的颜色个数,你也可以传递一个unsigned char**变量来获取一个8-bit颜色深度的输出图像.最后的一个参数style(类型)用来对返回的图像进行一些更深入的控制,详情请参考 wxWidgets的手册.
下面的代码演示了怎样将一幅图片的颜色消减到最多256色:
```
#include "wx/image.h"
#include "wx/quantize.h"
wxImage image(wxT("image.png"));
int maxColorCount = 256;
int colors = image.CountColours();
wxPalette* palette = NULL;
if (colors > maxColorCount )
{
wxImage reducedImage;
if (wxQuantize::Quantize(image, reducedImage,
& palette, maxColorCount))
{
colors = reducedImage.CountColours();
image = reducedImage;
}
}
```
一个wxImage可以设置一个wxPalette,例如加载GIF文件的时候. 然后,图片内部仍然是以RGB的方式存储数据的,调色板仅代表图片加载时候的颜色隐射关系.调色板的另外一个用途是某些图片处理函数用它来将图片保存为低颜色深度的图片,例如windows的BMP图片处理过程将检测是否设置了wxBMP_8BPP_PALETTE标记,如果设置了,则将使用调色板.而如果设置了wxBMP_8BPP标记(而不是wxBMP_8BPP_PALETTE),它将使用自己的算法进行颜色消减.另外某些图片处理过程自己也进行颜色消减,比如PCX的处理过程,除非它认为剩余的颜色个数已经足够低了,否则它将对图片的颜色进行消减.
关于调色板更多的信息请参考第5章的"调色板"小节.
直接操作wxImage 的元数据
你可以直接通过GetData函数访问wxImage的元数据以便以比GeTRed, GetBlue, GetGreen和SetRGB更快的方式对其进行操作,下面举了一个使用这种方法将一个图片转换成灰度图片的方法:
```
void wxImage::ConvertToGrayScale(wxImage& image)
{
double red2Gray = 0.297;
double green2Gray = 0.589;
double blue2Gray = 0.114;
int w = image.GetWidth(), h = image.GetHeight();
unsigned char *data = image.GetData();
int x,y;
for (y = 0; y < h; y++)
for (x = 0; x < w; x++)
{
long pos = (y * w + x) * 3;
char g = (char) (data[pos]*red2Gray +
data[pos+1]*green2Gray +
data[pos+2]*blue2Gray);
data[pos] = data[pos+1] = data[pos+2] = g;
}
}
```
- 第一章 介绍
- 1.1 为什么要使用wxWidgets?
- 1.2 wxWidgets的历史
- 1.3 wxWidgets社区
- 1.4 wxWidgets和面向对象编程
- 1.5 wxWidgets的体系结构
- 1.6 许可协议
- 第一章小结
- 第二章 开始使用
- 2.1 一个小例子
- 2.2 应用程序类
- 2.3 Frame窗口类
- 2.4 事件处理函数
- 2.5 Frame窗口的构造函数
- 2.6 完整的例子
- 2.7 wxWidgets程序一般执行过程
- 2.8 编译和运行程序
- 第二章小结
- 第三章 事件处理
- 3.1 事件驱动编程
- 3.2 事件表和事件处理过程
- 3.3 过滤某个事件
- 3.4 挂载事件表
- 3.5 动态事件处理方法
- 3.6 窗口标识符
- 3.7 自定义事件
- 第三章小结
- 第四章 窗口的基础知识
- 4.1 窗口解析
- 4.2 窗口类概览
- 4.3 基础窗口类
- 4.4 顶层窗口
- 4.5 容器窗口
- 4.6 非静态控件
- 4.7 静态控件
- 4.8 菜单
- 4.9 控制条
- 第四章小结
- 第五章绘画和打印
- 5.1 理解设备上下文
- 5.2 绘画工具
- 5.3 设备上下文中的绘画函数
- 5.4 使用打印框架
- 5.5 使用wxGLCanvas绘制三维图形
- 第五章小节
- 第六章处理用户输入
- 6.1 鼠标输入
- 6.2 处理键盘事件
- 6.3 处理游戏手柄事件
- 第六章小结
- 第七章使用布局控件进行窗口布局
- 7.1 窗口布局基础
- 7.2 窗口布局控件
- 7.3 使用布局控件进行编程
- 7.4 更多关于布局的话题
- 第七章小结
- 第八章使用标准对话框
- 8.1信息对话框
- 8.2 文件和目录对话框
- 8.3 选择和选项对话框
- 8.4 输入对话框
- 8.5 打印对话框
- 第八章小结
- 第九章创建定制的对话框
- 9.1 创建定制对话框的步骤
- 9.2 一个例子:PersonalRecordDialog
- 9.3 在小型设备上调整你的对话框
- 9.4 一些更深入的话题
- 9.5 使用wxWidgets资源文件
- 第九章小结
- 第十章使用图像编程
- 10.1 wxWidgets中图片相关的类
- 10.2 使用wxBitmap编程
- 10.3 使用wxIcon编程
- 10.4 使用wxCursor编程
- 10.5 使用wxImage编程
- 10.6 图片列表和图标集
- 10.7 自定义wxWidgets提供的小图片
- 第十章小结
- 第十一章剪贴板和拖放操作
- 11.1 数据对象
- 11.2 使用剪贴板
- 11.3 实现拖放操作
- 第十一章小结
- 第十二章高级窗口控件
- 12.1 wxTreeCtrl
- 12.2 wxListCtrl
- 12.3 wxWizard
- 12.4 wxHtmlWindow
- 12.5 wxGrid
- 12.6 wxTaskBarIcon
- 12.7 编写自定义的控件
- 第十二章小结
- 第十三章数据结构类
- 13.1 为什么没有使用STL?
- 13.2 字符串类型
- 13.3 wxArray
- 13.4 wxList和wxNode
- 13.5 wxHashMap
- 13.6 存储和使用日期和时间
- 13.7 其它常用的数据类型
- 第十三章小结
- 第十四章文件和流操作
- 14.1 文件类和函数
- 14.2 流操作相关类
- 第十四章小结
- 第十五章内存管理,调试和错误处理
- 15.1 内存管理基础
- 15.2 检测内存泄漏和其它错误
- 15.3 构建自防御的程序
- 15.4 错误报告
- 15.5 提供运行期类型信息
- 15.6 使用wxModule
- 15.7 加载动态链接库
- 15.8 异常处理
- 15.9 调试提示
- 第十五章小结
- 第十六章编写国际化程序
- 16.1 国际化介绍
- 16.2 从翻译说起
- 16.3 字符编码和Unicode
- 16.4 数字和日期
- 16.5 其它媒介
- 16.6 一个小例子
- 第十六章小结
- 第十七章编写多线程程序
- 17.1 什么时候使用多线程,什么时候不要使用
- 17.2 使用wxThread
- 17.3 用于线程同步的对象
- 17.4 多线程的替代方案
- 第十七章小结
- 第十八章使用wxSocket编程
- 18.1 Socket类和功能概览
- 18.2 Socket及其基本处理介绍
- 18.3 Socket标记
- 18.4 使用Socket流
- 18.5 替代wxSocket
- 第十八章小结
- 第十九章使用文档/视图框架
- 19.1 文档/视图基础
- 19.2 文档/视图框架的其它能力
- 19.3 实现Undo/Redo的策略
- 第十九章小结
- 第二十章完善你的应用程序
- 20.1 单个实例和多个实例
- 20.2 更改事件处理机制
- 20.3 降低闪烁
- 20.4 实现联机帮助
- 20.5 解析命令行参数
- 20.6 存储应用程序资源
- 20.7 调用别的应用程序
- 20.8 管理应用程序设置
- 20.9 应用程序安装
- 20.10 遵循用户界面设计规范
- 20.11 全书小结