💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 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章:文件和流操作.