10.2 使用wxBitmap编程

你可以使用wxBitmap来作下面的事情:

  1. 通过设备上下文将其画在一个窗口上.
  2. 在某些类(比如wxBitmapButton, wxStaticBitmap, and wxToolBar)中将其作为一个图片标签.
  3. 使用其作为双缓冲区(在将某个图形绘制到屏幕上之前先绘制在一块缓冲区上).

某些平台(特别是windows平台)上限制了系统中bitmap资源的数目,因此如果你需要使用很多的bitmap,你最好使用wxImage类来保存它们,而只在使用的时候将其转化成bitmap.

在讨论怎样创建wxBitmap之前,让我们先来讨论一下几个主要的函数(如下表所示)

wxBitmap 代表一个bitmap,可以通过指定宽度和高度,或者指定另外一个bitmap,或者指定一个wxImage,XPM数据(char**), 原始数据(char[]), 或者一个指定类型的文件名的方式来创建.
ConvertToImage 转换成一个wxImage,保留透明部分.
CopyFromIcon 从一个wxIcon创建一个wxBitmap.
Create 从图片数据或者一个给定的大小创建一个bitmap.
GetWidth, GetHeight 返回图片大小.
Getdepth 返回图片颜色深度.
GetMask, SetMask 返回绑定的wxMask对象或者NULL.
GetSubBitmap 将位图其中的某一部分创建为一个新的位图.
LoadFile, SaveFile 从某种支持格式的文件加载或者保存到文件里.
Ok 如果bitmap的数据已经具备则返回True.

创建一个wxBitmap

可以通过下面的几个途径来创建一个wxBitmap对象.

你可以直接通过默认的构造函数创建一个不包含数据的bitmap,不过你几乎不能用这个bitmap作任何事情,除非你调用Create或者LoadFile或者用另外一个bitmap赋值以便使其拥有具体的bitmap数据.

你还可以通过指定宽度和高度的方法创建一个位图,这种情况下创建的位图将被填充以随机的颜色,你需要在其上绘画以便更好的使用它,下面的代码演示了怎样创建一个200x100的图片并且将其的背景刷新为白色.

// 使用当前的颜色深度创建一个200x100的位图
wxBitmap bitmap(200, 100,  -1);
// 创建一个内存设备上下文
wxMemoryDC dc;
// 将创建的图片和这个内存设备上下文关联
dc.SelectObject(bitmap);
// 设置背景颜色
dc.SetBackground(*wxWHITE_BRUSH);
// 绘制位图背景
dc.Clear();
// 解除设备上下文和位图的关联
dc.SelectObject(wxNullBitmap);

你也可以从一个wxImage对象创建一个位图,并且保留这个image对象的颜色遮罩或者alpha通道:

// 加载一幅图像
wxImage image(wxT("image.png"), wxBITMAP_TYPE_PNG);
// 将其转换成bitmap
wxBitmap bitmap(image);

通过CopyFromIcon函数可以通过图标文件创建一个bitmap:

// 加载一个图标
wxIcon icon(wxT("image.xpm"), wxBITMAP_TYPE_XPM);
// 将其转换成位图
wxBitmap bitmap;
bitmap.CopyFromIcon(icon);

或者你可以通过指定类型的方式从一个图片文件直接加载一个位图:

