5.2 绘画工具

在wxWidgets中,绘画操作就象是一个技术非常高超的艺术家,快速的选择颜色,绘画工具,然后画场景的一小部分,然后换不同的颜色,不同的工具,再绘画场景的其它部分,周而反复的操作。因此,接下来我们来介绍一下wxColour,wxPen, wxBrush, wxFont和wxPalette这些绘画工具。还有其它的一些内容也是有帮助的,比如wxRect,wxRegion,wxPoint和wxSize,我们会在第13章,�数据结构类�中对它们进行介绍。

注意这些类使用了�引用记数�的方法,使用内部指针以避免大块的内存拷贝,在大多数情况下,你可以直接以局部变量的方式定义颜色,画笔,画刷和字体对象而不用担心性能。如果你的程序确实因此而拥有性能上的问题,你才需要考虑采取一些方法来提高性能,比如将其中的一些局部变量改变成类的成员。

wxColour

你可以使用wxColour类来定义各种各样的颜色。(因为wxWidgets开始于爱丁堡,它的API使用英式拼写,不过对于那些对拼写很敏感的人,wxWidgets还是给wxColor定义了一个别名wxColour)。

你可以使用SetTextForeground和SetTextBackground函数来定义一个设备上下文中文本的颜色,也可以使用wxColour来创建画笔和刷子。

wxColour对象有很多种创建方法,你可以使用RGB三元色的值(0到255)来构建wxColour,或者通过一个标准的字符串,比如WHITE或者CYAN,或者从另外一个wxColour对象创建。或者你还可以直接使用系统预定的颜色对象指针: wxBLACK, wxWHITE, wxRED, wxBLUE, wxGREEN, wxCYAN,和 wxLIGHT_GREY.还有一个wxNullColour对象用来代表未初始化的颜色,它的Ok函数总是返回False。

使用wxSystemSettings类可以获取很多系统默认的颜色,比如3D表面颜色,默认的窗口背景颜色,菜单文本颜色等等。请参考相关文档中wxSystemSettings::GetColour的部分来获取详细的列表。

下面的例子演示了创建wxColour的方法:

wxColour color(0, 255, 0); // green
wxColour color(wxT("RED")); // red
// 使用面板的三维表面系统颜色
wxColour color(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));

wxTheColourDatabase指针用来在系统之类的颜色和颜色名之间建立映射,通过颜色名寻找对应的颜色对象或者通过颜色对象来寻找对应的颜色名,如下所示:

wxTheColourDatabase->Add(wxT("PINKISH"), wxColour(234, 184, 184));
wxString name = wxTheColourDatabase->FindName(
                                         wxColour(234, 184, 184));
wxString color = wxTheColourDatabase->Find(name);

下面列出了目前支持的标准颜色:aquamarine, black, blue, blue violet, brown, cadet blue, coral, cornflower blue, cyan, dark gray, dark green, dark olive green, dark orchid, dark slate blue, dark slate gray dark turquoise, dim gray, firebrick, forest green, gold, goldenrod, gray, green, green yellow, indian red, khaki, light blue, light gray, light steel blue, lime green, magenta, maroon, medium aquamarine, medium blue, medium forest green, medium goldenrod, medium orchid, medium sea green, medium slate blue, medium spring green, medium turquoise, medium violet red, midnight blue, navy, orange, orange red, orchid, pale green, pink, plum, purple, red, salmon, sea green, sienna, sky blue, slate blue, spring green, steel blue, tan, thistle, turquoise, violet, violet red, wheat, white, yellow, 和 yellow green.

wxPen

你可以使用SetPen函数指定一个设备上下文使用的画笔(wxPen)。画笔指定了随后的绘画操作中线条的颜色,粗细以及线条类型。wxPen的开销很小,你可以放心的在你的绘图代码中创建局部变量类型的画笔对象而不用对它们进行全局存储。

下表列出了目前支持的画笔线条类型,其中Hatch和stipple类型目前的GTK+版本不支持:

线形 示例 描述
wxSOLID 纯色线.
wxTRANSPARENT 透明颜色.
wxDOT 纯点线.
wxLONG_DASH 长虚线.
wxSHORT_DASH 短虚线.在windows平台上等同于wxLONG_SASH.
wxDOT_DASH 短线和点间隔线.
wxSTIPPLE 使用一个位图代替点的点虚线,这个位图是其构造函数的第一个参数
wxUSER_DASH 自定义虚线. 参考用户手册.
wxBDIAGONAL_HATCH 反斜线虚线.
wxCROSSDIAG_HATCH 交叉虚线.
wxFDIAGONAL_HATCH 斜线虚线.
wxCROSS_HATCH 十字虚线.
wxHORIZONTAL_HATCH 水平线段虚线.
wxVERTICAL_HATCH 垂直线段虚线.

使用SetCap定义粗线条的末端的样子:wxCAP_ROUND是默认的设置,只是粗线条的末端应该使用圆形,wxCAP_PROJECTING则只是使用方形并且有一个凸起,wxCAP_BUTT则只是直接使用方形。

