Visual C++中DDB與DIB位圖編程全攻略
來源: 天極網(wǎng)
1. 基本概念
先來用通俗的語句講解位圖和調(diào)色板的概念。
我
們知道,自然界中的所有顏色都可以由紅、綠、藍(lán)(R,G,B)三基色組合而成。針對含有紅、綠、藍(lán)色成分的多少,可以對其分別分成0~255個等級,而
紅、綠、藍(lán)的不同組合共有256×256×256種,因此約能表示1600萬種顏色。對于人眼而言,這已經(jīng)是"真彩色"了。
對每個像素進(jìn)行了(R,G,B)量化的圖像就是位圖,其在計算機(jī)中對應(yīng)文件的擴(kuò)展名一般為.bmp。既然用R,G,B的量化值就可以直接記錄一張位圖的所有像素,那我們需要調(diào)色板干什么呢?
首先,我們可以計算完全利用(R,G,B)組合來存儲一個800×600的位圖所需要的空間為:
800×600×3 = 1440000(字節(jié))= 1.37M(字節(jié))
驚人的大!因此,調(diào)色板橫空出世了,它的功能在于緩解位圖文件存儲空間過大的問題。
假設(shè)一個位圖為16色,其像素總數(shù)為800×600。我們只需要用4個bit就可以存儲這個位圖的每個像素在16種顏色中所處的等級,然后調(diào)色板提供了這16種等級對應(yīng)的(R,G,B)值,這樣,存儲這個16色位圖只需要:
800×600×4/8 = 240000(字節(jié))= 0.22 M(字節(jié))
額外的存儲R,G,B表的開銷(即調(diào)色板Palette,也稱為顏色查找表LUT)僅僅為16×3=48字節(jié)。
存儲空間被大為減少!
常見的位圖有單色、16色、256色、16位及24位真彩色5種,對于前三者(即不大于256色)都可以調(diào)色板方式進(jìn)行存儲,而對16位及24位真彩色以調(diào)色板進(jìn)行存儲是不劃算的,它們直接按照R,G,B分量進(jìn)行存儲。
在此基礎(chǔ)上我們來分析DDB位圖(Device-dependent bitmap,與設(shè)備相關(guān)的位圖)與DIB位圖(Device-independent bitmap,與設(shè)備無關(guān)的位圖)的概念以及二者的區(qū)別。
DDB依賴于具體設(shè)備,它只能存在于內(nèi)存中(視頻內(nèi)存或系統(tǒng)內(nèi)存),其顏色模式必須與特定的輸出設(shè)備相一致,使用系統(tǒng)調(diào)色板。一般只能載入色彩較簡單的DDB位圖,對于顏色較豐富的位圖,需使用DIB才能長期保存。
DIB
不依賴于具體設(shè)備,可以用來永久性地保存圖象。DIB一般是以*.BMP文件的形式保存在磁盤中的,有時也會保存在*.DIB文件中。
DIB位圖的特點(diǎn)是將顏色信息儲存在位圖文件自身的顏色表中,應(yīng)用程序要根據(jù)此顏色表為DIB創(chuàng)建邏輯調(diào)色板。因此,在輸出一幅DIB位圖之前,程序應(yīng)該
將其邏輯調(diào)色板選入到相關(guān)的設(shè)備上下文并實(shí)現(xiàn)到系統(tǒng)調(diào)色板中。
2. 例程簡述
本文后續(xù)的講解都基于這樣的一個例子工程,它是一個基于對話框的MFC應(yīng)用程序,包括2個父菜單:
(1) DDB位圖
DDB位圖父菜單又包括兩個子菜單:
a. ID:IDM_LOADDDBPIC caption:加載
單擊事件:加載資源中的DDB位圖并顯示之
b. ID:IDM_MARK_DDBPIC caption:標(biāo)記
單擊事件:在DIB位圖中透明地添加天極網(wǎng)logo
(2) DIB位圖
DIB位圖父菜單又包括兩個子菜單:
a. ID:IDM_OPENDIBPIC caption:打開
單擊事件:彈出文件對話框,打開.bmp位圖文件,并顯示
b. ID:IDM_MARK_DIBPIC caption:標(biāo)記
單擊事件:在DIB位圖中透明地添加天極網(wǎng)logo
工程中還包含下列位圖資源:
(1)IDB_LOADED_BITMAP:要加載的位圖資源
(2)IDB_YESKY_BITMAP:天極網(wǎng)logo
后續(xù)篇章將集中在對4個子菜單單擊事件消息處理函數(shù)的講解,下面的代碼是整個對話框類CBitMapExampleDlg的消息映射:
BEGIN_MESSAGE_MAP(CBitMapExampleDlg, CDialog)
//{{AFX_MSG_MAP(CBitMapExampleDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_COMMAND(IDM_LOADDDBPIC, OnLoadddbpic)
ON_COMMAND(IDM_MARK_DDBPIC, OnMarkDdbpic)
ON_COMMAND(IDM_OPENDIBPIC, OnOpendibpic)
ON_COMMAND(IDM_MARK_DIBPIC,OnMarkDibpic) //}}AFX_MSG_MAP
END_MESSAGE_MAP()
?
3. DDB位圖編程
先看DDB加載按鈕的單擊事件代碼:
void CBitMapExampleDlg::OnLoadddbpic()
{
1: CBitmap bmpDraw;
2: bmpDraw.LoadBitmap( IDB_LOADED_BITMAP );//裝入要加載的DDB位圖
3: BITMAP bmpInfo;
4: bmpDraw.GetBitmap( &bmpInfo ); //獲取要加載DDB位圖的尺寸
5: CDC memDC;//定義一個兼容DC
6: CClientDC dc( this );
7: memDC.CreateCompatibleDC( &dc );//創(chuàng)建兼容DC
8: CBitmap* pbmpOld = memDC.SelectObject( &bmpDraw );//保存原有DDB,并選入新DDB入DC
9: dc.BitBlt( 0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, &memDC, 0, 0, SRCCOPY );
10: memDC.SelectObject( pbmpOld );//選入原DDB
}
上述代碼將產(chǎn)生如圖1所示的效果,位圖被安置在對話框(0,0)坐標(biāo)開始的位置上。
圖1 加載DDB位圖資源
?
我們來逐行解析上述代碼是怎樣產(chǎn)生圖1的效果的。
第1、2行定義了一個CBitmap對象,并調(diào)用其成員函數(shù)
LoadBitmap加載工程中的位圖資源IDB_LOADED_BITMAP。第3、4行定義了BITMAP結(jié)構(gòu)體的實(shí)例并調(diào)用CBitmap的成員函
數(shù)GetBitmap獲得位圖信息,BITMAP結(jié)構(gòu)體定義在頭文件中,其形式為:
/* Bitmap Header Definition */
typedef struct tagBITMAP
{
LONG bmType; //必需為0
LONG bmWidth; //位圖的寬度(以像素為單位)
LONG bmHeight; //位圖的高度(以像素為單位)
LONG bmWidthBytes; //每一掃描行所需的字節(jié)數(shù),應(yīng)是偶數(shù)
WORD bmPlanes; //色平面數(shù)
WORD bmBitsPixel; //色平面的顏色位數(shù)
LPVOID bmBits; //指向存儲像素陣列的數(shù)組
} BITMAP, *PBITMAP, NEAR *NPBITMAP, FAR *LPBITMAP;
第5~8行的作用是:構(gòu)建一個CDC對象,調(diào)用CDC::CreateCompatibleDC創(chuàng)建一個兼容的內(nèi)存設(shè)備上下文,接著調(diào)用CDC::SelectObject將DDB選入內(nèi)存設(shè)備上下文中。
第9行調(diào)用函數(shù)CDC::BitBlt繪制位圖,CDC::BitBlt的原型為:
CDC::BitBlt(int x, int y, int nWidth, int nHeight, CDC *pSrcDC, int xSrc, int ySrc, DWORD dwRop)
CDC::BitBlt執(zhí)行的操作為將源DC中位圖復(fù)制到目的DC中。其中前四個參數(shù)為目的區(qū)域的坐標(biāo)(x,y)及長度和寬度(Width, nHeight),第五個參數(shù)是源DC指針,接下來的參數(shù)是源DC中的起始坐標(biāo),最后一個參數(shù)為光柵操作的類型。
第10行調(diào)用CDC::SelectObject把原來的DDB選入到內(nèi)存設(shè)備上下文中并使新DDB脫離出來。
與CDC::BitBlt對應(yīng)的還有另一個函數(shù)CDC::StretchBlt,它具有縮放功能,其原型為:
BOOL CDC::StretchBlt(int x, int y, int nWidth, int nHeight, CDC *pSrcDC, int
xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop);
該函數(shù)把位圖從源矩形拷貝到目的矩形中,如果源和目的矩形尺寸不同,那么將縮放位圖的功能以適應(yīng)目的矩形的大小。函數(shù)的大部分參數(shù)與BitBlt的相同,但多了兩個參數(shù)nSrcWidth和nSrcHeight用來指定源矩形的寬和高。
如果我們將函數(shù)CBitMapExampleDlg::OnLoadddbpic() 中的第9行改為:
CRect clientRect;
GetClientRect(&clientRect); //獲得對話框窗口的大小
dc.StretchBlt(0, 0, clientRect.right, clientRect.bottom, &memDC, 0, 0,
bmpInfo.bmWidth, bmpInfo.bmHeight, SRCCOPY);
則單擊加載按鈕后的對話框如圖2所示,位圖被拉伸至整個對話框的范圍。
圖2 拉伸位圖
CDC::BitBlt和dc.StretchBlt函數(shù)中的dwRop參數(shù)較為有用,它定義光柵操作的類型。請看"DDB位圖"父菜單下"標(biāo)記"子菜單單擊事件的消息處理函數(shù)代碼:
void CBitMapExampleDlg::OnMarkDdbpic()
{
CBitmap bmpDraw;
bmpDraw.LoadBitmap(IDB_YESKY_BITMAP); //裝入天極網(wǎng)logo DDB位圖資源
BITMAP bmpInfo;
bmpDraw.GetBitmap(&bmpInfo); //獲取天極網(wǎng)logo位圖的尺寸
CDC memDC; //定義一個兼容DC
CClientDC dc(this);
memDC.CreateCompatibleDC(&dc); //創(chuàng)建DC
CBitmap *pbmpOld = memDC.SelectObject(&bmpDraw);
//保存原有DDB,并選入天極網(wǎng)logo位圖入DC
dc.BitBlt(0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, &memDC, 0, 0, SRCAND);
memDC.SelectObject(pbmpOld); //選入原DDB
}
單擊該按鈕后,將產(chǎn)生如圖3的效果,天極網(wǎng)的logo被透明地添加到了位圖中!
圖3 在DDB位圖中加入天極網(wǎng)logo
能產(chǎn)生這個效果的原因在于我們在代碼行:
dc.BitBlt ( 0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, &memDC, 0, 0, SRCAND );
中使用了參數(shù)SRCAND(不同于先前代碼中SRCCOPY,它僅僅意味著復(fù)制源位圖到目的位圖),它的含義為源和目的間進(jìn)行AND操
作。我們不知道天極網(wǎng)的編輯同志是怎么為文章中的圖片加logo的,有可能他們就使用了具有自動AND功能的圖像加logo批處理軟件。的確,我們可以利
用例程中的原理寫一個批處理軟件,一次對一堆圖片自動添加logo。
參數(shù)dwRop除了可以為SRCAND和SRCCOPY外,還可以有如下取值:
BLACKNESS:輸出區(qū)域?yàn)楹谏?br />
DSTINVERT:反轉(zhuǎn)目的位圖
MERGECOPY:用與操作把圖案(Pattern)與源位圖融合起來
MERGEPAINT:用或操作把反轉(zhuǎn)的源位圖與目的位圖融合起來
NOTSRCCOPY:把源位圖反轉(zhuǎn)然后拷貝到目的地
NOTSRCERASE:用或操作融合源和目的位圖,然后再反轉(zhuǎn)
PATCOPY:把圖案拷貝到目的位圖中
PATINVERT:用異或操作把圖案與目的位圖相融合
PATPAINT:用或操作融合圖案和反轉(zhuǎn)的源位圖,然后用或操作把結(jié)果與目的位圖融合
SRCERASE:先反轉(zhuǎn)目的位圖,再用與操作將其與源位圖融合
SRCINVERT:用異或操作融合源位圖和目的位圖
SRCPAINT:用或操作融合源位圖和目的位圖
WHITENESS:輸出區(qū)域?yàn)榘咨?br />
合理利用這些取值將幫助我們制作出特定要求的圖像處理軟件。
從上述實(shí)例我們可以看出,在VC中使用CBitmap類,必須將位圖放入工程的資源中,并使用類 CBitmap的成員函數(shù)LoadBitmap加載之,再通過CDC類的成員函數(shù)BitBlt進(jìn)行DC拷貝等操作達(dá)到顯示的目的。CBitmap有顯示的不足:
(1) 位圖需要放入工程資源中,這將導(dǎo)致工程的可執(zhí)行文件變大;
(2) 因?yàn)槲粓D需放入工程資源中,而資源中不能無窮無盡地包含位圖,應(yīng)用程序無法自適應(yīng)地選取其它位圖,能使用的位圖十分有限的;
(3) 類CBitmap只是DDB位圖操作API的封裝,不能獨(dú)立于平臺。
DIB位圖則可以解決上述問題,其特點(diǎn)是以.BMP位圖文件格式存儲獨(dú)立于平臺的圖像數(shù)據(jù),下面我們來詳細(xì)分析。
4. DIB位圖編程
4.1位圖文件格式
先來分析DIB位圖文件的格式。位圖文件分為四部分:
(1)位圖文件頭BITMAPFILEHEADER
位圖文件頭BITMAPFILEHEADER是一個結(jié)構(gòu)體,長度為14字節(jié),定義為:
typedef struct tagBITMAPFILEHEADER
{
WORD bfType; //文件類型,必須是0x424D,即字符串"BM"
DWORD bfSize; //文件大小,包括BITMAPFILEHEADER的14個字節(jié)
WORD bfReserved1; //保留字
WORD bfReserved2; //保留字
DWORD bfOffBits; //從文件頭到實(shí)際的位圖數(shù)據(jù)的偏移字節(jié)數(shù)
} BITMAPFILEHEADER;
(2)位圖信息頭BITMAPINFOHEADER
位圖信息頭BITMAPINFOHEADER也是一個結(jié)構(gòu)體,長度為40字節(jié),定義為:
typedef struct tagBITMAPINFOHEADER
{
DWORD biSize; //本結(jié)構(gòu)的長度,為40
LONG biWidth; //圖象的寬度,單位是象素
LONG biHeight; //圖象的高度,單位是象素
WORD biPlanes; //必須是1
WORD biBitCount;
//表示顏色時要用到的位數(shù),1(單色), 4(16色), 8(256色), 24(真彩色)
DWORD biCompression;
//指定位圖是否壓縮,有效的值為BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS等,BI_RGB表示不壓縮
DWORD biSizeImage;
//實(shí)際的位圖數(shù)據(jù)占用的字節(jié)數(shù),即 biSizeImage=biWidth’ × biHeight,biWidth’是biWidth 按照4的整倍數(shù)調(diào)整后的結(jié)果
LONG biXPelsPerMeter; //目標(biāo)設(shè)備的水平分辨率,單位是每米的象素個數(shù)
LONG biYPelsPerMeter; //目標(biāo)設(shè)備的垂直分辨率,單位是每米的象素個數(shù)
DWORD biClrUsed; //位圖實(shí)際用到的顏色數(shù),0表示顏色數(shù)為2biBitCount
DWORD biClrImportant; //位圖中重要的顏色數(shù),0表示所有顏色都重要
} BITMAPINFOHEADER;
(3)調(diào)色板Palette
調(diào)
色板Palette針對的是需要調(diào)色板的位圖,即單色、16色和256色位圖。對于不以調(diào)色板方式存儲的位圖,則無此項(xiàng)信息。調(diào)色板是一個數(shù)組,共有
biClrUsed個元素(如果該值為0,則有2biBitCount個元素)。數(shù)組中每個元素是一個RGBQUAD結(jié)構(gòu)體,長度為4個字節(jié),定義為:
typedef struct tagRGBQUAD
{
BYTE rgbBlue; //藍(lán)色分量
BYTE rgbGreen; //綠色分量
BYTE rgbRed; //紅色分量
BYTE rgbReserved; //保留值
} RGBQUAD;
(4)實(shí)際的位圖數(shù)據(jù)ImageDate
對于用到調(diào)色板的位圖,實(shí)際的圖象數(shù)據(jù)ImageDate為該象素的顏色在調(diào)色板中的索引值;對于真彩色圖,圖象數(shù)據(jù)則為實(shí)際的R、G、B值:
a.單色位圖:用1bit就可以表示象素的顏色索引值;
b.16色位圖:用4bit可以表示象素的顏色索引值;
c. 256色位圖:1個字節(jié)表示1個象素的顏色索引值;
d.真彩色:3個字節(jié)表示1個象素的顏色R,G,B值。
此外,位圖數(shù)據(jù)每一行的字節(jié)數(shù)必須為4的整倍數(shù),如果不是,則需要補(bǔ)齊。奇怪的是,位圖文件中的數(shù)據(jù)是從下到上(而不是從上到下)、從左到右方式存儲的。
4.2位圖的顯示
Visual C++ MFC中沒有提供一個專門的類來處理DIB位圖,因此,為了方便地使用位圖文件,我們有必要派生一個CDib類。類的源代碼如下:
(1) CDib類的聲明
// DIB.h:類CDib聲明頭文件
#ifndef __DIB_H__
#define __DIB_H__
#include
class CDib
{
public:
CDib();
~CDib();
BOOL Load( const char * );
BOOL Save( const char * );
BOOL Draw( CDC *, int nX = 0, int nY = 0, int nWidth = -1, int nHeight = -1, int mode = SRCCOPY);
BOOL SetPalette( CDC * );
private:
CPalette m_Palette;
unsigned char *m_pDib, *m_pDibBits;
DWORD m_dwDibSize;
BITMAPINFOHEADER *m_pBIH;
RGBQUAD *m_pPalette;
int m_nPaletteEntries;
};
#endif
(2) CDib類的實(shí)現(xiàn)
// DIB.cpp:類CDib實(shí)現(xiàn)文件
#include "stdafx.h"
#include "DIB.h"
CDib::CDib()
{
m_pDib = NULL;
}
CDib::~CDib()
{
// 如果位圖已經(jīng)被加載,釋放內(nèi)存
if (m_pDib != NULL)
delete []m_pDib;
}
下面這個函數(shù)非常重要,其功能為加載位圖,類似于CBitmap類的LoadBitmap函數(shù):
BOOL CDib::Load(const char *pszFilename)
{
CFile cf;
// 打開位圖文件
if (!cf.Open(pszFilename, CFile::modeRead))
return (FALSE);
// 獲得位圖文件大小,并減去BITMAPFILEHEADER的長度
DWORD dwDibSize;
dwDibSize = cf.GetLength() - sizeof(BITMAPFILEHEADER);
// 為DIB位圖分配內(nèi)存
unsigned char *pDib;
pDib = new unsigned char[dwDibSize];
if (pDib == NULL)
return (FALSE);
BITMAPFILEHEADER BFH;
// 讀取位圖文件數(shù)據(jù)
try
{
// 文件格式是否正確有效
if ( cf.Read(&BFH, sizeof(BITMAPFILEHEADER)) != sizeof(BITMAPFILEHEADER) ||
BFH.bfType != ’MB’ || cf.Read(pDib, dwDibSize) != dwDibSize)
{
delete []pDib;
return (FALSE);
}
}
catch (CFileException *e)
{
e->Delete();
delete []pDib;
return (FALSE);
}
// delete先前加載的位圖
if (m_pDib != NULL)
delete m_pDib;
// 將臨時Dib數(shù)據(jù)指針和Dib大小變量賦給類成員變量
m_pDib = pDib;
m_dwDibSize = dwDibSize;
// 為相應(yīng)類成員變量賦BITMAPINFOHEADER和調(diào)色板指針
m_pBIH = (BITMAPINFOHEADER*)m_pDib;
m_pPalette = (RGBQUAD*) &m_pDib[sizeof(BITMAPINFOHEADER)];
// 計算調(diào)色板中實(shí)際顏色數(shù)量
m_nPaletteEntries = 1 << m_pBIH->biBitCount;
if (m_pBIH->biBitCount >8)
m_nPaletteEntries = 0;
else if (m_pBIH->biClrUsed != 0)
m_nPaletteEntries = m_pBIH->biClrUsed;
// 為相應(yīng)類成員變量賦image data指針
m_pDibBits = &m_pDib[sizeof(BITMAPINFOHEADER) + m_nPaletteEntries * sizeof (RGBQUAD)];
// delete先前的調(diào)色板
if (m_Palette.GetSafeHandle() != NULL)
m_Palette.DeleteObject();
// 如果位圖中存在調(diào)色板,創(chuàng)建LOGPALETTE 及CPalette
if (m_nPaletteEntries != 0)
{
LOGPALETTE *pLogPal = (LOGPALETTE*)new char[sizeof(LOGPALETTE) + m_nPaletteEntries *sizeof(PALETTEENTRY)];
if (pLogPal != NULL)
{
pLogPal->palVersion = 0x300;
pLogPal->palNumEntries = m_nPaletteEntries;
for (int i = 0; i < m_nPaletteEntries; i++)
{
pLogPal->palPalEntry[i].peRed = m_pPalette[i].rgbRed;
pLogPal->palPalEntry[i].peGreen = m_pPalette[i].rgbGreen;
pLogPal->palPalEntry[i].peBlue = m_pPalette[i].rgbBlue;
}
//創(chuàng)建CPalette并釋放LOGPALETTE的內(nèi)存
m_Palette.CreatePalette(pLogPal);
delete []pLogPal;
}
}
return (TRUE);
}
//函數(shù)功能:保存位圖入BMP文件
BOOL CDib::Save(const char *pszFilename)
{
if (m_pDib == NULL)
return (FALSE);
CFile cf;
if (!cf.Open(pszFilename, CFile::modeCreate | CFile::modeWrite))
return (FALSE);
try
{
BITMAPFILEHEADER BFH;
memset(&BFH, 0, sizeof(BITMAPFILEHEADER));
BFH.bfType = ’MB’;
BFH.bfSize = sizeof(BITMAPFILEHEADER) + m_dwDibSize;
BFH.bfOffBits = sizeof(BITMAPFILEHEADER) +
sizeof(BITMAPINFOHEADER) + m_nPaletteEntries *sizeof(RGBQUAD);
cf.Write(&BFH, sizeof(BITMAPFILEHEADER));
cf.Write(m_pDib, m_dwDibSize);
}
catch (CFileException *e)
{
e->Delete();
return (FALSE);
}
return (TRUE);
}
下面這個函數(shù)也非常重要,其功能為在pDC指向的CDC中繪制位圖,起點(diǎn)坐標(biāo)為(nX,nY),繪制寬度和高度為nWidth、nHeight,最后一個參數(shù)是光柵模式:
BOOL CDib::Draw(CDC *pDC, int nX, int nY, int nWidth, int nHeight, int mode)
{
if (m_pDib == NULL)
return (FALSE);
// 獲取位圖寬度和高度賦值
if (nWidth == - 1)
nWidth = m_pBIH->biWidth;
if (nHeight == - 1)
nHeight = m_pBIH->biHeight;
// 繪制位圖
StretchDIBits(pDC->m_hDC,
nX, nY, nWidth, nHeight, 0, 0, m_pBIH->biWidth, m_pBIH->biHeight,
m_pDibBits, (BITMAPINFO*)m_pBIH, BI_RGB, mode);
return (TRUE);
}
//函數(shù)功能:設(shè)置調(diào)色板
BOOL CDib::SetPalette(CDC *pDC)
{
if (m_pDib == NULL)
return (FALSE);
// 檢查當(dāng)前是否有一個調(diào)色板句柄,對于大于256色的位圖,為NULL
if (m_Palette.GetSafeHandle() == NULL)
return (TRUE);
// 選擇調(diào)色板,接著實(shí)施之,最后恢復(fù)老的調(diào)色板
CPalette *pOldPalette;
pOldPalette = pDC->SelectPalette(&m_Palette, FALSE);
pDC->RealizePalette();
pDC->SelectPalette(pOldPalette, FALSE);
return (TRUE);
}
從整個CDib類的代碼中我們可以看出,DIB位圖的顯示需遵循如下步驟:
(1)讀取位圖,本類中使用pDib = new unsigned char[dwDibSize]為位圖中的信息分配內(nèi)存,另一種方法是調(diào)用API函數(shù)CreateDIBSection,譬如:
m_hBitmap = ::CreateDIBSection(pDC->GetSafeHdc(),
(LPBITMAPINFO) m_lpBMPHdr, DIB_RGB_COLORS,
(LPVOID*) &m_lpDIBits, NULL, 0);
m_hBitmap定義為:
HBITMAP m_hBitmap;
(2)根據(jù)讀取的位圖信息,計算出調(diào)色板大小,然后創(chuàng)建調(diào)色板;
(3)調(diào)用CDib::SetPalette( CDC *pDC )設(shè)置調(diào)色板,需要用到CDC::SelectPalette及CDC::RealizePalette兩個函數(shù);
(4)
調(diào)用CDib::Draw(CDC *pDC, int nX, int nY, int nWidth, int nHeight, int
mode)函數(shù)繪制位圖。在此函數(shù)中,真正發(fā)揮顯示位圖作用的是對StretchDIBits
API函數(shù)的調(diào)用。StretchDIBits函數(shù)具有縮放功能,其最后一個參數(shù)也是光柵操作的模式。
下面給出DIB位圖的打開及顯示并在其中加入天極網(wǎng)logo的函數(shù)源代碼。"DIB位圖"父菜單下"打開"子菜單的單擊事件消息處理函數(shù)為(其功能為打開位圖并顯示之):
void CBitMapExampleDlg::OnOpendibpic()
{
// 彈出文件對話框,讓用戶選擇位圖文件
CFileDialog fileDialog(TRUE, "*.BMP", NULL, NULL,"位圖文件(*.BMP)|*.bmp;*.BMP|");
if (IDOK == fileDialog.DoModal())
{
// 加載位圖并顯示之
CDib dib;
if (dib.Load(fileDialog.GetPathName()))
{
CClientDC dc(this);
dib.SetPalette(&dc);
dib.Draw(&dc);
}
}
}
"DIB位圖"父菜單下"標(biāo)記"子菜單的單擊事件消息處理函數(shù)為(其功能為給位圖加上天極網(wǎng)logo):
void CBitMapExampleDlg::OnMarkDibpic()
{
// 彈出文件對話框,讓用戶選擇標(biāo)記logo
CFileDialog fileDialog(TRUE, "*.BMP", NULL, NULL, "標(biāo)記位圖文件(*.BMP)|*.bmp;*.BMP|");
if (IDOK == fileDialog.DoModal())
{
// 加載標(biāo)記logo位圖并與目標(biāo)位圖相與
CDib dib;
if (dib.Load(fileDialog.GetPathName()))
{
CClientDC dc(this);
dib.SetPalette(&dc);
dib.Draw(&dc, 0, 0, - 1, - 1, SRCAND);
}
}
}
圖4顯示了DIB位圖加載天極網(wǎng)logo后的效果,要好于圖3中加天極網(wǎng)logo后的DDB位圖。圖4顯示的是真彩色位圖相互與的結(jié)果,而圖3中的圖像顏色被減少了。
圖4 在DIB位圖中加入天極網(wǎng)logo
5. 結(jié)束語
本文介紹了位圖及調(diào)色板的概念,并講解了DDB位圖與DIB位圖的區(qū)別。在此基礎(chǔ)
上,本文以實(shí)例講解了DDB位圖和DIB位圖的操作方式。DDB位圖的處理相對比較簡單,對于DIB位圖,我們需要定義一個MFC所沒有的新類CDib,
它屏蔽位圖信息的讀取及調(diào)色板創(chuàng)建的技術(shù)細(xì)節(jié),應(yīng)用程序可以方便地使用之。
本文中的所有程序在Visual C++6.0及Windows XP平臺上調(diào)試通過。