1.位圖和調(diào)色板的概念
如今Windows(3.x以及95,NT)系列已經(jīng)成為決大多數(shù)用戶使用的操作系統(tǒng)。它比DOS成功的一個(gè)重要因素是它可視化的漂亮界面,例如你可以在桌面上鋪上你喜歡的墻紙。那么Windows是如何顯示圖象的呢?這就要談到位圖(Bitmap)。
我們知道,普通的顯示器屏幕是由許許多多的點(diǎn)構(gòu)成的,我們稱(chēng)之為象素。顯示時(shí)采用掃描的方法:電子槍每次從左到右掃描一行,為每個(gè)象素著色,然后從上到下這樣掃描若干行,就掃過(guò)了一屏。為了防止閃爍,每秒要重復(fù)上述過(guò)程幾十次。例如我們常說(shuō)的屏幕分辨率為640*480,刷新頻率為70Hz,意思是說(shuō)每行要掃描640個(gè)象素,一共有480行,每秒重復(fù)掃描屏幕70次。我們稱(chēng)這種顯示器為位映象設(shè)備。所謂位映象,就是指一個(gè)二維的象素矩陣,而位圖就是采用位映象方法顯示和存儲(chǔ)的圖象。舉個(gè)例子,下圖1是一幅普通的黑白位圖,圖2是被放大后的圖,圖中每個(gè)方格代表了一個(gè)象素,我們可以看到:整個(gè)骷髏就是由這樣一些黑點(diǎn)和白點(diǎn)組成的。