使用SetJoin函数来设置当有线段相连时候的联结方式,默认的值是wxJOIN_ROUND,这种情况下转角是圆形的,其它可选的值还有wxJOIN_BEVEL和wxJOIN_MITER.

你也可以直接使用预定的画笔对象:wxRED_PEN, wxCYAN_PEN, wxGREEN_PEN, wxBLACK_PEN, wxWHITE_PEN, wxtrANSPARENT_PEN, wxBLACK_DASHED_PEN, wxGREY_PEN, wxMEDIUM_GREY_PEN 和 wxLIGHT_GREY_PEN.这些都是指针,所以在SetPen函数中使用的时候,应该使用"*"号指向它们的实例。还有一个预定义的对象(不是指针)wxNullPen,可以用来复位设备上下文中的画笔。

下面是创建画笔的一些演示代码,都用来产生一个纯红色的画笔:

wxPen pen(wxColour(255, 0, 0), 1, wxSOLID);
wxPen pen(wxT("RED"), 1, wxSOLID);
wxPen pen = (*wxRED_PEN);
wxPen pen(*wxRED_PEN);

上面例子中的最后两行使用了引用记数的方法,实际上内部指向同一个对象。这种引用记数的方法在绘画对象中很常用,它使得对象赋值和拷贝的系统开销非常小,不过同时它意味着一个对象的改变将会影响到其它所有使用同一个引用的对象。

一个既可以简化画笔对象的创建和释放过程,又不需要将画笔对象存储在自己的对象中的方法,是使用全局指针wxThePenList来创建和存储所有你需要的画笔对象,如下所示:

wxPen* pen = wxThePenList->FindOrCreatePen(*wxRED, 1, wxSOLID);

这个wxThePenList指向的对象将负责存储所有的画笔对象并且在应用程序退出的时候自动释放所有的画笔。很显然,你应该小心不要过量使用这个对象以免画笔对象占用大量的系统内存,而且也要注意前面我们提到过的使用引用对象的问题,你可以使用RemovePen函数从 wxThePenList中删除一个画笔但是却不释放它所占的内存。

wxBrush

设备上下文当前使用的画刷对象可以用SetBrush函数指定,它决定设备上下文中图像的填充方式。你也可以使用它来定义设备上下文的默认背景,这样的定义方式可以使得背景不只是简单的纯色。和画笔对象一样,画刷对象的系统消耗也非常小,你可以直接使用局部变量的方式定义它。

画刷的构造函数采用一个颜色参数和一个画刷类型参数,如下表所示:

画刷类型 例子 描述
wxSOLID 纯色画刷.
wxTRANSPARENT 透明画刷.
wxBDIAGONAL_HATCH 反斜线画刷.
wxCROSSDIAG_HATCH 交叉画刷.
wxFDIAGONAL_HATCH 斜线画刷.
wxCROSS_HATCH 十字画刷.
wxHORIZONTAL_HATCH 水平线画刷.
wxVERTICAL_HATCH 垂直线画刷.
wxSTIPPLE 位图画刷, 其位图在构造函数中指定.

你也可以直接使用下面这些系统预定义的画刷:wxBLUE_BRUSH, wxGREEN_BRUSH, wxWHITE BRUSH, wxBLACK_BRUSH, wxGREY_BRUSH, wxMEDIUM_GREY_BRUSH, wxLIGHT_GREY_BRUSH, wxtrANSPARENT_BRUSH, wxCYAN_BRUSH和 wxRED_BRUSH. 这些都是指针,类似的还有wxNullBrush用来复位设备上下文的画刷。

下面是创建红色纯色画刷的例子:

wxBrush brush(wxColour(255, 0, 0), wxSOLID);
wxBrush brush(wxT("RED"), wxSOLID);
wxBrush brush = (*wxRED_BRUSH); // a cheap operation
wxBrush brush(*wxRED_BRUSH);

和画笔一样,画刷也有一个用来保存列表的全局指针指针: wxTheBrushList,你可以象下面这样使用它:

wxBrush* brush = wxTheBrushList->FindOrCreateBrush(*wxBLUE, wxSOLID);

同样要避免在应用程序过量使用以及要注意引用记数使用的问题。使用RemoveBrush来从wxTheBrushList中移除一个画刷而不释放其内存。

wxFont

你可以使用字体对象来设置一个设备上下文使用的字体,字体对象有下面一些属性:

字体大小用来以点(1/72英寸)为单位指定字体中的最大高度。wxWidgets会选择系统中最接近的字体。

字体家族用来指定一个家族系列,象下表中描述的那样,指定一个字体家族而不指定一个字体的名字是为了移植的方便,因为你不大可能要求某个字体的名字存在于所有的平台。

字体家族标识符 例子 描述
wxFONTFAMILY_SWISS 非印刷字体,依平台的不同通常是Helvetica或Arial.
wxFONTFAMILY_ROMAN 一种正规的印刷字体.
wxFONTFAMILY_SCRIPT 一种艺术字体.
wxFONTFAMILY_MODERN 一种等宽字体.通常是Courier
wxFONTFAMILY_DECORATIVE 一种装饰字体.
wxFONTFAMILY_DEFAULT wxWidgets选择一个默认的字体家族.

