【Visual C++】遊戲開發筆記之十四 遊戲畫面繪圖(四) 華麗的CImage類

本系列文章由zhmxy555編寫,轉載請註明出處。 

作者:毛星雲    郵箱: [email protected]    歡迎郵件交流編程心得


我們知道,Visual C++中的CBitmap類的功能簡直太弱小了,這曾經讓Visual C++在圖像處理方面的定位比較的尷尬。之前筆記裏面,我們採用的CBitmap配合GDI進行透明圖像的處理非常的繁瑣,而且受到圖像素材的限制,可以說是比較的落後,不是太實用。


爲了解決這個問題,這節筆記我們將系統的學習MFC和ATL中新增一個圖像處理的類,它就是華麗而強大的CImage類。

由於本節筆記是對CImage類的一個非常系統近乎完全的介紹,我儘量讓它涵蓋到了CImage類的所有的屬性和類成員,所以

篇幅也許比以往的筆記內容都長,裏面的不少內容是用到的時候才需要掌握或者查閱的,並不用強行記憶。




一,概念講解部分



1.CImage類的定位和概述


首先,我們簡單介紹一下CImage類的定位。

CImage是MFC和ATL共享的新類,它提供了增強的位圖支持,包括加載、保存和轉換JPEG,BMP,GIF,PNG圖像格式的能力。可以說是微軟意識到了CBitmap的不足,然後推出了一個CBitmap的增強版。使用CImage類,需在代碼頭部加入包含atlimage.h文件,即添加代碼#include "atlimage.h"。

由於CImage擁有功能強大的類成員函數的支持,它便具有了下列四個比較出彩的特性: 

1、AlphaBlend支持像素級的顏色混合,從而實現透明和半透明的效果。 

2、PlgBlt能使一個矩形區域的位圖映射到一個平行四邊形區域中,而且還可能使用位屏蔽操作。 

3、TransparentBlt在目標區域中產生透明圖像

4、MaskBlt在目標區域中產生源位圖與屏蔽位圖合成的效果。



2.以CImage類做媒,讓CBitmap類也能處理豐富的圖片格式


解決的思路比較明朗,我們採用CImage類的Load函數加載圖片,之後用Detch取得HBITMAP的句柄,然後再將此句柄附加給CBitmap的對象就行了。

這樣就實現了讓CBitmap類也可以操作JPG/JPEG/GIF/PNG格式的圖片。

具體代碼如下:

  1. #include "atlimage.h"  
  2. CImage image;               //定義一個CBitmap類  
  3. image.Load(“filename”); //filename爲要加載的文件地址  
  4. HBITMAP hBitmap=image.Detach(); //返回被分離的圖片的句柄  
  5. CBitmap bmp;                     // 定義一個bitmap  
  6. bmp.Attach(hBitmap);        //進行句柄的附加  

然後就可以用CBitmap進行餘下的操作了。


3.CImage額外的一些性質


CImage類對於DIB(device-independent bitmap)設備無關位圖文件和非DIB都可以處理。我們可以通過Create函數或者CImage::Load來處理DIB部分,用Attach函數來將非DIB部分附加到一個CImage對象上。

對於以下函數,只支持DIB部分的位圖文件,他們是:

GetBitsGetColorTable,GetMaxColorTableEntries,GetPitch,GetPixelAddress,IsIndexed,SetColorTable。


我們可以通過CImage類中的IsDIBSection()函數來幫助我們判斷一個位圖文件是否爲DIB部分,其定義如下:

  1. bool IsDIBSection( ) const throw( );  //如果返回值爲true,則該文件爲DIB;返回flase則不是DIB文件  


我們需要注意的是,CImage不能被選到一個新的CDC( class of device-context設備描述表的類),CImage會爲圖像創建自己的HDC(設備描述表DC的句柄)。因爲一個HBITMAP只能被選入到一個HDC中一次,也就是說這個與CImage相關的HBITMAP不能被選到一個其他的HDC中。