// 从文件加载
wxBitmap bitmap(wxT("picture.png", wxBITMAP_TYPE_PNG);
if (!bitmap.Ok())
{
    wxMessageBox(wxT("Sorry, could not load file."));
}

wxBitmap可以加载所有的可以被wxImage加载的图片类型,使用的则是各个平台定义的针对特定类型的加载方法.最常用的图片格式比如"PNG, JPG,BMP和XPM"等在各个平台上都支持读取和保存操作,不过你需要确认你的wxWidgets在编译的时候已经打开了对应的支持.

目前支持的图形类型处理函数如下表所示:

wxBMPHandler 用来加载windows位图文件.
wxPNGHandler 用来加载PNG类型的文件.这种文件支持透明背景以及alpha通道.
wxJPEGHandler 用来支持JPEG文件
wxGIFHandler 因为版权方面的原因,仅支持GIF格式的加载.
wxPCXHandler 用来支持PCX. wxPCXHandler会自己计算图片中颜色的数目,如果没有超过256色,则采用8bits颜色深度,否则就采用24 bits颜色深度.
wxPNMHandler 用来支持PNM文件格式. 对于加载来说PNM格式可以支持ASCII和raw RGB两种格式.但是保存的时候仅支持raw RGB格式.
wxTIFFHandler 用来支持TIFF.
wxIFFHandler 用来支持IFF格式.
wxXPMHandler 用来支持XPM格式.
wxICOHandler 用来支持windows平台图标文件.
wxCURHandler 用来支持windows平台光标文件.
wxANIHandler 用来支持windows平台动画光标文件.

在Mac OS X平台上,还可以通过指定wxBITMAP_TYPE_PICT_RESOURCE来加载一个PICT资源.

如果你希望在不同的平台上从不同的位置加载图片,你可以使用wxBITMAP宏,如下所示:

#if !defined(__WXMSW__) && !defined(__WXPM__)
#include "picture.xpm"
#endif
wxBitmap bitmap(wxBITMAP(picture));

这将使得程序在windows和OS/2平台上从资源文件中加载图片,而在别的平台上,则从一个picture_xpm变量中加载xpm格式的图片,因为XPM在所有的平台上都支持,所以这种使用方法并不常见.

设置一个wxMask

每个wxBitmap对象都可以指定一个wxMask,所谓wxMask指的是一个单色图片用来指示原图中的透明区域.如果你要加载的图片中包含透明区域信息(比如XPM,PNG或者GIF格式),那么wxMask将被自动创建,另外你也可以通过代码创建一个wxMask然后调用 SetMask函数将其和对应的wxBitmap对象相关连.你还可以从wxBitmap对象创建一个wxMask,或者通过给一个wxBitmap对象指定一种透明颜色来创建一个wxMask对象.

下面的代码创建了一个拥有透明色的灰阶位图mainBitmap,它的大小是32x32象素,原始图形从imageBits数据创建,遮罩图形从maskBits创建,遮罩中1代表不透明,0代表透明颜色.

static char imageBits[] = { 255, 255, 255, 255, 31,
  255, 255, 255, 31, 255, 255, 255, 31, 255, 255, 255,
  31, 255, 255, 255, 31, 255, 255, 255, 31, 255, 255,
  255, 31, 255, 255, 255, 31, 255, 255, 255, 25, 243,
  255, 255, 19, 249, 255, 255, 7, 252, 255, 255, 15, 254,
  255, 255, 31, 255, 255, 255, 191, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255 };
static char maskBits[] = { 240, 1, 0, 0, 240, 1,
  0, 0, 240, 1, 0, 0, 240, 1, 0, 0, 240, 1, 0, 0, 240, 1,
  0, 0, 240, 1, 0, 0, 240, 1, 0, 0, 255, 31, 0, 0, 255,
  31, 0, 0, 254, 15, 0, 0, 252, 7, 0, 0, 248, 3, 0, 0,
  240, 1, 0, 0, 224, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0 };
wxBitmap mainBitmap(imageBits, 32, 32);
wxBitmap maskBitmap(maskBits, 32, 32);
mainBitmap.SetMask(new wxMask(maskBitmap));

XPM图形格式

在使用小的需要支持透明的图片(比如工具栏上的小图片或者notebook以及树状控件上的小图片)的时候,wxWidgets的程序员通常偏爱使用XPM格式,它的最大的特点是采用C/C++语言的语法,既可以被程序动态加载,也可以直接编译到可执行代码中去,下面是一个例子:

// 你也可以用 #include "open.xpm"
static char *open_xpm[] = {
/* 列数 行数 颜色个数 每个象素的字符个数 */
"16 15 5 1",
"  c None",
". c Black",
"X c Yellow",
"o c Gray100",
"O c #bfbf00",
/* 象素 */
"                ",
"          ...   ",
"         .   . .",
"              ..",
"  ...        ...",
" .XoX.......    ",
" .oXoXoXoXo.    ",
" .XoXoXoXoX.    ",
" .oXoX..........",
" .XoX.OOOOOOOOO.",
" .oo.OOOOOOOOO. ",
" .X.OOOOOOOOO.  ",
" ..OOOOOOOOO.   ",
" ...........    ",
"                "
};
wxBitmap bitmap(open_xpm);

正如你看到的那样,XPM是使用字符编码的.在图片数据前面,有一个调色板区域,使用字符和颜色对应的方法,颜色既可以用标准标识符表示,也可以用16进制的RGB值表示,使用关键字None来表示透明区域.尽管在windows系统上,XPM并不被大多数的图形处理工具支持,不过你还是可以通过一些工具把PNG格式转换成XPM格式,比如DialogBlocks自带的ImageBlocks工具,或者你可以直接使用 wxWidgets编写一个你自己的转换工具.

使用位图绘画

使用位图绘画的方式有两种,你可以使用内存设备上下文绑定一个位图,然后使用wxDC::Blit函数,也可以直接使用wxDC:: DrawBitmap函数,前者允许你使用位图的一部分进行绘制.在两种方式下,如果这个图片支持透明或者alpha通道,你都可以通过将最后一个参数指定为True或者False来打开或者关闭透明支持.

这两种方法的用法如下:

// Draw a bitmap using a wxMemoryDC
wxMemoryDC memDC;
memDC.SelectObject(bitmap);
// Draw the bitmap at 100, 100 on the destination DC
destDC.Blit(100, 100,                         // Draw at (100, 100)
    bitmap.GetWidth(), bitmap.GetHeight(),    // Draw full bitmap
    & memDC,                                  // Draw from memDC
    0, 0,                                     // Draw from bitmap origin
    wxCOPY,                                   // Logical operation
    true);                                    // Take mask into account
memDC.SelectObject(wxNullBitmap);
// Alternative method: use DrawBitmap
destDC.DrawBitmap(bitmap, 100, 100, true);

第五章,"绘画和打印"中对使用bitmap绘画有更详细的描述.

打包位图资源

如果你曾是一个windows平台的程序员,你可能习惯从可执行文件的资源部分加载一幅图片,当然在wxWidgets中也可以这样作, 你只需要指定一个资源名称一个资源类型wxBITMAP_TYPE_BMP_RESOURCE,不过这种作法是平台相关的.你可能更倾向于使用另外一种平台无关的解决方案.

一个可移植的方法是,你可以将你用到的所有数据文件,包括HTML网页,图片或者别的任何类型的文件压缩在一个zip文件里,然后你可以用wxWidgets提供的虚拟文件系统对加载这个zip文件的其中任何一个或几个文件,如下面的代码所示:

// 创建一个文件系统
wxFileSystem*fileSystem = new wxFileSystem;
wxString archiveURL(wxT("myapp.bin"));
wxString filename(wxT("myimage.png"));
wxBitmapType bitmapType = wxBITMAP_TYPE_PNG;
// 创建一个URL
wxString combinedURL(archiveURL + wxString(wxT("#zip:")) + filename);
wxImage image;
wxBitmap bitmap;
// 打开压缩包中的对应文件
wxFSFile* file = fileSystem->OpenFile(combinedURL);
if (file)
{
    wxInputStream* stream = file->GetStream();

    // Load and convert to a bitmap
    if (image.LoadFile(* stream, bitmapType))
        bitmap = wxBitmap(image);

    delete file;
}
delete fileSystem;
if (bitmap.Ok())
{
    ...
}

更多关于虚拟文件系统的信息请参考第14章:文件和流操作.