字体类型可以是wxNORMAL, wxSLANT或wxITALIC。其中wxSLANT可能不是所有的平台或者所有的字体都支持。

weight属性的值则可以是wxNORMAL, wxLIGHT或wxBOLD.

下划线可以被设置或者关闭。

字体名属性是可选参数,用来指定一个特定的字体,如果其为空,则将使用指定字体家族默认的字体。

可选的编码方式参数用来指定字体编码和程序用设备上下文绘画的文本的编码方式的映射,详情请参考第16章,�编写国际化应用程序�。

你可以使用默认的构造函数创建一个字体,或者使用上表中列出的字体家族创建一个字体。

也可以使用下面这些系统预定义的字体: wxNORMAL_FONT, wxSMALL_FONT, wxITALIC_FONT和wxSWISS_FONT. 除了wxSMALL_FONT以外,其它的字体都使用同样大小的系统默认字体(wxSYS_DEFAULT_GUI_FONT), 而wxSMALL_FONT则比另外的三个字体小两个点.你可以使用wxSystemSettings::GetFont来获取当前系统的默认字体。

要使用字体,你需要在进行任何字体相关的操作(比如DrawText和GetTextExtent)之前,使用wxDC::SetFont函数设置字体。

下面的代码演示了怎样构造一个字体对象:

wxFont font(12, wxFONTFAMILY_ROMAN, wxITALIC, wxBOLD, false);
wxFont font(10, wxFONTFAMILY_SWISS, wxNORMAL, wxBOLD, true,
            wxT("Arial"), wxFONTENCODING_ISO8859_1));
wxFont font(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));

和画笔和画刷一样,字体也有一个全局列表指针wxTheFontList,用来查找以前创建的字体或者创建一个新的字体:

wxFont* font = wxTheFontList->FindOrCreateFont(12, wxSWISS,
                                               wxNORMAL, wxNORMAL);

同样的,避免大量使用这个全局指针,因为其分配的内存要到程序退出的时候才会释放。你可以使用RemoveFont从中移除一个字体但是不释放相关的内存。

在本章晚些时候我们会看到一些使用文本和字体的例子。同时你也可以看一下字体例子程序,它允许你选择一个字体然后看看某些文本是什么样子,也可以让你更改字体的大小和别的属性。

wxPalette

调色板是一个表,这个表的大小通常是256,表中的每一个索引被映射到一个对应的rgb颜色值。通常在不同的应用程序需要在同一个显示设备上共享确定数目的颜色的时候使用。通过设置一个调色板,应用程序之间的颜色可以取得一个平衡。调色板也被用来把一个低颜色深度的图片映射到当前可用的颜色,因此每个wxBitmap都有一个可选的调色板。

因为现在大多数电脑的显示设备都支持真彩色,调色板已经很少使用了。应用程序定义的RGB颜色可以直接被显示设备映射到最接近的颜色。

创建一个调色板需要提供一个调色板大小参数和三个分别代表红绿蓝三种颜色的数组。你可以通过GetColoursCount函数得到当前调色板中条目的数量。GetRGB函数通过索引找到其代替的颜色的RGB值,而GetPixel则通过RGB值得到其相应的索引。

使用wxDC::SetPalette函数给某个设备上下文指定一个调色板。比如,你可以给当前的设备上下文指定一个位于某个低颜色深度的wxBitmap对象中的调色板,一边让设备上下文知道怎样把这个图片中的索引颜色映射到真实的RGB颜色。当在一个指定了调色板的设备上下文中使用 wxColour绘画的时候,系统会自动在设备上下文的调色板中查找最匹配的颜色的索引,因此你应该指定一个和你要用的颜色最接近的调色板。

wxPalette的另外一个用法是用来查询一个低颜色深度图像文件(比如GIF)中的图像的不同的颜色。如果这个图像拥有一个调色板,即时这个图片已经被转换成RGB格式,区分图像中的不同颜色也仍然是个很容易的事。类似的,通过调色板,你也可以把一个真彩色的图片转换成低颜色深度的图片,下面的代码演示了怎样将一个真彩色的PNG文件转换成8bit的windows位图文件:

// 加载这个PNG文件
wxImage image(wxT("image.png"), wxBITMAP_TYPE_PNG);
// 创建一个调色板
unsigned char* red = new unsigned char[256];
unsigned char* green = new unsigned char[256];
unsigned char* blue = new unsigned char[256];
for (size_t i = 0; i < 256; i ++)
{
    red[i] = green[i] = blue[i] = i;
}
wxPalette palette(256, red, green, blue);
// 设置调色板和颜色深度
image.SetPalette(palette);
image.SetOption(wxIMAGE_OPTION_BMP_FORMAT, wxBMP_8BPP_PALETTE);
// 存储文件
image.SaveFile(wxT("image.bmp"), wxBITMAP_TYPE_BMP);

降低颜色深度的更实用的方法请参考第10章,�在程序中使用图片�中的�降低颜色深度�小节,介绍怎样用wxQuantize类来作这件事。

wxWidgets定义了一个空的调色板对象wxNullPalette.

(译者注:这一小节翻译的太费劲了)