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;
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论