|
Posted on 2012-04-15 16:37 zljpp 閱讀(191) 評論(0) 編輯 收藏
我們知道,Visual C++中的CBitmap類的功能簡直太弱小了,這曾經讓Visual C++在圖像處理方面的功能比較尷尬。之前筆記里面,我們采用的CBitmap配合GDI進行透明圖像的處理有些晦澀繁瑣,而且受到圖像素材的限制,可以說是有些落后,不是太實用。 為了解決這個問題,這節筆記我們將系統的學習MFC和ATL中新增一個圖像處理的類,它就是華麗而強大的CImage類。 由于本節筆記是對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格式的圖片。 具體代碼如下:
#include "atlimage.h"
CImage image; //定義一個CBitmap類
image.Load(“filename”); //filename為要加載的文件地址
HBITMAP hBitmap=image.Detach(); //返回被分離的圖片的句柄
CBitmap bmp; // 定義一個bitmap
bmp.Attach(hBitmap); //進行句柄的附加 CImage類對于DIB(device-independent bitmap)設備無關位圖文件和非DIB都可以處理。我們可以通過Create函數或者CImage::Load來處理DIB部分,用Attach函數來將非DIB部分附加到一個CImage對象上。 對于以下函數,只支持DIB部分的位圖文件,他們是: GetBitsGetColorTable,GetMaxColorTableEntries,GetPitch,GetPixelAddress,IsIndexed,SetColorTable。
我們可以通過CImage類中的IsDIBSection()函數來幫助我們判斷一個位圖文件是否為DIB部分,其定義如下: 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類用于貼圖的一般的使用方法
使用方法不唯一,最常用的方法如下,該方法大致分為三部分: <2> 定義一個CImage類對象,然后調用CImage::Load方法裝載一個外部圖像文件。Load方法有如下兩種重載: HRESULT Load(
LPCTSTR pszFileName //包含加載文件名的字符串指針
) throw( );
HRESULT Load(
IStream* pStream //指向包含加載文件名的流的指針
) throw(); <3> 調用CImage::Draw方法繪制圖像。 CImage::Draw 將一個位圖文件從源設備描述表復制到當前設備描述表 該函數有如下六種重載:
BOOL Draw(
HDC hDestDC, //目標設備環境DC的句柄
int xDest, //目的矩形的左上角X坐標(邏輯單位)
int yDest, //目的矩形的左上角Y坐標(邏輯單位)
int nDestWidth, //目標矩形的寬度(就是設定貼過去的圖片的寬度)
int nDestHeight, //目標矩形的高度(就是設定鐵鍋的圖片的高度)
int xSrc, //源矩形的左上角X坐標
int ySrc, //源矩形的左上角Y坐標
int nSrcWidth, //源矩形的寬度
int nSrcHeight //源矩形的高度
) const throw( );
BOOL Draw(
HDC hDestDC, //目標環境DC的句柄
const RECT& rectDest, //一個RECT結構的引用,用來確定目標圖像。
const RECT& rectSrc //一個RECT結構體的引用,用來確定源圖像
) const throw( );
BOOL Draw(
HDC hDestDC, //目標環境DC的句柄
int xDest, //目標矩形的左上角X坐標
int yDest //目標矩形的左上角Y坐標
) const throw( ); //
BOOL Draw(
HDC hDestDC, //目標環境DC的句柄
const POINT& pointDest //一個POINT結構體,用來確定目的矩形的左上角坐標
) const throw( );
BOOL Draw(
HDC hDestDC, //目標環境DC的句柄
int xDest, //目標矩形的左上角X坐標
int yDest, //目標矩形的左上角Y坐標
int nDestWidth, //目標矩形的寬度
int nDestHeight //目標矩形的寬度
) const throw( );
BOOL Draw(
HDC hDestDC, //目標環境DC的句柄
const RECT& rectDest //一個RECT結構的引用,用來確定目標圖像。
) const throw( ); 在上面的Draw函數的各種重載中,對于沒有指定源矩形的版本,則整個源圖像就是默認的源矩形。對于沒有指定目的矩形尺寸的,則源圖片的尺寸就是默認的目的矩形尺寸。
需要注意的是,Draw方法綜合了StretchBlt、TransparentBlt和AlphaBlend函數的功能。。通常情況下,Draw()函數作用和StretchBlt()函數一致。但是當我們的圖像中存在透明的顏色和alpha通道的時候,Draw()函數作用和TransparentBlt()或者AlphaBlend()函數一致。所以,在一般情況下,我們都盡量調用Draw方法來繪制圖像。
我將CImage類的所有類函數按功能分為了四大類,可以更方面的了解各函數的定位和作用,也方便大家查閱:
Attach 附加一個HBITMAP到CImage對象,位圖類型DIB與否都可以
Create 創建一個DIB部分的位圖,并將其附加到之前創建的CImage對象
CreateEX 創建一個DIB部分的位圖(擁有額外的參數),并將其附加到之前 創建的CImage對象
Destroy 從CImage類上分離該位圖并進行刪除
ReleaseGDIPlus 釋放GDI+使用的源
GetExporterFilterString 返回系統支持的輸入文件格式類型及其描述
GetImporterFilterString 返回系統支持的輸出文件格式類型及其描述
LoadFromResource 從指定的源處加載一個圖像資源
IsIndexed 判斷一個位圖顏色映射到了一個索引調色盤
GetColorTable 返回顏色表中RGB值的范圍條目
GetExporterFilterString 返回系統支持的輸入文件格式類型及其描述
GetImporterFilterString 返回系統支持的輸出文件格式類型及其描述
GetMaxColorTableEntries 返回顏色表條目中的最大值
GetPitch 返回當前圖片的間距(單位為字節),用來決定像素格式的
GetTransparentColor 返回顏色表中透明色的位置
GetWidth 返回當前圖片的寬度(單位為像素)
AlphaBlend 顯示一個半透明或者透明像素的位圖
BitBlt 從源設備描述表復制一個位圖文件到當前設備描述表
Draw 從源矩形復制一個位圖到目的矩形,該函數伸縮或者拉伸位圖來適應目標矩 形的尺寸,如果有必要,會處理Alpha值和透明顏色。
MaskBlt 用指定的掩碼和光柵操作來結合顏色數據和目的位圖
PlgBlt 執行一個從源設備描述表的矩形到目標設備描述表的平行 四邊形的塊狀位圖轉換
SetColorTabel 在DIB的顏色表中設定一系列條目的RGB顏色的值
SetPixelIndexed 設置在指定坐標處的像素(使用索調色板的索引值)。
SetPixelRGB 設置在指定坐標處的像素(使用RGB值)
SetTransparentColor 設置將被視為透明色的顏色的索引值(只能選取調色板中的 一種顏色)
StretchBlt 從源矩形復制一個位圖到目的矩形,如果有必要,該函數會 伸縮或者拉伸位圖來適應目標矩形的尺寸,
TransparentBlt 從源設備描述表中復制一個帶有透明色的位圖到當前設備 描述表
介紹了這么多了,下面我們依然用一個實例來鞏固本節筆記的知識。 我們知道,CImage支持透明PNG的貼圖,下面我們就運用透明PNG的貼圖,來代替之前的掩碼操作貼圖。 準備兩張素材圖,一張背景圖,一張需要進行透明操作的人物圖。 這次的選材就很廣了,沒有之前透明操作需要自己一定的美工功底或者美工童鞋支持的諸多限制了。 人物圖 onion.bmp 130x130(呵呵,可愛的洋蔥頭~~) <第二步> 將人物圖 onion.bmp用photoshop等圖像處理軟件進行摳圖操作,除去紅黃相間的背景圖,并將背景圖用透明圖層代替,再將圖片大小調節成85x113,用png格式輸出,效果如下: 處理好的人物圖 onion.png 85x113 
<第三步> 將bg.bmp以及onion.png放到工程目錄下,并在源文件寫入代碼并運行。
該代碼和筆記六中代碼的思路基本相同,只不過,將筆記六中使用掩碼操作進行透明化處理的方式換成了png透明貼圖的方式,更加的直觀和易懂易用。
詳細注釋的源代碼如下
#include "stdafx.h"
#include "atlimage.h"
//全局變量聲明
HINSTANCE hInst;
HBITMAP bg; //聲明一個位圖對象,用于存儲背景圖
HDC mdc; //聲明一個內存DC"mdc",用來暫存位圖
//全局函數聲明
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void MyPaint(HDC hdc);
////****Winmain函數,程序入口點函數**************************************
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
MyRegisterClass(hInstance);
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
//消息循環
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
//****設計一個窗口類,類似填空題,使用窗口結構體*************************
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(NULL,IDI_APPLICATION);
wcex.hCursor = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "canvas";
wcex.hIconSm = NULL;
return RegisterClassEx(&wcex);
}
//****初始化函數*************************************
// 1.建立與窗口DC兼容的內存DC
// 2.從文件加載背景圖及透明的洋蔥頭
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
HDC hdc;
hInst = hInstance;
hWnd = CreateWindow("canvas", "淺墨的繪圖窗口" , WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
MoveWindow(hWnd,10,10,600,444,true);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
hdc = GetDC(hWnd); //獲得窗口DC
mdc = CreateCompatibleDC(hdc); //創建與窗口兼容的內存DC(mdc)
bg = (HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,600,444,LR_LOADFROMFILE);
MyPaint(hdc);
ReleaseDC(hWnd,hdc);
return TRUE;
}
//****自定義繪圖函數*********************************
//透明貼圖
void MyPaint(HDC hdc)
{
SelectObject(mdc,bg);
BitBlt(hdc,0,0,600,450,mdc,0,0,SRCCOPY); //先將背景圖貼到顯示窗口中
CImage image; //定義一個CImage對象,用于透明貼圖
CString str; //定義一個CString對象,用于存放文件名字符串
str="onion.png"; //將字符串賦值為文件名
image.Load(str); //在image中載入圖像文件
image.Draw(hdc,120,180,85,113,0,0,85,113); //調用Draw進行透明貼圖
//或者為image.TransparentBlt(hdc, 120, 180, 85, 113,CLR_INVALID );//調用TransparentBlt進行透明貼圖
image.Destroy();
}
//****消息處理函數**********************************
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_PAINT: //窗口重繪消息
hdc = BeginPaint(hWnd, &ps);
MyPaint(hdc);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY: //窗口結束消息
DeleteDC(mdc);
DeleteObject(bg);
PostQuitMessage(0);
break;
default: //其他消息
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
} 最后得到的效果圖如下: 
我們可以改變CImage::Draw函數的參數值,讓“洋蔥頭”出現在地圖不同的地方。 通過這個實例可以發現,用CImage類進行透明貼圖,實在是方便多了。
本節筆記到這里就結束了,由于近期在做一個純flash的網站,更新速度和評論的回復都不像往常那么及時,希望大家能夠體諒。
感謝一直支持【Visual C++】游戲開發筆記系列專欄的朋友們,也請大家繼續關注我的專欄,我一有時間就會把自己的學習心得,覺得比較好的知識點寫出來和大家一起分享。 精通游戲開發的路還很長很長,非常希望能和大家一起交流,共同學習,共同進步。 大家看過后覺得值得一看的話,可以頂一下這篇文章,你們的支持是我繼續寫下去的動力~ 如果文章中有什么疏漏的地方,也請大家指正。也希望大家可以多留言來和我探討編程相關的問題。
作者:zhmxy555 發表于2012-4-3 4:15:13 原文鏈接
|