如果需要一個CDC,我們可以從CImage中獲取HDC,然後使用CDC::FromHandle函數。


4.CImage兼容性的說明


在CImage中,有如下兼容性的要求:

只支持Windows NT4.0以上系統的成員函數:PlgBlt,MaskBlt,AlphaBlend。

只支持Windows 2000,98以上系統的成員函數:TransparentBlt,Draw

其實由於目前都是Windows XP以上的操作系統,這個知識點了解一下就行。



5.CImage類用於貼圖的一般的使用方法


使用方法不唯一,最常用的方法如下,該方法大致分爲三部分:

<1> 在源文件中添加CImage類的包含文件: 

#include "atlimage.h"

<2> 定義一個CImage類對象,然後調用CImage::Load方法裝載一個外部圖像文件。Load方法有如下兩種重載:

  1. HRESULT Load(  
  2.    LPCTSTR pszFileName    //包含加載文件名的字符串指針  
  3. throw( );  
  4.   
  5.   
  6. HRESULT Load(  
  7.    IStream* pStream   //指向包含加載文件名的流的指針  
  8. throw();  



<3> 調用CImage::Draw方法繪製圖像

下面重點介紹一下Draw方法。

CImage::Draw  將一個位圖文件從源設備描述表複製到當前設備描述表

該函數有如下六種重載:

  1. BOOL Draw(  
  2.    HDC hDestDC,         //目標設備環境DC的句柄  
  3.    int xDest,           //目的矩形的左上角X座標(邏輯單位)  
  4.    int yDest,           //目的矩形的左上角Y座標(邏輯單位)  
  5.    int nDestWidth,     //目標矩形的寬度(就是設定貼過去的圖片的寬度)  
  6.    int nDestHeight,    //目標矩形的高度(就是設定鐵鍋的圖片的高度)  
  7.    int xSrc,            //源矩形的左上角X座標  
  8.    int ySrc,            //源矩形的左上角Y座標  
  9.    int nSrcWidth,      //源矩形的寬度  
  10.    int nSrcHeight      //源矩形的高度  
  11. const throw( );  
  12.   
  13.   
  14. BOOL Draw(  
  15.    HDC hDestDC,       //目標環境DC的句柄  
  16.    const RECT& rectDest,        //一個RECT結構的引用,用來確定目標圖像。  
  17.    const RECT& rectSrc         //一個RECT結構體的引用,用來確定源圖像  
  18. const throw( );  
  19.   
  20.   
  21. BOOL Draw(  
  22.    HDC hDestDC,         //目標環境DC的句柄  
  23.    int xDest,        //目標矩形的左上角X座標  
  24.    int yDest         //目標矩形的左上角Y座標  
  25. const throw( );        //  
  26. BOOL Draw(  
  27.    HDC hDestDC,        //目標環境DC的句柄  
  28.    const POINT& pointDest         //一個POINT結構體,用來確定目的矩形的左上角座標  
  29. const throw( );  
  30.   
  31.   
  32. BOOL Draw(  
  33.    HDC hDestDC,        //目標環境DC的句柄  
  34.    int xDest,        //目標矩形的左上角X座標  
  35.    int yDest,        //目標矩形的左上角Y座標  
  36.    int nDestWidth,        //目標矩形的寬度  
  37.    int nDestHeight         //目標矩形的寬度  
  38. const throw( );  
  39.   
  40.   
  41. BOOL Draw(  
  42.    HDC hDestDC,        //目標環境DC的句柄  
  43.    const RECT& rectDest         //一個RECT結構的引用,用來確定目標圖像。  
  44. const throw( );  


在上面的Draw函數的各種重載中,對於沒有指定源矩形的版本,則整個源圖像就是默認的源矩形。對於沒有指定目的矩形尺寸的,則源圖片的尺寸就是默認的目的矩形尺寸。


需要注意的是,Draw方法綜合了StretchBlt、TransparentBlt和AlphaBlend函數的功能。。通常情況下,Draw()函數作用和StretchBlt()函數一致。但是當我們的圖像中存在透明的顏色和alpha通道的時候,Draw()函數作用和TransparentBlt()或者AlphaBlend()函數一致。所以,在一般情況下,我們都儘量調用Draw方法來繪製圖像。



6.對CImage所有類成員的介紹


我將CImage類的所有類函數按功能分爲了四大類,可以更方面的瞭解各函數的定位和作用,也方便大家查閱:


<1>功能爲創建與連接,釋放的函數


Attach         附加一個HBITMAP到CImage對象,位圖類型DIB與否都可以

Create         創建一個DIB部分的位圖,並將其附加到之前創建的CImage對象

CreateEX      創建一個DIB部分的位圖(擁有額外的參數),並將其附加到之前 創建的CImage對象

Destroy       從CImage類上分離該位圖並進行刪除

Detach       從CImage類裏分離該位圖

ReleaseDC                釋放設備描述表中的數據

ReleaseGDIPlus             釋放GDI+使用的源

<2>功能爲輸入與輸出的函數


GetExporterFilterString 返回系統支持的輸入文件格式類型及其描述

GetImporterFilterString 返回系統支持的輸出文件格式類型及其描述

LoadFromResource           從指定的源處加載一個圖像資源

Load                        從指定文件處加載一個圖像資源

IsIndexed            判斷一個位圖顏色映射到了一個索引調色盤

IsNull                判斷一個源位圖是否被當前載入

Save                       以指定的類型來保存圖像

<3>關於位圖類型與參數的函數


GetBits        返回一個指向該位圖實際像素值指針

GetBPP        返回該位圖每個像素的位

GetColorTable  返回顏色表中RGB值的範圍條目

GetDC         返回目前被選擇的設備描述表

GetExporterFilterString 返回系統支持的輸入文件格式類型及其描述

GetImporterFilterString 返回系統支持的輸出文件格式類型及其描述

GetHeight          返回當前圖像的像素高度

GetMaxColorTableEntries    返回顏色表條目中的最大值

GetPitch             返回當前圖片的間距(單位爲字節),用來決定像素格式的

GetTransparentColor  返回顏色表中透明色的位置

GetWidth            返回當前圖片的寬度(單位爲像素)

<4>功能爲圖形繪製與位圖塊傳輸相關的函數


AlphaBlend     顯示一個半透明或者透明像素的位圖

BitBlt          從源設備描述表複製一個位圖文件到當前設備描述表

Draw         從源矩形複製一個位圖到目的矩形,該函數伸縮或者拉伸位圖來適應目標矩 形的尺寸,如果有必要,會處理Alpha值和透明顏色。

MaskBlt                     用指定的掩碼和光柵操作來結合顏色數據和目的位圖

PlgBlt                       執行一個從源設備描述表的矩形到目標設備描述表的平行 四邊形的塊狀位圖轉換

SetColorTabel              在DIB的顏色表中設定一系列條目的RGB顏色的值

SetPixelIndexed            設置在指定座標處的像素(使用索調色板的索引值)。

SetPixelRGB                設置在指定座標處的像素(使用RGB值)

SetPixel                    在指定座標處設置像素的顏色

SetTransparentColor        設置將被視爲透明色的顏色的索引值(只能選取調色板中的 一種顏色)

StretchBlt                  從源矩形複製一個位圖到目的矩形,如果有必要,該函數會 伸縮或者拉伸位圖來適應目標矩形的尺寸,

TransparentBlt              從源設備描述表中複製一個帶有透明色的位圖到當前設備 描述表





二,實例運用部分


運用CImage完成透明貼圖的一個完整實例

介紹了這麼多了,下面我們依然用一個實例來鞏固本節筆記的知識。

我們知道,CImage支持透明PNG的貼圖,下面我們就運用透明PNG的貼圖,來代替之前的掩碼操作貼圖。

<第一步>

準備兩張素材圖,一張背景圖,一張需要進行透明操作的人物圖。

這次的選材就很廣了,沒有之前透明操作需要自己一定的美工功底或者美工童鞋支持的諸多限制了。

我選的兩張圖片素材如下:

背景圖 bg.bmp  640x444



人物圖  onion.bmp  130x130(呵呵,可愛的洋蔥頭~~)


<第二步>

將人物圖 onion.bmp用photoshop等圖像處理軟件進行摳圖操作,除去紅黃相間的背景圖,並將背景圖用透明圖層代替,再將圖片大小調節成85x113,用png格式輸出,效果如下:

處理好的人物圖  onion.png  85x113


<第三步>

將bg.bmp以及onion.png放到工程目錄下,並在源文件寫入代碼並運行。


該代碼和筆記六中代碼的思路基本相同,只不過,將筆記六中使用掩碼操作進行透明化處理的方式換成了png透明貼圖的方式,更加的直觀和易懂易用。




詳細註釋的源代碼如下


  1. #include "stdafx.h"    
  2. #include "atlimage.h"  
  3. //全局變量聲明    
  4. HINSTANCE hInst;    
  5. HBITMAP bg;        //聲明一個位圖對象,用於存儲背景圖    
  6. HDC  mdc;        //聲明一個內存DC"mdc",用來暫存位圖  
  7. //全局函數聲明    
  8. ATOM     MyRegisterClass(HINSTANCE hInstance);    
  9. BOOL     InitInstance(HINSTANCEint);    
  10. LRESULT CALLBACK    WndProc(HWNDUINTWPARAMLPARAM);    
  11. void     MyPaint(HDC hdc);    
  12. ////****Winmain函數,程序入口點函數**************************************    
  13. int APIENTRY WinMain(HINSTANCE hInstance,    
  14.                      HINSTANCE hPrevInstance,    
  15.                      LPSTR     lpCmdLine,    
  16.                      int       nCmdShow)    
  17. {    
  18. MSG msg;    
  19. MyRegisterClass(hInstance);    
  20. if (!InitInstance (hInstance, nCmdShow))     
  21. {    
  22. return FALSE;    
  23. }    
  24. //消息循環    
  25. while (GetMessage(&msg, NULL, 0, 0))     
  26. {    
  27. TranslateMessage(&msg);    
  28. DispatchMessage(&msg);    
  29. }    
  30. return msg.wParam;    
  31. }    
  32. //****設計一個窗口類,類似填空題,使用窗口結構體*************************    
  33. ATOM MyRegisterClass(HINSTANCE hInstance)    
  34. {    
  35. WNDCLASSEX wcex;    
  36. wcex.cbSize = sizeof(WNDCLASSEX);     
  37. wcex.style   = CS_HREDRAW | CS_VREDRAW;    
  38. wcex.lpfnWndProc    = (WNDPROC)WndProc;    
  39. wcex.cbClsExtra  = 0;    
  40. wcex.cbWndExtra  = 0;    
  41. wcex.hInstance   = hInstance;    
  42. wcex.hIcon   = LoadIcon(NULL,IDI_APPLICATION);    
  43. wcex.hCursor     = NULL;    
  44. wcex.hCursor     = LoadCursor(NULL, IDC_ARROW);    
  45. wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);    
  46. wcex.lpszMenuName   = NULL;    
  47. wcex.lpszClassName  = "canvas";    
  48. wcex.hIconSm     = NULL;    
  49. return RegisterClassEx(&wcex);    
  50. }    
  51. //****初始化函數*************************************    
  52. // 1.建立與窗口DC兼容的內存DC    
  53. // 2.從文件加載背景圖及透明的洋蔥頭   
  54. BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)    
  55. {    
  56. HWND hWnd;    
  57. HDC hdc;    
  58. hInst = hInstance;    
  59. hWnd = CreateWindow("canvas""淺墨的繪圖窗口" , WS_OVERLAPPEDWINDOW,    
  60. CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);    
  61. if (!hWnd)    
  62. {    
  63. return FALSE;    
  64. }    
  65. MoveWindow(hWnd,10,10,600,444,true);    
  66. ShowWindow(hWnd, nCmdShow);    
  67. UpdateWindow(hWnd);    
  68. hdc = GetDC(hWnd);                     //獲得窗口DC    
  69. mdc = CreateCompatibleDC(hdc);           //創建與窗口兼容的內存DC(mdc)    
  70. bg = (HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,600,444,LR_LOADFROMFILE);    
  71.    
  72. MyPaint(hdc);    
  73. ReleaseDC(hWnd,hdc);    
  74. return TRUE;    
  75. }    
  76. //****自定義繪圖函數*********************************    
  77. //透明貼圖    
  78. void MyPaint(HDC hdc)    
  79. {    
  80. SelectObject(mdc,bg);    
  81. BitBlt(hdc,0,0,600,450,mdc,0,0,SRCCOPY);    //先將背景圖貼到顯示窗口中    
  82. CImage image;      //定義一個CImage對象,用於透明貼圖  
  83. CString str;      //定義一個CString對象,用於存放文件名字符串  
  84. str="onion.png";              //將字符串賦值爲文件名  
  85. image.Load(str);                    //在image中載入圖像文件  
  86. image.Draw(hdc,120,180,85,113,0,0,85,113);    //調用Draw進行透明貼圖  
  87. //或者爲image.TransparentBlt(hdc, 120, 180, 85, 113,CLR_INVALID );//調用TransparentBlt進行透明貼圖  
  88. image.Destroy();  
  89. }    
  90. //****消息處理函數**********************************    
  91. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)    
  92. {    
  93. PAINTSTRUCT ps;    
  94. HDC hdc;    
  95. switch (message)    
  96. {    
  97. case WM_PAINT:   //窗口重繪消息    
  98. hdc = BeginPaint(hWnd, &ps);    
  99. MyPaint(hdc);    
  100. EndPaint(hWnd, &ps);    
  101. break;    
  102. case WM_DESTROY:     //窗口結束消息    
  103. DeleteDC(mdc);    
  104. DeleteObject(bg);    
  105. PostQuitMessage(0);    
  106. break;    
  107. default:     //其他消息    
  108. return DefWindowProc(hWnd, message, wParam, lParam);    
  109.    }    
  110.    return 0;    
  111. }    



最後得到的效果圖如下:



我們可以改變CImage::Draw函數的參數值,讓“洋蔥頭”出現在地圖不同的地方。





通過這個實例可以發現,用CImage類進行透明貼圖,實在是方便多了。




本節筆記到這裏就結束了,由於近期在做一個純flash的網站,更新速度和評論的回覆都不像往常那麼及時,希望大家能夠體諒。


本節筆記的源代碼請點擊這裏下載:   【Visual C++】Code_Note_14


感謝一直支持【Visual C++】遊戲開發筆記系列專欄的朋友們,也請大家繼續關注我的專欄,我一有時間就會把自己的學習心得,覺得比較好的知識點寫出來和大家一起分享。

精通遊戲開發的路還很長很長,非常希望能和大家一起交流,共同學習,共同進步。

大家看過後覺得值得一看的話,可以頂一下這篇文章,你們的支持是我繼續寫下去的動力~

如果文章中有什麼疏漏的地方,也請大家指正。也希望大家可以多留言來和我探討編程相關的問題。

最後,謝謝你們一直的支持~~~


——————————淺墨於2012年4月3日

The end.

原文地址:http://blog.csdn.net/zhmxy555/article/details/7422922

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章