圖1.骷髏(左) 圖2.放大后的骷髏位圖(右)
那么,彩色圖是怎么回事呢?
我們先來(lái)說(shuō)說(shuō)三元色RGB概念。我們知道,自然界中的所有顏色都可以由紅,綠,藍(lán)(R,G,B)組合而成。有的顏色含有紅色成分多一些,如深紅;有的含有紅色成分少一些,如淡紅。針對(duì)含有紅色成分的多少,可以分成0到255共256個(gè)等級(jí),0級(jí)表示不含紅色成分,255級(jí)表示含有100%的紅色成分。同樣,綠色和藍(lán)色也被分成256級(jí)。這種分級(jí)的概念被稱(chēng)作量化。這樣,根據(jù)紅,綠,藍(lán)各種不同的組合我們就能表示出256*256*256,約1千6百萬(wàn)種顏色。這么多顏色對(duì)于我們?nèi)搜蹃?lái)已經(jīng)足夠了。
下表是常見(jiàn)的一些顏色的RGB組合值。
顏色RGB
紅25500
藍(lán)00255
綠02550
黃2552550
紫2550255
青0255255
白255255255
黑000
灰128128128
你大概已經(jīng)明白了,當(dāng)一幅圖中每個(gè)象素賦予不同的RGB值時(shí),就能呈現(xiàn)出五彩繽紛的顏色了,這樣就形成了彩色圖。對(duì),是這樣的,但實(shí)際上的做法還有些差別。
讓我們來(lái)看看下面的例子。
有一個(gè)長(zhǎng)寬各為200個(gè)象素,顏色數(shù)為16色的彩色圖,每一個(gè)象素都用R,G,B三個(gè)分量表示,因?yàn)槊總€(gè)分量有256個(gè)級(jí)別,要用8位(bit),即一個(gè)字節(jié)(byte)來(lái)表示,所以每個(gè)象素需要用3個(gè)字節(jié)。整個(gè)圖象要用200*200*3,約120k字節(jié),可不是一個(gè)小數(shù)目呀!如果我們用下面的方法,就能省的多。 因?yàn)槭且粋€(gè)16色圖,也就是說(shuō)這幅圖中最多只有16種顏色,我們可以用一個(gè)表:表中的每一行記錄一種顏色的R,G,B值。這樣當(dāng)我們表示一個(gè)象素的顏色時(shí),只需要指出該顏色是在第幾行,即該顏色在表中的索引值。舉個(gè)例子,如果表的第0行為255,0,0(紅色),那么當(dāng)某個(gè)象素為紅色時(shí),只需要標(biāo)明0即可。 讓我們?cè)賮?lái)計(jì)算一下:16種狀態(tài)可以用4位(bit)表示,所以一個(gè)象素要用半個(gè)字節(jié)。整個(gè)圖象要用200*200*0.5,約20k字節(jié),再加上表占用的字節(jié)為3*16=48字節(jié).整個(gè)占用的字節(jié)數(shù)約為前面的1/6,省很多吧。
這張RGB的表,即是我們常說(shuō)的調(diào)色板(Palette),另一種叫法是顏色查找表LUT(LookUpTable),似乎更確切一些。Windows位圖中便用到了調(diào)色板技術(shù).其實(shí)是不光是Windows位圖,許多圖象文件格式如pcx,tif,gif等都用到了。所以很好地掌握調(diào)色板的概念是十分重要的.
有一種圖,它的顏色數(shù)高達(dá)256*256*256種,也就是說(shuō)包含我們上述提到的R,G,B顏色表示方法中所有的顏色,這種圖叫做真彩色圖(TrueColor)。真彩色圖并不是說(shuō)一幅圖包含了所有的顏色,而是說(shuō)它具有顯示所有顏色的能力,即最多可以包含所有的顏色。表示真彩色圖時(shí),每個(gè)象素直接用R,G,B三個(gè)分量字節(jié)表示,而不采用調(diào)色板技術(shù),原因很明顯:如果用調(diào)色板,表示一個(gè)象素也要用24位,這是因?yàn)槊糠N顏色的索引要用24位(因?yàn)榭偣灿?的24次方種顏色,即調(diào)色板有2的24次方行),和直接用R,G,B三個(gè)分量表示用的字節(jié)數(shù)一樣,不但沒(méi)有任何便宜,還要加上一個(gè)256*256*256*3個(gè)字節(jié)的大調(diào)色板。所以真彩色圖直接用R,G,B三個(gè)分量表示,它又叫做24位色圖。
2.Bmp文件格式
介紹完位圖和調(diào)色板的概念,下面就讓我們來(lái)看一看Windows的位圖文件(.bmp文件)的格式是什么樣子的。 bmp文件大體上分成四個(gè)部分,如圖3所示。
位圖文件頭BITMAPFILEHEADER
|
位圖信息頭BITMAPINFOHEADER
|
調(diào)色板Palette
|
實(shí)際的位圖數(shù)據(jù)ImageDate
|
圖3.Windows位圖文件結(jié)構(gòu)示意圖
第一部分為位圖文件頭BITMAPFILEHEADER,是一個(gè)結(jié)構(gòu),其定義如下:
typedefstructtagBITMAPFILEHEADER{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
這個(gè)結(jié)構(gòu)的長(zhǎng)度是固定的,為14個(gè)字節(jié)(WORD為無(wú)符號(hào)16位整數(shù),DWORD為無(wú)符號(hào)32位整數(shù)),各個(gè)域的說(shuō)明如下:
bfType
指定文件類(lèi)型,必須是0x424D,即字符串"BM",也就是說(shuō)所有.bmp文件的頭兩個(gè)字節(jié)都是"BM"
bfSize
指定文件大小,包括這14個(gè)字節(jié)
bfReserved1,bfReserved2
為保留字,不用考慮
bfOffBits
為從文件頭到實(shí)際的位圖數(shù)據(jù)的偏移字節(jié)數(shù),即圖3中前三個(gè)部分的長(zhǎng)度之和。
第二部分為位圖信息頭BITMAPINFOHEADER,也是一個(gè)結(jié)構(gòu),其定義如下:
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER; 這個(gè)結(jié)構(gòu)的長(zhǎng)度是固定的,為40個(gè)字節(jié)(WORD為無(wú)符號(hào)16位整數(shù),DWORD無(wú)符號(hào)32位整數(shù),LONG為32位整數(shù)),各個(gè)域的說(shuō)明如下:
biSize
指定這個(gè)結(jié)構(gòu)的長(zhǎng)度,為40
biWidth
指定圖象的寬度,單位是象素
biHeight
指定圖象的高度,單位是象素
biPlanes
必須是1,不用考慮
biBitCount
指定表示顏色時(shí)要用到的位數(shù),常用的值為1(黑白二色圖),4(16色圖),8(256色),24(真彩色圖)(新的.bmp格式支持32位色,這里就不做討論了)。
biCompression
指定位圖是否壓縮,有效的值為BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS(都是一些Windows定義好的常量)。要說(shuō)明的是,Windows位圖可以采用RLE4,和RLE8的壓縮格式,但用的不多。我們今后所討論的只有第一種不壓縮的情況,即biCompression為BI_RGB的情況。
biSizeImage
指定實(shí)際的位圖數(shù)據(jù)占用的字節(jié)數(shù),其實(shí)也可以從以下的公式中計(jì)算出來(lái):
biSizeImage=biWidth'*biHeight
要注意的是:上述公式中的biWidth'必須是4的整倍數(shù)(所以不是biWidth,而是biWidth',表示大于或等于biWidth的,離4最近的整倍數(shù)。舉個(gè)例子,如果biWidth=240,則biWidth'=240;如果biWidth=241,biWidth'=244)如果biCompression為BI_RGB,則該項(xiàng)可能為零
biXPelsPerMeter
指定目標(biāo)設(shè)備的水平分辨率,單位是每米的象素個(gè)數(shù),關(guān)于分辨率的概念,我們將在打印部分詳細(xì)介紹。
biYPelsPerMeter
指定目標(biāo)設(shè)備的垂直分辨率,單位同上。
biClrUsed
指定本圖象實(shí)際用到的顏色數(shù),如果該值為零,則用到的顏色數(shù)為2的biBitCount次方。
biClrImportant
指定本圖象中重要的顏色數(shù),如果該值為零,則認(rèn)為所有的顏色都是重要的。
第三部分為調(diào)色板(Palette),當(dāng)然,這里是對(duì)那些需要調(diào)色板的位圖文件而言的。有些位圖,如真彩色圖,前面已經(jīng)講過(guò),是不需要調(diào)色板的,BITMAPINFOHEADER后直接是位圖數(shù)據(jù)。
調(diào)色板實(shí)際上是一個(gè)數(shù)組,共有biClrUsed個(gè)元素(如果該值為零,則有2的biBitCount次方個(gè)元素)。數(shù)組中每個(gè)元素的類(lèi)型是一個(gè)RGBQUAD結(jié)構(gòu),占4個(gè)字節(jié),其定義如下:
typedef struct tagRGBQUAD{
BYTE rgbBlue; //該顏色的藍(lán)色分量
BYTE rgbGreen; //該顏色的綠色分量
BYTE rgbRed; //該顏色的紅色分量
BYTE rgbReserved; //保留值
} RGBQUAD;
第四部分就是實(shí)際的圖象數(shù)據(jù)了。對(duì)于用到調(diào)色板的位圖,圖象數(shù)據(jù)就是該像素顏在調(diào)色板中的索引值,對(duì)于真彩色圖,圖象數(shù)據(jù)就是實(shí)際的R,G,B值。下面就2色,16色,256色位圖和真彩色位圖分別介紹。
對(duì)于2色位圖,用1位就可以表示該像素的顏色(一般0表示黑,1表示白),所以一個(gè)字節(jié)可以表示8個(gè)像素。
對(duì)于16色位圖,用4位可以表示一個(gè)像素的顏色,所以一個(gè)字節(jié)可以表示2個(gè)像素。
對(duì)于256色位圖,一個(gè)字節(jié)剛好可以表示1個(gè)像素。
對(duì)于真彩色圖,三個(gè)字節(jié)才能表示1個(gè)像素。
要注意兩點(diǎn):
1.每一行的字節(jié)數(shù)必須是4的整倍數(shù),如果不是,則需要補(bǔ)齊。這在前面介紹biSizeImage時(shí)已經(jīng)提到了。
2.一般來(lái)說(shuō),.BMP文件的數(shù)據(jù)從下到上,從左到右的。也就是說(shuō),從文件中最先讀到的是圖象最下面一行的左邊第一個(gè)像素,然后是左邊第二個(gè)像素…接下來(lái)是倒數(shù)第二行左邊第一個(gè)像素,左邊第二個(gè)像素…依次類(lèi)推,最后得到的是最上面一行的最右一個(gè)像素。
好了,終于介紹完bmp文件結(jié)構(gòu)了,是不是覺(jué)得頭有些大?別著急,對(duì)照著下面的程序,你就會(huì)很清楚了.
3.顯示一個(gè)bmp文件的C程序
下面的函數(shù)LoadBmpFile,其功能是從一個(gè).bmp文件中讀取數(shù)據(jù)(包括BITMAPINFOHEADER,調(diào)色板和實(shí)際圖象數(shù)據(jù))將其存儲(chǔ)在一個(gè)全局內(nèi)存句柄hImgData中,這個(gè)hImgData將在以后的圖象處理程序中用到。同時(shí)填寫(xiě)一個(gè)類(lèi)型為HBITMAP的全局變量hBitmap和一個(gè)類(lèi)型為HPALETTE的全局變量hPalette。這兩個(gè)變量將在處理WM_PAINT消息時(shí)用到,用來(lái)顯示出位圖。該函數(shù)的兩個(gè)參數(shù)分別是用來(lái)顯示位圖的窗口句柄,和.bmp文件名(全路徑),當(dāng)函數(shù)成功時(shí),返回TRUE,否則返回FALSE.
BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;
BOOL LoadBmpFile(HWND hWnd,char* BmpFileName)
{
HFILE hf; //文件句柄
LPBITMAPINFOHEADER lpImgData; //指向BITMAPINFOHEADER結(jié)構(gòu)的指針
LOGPALETTE *pPal; //指向邏輯調(diào)色板結(jié)構(gòu)的指針
LPRGBQUAD lpRGB; //指向RGBQUAD結(jié)構(gòu)的指針
HPALETTE hPrevPalette;//用來(lái)保存設(shè)備中原來(lái)的調(diào)色板
HDC hDc; //設(shè)備句柄
HLOCAL hPal; //存儲(chǔ)調(diào)色板的局部?jī)?nèi)存句柄
DWORD LineBytes; //每一行的字節(jié)數(shù)
DWORD ImgSize; //實(shí)際的圖象數(shù)據(jù)占用的字節(jié)數(shù)
DWORD NumColors; //實(shí)際用到的顏色數(shù),即調(diào)色板數(shù)組中的顏色個(gè)數(shù)
DWORD i;
if((hf=_lopen(BmpFileName,OF_READ))==HFILE_ERROR){
MessageBox (hWnd,"Filec:\\test.bmpnotfound!","ErrorMessage",
MB_OK|MB_ICONEXCLAMATION);
return FALSE;//打開(kāi)文件錯(cuò)誤,返回
}
//將BITMAPFILEHEADER結(jié)構(gòu)從文件中讀出,填寫(xiě)到bf中
_lread(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER));
//將BITMAPINFOHEADER結(jié)構(gòu)從文件中讀出,填寫(xiě)到bi中
_lread(hf,(LPSTR)&bi,sizeof(BITMAPINFOHEADER));
/*我們定義了一個(gè)宏#define WIDTHBYTES(i) ((i+31)/32*4),上面曾經(jīng)提到過(guò),每一行的字節(jié)數(shù)必須是4的整倍數(shù),只要調(diào)用WIDTHBYTES(bi.biWidth*bi.biBitCount)就能完成這一換算.舉一個(gè)例子,對(duì)于2色圖,如果圖象寬是31,則每一行需要31位存儲(chǔ),合3個(gè)字節(jié)加7位,因?yàn)樽止?jié)數(shù)必須是4的整倍數(shù),所以應(yīng)該是4,而此時(shí)的biWidth=31,biBitCount=1,WIDTHBYTES(31*1)=4,和我們?cè)O(shè)想的一樣。再舉一個(gè)256色的例子,如果圖象寬是31,則每一行需要31個(gè)字節(jié)存儲(chǔ),因?yàn)樽止?jié)數(shù)必須是4的整倍數(shù),所以應(yīng)該是32,而此時(shí)的biWidth=31,biBitCount=8,WIDTHBYTES(31*8)=32,和我們?cè)O(shè)想的一樣。你可以多舉幾個(gè)例子來(lái)驗(yàn)證一下*/
//LineBytes為每一行的字節(jié)數(shù)
LineBytes=(DWORD)WIDTHBYTES(bi.biWidth*bi.biBitCount);
//ImgSize為實(shí)際的圖象數(shù)據(jù)占用的字節(jié)數(shù)
ImgSize=(DWORD)LineBytes*bi.biHeight;
//NumColors為實(shí)際用到的顏色數(shù),即調(diào)色板數(shù)組中的顏色個(gè)數(shù)
if(bi.biClrUsed!=0)
NumColors=(DWORD)bi.biClrUsed;//如果bi.biClrUsed不為零,就是本圖象實(shí)際
//用到的顏色數(shù)
else//否則,用到的顏色數(shù)為2的biBitCount次方。
switch(bi.biBitCount){
case1:
NumColors=2;
break;
case4:
NumColors=16;
break;
case8:
NumColors=256;
break;
case24:
NumColors=0;//對(duì)于真彩色圖,沒(méi)用到調(diào)色板
break;
default:
//不處理其它的顏色數(shù),認(rèn)為出錯(cuò)。
MessageBox(hWnd,"Invalidcolornumbers!","ErrorMessage",
MB_OK|MB_ICONEXCLAMATION);
_lclose(hf);
return FALSE;//關(guān)閉文件,返回FALSE
}
if(bf.bfOffBits!=(DWORD)(NumColors*sizeof(RGBQUAD)+sizeof(BITMAPFILEHEADER)
+sizeof(BITMAPINFOHEADER)))
{
//計(jì)算出的偏移量與實(shí)際偏移量不符,一定是顏色數(shù)出錯(cuò)
MessageBox(hWnd,"Invalidcolornumbers!","ErrorMessage",
MB_OK|MB_ICONEXCLAMATION);
_lclose(hf);
return FALSE;//關(guān)閉文件,返回FALSE
}
bf.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+NumColors
*sizeof(RGBQUAD)+ImgSize;
//分配內(nèi)存,大小為BITMAPINFOHEADER結(jié)構(gòu)長(zhǎng)度加調(diào)色板+實(shí)際位圖數(shù)據(jù)
if((hImgData=GlobalAlloc(GHND,(DWORD)(sizeof(BITMAPINFOHEADER)+
NumColors*sizeof(RGBQUAD)+ImgSize)))==NULL)
{
//分配內(nèi)存錯(cuò)誤
MessageBox(hWnd,"Errorallocmemory!","ErrorMessage",
MB_OK|MB_ICONEXCLAMATION);
_lclose(hf);
return FALSE;//關(guān)閉文件,返回FALSE
}
//指針lpImgData指向該內(nèi)存區(qū)
lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);
//文件指針重新定位到BITMAPINFOHEADER開(kāi)始處
_llseek(hf,sizeof(BITMAPFILEHEADER),SEEK_SET);
//將文件內(nèi)容讀入lpImgData
_hread(hf,(char*)lpImgData,(long)sizeof(BITMAPINFOHEADER)
+(long)NumColors*sizeof(RGBQUAD)+ImgSize);
_lclose(hf);//關(guān)閉文件
if(NumColors!=0) //NumColors不為零,說(shuō)明用到了調(diào)色板
{
//為邏輯調(diào)色板分配局部?jī)?nèi)存,大小為邏輯調(diào)色板結(jié)構(gòu)長(zhǎng)度加NumColors個(gè)
//PALETTENTRY大小
hPal=LocalAlloc(LHND,sizeof(LOGPALETTE)+NumColors*sizeof(PALETTEENTRY));
//指針pPal指向該內(nèi)存區(qū)
pPal=(LOGPALETTE*)LocalLock(hPal);
//填寫(xiě)邏輯調(diào)色板結(jié)構(gòu)的頭
pPal->palNumEntries=NumColors;
pPal->palVersion=0x300;
//lpRGB指向的是調(diào)色板開(kāi)始的位置
lpRGB=(LPRGBQUAD)((LPSTR)lpImgData+(DWORD)sizeof(BITMAPINFOHEADER));
//填寫(xiě)每一項(xiàng)
for(i=0;i<NumColors;i++)
{
pPal->palPalEntry[i].peRed=lpRGB->rgbRed;
pPal->palPalEntry[i].peGreen=lpRGB->rgbGreen;
pPal->palPalEntry[i].peBlue=lpRGB->rgbBlue;
pPal->palPalEntry[i].peFlags=(BYTE)0;
lpRGB++;//指針移到下一項(xiàng)
}
//產(chǎn)生邏輯調(diào)色板,hPalette是一個(gè)全局變量
hPalette=CreatePalette(pPal);
//釋放局部?jī)?nèi)存
LocalUnlock(hPal);
LocalFree(hPal);
}
//獲得設(shè)備上下文句柄
hDc=GetDC(hWnd);
if(hPalette)//如果剛才產(chǎn)生了邏輯調(diào)色板
{
//將新的邏輯調(diào)色板選入DC,將舊的邏輯調(diào)色板句柄保存在hPrevPalette
hPrevPalette=SelectPalette(hDc,hPalette,FALSE);
RealizePalette(hDc);
}
//產(chǎn)生位圖句柄
hBitmap=CreateDIBitmap(hDc, (LPBITMAPINFOHEADER)lpImgData,(LONG)CBM_INIT,
(LPSTR)lpImgData+sizeof(BITMAPINFOHEADER)+NumColors*sizeof(RGBQUAD),
(LPBITMAPINFO)lpImgData,DIB_RGB_COLORS);
//將原來(lái)的調(diào)色板(如果有的話)選入設(shè)備上下文句柄
if(hPalette&&hPrevPalette)
{
SelectPalette(hDc,hPrevPalette,FALSE);
RealizePalette(hDc);
}
ReleaseDC(hWnd,hDc); //釋放設(shè)備上下文
GlobalUnlock(hImgData); //解鎖內(nèi)存區(qū)
Return TRUE; //成功返回
}
上面的程序中,要說(shuō)明的有兩點(diǎn):
第一,對(duì)于需要調(diào)色板的圖,要想正確的顯示,必須根據(jù).bmp文件,產(chǎn)生邏輯調(diào)色板。產(chǎn)生的方法是:1.為邏輯調(diào)色板指針?lè)峙鋬?nèi)存,大小為邏輯調(diào)色板結(jié)構(gòu)(LOGPALETTE)長(zhǎng)度加NumColors個(gè)PALETTENTRY大小。(調(diào)色板的每一項(xiàng)都是一個(gè)PALETTEENTRY結(jié)構(gòu)),2.填寫(xiě)邏輯調(diào)色板結(jié)構(gòu)的頭pPal->palNumEntries=NumColors;pPal->palVersion=0x300;3.從文件中讀取調(diào)色板的RGB值,填寫(xiě)到每一項(xiàng)中。4,產(chǎn)生邏輯調(diào)色板:hPalette=CreatePalette(pPal)
第二,產(chǎn)生位圖(BITMAP)句柄,該項(xiàng)工作由函數(shù)CreateDIBitmap來(lái)完成。hBitmap=CreateDIBitmap(hDc,LPBITMAPINFOHEADER)lpImgData,(LONG)CBM_INIT, (LPSTR)lpImgData+sizeof(BITMAPINFOHEADER)+NumColors*sizeof(RGBQUAD), (LPBITMAPINFO)lpImgData,DIB_RGB_COLORS); CreateDIBitmap的作用是產(chǎn)生一個(gè)和Windows設(shè)備無(wú)關(guān)的位圖。該函數(shù)的第一項(xiàng)參數(shù)為設(shè)備上下文句柄,如果位圖用到了調(diào)色板,要在調(diào)用CreateDIBitmap之前將邏輯調(diào)色板選入該設(shè)備上下文中,產(chǎn)生hBitmap后,再把原調(diào)色板選入該設(shè)備上下文中,并釋放該上下文;第二項(xiàng)為指向BITMAPINFOHEADER的指針;第三項(xiàng)就用常量CBM_INI,不用考慮;第四項(xiàng)為指向調(diào)色板的指針;第五項(xiàng)為指向BITMAPINFO(包括BITMAPINFOHEADER,調(diào)色板,及實(shí)際的圖象數(shù)據(jù))的指針;第六項(xiàng)就用常量DIB_RGB_COLORS,不用考慮。
上面提到了設(shè)備上下文,相信編過(guò)Windows程序的讀者對(duì)它并不陌生,這里再簡(jiǎn)單的介紹一下。Windows操作系統(tǒng)統(tǒng)一管理著諸如顯示,打印等操作,將它們看作是一個(gè)個(gè)的設(shè)備,每一個(gè)設(shè)備都有一個(gè)復(fù)雜的數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)。所謂設(shè)備上下文就是指這個(gè)數(shù)據(jù)結(jié)構(gòu)。然而,我們不能直接和這些設(shè)備上下文打交道,只能通過(guò)引用標(biāo)識(shí)它的句柄(實(shí)際上是一個(gè)整數(shù)),讓W(xué)indows去做相應(yīng)的處理。產(chǎn)生的邏輯調(diào)色板句柄hPalette和位圖句柄hBitmap要在處理WM_PAINT消息時(shí)使用,這樣才能在屏幕上顯示出來(lái),處理過(guò)程如下面的程序。
StaticHDC hDC,hMemDC;
PAINTSTRUCT ps;
case WM_PAINT:
{
hDC=BeginPaint(hwnd,&ps);//獲得屏幕設(shè)備上下文
if(hBitmap)//hBitmap一開(kāi)始是NULL,當(dāng)不為NULL時(shí)表示有圖
{
hMemDC=CreateCompatibleDC(hDC);//建立一個(gè)內(nèi)存設(shè)備上下文
if(hPalette)//有調(diào)色板
{
//將調(diào)色板選入屏幕設(shè)備上下文
SelectPalette(hDC,hPalette,FALSE);
//將調(diào)色板選入內(nèi)存設(shè)備上下文
SelectPalette(hMemDC,hpalette,FALSE);
RealizePalette(hDC);
}
//將位圖選入內(nèi)存設(shè)備上下文
SelectObject(hMemDC,hBitmap);
//顯示位圖
BitBlt(hDC,0,0,bi.biWidth,bi.biHeight,hMemDC,0,0,SRCCOPY);
//釋放內(nèi)存設(shè)備上下文
DeleteDC(hMemDC);
}
//釋放屏幕設(shè)備上下文
EndPaint(hwnd,&ps);
break;
}
在上面的程序中,我們調(diào)用CreateCompatibleDC創(chuàng)建一個(gè)內(nèi)存設(shè)備上下文。SelectObject函數(shù)將于設(shè)備無(wú)關(guān)的位圖選入內(nèi)存設(shè)備上下文中。然后我們調(diào)用BitBlt函數(shù)在內(nèi)存設(shè)備上下文和屏幕設(shè)備上下文中進(jìn)行位拷貝。由于所有操作都是在內(nèi)存中進(jìn)行,所以是最快的。
BitBlt函數(shù)的參數(shù)分別為:1.目標(biāo)設(shè)備上下文,在上面的程序里,為屏幕設(shè)備上下文,如果改成打印設(shè)備上下文,就不是顯示位圖,而是打印;2.目標(biāo)矩形左上角點(diǎn)x坐標(biāo);3.目標(biāo)矩形左上角點(diǎn)y坐標(biāo),在上面的程序中,2和3為(0,0),表示顯示在窗口的左上角;4.目標(biāo)矩形的寬度;5.目標(biāo)矩形的高度;6.源設(shè)備上下文,在上面的程序里,為內(nèi)存設(shè)備上下文;7.源矩形左上角點(diǎn)x坐標(biāo);8.源矩形左上角點(diǎn)y坐標(biāo);9.操作方式,在這里為SRCCOPY,表示直接將源矩形拷貝到目標(biāo)矩形。還可以是反色,擦除,做"與"運(yùn)算等操作,具體細(xì)節(jié)見(jiàn)VC++幫助。你可以試著改改第2,3,4,5,7,8,9項(xiàng)參數(shù),就能體會(huì)到它們的含義了。
終于講完了。是不是覺(jué)得有點(diǎn)枯燥?這一講是有點(diǎn)兒枯燥,特別是當(dāng)你對(duì)Windows的編程并不很清楚時(shí),就更覺(jué)得如此。不過(guò),當(dāng)一幅漂亮的bmp圖顯示在屏幕上時(shí),你還是會(huì)興奮的大叫"Yeah",至少當(dāng)年我是這樣。
最后,再介紹一個(gè)命令行編譯的竅門(mén)。為什么要用命令行編譯呢?主要有兩個(gè)好處:
第一,不用進(jìn)入IDE(集成開(kāi)發(fā)環(huán)境),節(jié)省了時(shí)間,而且編譯速度也比較快。
第二,對(duì)于簡(jiǎn)單的程序,不用生成項(xiàng)目文件.mdp或.mak,直接就能生成.exe文件,這一點(diǎn),在下面的例子中可以看到。
在安裝VisualC++完畢時(shí),在bin目錄下會(huì)產(chǎn)生一個(gè)VCVARS32.BAT文件,它的作用是在命令行編譯時(shí)設(shè)置正確的環(huán)境變量,如存放頭文件的INCLUDE目錄,存放庫(kù)文件的LIB目錄等,如果你沒(méi)找到這個(gè)批處理文件,可以參考下面的例子,自己做一個(gè)批處理。
@echo off
set MSDevDir=d:\MSDEV
set VcOsDir=WIN95
set PATH="%MSDevDir%\BIN";"%MSDevDir%\BIN\%VcOsDir%";"%PATH%"
set INCLUDE=%MSDevDir%\INCLUDE;%MSDevDir%\MFC\INCLUDE;%INCLUDE%
set LIB=%MSDevDir%\LIB;%MSDevDir%\MFC\LIB;%LIB%
set VcOsDir=
只要把上面的"d:\MSDEV"改成你自己的VC目錄就可以了。在DOSPROMPT下執(zhí)行該批處理文件,執(zhí)行set命令,你就能看到新設(shè)置的環(huán)境變量了。如下所示:
PATH=D:\MSDEV\BIN;D:\MSDEV\BIN\WIN95;C:\WIN95;C:\WIN95\COMMAND;C:\WIN95\SYSTEM;
INCLUDE=d:\msdev\INCLUDE;d:\msdev\MFC\INCLUDE;
LIB=d:\msdev\LIB;d:\msdev\MFC\LIB;
現(xiàn)在我們就可以進(jìn)行命令行編譯了。(當(dāng)然,你也可以使用IDE,先new一個(gè)project,然后把.c和.rc文件插入到project中,編譯運(yùn)行。)
首先編譯資源文件,輸入rc bmp.rc,將生成bmp.res文件,接著輸入cl bmp.c bmp.res user32.lib gdi32.lib,就生成bmp.exe了。可以看到,我們并沒(méi)有用到項(xiàng)目文件,所以,對(duì)于這種簡(jiǎn)單的程序來(lái)說(shuō),使用命令行編譯還是非常方便的。好了,運(yùn)行bmp.exe,欣賞一下你今天的勞動(dòng)成果。
注意事項(xiàng):
命令行編譯過(guò)程如下:
vcvars32
rc bmp.rc
cl bmp.c bmp.res user32.lib gdi32.lib
========
后來(lái)發(fā)現(xiàn)原文出自
http://vipbase.net/ipbook/此書(shū)是清華學(xué)生寫(xiě)的,做圖像編程的入門(mén)教材很不錯(cuò)!
posted on 2009-07-10 22:59
鷹擊長(zhǎng)空 閱讀(1181)
評(píng)論(0) 編輯 收藏 引用