# 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 全書小結