位圖[轉(zhuǎn)載]
一、概述
?????
在Windows中每屏是一個(gè)圖形圖像,靈巧的Windows制作系統(tǒng),面對龐大的圖形編程任務(wù),建立了為繪畫多彩的邊界、按鈕、圖標(biāo)、字體的函數(shù)庫。當(dāng)
然啦,通過Windows
API,這些函數(shù)都是可調(diào)用的。所謂Windows顯示屏幕以及數(shù)量眾多的打印機(jī)其實(shí)都是屬于“光柵設(shè)備”。在光柵設(shè)備中,一幅圖象由多條掃描線以及能訪
問的單獨(dú)像素構(gòu)成。Windows也支持非光柵設(shè)備,比如繪圖儀等,本人對此一無經(jīng)驗(yàn),無從談起,想來也差不到哪兒去。以下只以顯示器為重點(diǎn)展開討論。
?????
計(jì)算機(jī)視頻系統(tǒng)的核心是內(nèi)存。該內(nèi)存包含代表著顯示圖案的數(shù)據(jù),而這些圖案顯示在監(jiān)視器(顯示器)上。每次鼠標(biāo)移動(dòng)時(shí),內(nèi)存中的少量數(shù)據(jù)發(fā)生變化。然后你
會(huì)看到鼠標(biāo)指針在屏幕上移動(dòng)。每次以及每一個(gè)圖形操作都會(huì)影響視頻內(nèi)存,因?yàn)镚DI執(zhí)行計(jì)算并以相應(yīng)方式更改視頻內(nèi)存。計(jì)算機(jī)中還有一個(gè)存放圖象數(shù)據(jù)的內(nèi)
存,叫做位圖內(nèi)存。位圖內(nèi)存與視頻內(nèi)存的重要的一個(gè)區(qū)別是∶位圖內(nèi)存看不到,而視頻內(nèi)存則能夠看到。也就是說放在位圖內(nèi)存的圖象數(shù)據(jù)并不反映在屏幕上,而
視頻內(nèi)存中存放的圖象信息則反映在監(jiān)視器上。如果把駐留在位圖內(nèi)存的數(shù)據(jù)移動(dòng)到視頻內(nèi)存中,那么圖象將顯示在監(jiān)視器上。以一個(gè)桌面圖標(biāo)為例,圖標(biāo)從磁盤加
載到內(nèi)存中(位圖內(nèi)存)中,然后內(nèi)存被移到視頻內(nèi)存中的適當(dāng)?shù)刂飞希@樣當(dāng)視頻內(nèi)存通過視頻硬件被顯示到屏幕上時(shí),圖標(biāo)成為可見的。
?????
從圖象的種類來講,Windows中存在兩種位圖,一種叫與設(shè)備有關(guān)位圖(或叫設(shè)備相關(guān)位圖),另一種叫與設(shè)備無關(guān)位(或叫設(shè)備無關(guān)位圖,或DIB)。除
非特別聲明,W
indows中的位圖都是與設(shè)備有關(guān)的位圖。有些朋友一說位圖,就可能想到BMP圖象,但它是屬于與設(shè)備無關(guān)的位圖。在編程的領(lǐng)域我們常常說位圖,一般并
不是指BMP圖象,而是指對他們來說也是想象中存在的那個(gè)叫與設(shè)備有關(guān)的位圖。另一個(gè)區(qū)別與設(shè)備有關(guān)位圖和與設(shè)備無關(guān)位圖的重要依據(jù)是,判斷該位圖是否具
有句柄。具有句柄的位圖便是與設(shè)備有關(guān)的位圖,因?yàn)樗荊DI對象之一。常見的BMP圖象則屬于與設(shè)備無關(guān)的位圖。我們一般不叫它位圖,而是叫做“BMP
圖象”或者叫做“DIB”,它是一種數(shù)據(jù)的組織方式,并非GDI繪圖對象。
?????
可以把與設(shè)備無關(guān)位圖理解為對與設(shè)備有關(guān)位圖數(shù)據(jù)的一種標(biāo)準(zhǔn)格式的數(shù)據(jù)保存方式。比如我們平常看到的附加名為BMP的文件。這種位圖文件會(huì)在文件頭上放置
文件的組織信息,形式上講和一些數(shù)據(jù)庫文件的文件頭起著同樣的作用━━描述文件的結(jié)構(gòu)。文件頭后面緊跟著的是圖象的顏色數(shù)據(jù)。由于這種機(jī)制的存在,使得與
設(shè)備無關(guān)的位圖可以在各種設(shè)備之間進(jìn)行讀寫。
????? 與設(shè)備無關(guān)的位圖只有一種格式,而與設(shè)備有關(guān)的位圖則可能...大概有多少種設(shè)備就有多少種格式吧?鬼才知道!幸運(yùn)的是您根本沒有必要了解它的格式,只需簡單理解為保存圖象數(shù)據(jù)的內(nèi)存塊就可以了。就好像您跟本不用了解我長得什么模樣,也照樣能和我打交道一樣。
??????
PC的視頻系統(tǒng)顯示一個(gè)像素的矩陣(整齊排列的小光點(diǎn))。在一個(gè)基于字符的環(huán)境中,視頻系統(tǒng)包含一套"硬件編程"的形狀,代表標(biāo)準(zhǔn)字符的位置及各種畫圖字
符(直線、角線、實(shí)線等等)和幾個(gè)符號(笑臉、鉆石、鏟等)。通過發(fā)送給系統(tǒng)一個(gè)ASCII碼或ANSI字符碼來顯示一個(gè)字符。而在Windows這樣一
個(gè)圖形顯示系統(tǒng)中,計(jì)算機(jī)及其軟件定義出現(xiàn)在屏幕上的圖形形狀。這些形狀是用位圖來代表的。用這種方法的最主要的優(yōu)點(diǎn)是圖象和文本能以不同的大小、字體、
方式來顯示。那么,準(zhǔn)確地說,什么是位圖呢?它們是數(shù)據(jù)元素的集合,這些數(shù)據(jù)元素決定在每個(gè)屏幕位置上顯示什么樣的顏色。在單色的(黑和白)圖形圖像中,
每位代表一個(gè)屏幕像素,0對應(yīng)黑(沒有顏色),1對應(yīng)白(顯示色)。不久您就會(huì)看到,位圖能描述彩色圖像。每個(gè)像素的位數(shù)決定能在單個(gè)位圖中出現(xiàn)的不同色
彩的數(shù)目。除了每個(gè)像素一位的單色視頻,還有其他三種∶每個(gè)像素4位產(chǎn)生16種顏色,8位產(chǎn)生256種顏色,24位產(chǎn)生16'777'216種顏色(補(bǔ)
充:我已經(jīng)親眼看到了32位顯卡)。如下表所示∶
???????????????????????? Windows支持的彩色
每個(gè)像素的位數(shù)?????? 顏色總數(shù)?????????????????????? ?典型設(shè)備
1?????????????????????????? ??2???????????? ????????????? ?單色圖形
4??????????????????????????? 16??????????????????????? ?? 標(biāo)準(zhǔn)備VGA
8??????????????????????????? 256???????????????????????? 256色VGA
16????????????????????????? 32'768或65'536????? 32K或64K色SVGA
24????????????????????????? 16'777'216???????????? 24位真彩色設(shè)
??????
計(jì)算機(jī)顯示系統(tǒng)的性能決定Windows對彩色圖象的處理。現(xiàn)在常看到的顯示器是SVGA(AGP也出來了,我的就是,但資料少無從談起)。理論上講,
SVGA系統(tǒng)能顯示驚人的含有16'777'216種顏色數(shù)組。把能夠顯示16'000'000種顏色范圍的系統(tǒng)通常稱為真彩色(true
color)。那么"真彩色"的真實(shí)意思是什么呢?它源于對是否一個(gè)彩色顯示器能顯示無限大范圍的顏色的想象,圖象將看起來完全逼“真”。真彩色的顯示用
24位來決定每個(gè)像素的顏色。24位被分成三組,每組8位,來表示紅、綠、藍(lán)三種顏色。(參考∶自然界的顏色是通過紅、綠、藍(lán)(RGB)三種顏色來組合而
成的。叫做三元色。)
二、與設(shè)備無關(guān)位圖(DIB)
?????
所謂DIB是指與設(shè)備無關(guān)的位圖。DIB并不是Windows的對象,它沒有自己的句柄,實(shí)際上是一種數(shù)據(jù)文件。和很多數(shù)據(jù)文件相同,DIB位圖文件的開
頭部分有它內(nèi)在的數(shù)據(jù)結(jié)構(gòu)描述部分。不少書做了一些示意圖來說明這個(gè)問題,但我覺得還是親眼目睹DIB文件的內(nèi)部更好一些,百聞不如一睹嘛。
????? 現(xiàn)在我們準(zhǔn)備要看的BMP文件是一個(gè)256色位圖,文件名叫Hua.bmp ,這個(gè)文件曾在《VB前線》的演示程序第25號中使用過。
????? 首先讓我們查看一下文件在磁盤中的大體狀況∶
D:\vbplay\vbplay25\>dir
Volume in drive D is PRO???????
?Volume Serial Number is 268F-AB4B
?Directory of D:\vbplay\vbplay25
.????????????? <DIR>??????? 03-27-99? 20:47 .
..???????????? <DIR>??????? 03-27-99? 20:47 ..
PLAY25?????? VBW?????????? 111? 09-20-99? 23:43 play25.vbw
MSSCCPRJ? SCC?????????? ?193? 04-07-99? 23:52 MSSCCPRJ.SCC
BITMAP?????? CLS????????????12,711? 06-16-99? 21:27 Bitmap.cls
MYMEMORY
CLS????????????3,419? 04-08-99?? 2:07 MyMemory.cls???????????
PLAY25?????? VBP?????????? ?636? 06-16-99? 21:42 play25.vbp
HUA??????????? BMP?????????? 96,446? 04-04-99? 12:06 hua.bmp
PL25_1????? ?FRM?????????? 3,865? 06-16-99? 21:25 pl25_1.frm
???????? 7 file(s)??????? 117,381 bytes
???????? 2 dir(s)???? 569,049,088 bytes free
????? 看到了吧?它的大小是96?46個(gè)字節(jié)。接下來,我們可以用DEBUG的d命令以16進(jìn)制形式來顯示文件的文件頭信息。操作如下∶
D:\vbplay\vbplay25\>debug hua.bmp
-d 100 1000
0EAE:0100? 42 4D BE 78 01 00 00 00-00 00 36 04 00 00 28 00?? BM.x......6...(.
0EAE:0110? 00 00 69 01 00 00 06 01-00 00 01 00 08 00 00 00?? ..i.............
0EAE:0120? 00 00 88 74 01 00 C4 0E-00 00 C4 0E 00 00 00 00?? ...t............
0EAE:0130? 00 00 00 00 00 00 00 00-00 00 80 80 80 00 00 00?? ................
0EAE:0140? 80 00 00 80 80 00 00 80-00 00 80 80 00 00 80 00?? ................
0EAE:0150? 00 00 80 00 80 00 40 80-80 00 40 40 00 00 FF 80?? ......@...@@....
0EAE:0160? 00 00 80 40 00 00 FF 00-40 00 00 40 80 00 FF FF?? ...@....@..@....
省略了57行∶50 - 16 -1 = 39(16進(jìn)制) = 57(10進(jìn)制)
0EAE:0500? CC 00 AD E8 FD 00 78 AE-D3 00 1F 38 65 00 66 89?? ......x....8e.f.
0EAE:0510? F0 00 63 9B C3 00 79 AD-DA 00 08 13 73 00 0A 1C?? ..c...y.....s...
0EAE:0520? A5 00 BB ED FD 00 83 C1-E5 00 2A 50 74 00 7C B3?? ..........*Pt.|.
0EAE:0530? ED 00 56 82 BD 00 B3 66-B3 B3 6C B3 BD 8B C9C9?? ..V....f..l.....
0EAE:0540? BD 50 21 60 6C 6C 44 44-B3 44 6C 86 44 66 44 D9?? .P!`llDD.Dl.DfD.
0EAE:0550? 44 B3 6C C9 87 7B B9 7B-44 BD 87 60 87 C9 6C AB?? D.l..{.{D..`..l.
0EAE:0560? 60 D6 6C AB AB 2A 88 55-88 7B 7B 7B 7B 3D 3D 3D?? `.l..*.U.{{{{===
0EAE:0570? C5 3D C5 24 7B 7B 7B 7B-B4 B9 7B B9 B5 C6 3D7B?? .=.${{{{..{...={
(以下全部省略)
0EAE:1000? B3??????????????????????????????????????????????? .
-q
?????
注∶若使用D:\vbplay\vbplay25\>debug hua.bmp >>
Temp.txt則可以把結(jié)果保存到Temp.txt文件,并可加以分析研究。但這樣給出命令后什么都看不見。這時(shí),你應(yīng)當(dāng)鍵入d 100
1000這個(gè)命令后回車,然后再按q就可以返回到DOS命令行提示符下。這段內(nèi)容是這樣獲取的。詳細(xì)內(nèi)容,可參考有關(guān)DOS命令參考手冊。用
pctools5.0也可以。
????? 通過上述方式獲取的是該位圖文件第一個(gè)字節(jié)開始的若干個(gè)字節(jié)的16進(jìn)制形式的文本內(nèi)容。以下,我們具體分析一下。在分析以前為了在整體上把握DIB文件的結(jié)構(gòu),有必要先給出其整體情況,如下表格所示∶
DIB文件的組成
三大部分????????????????????????????????????????? 字節(jié)????????????????????????? ?說明
BITMAPFILEHEADER??????????????????????? 14????????????????
用紅色表示的部分
BITMAPINFO?BITMAPINFOHEADER?? 40???????????????? 用深綠色表示的部分
RGBQUAD(結(jié)構(gòu)數(shù)組)???????????????????????? 最小0,最大4×256。(在16位以上的位圖中,根本不存在此部分)?????????????????????????????????????????? ?用深紅色表示的部分
數(shù)據(jù)部分?????????????????????????????????????????? 大于0(具體大小信息記錄在BITMAPINFOHEADER結(jié)構(gòu)中)???????????????????????????????? 用下劃線表示的部分
????? 可以看出,DIB的文件頭主要由兩個(gè)部分組成,即BITMAPFILEHEADER和BITMAPINFO,
?????? 而BITMAPINFO又分為BITMAPINFOHEADER和RGBQUAD兩個(gè)小部分。讓我們一個(gè)一個(gè)分析。首先是開頭以紅色表示的14個(gè)字節(jié),如下
42 4D BE 78 01 00 00 00-00 00 36 04 00 00
這是BITMAPFILEHEADER結(jié)構(gòu)部分。BITMAPFILEHEADER結(jié)構(gòu)如表所示∶
??????????????????? BITMAPFILEHEADER結(jié)構(gòu)
結(jié)構(gòu)內(nèi)各字段??????????????????? 數(shù)據(jù)類型???????????????????????????? 說明
bfType????????????????????????? Integer??????????????????? 指定文件類型,必須 BM
bfSize?????????????????????????? Long??????????????????????? 指定位圖文件大小,以字節(jié)為單位
bfReserved1???????????????? Integer??????????????????? 保留未用,必須設(shè)為0
bfReserved2???????????????? Integer??????????????????? 同上
bfOffBits??????????????????????? Long?????????????????????? 從此結(jié)構(gòu)到位圖數(shù)據(jù)位的字節(jié)偏移量
????? 此結(jié)構(gòu),主要記錄了DIB文件大小以及結(jié)構(gòu)有關(guān)的信息。在很多情況下,可以計(jì)算來獲取這些這些信息,所以很多人并不訪問這個(gè)結(jié)構(gòu)內(nèi)的數(shù)據(jù)。不少人說
BITMAPFILEHEADER結(jié)構(gòu)記錄著無大用處的信息。其中bfOffBits說明的是從此結(jié)構(gòu)到位圖數(shù)據(jù)位的字節(jié)偏移量。通過對此字段的訪問,我們可以知道這個(gè)位圖文件的顏色數(shù)據(jù)是從哪里開始的,或者可以知道DIB文件頭的結(jié)構(gòu)有多長。
m_BMFileHeader.bfType = 42 4D
???????
這是一個(gè)Integer整型數(shù)據(jù),對DIB文件來說此數(shù)據(jù)必須是42 4D。那么為什么定為42
4D呢,其實(shí)是在ASCII碼表中42即十進(jìn)制的66表示大寫B(tài),4D即十進(jìn)制的77表示M,也就是說字符擝M數(shù)囊饈丁?梢遠(yuǎn)哉飧穌褪蕕姆夢世磁卸銜
募欠袷荄IB(或BMP)文件。
m_BMFileHeader.bfSize = BE 78 01 00 = &H178BE = 96446個(gè)字節(jié)
??? 說明文件的總長度。注意,右側(cè)的數(shù)據(jù)是高字節(jié),即右側(cè)字節(jié)的地址比左側(cè)字節(jié)的地址值大,所以BE 78 01 00 = &H178BE。在上面,用dir命令觀察文件時(shí),我們已經(jīng)看到文件的長度正是96446個(gè)字節(jié)。如∶
??? HUA????? BMP?????? 96,446? 04-04-99? 12:06 hua.bmp
m_BMFileHeader.bfReserved1 = 00 00? (系統(tǒng)保留,沒有設(shè)置數(shù)據(jù))
m_BMFileHeader.bfReserved2 = 00 00? (系統(tǒng)保留,沒有設(shè)置數(shù)據(jù))
m_BMFileHeader.bfOffBits = 36 04 00 00 = &H00000436 = 1078 =14 + 40 + 256 * 4
???
(從文件開始處起,帶下劃線的數(shù)據(jù)位的以字節(jié)為單位的長度,總共1078個(gè)字節(jié)。對真彩色圖象來說,這個(gè)數(shù)據(jù)經(jīng)常是36 00 00
00,即54。說明文件頭只有BITMAPFILEHEADER(14個(gè)字節(jié)) +
BITMAPINFOHEADER(40個(gè)字節(jié))的部分,即沒有RGBQUAD部分。為什么沒有RGBQUAD部分也可以?稍后你就明白怎么一回事。繼續(xù)
往下看,繼續(xù))
??? 與此結(jié)構(gòu)緊連在一起的下一個(gè)叫BITMAPINFO的結(jié)構(gòu),其組成如下表所示∶
????????????????? BITMAPINFO結(jié)構(gòu)(表二)
結(jié)構(gòu)內(nèi)各字段?????????????????? 數(shù)據(jù)類型???????????????????????????? ?說明
bmiHeader???????????? ?BITMAPINFOHEADER????? 一個(gè) BITMAPINFOHEADER
bmiColors??????????????? RGBQUAD????????????????????? 一個(gè) RGBQUAD結(jié)構(gòu)組成的數(shù)組
?????
從此表中可以看出,結(jié)構(gòu)內(nèi)部還存在兩個(gè)結(jié)構(gòu),即BITMAPINFOHEADER和RGBQUAD。因此,從某種意義上講,位圖文件的文件頭結(jié)構(gòu)可以說為
三個(gè)結(jié)構(gòu)來構(gòu)成的: BITMAPFILEHEADER
、BITMAPINFOHEADER和RGBQUAD。但,必須認(rèn)清的是BITMAPINFO結(jié)構(gòu)并不是離開BITMAPINFOHEADER和
RGBQUAD這兩個(gè)結(jié)構(gòu)而獨(dú)立存在的。BITMAPINFOHEADER結(jié)構(gòu)的長度是固定著的,為40個(gè)字節(jié)。BITMAPINFOHEADER結(jié)構(gòu)的
有關(guān)說明,可參考如下表∶
???????????? BITMAPINFOHEADER結(jié)構(gòu)(表三)
結(jié)構(gòu)內(nèi)各字段????????????????????? 數(shù)據(jù)類型????????????????? ?說明
biSize????????????????????????????? Long?????????????????? 結(jié)構(gòu)長度(40)
biWidth???????????????????????????Long????????????????? ?指定位圖的寬度,以像素為單位
biHeight????????????????????????? Long?????????????????? 指定位圖的高度,以像素為單位
biPlanes??????????????????????? ?Integer????????????????指定目標(biāo)設(shè)備的級數(shù)(必須為 1 )
biBitCount????????????????????? Integer????????????????每一個(gè)像素的位(1,4,8,16,24,32)
biCompression????????????? ?Long??????????????????? 指定壓縮類型(BI_RGB 為不壓縮)
biSizeImage?????????????????? Long??????????????????? 指定圖象的大小,以字節(jié)為單位
biXPelsPerMeter???????????? Long?????????????????? 指定設(shè)備水平分辨率,以每米的像素為單位
biYPelsPerMeter???????????? Long?????????????????? 垂直分辨率,其他同上
biClrUsed?????????????????????? Long?????????????????? 在顏色表中實(shí)際使用的色彩索引的個(gè)數(shù),用O表示全要使用
biClrImportant?????????????? Long?????????????????? 指定認(rèn)為重要的顏色索引個(gè)數(shù),用 0 表示所有顏色均重要
???? BITMAPINFOHEADER結(jié)構(gòu)主要記載了數(shù)據(jù)區(qū)的大小及顏色信息。必須認(rèn)清的是,
biSizeImage字段說明的是圖象的大小,也就是數(shù)據(jù)部分的大小,而不是文件的大小。如果我們只把位圖的數(shù)據(jù)部分讀入到內(nèi)存(而把結(jié)果部分讀入到數(shù)組),就需要按此大小來申請內(nèi)存大小。為了便于對照,我把上面給出的示范數(shù)據(jù)在這里重貼一下了∶
0EAE:0100??????????????????????????????????????????????? 28 00?? BM.x......6...(.
0EAE:0110? 00 00 69 01 00 00 06 01-00 00 01 00 08 00 00 00?? ..i.............
0EAE:0120? 00 00 88 74 01 00 C4 0E-00 00 C4 0E 00 00 00 00?? ...t............
0EAE:0130? 00 00 00 00 00 00??????????????????????????????????? ................
??? 分析說明如下∶
biSize = 28 00 00 00 = &H28 = 40
此結(jié)構(gòu)的長度,總共40個(gè)字節(jié)。總是這個(gè)數(shù)據(jù)。
?biWidth = 69 01 00 00 = &H169 = 361
?圖象的寬度(像素質(zhì)為單位)。把此圖片放到PictureBox控件,并運(yùn)行如下命令∶
?Text1.Text = Picture1.ScaleX(Picture1.Picture.Width, vbHimetric, vbPixels)
?結(jié)果為∶360.9827。可以看出VB的計(jì)算存在一點(diǎn)誤差。但如果你用整形變量來讀取這個(gè)數(shù)據(jù),那么就一樣了。
biHeigh = 06 01 00 00 = &H106 = 262
圖象的高度,請參照bitWidth .
biPlanes = 01 00 = &H1 = 1???
必須為1。
biBitCount? = 08 00 = &H8 = 8
每
個(gè)像素的位。當(dāng)前我例舉的這個(gè)叫hua.bmp的位圖文件是以8個(gè)位來表示一個(gè)像素的,即一個(gè)字節(jié)。一個(gè)256(2的8次方)色位圖能顯示的顏色總數(shù)限定
在256種顏色,但這并不意味著任何256色位圖都只能顯示相同的256種顏色。一個(gè)256色位圖所能夠顯示的256種顏色被規(guī)定在該位圖的
RGBQUAD結(jié)構(gòu)中。256色位圖所能顯示的顏色范圍,能夠達(dá)到設(shè)備所允許的范圍(16位增強(qiáng)模式中可能的顏色數(shù)為2的16次方,24位真彩模式中可能
的顏色數(shù)為2
的24次方),但必須是其中的256個(gè)顏色。RGBQUAD結(jié)構(gòu)所描述的顏色值用來產(chǎn)生調(diào)色板。在這種情況下數(shù)據(jù)區(qū)內(nèi)存放的是對這
256種顏色的索引值,而不是實(shí)際顏色。當(dāng)用1位來表示一個(gè)像素的時(shí)候,RGBQUAD的數(shù)組個(gè)數(shù)為2,存放著黑色和白色兩個(gè)顏色,而數(shù)據(jù)區(qū)存放著1和0
兩個(gè)索引值;當(dāng)用4位來表示一個(gè)像素的時(shí)候,則RGBQUAD的數(shù)組個(gè)數(shù)為16,存放著16種顏色,數(shù)據(jù)區(qū)存放著0至15的索引號;當(dāng)8位的時(shí)候存放
256種顏色,數(shù)據(jù)區(qū)存放著0至255的索引號;而當(dāng)像素位超過或等于16位的時(shí)候,RGBQUAD結(jié)構(gòu)部分不存在,數(shù)據(jù)區(qū)存放著以16位或24位表示的
實(shí)際顏色。在這種時(shí)候就不存在調(diào)色板了。其中的原因一想就知道,難道我們要設(shè)上萬種的調(diào)色板項(xiàng)?
biCopression = 00 00 00 00 = &H0 = 0
指定壓縮類型。不是說BI_RGB 為不壓縮嗎?讓我查查,BI_RGB究竟多少?
來嘍! Const BI_RGB& = 0& ,呵呵,原來這種文件叫做沒有壓縮。
還有∶Const BI_RLE4& = 2&?? 和 Const BI_RLE8& = 1&?? 估計(jì)沒有什么用處,想壓縮就采用JPG吧,何必用BMP ?
biSizeImage = 88 74 01 00 = &H17488 = 95368
圖象數(shù)據(jù)的長度(大小)。從數(shù)據(jù)關(guān)系上來講,應(yīng)該是∶圖象數(shù)據(jù)的長度+文件頭長度=文件長度。讓我們驗(yàn)證一下,是否是這樣∶
biSizeImage + m_BMFileHeader.bfOffBits
= 95368 + 1078 = 96446 = m_BMFileHeader.bfSize
@@~果然如此!
???
biXPelsPerMeter = C4 0E 00 00 = &HEC4 = 3780
biYPelsPerMeter = C4 0E 00 00 = &HEC4 = 3780
水平分辨率和垂直分辨率。
ibClrUsed = 00 00 00 00 = &H0 = 0
顏色表(調(diào)色板)中使用的索引個(gè)數(shù)。既然是0,說明全要使用了。全就是256個(gè),再多了不可能。你千萬不要用此數(shù)據(jù)來判斷RGBQUAD數(shù)組的個(gè)數(shù)。
biClrImportant = 00 00 00 00 = &H0 = 0
指定認(rèn)為重要的顏色索引個(gè)數(shù),用 0 表示所有顏色均重要。請大家多多去選用0吧,這樣才能有利于團(tuán)結(jié)。
??? 與BITMAPINFOHEADER 結(jié)構(gòu)不同RGBQUAD是一個(gè)數(shù)組,其長度(RGBQUAD結(jié)構(gòu)長度×數(shù)組元素個(gè)數(shù))可以是多種情況,但不能超過256個(gè)。RGBQUAD結(jié)構(gòu)如表所示∶
RGBQUAD結(jié)構(gòu)(表四)
結(jié)構(gòu)內(nèi)各字段?數(shù)據(jù)類型?說明
rgbBlue?Byte?指定彩色中藍(lán)色成分的多少
rgbGreen?Byte?指定彩色中綠色成分的多少
rgbRed?Byte?指定彩色中紅色成分的多少
rgbReserved?Byte?保留,可設(shè)為0
0EAE:0130????????????????????? 00 00-00 00 80 80 80 00 00 00?? ................
0EAE:0140? 80 00 00 80 80 00 00 80-00 00 80 80 00 00 80 00?? ................
0EAE:0150? 00 00 80 00 80 00 40 80-80 00 40 40 00 00 FF 80?? ......@...@@....
0EAE:0160? 00 00 80 40 00 00 FF 00-40 00 00 40 80 00 FF FF?? ...@....@..@....
以下省略(還很長)
您大概已經(jīng)注意到了,我為什么用紅色標(biāo)示了一些 00 呢?原因是因?yàn)樗鼈兌际前装V,只占地方,不起作用,是微軟公司保留的RGBQUAD結(jié)構(gòu)中的rgbReserved字段。每個(gè) 00
和 00 和之間有三個(gè)字節(jié),當(dāng)然,他們分別表示RGB顏色的了。這樣顏色表中索引號為0的顏色應(yīng)當(dāng)是等于RGB(&H0,&H0,&H0),索引號為1的顏色應(yīng)當(dāng)是等于RGB(&H80,&H80,
&H80),索引號為2的顏色那就是等于RGB(&H0,&H0,&H80),依次類推,至到索引號為255的那個(gè)顏色。
OKay!現(xiàn)在已經(jīng)把文件頭講得夠細(xì)的了,不能再細(xì),再細(xì)了就沒戲了。接著看一下數(shù)據(jù)部分。
0EAE:0530? ED 00 56 82 BD 00 B3 66-B3 B3 6C B3 BD 8B C9 C9?? ..V....f..l.....
0EAE:0540? BD 50 21 60 6C 6C 44 44-B3 44 6C 86 44 66 44 D9?? .P!`llDD.Dl.DfD.
以下省略
我們可以看到第一個(gè)像素點(diǎn)的數(shù)據(jù)是B3,即179,說
明此顏色相當(dāng)與顏色表中的索引號為237的那個(gè)顏色。那么那個(gè)顏色究竟什么顏色呢?糟糕,給我省略了,沒有列出來。來找一個(gè)沒有省略的吧,看看有沒有。好
象沒有。算了,就到這里吧。其實(shí)找也是那么一回事。不過我還是告訴你吧,因?yàn)槟阕约赫业脑捒赡苷也坏健T蚴恰眯惺且阅嫘虼娣诺摹?br />也就是說,位圖
文件的像素?cái)?shù)據(jù)部分中的第一行實(shí)際上是圖象中的最后一行。Dan說∶除非將BITMAPINFOHEADER 結(jié)構(gòu)的 biHeight
段設(shè)為負(fù)值,否則起點(diǎn)就位于左下角。但大多情況下biHeight并非負(fù)值,所以起點(diǎn)一般也就在左下角了。但這并不是說掃描行的起點(diǎn)等于圖象的高度,起點(diǎn)
還是等于0,終點(diǎn)才等于高度。也就是說掃描行的編號是從最后一行開始順序排列為 0,1,2,3 的。
現(xiàn)在,我們已經(jīng)完成了對一個(gè)位圖文件的全面分析。如果
還有需要講的話,那么有兩點(diǎn)。第一點(diǎn)是關(guān)于真彩色位圖。已經(jīng)在上面提到過,真彩色位圖是沒有顏色表的。這種圖的數(shù)據(jù)部分存放的是實(shí)際的RGB顏色。那么如
何知道沒有顏色表呢?其實(shí)很簡單,好象上面也已經(jīng)講到了,m_BMFileHeader.bfOffBits? 將等于
&H36,即54。還有biBitCount
會(huì)是大于等于16位。
第二點(diǎn),是一個(gè)很討厭的規(guī)定。我們把位圖中的一行數(shù)據(jù)信息叫做掃描線。Windows
規(guī)定,每一個(gè)掃描線必須結(jié)束與一個(gè)32位的邊界。也就是說,一個(gè)掃描線的位長度(按位計(jì)算)必須整除于32,或字節(jié)長度必須整除于4 。也就是說如果只有8位,那么拿空白的24
位來補(bǔ)充,如果只有48位,那么也就拿空白的16位來補(bǔ)充。我們可以證實(shí)一下∶
在剛才的圖片中,我們已經(jīng)知道圖象的寬度為361個(gè)像
素點(diǎn),高度為262個(gè)像素點(diǎn)。那么數(shù)據(jù)區(qū)的總的像素點(diǎn)個(gè)數(shù)為361×262=94582。本例中一個(gè)字節(jié)代表一個(gè)像素,但字節(jié)個(gè)數(shù)并不等于94582。正
如已經(jīng)看到的那樣,是為biSizeImage =
95368。還缺少786個(gè)字節(jié)。這說明一些位給補(bǔ)上了。總共是262行,因此說明每行補(bǔ)充了786÷262=3個(gè)字節(jié),即24位。為什么每行補(bǔ)充了24
位呢?現(xiàn)在看來只有一種可能。也就是,32位等于是4個(gè)字節(jié),因此一行361個(gè)字節(jié)除以4以后,必須剩下1,Windows才補(bǔ)充了不足的3
個(gè)字節(jié)。呵呵,361 MOD 4 = 1 @@~
計(jì)算DIB各行中的字節(jié)數(shù)(掃描線長度)的VB代碼總結(jié)如下∶
imageWidth? -->位圖的寬度(像素為單位)
?bmBits????? -->每像素的位個(gè)數(shù)
Function GetLineWidth(bmBits As Integer, imageWidth As Long) As Long
?? GetLineWidth = imageWidth
?? Select Case bmBits
????? Case 1
????????? GetLineWidth = (GetLineWidth + 7) / 8
????? Case 4
????????? GetLineWidth = (GetLineWidth + 1) / 2
????? Case 8
????? Case 16
????????? GetLineWidth = GetLineWidth * 2
????? Case 24
????????? GetLineWidth = GetLineWidth * 3
????? Case 32
????????? GetLineWidth = GetLineWidth * 4
????? Case Else
????????? GetLineWidth = 0???? 'error
????????? Exit Function
?? End Select
????? If (GetLineWidth And 3&) > 0 Then _
????????? GetLineWidth = (GetLineWidth And &HFFFFFFFC) + 4
End Function
以下給出了與設(shè)備無關(guān)位圖有關(guān)的函數(shù)
函??? 數(shù)????????????????????????????????????? 說??? 明
CreateDIBitmap??????????? 在一幅DIB的基礎(chǔ)上創(chuàng)建與設(shè)備有關(guān)位圖
CreateDIBSection???????? 創(chuàng)建一個(gè)DIBSection對象(一種新的位圖對象,和普通設(shè)備相關(guān)位圖相似,但也能在內(nèi)存中指定一個(gè)緩沖區(qū),用以保存DIB格式的位圖數(shù)據(jù)。具體內(nèi)容已超出本講座的范圍,讀者自己參考有關(guān)資料)
CreateDIBColorTalbe?? 為DIBSection對象取得顏色表信息。
GetDIBits???????????????????? 在與設(shè)備無關(guān)位圖里載如來自一幅與設(shè)備有關(guān)位圖的數(shù)據(jù)。很不錯(cuò)的函數(shù)。
SetDIBColorTalbe??????? 為DIBSection設(shè)置顏色表信息。
SetDIBits???????????????????? 在與設(shè)備相關(guān)位圖里用來自一個(gè)DIB的數(shù)據(jù)設(shè)置圖象。
SetDIBitsToDevice?????? 將來自DIB的數(shù)據(jù)直接設(shè)到一個(gè)設(shè)備。可用它將數(shù)據(jù)從DIB直接傳給屏幕或打印機(jī)。
StretchDIBits?????????????? 將來自DIB的數(shù)據(jù)設(shè)到設(shè)備場景,同時(shí)根據(jù)需要伸縮圖象。有些設(shè)備支持用這個(gè)函數(shù)將數(shù)據(jù)直接設(shè)到輸出設(shè)備。可用GetDeviceCaps函數(shù)判斷具體是否支持。常用函數(shù)之一。
?????? 好了,大家稍微休息以下,下一節(jié)我們學(xué)習(xí)與設(shè)備有關(guān)的位圖。
三、與設(shè)備有關(guān)的位圖(DDB)
?????
與設(shè)備有關(guān)的位圖,相對與與設(shè)備無關(guān)位圖來講,比較簡單。只所以簡單是因?yàn)槲覀兏緵]有必要去學(xué)習(xí)它的結(jié)構(gòu)。因?yàn)椋c設(shè)備有關(guān)的位圖的格式總是與其設(shè)備場
景(順便說一下,很多書把設(shè)備場景叫做“設(shè)備上下文”)有關(guān)。但2色位圖除外。如果你正坐在計(jì)算機(jī)前,不要認(rèn)為計(jì)算機(jī)中只存在一種與設(shè)備有關(guān)的位圖,其實(shí)
不然。所謂設(shè)備并不是指一臺計(jì)算機(jī),內(nèi)存,顯示緩存,打印機(jī),等都有各自的位圖數(shù)據(jù)格式。不要對與設(shè)備有關(guān)的位圖進(jìn)行對其結(jié)構(gòu)的任何猜測。你見過我嗎?沒
有吧?但你是否覺得與我交往仍然是很有效呢?我們可以通過Email,因特網(wǎng)進(jìn)行交流。你就把我理解為一個(gè)人,一個(gè)遠(yuǎn)方的朋友就可以了,是不是?對與設(shè)備
有關(guān)的位圖也就這樣想就可以了。它的確是存在著的,就在計(jì)算機(jī)的某個(gè)設(shè)備內(nèi)部(比如內(nèi)存,打印機(jī))。當(dāng)然我們不能通過Email跟它交往,而是要靠函數(shù),
范圍包括創(chuàng)建,獲取信息,設(shè)置信息,銷毀等等。用慣了你會(huì)發(fā)現(xiàn),這也很得勁兒,其難度也就相當(dāng)與給我發(fā)一個(gè)E-Mail──
填寫需要的地址,需要的標(biāo)題和內(nèi)容,然后發(fā)送。當(dāng)然,這里要做的是為一個(gè)函數(shù)的調(diào)用為其添寫需要的參數(shù)。如果你真的想見它一面,我倒有個(gè)方法,是家傳秘方
呦!好好聽,你可以拿錘子把你的計(jì)算機(jī)用力,請記住一定要用力,用力砸一下,然后打開機(jī)箱看看它出來了沒有,一般成功率能達(dá)到99%。但由于存在那1%的
失敗的可能,我得先聲明如果見不到后果自負(fù)∶)
?????
使用與設(shè)備無關(guān)位圖的過程當(dāng)中始終不能忘記的一點(diǎn)是,確保位圖與設(shè)備的兼容性。你不能把一個(gè)內(nèi)存設(shè)備場景中的位圖直接選入到打印機(jī)設(shè)備場景,會(huì)出現(xiàn)異常或
錯(cuò)誤。這里所說的設(shè)備其實(shí)就是指設(shè)備場景。為了創(chuàng)建一個(gè)與指定設(shè)備場景兼容的設(shè)備相關(guān)位圖,可以使用CreateBitmap或者
CreateCompatibleBitmap。但兩個(gè)函數(shù)的功能是不同的。我的經(jīng)驗(yàn)是,在創(chuàng)建一個(gè)Mask(掩模位圖)圖象的時(shí)候使用
CreateBitmap,而創(chuàng)建一個(gè)彩色位圖的時(shí)候使用CreateCompatibleBitmap。還有幾個(gè)函數(shù),如
CreateDIBitmap,它可以根據(jù)一個(gè)DIB(設(shè)備無關(guān)位圖)的基礎(chǔ)上創(chuàng)建一個(gè)與設(shè)備相關(guān)位圖。反正,你應(yīng)該注意到,幾乎所有的與設(shè)備相關(guān)位圖有
關(guān)的API函數(shù)都具有一個(gè)hDC參數(shù),有時(shí)候看起來沒有必要的,但應(yīng)當(dāng)清楚這主要是為了保證設(shè)備的兼容性而給出的。說明與設(shè)備有關(guān)位圖的有些信息是記錄在
設(shè)備場景中的。
?????
作為GDI對象,設(shè)備相關(guān)位圖被創(chuàng)建后,需要把它選入到設(shè)備場景,這和向設(shè)備場景選入一個(gè)畫筆完全一樣,可以用SelectObject函數(shù),只是在先前
要寫畫筆句柄的參數(shù)位置上寫下位圖句柄即可。當(dāng)你不再使用一個(gè)被你創(chuàng)建的位圖的時(shí)候,應(yīng)當(dāng)用DeleteObject函數(shù)刪除之,以釋放系統(tǒng)資源。為了刪
除它,你還需要用SelectObject函數(shù)把原來的位圖選回到設(shè)備場景,這樣你為設(shè)備場景選入過的位圖就退了回來,處于可以刪除或重新被選入的狀態(tài)。
這需要你在為設(shè)備場景選入一個(gè)你自己創(chuàng)建的位圖時(shí)保存舊的(原先的)位圖句柄。反正這和操縱畫筆或畫刷之類的GDI對象完全一樣。同樣,不可將一個(gè)位圖同
時(shí)選入兩個(gè)不同的設(shè)備場景。
?????
創(chuàng)建和使用位圖的常用技巧是,創(chuàng)建一個(gè)DC或多個(gè)DC以及相應(yīng)的位圖,然后在后臺(用戶看不到)進(jìn)行各種光柵運(yùn)算和加工處理后,最后把形成的圖象一次性發(fā)
送到關(guān)聯(lián)設(shè)備場景中(關(guān)聯(lián)設(shè)備場景中的圖象會(huì)自動(dòng)影射到視頻內(nèi)存,如PictureBox和hDC所代表的內(nèi)存。詳細(xì)情況請參考前期教程設(shè)備場景部分)中
(常用BitBlt),以獲取速度和最大限度地避免屏幕閃爍(后面給出了例子)。這主要是因?yàn)橄蚱聊惠敵鰣D象的函數(shù)執(zhí)行速度比較慢,而且圖象的加工過程對
用戶來說沒有必要看到的。為了創(chuàng)建若干個(gè)設(shè)備場景,你需要多次用CreateCompatibleDc函數(shù)(這也是最簡單的方法),它可以使你獲得一個(gè)與
指定設(shè)備場景兼容的設(shè)備場景。反正你應(yīng)該保證創(chuàng)建位圖中所使用的hDC參數(shù)和創(chuàng)建設(shè)備場景中所使用的hDC參數(shù)保持一致。別忘了最后用DeleteDC來
刪除你自己創(chuàng)建的設(shè)備場景。在刪除設(shè)備場景前,應(yīng)當(dāng)從該設(shè)備場景中抽出為其選入的位圖,并將其銷毀。以上這種技巧在一本VC書上叫做“雙緩沖”,意識是把
操作分成前臺和后臺兩部分同時(shí)進(jìn)行。
????? 接下來給出與設(shè)備有關(guān)位圖的函數(shù)吧,請您不要客氣隨便看。
?????????????????????????????? 與設(shè)備有關(guān)位圖函數(shù)
函??? 數(shù)????????????????????????????????????????? ?說??? 明
CreateBitmap?????????????????????? 創(chuàng)建一幅位圖,并選擇性地初始化位圖數(shù)據(jù)
CreateBitmapIndirect?????????? 在一個(gè)BITMAP數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ)上創(chuàng)建一幅位圖
CreateCompatibleBitmap???? 創(chuàng)建與指定設(shè)備場景兼容的一幅位圖
GetBitmapBits????????????????????? 獲取位圖的像素位數(shù)據(jù)
GetBitmapDimensionEx??????? 取得一幅位圖的大小
LoadBitmap???????????????????????? 從資源文件中載入一幅固有的系統(tǒng)位圖
LoadImage????????????????????????? 一個(gè)常規(guī)用途的函數(shù),用于裝載圖象、圖標(biāo)及指針
SetBitmapBits????????????????????? 根據(jù)一個(gè)數(shù)據(jù)緩沖區(qū)設(shè)置位圖圖象。使用GetDIBits吧,更好用。
SetBitmapDimensionEx??????? 設(shè)置位圖的大小。??????????????????
????????????????????????? 位圖傳輸函數(shù)
函??? 數(shù)??????????????????????????????? 說??? 明
BitBlt????????????????????? ?位塊傳輸。將一個(gè)圖象區(qū)域傳到另一個(gè)圖象區(qū)域。必須掌握到熟背。
PatBlt????????????????????? 圖案塊傳輸。根據(jù)指定圖案(就象由一個(gè)刷子表示的那樣)填充一個(gè)圖象區(qū)域。
GetStretchBltMode? 進(jìn)行伸縮處理時(shí),用于判斷Windows刪除線段或像素方式。
MaskBlt?????????????????? 執(zhí)行復(fù)雜的圖象傳輸,同時(shí)進(jìn)行掩模(MASK)處理
PlgBlt????????????????????? 平行四邊形塊傳輸。允許我們伸縮、扭曲及放置一幅圖象。這個(gè)函數(shù)我在Windows95中用過,可始終沒有成功。功能看起來很不錯(cuò)。
SetStretchBltMode? 進(jìn)行伸縮處理時(shí),用于決定Windows刪除線段或像素的方式。
StretchBlt??????????????? 將一幅位圖從一個(gè)設(shè)備場景復(fù)制到另一個(gè)。源和目標(biāo)DC相互間必須兼容。這個(gè)函數(shù)會(huì)在設(shè)備場景中定義一個(gè)目標(biāo)矩形,并在位圖中定義一個(gè)源圖象。源矩形會(huì)根據(jù)需要進(jìn)行伸縮,以便與目標(biāo)矩形的大小相符。
????
DIB并不存在于設(shè)備場景中(device
context)。在它被選入設(shè)備場景或在它被畫入設(shè)備場景前,DIB必須被翻譯為DDB。你可以用StretchDIBits函數(shù)完成這一工作。本教程
提供了一個(gè)源代碼例程。通過它你能夠?qū)W習(xí)DDB到DIB,再從DIB到DDB的轉(zhuǎn)化過程。程序名為vbplay46.vbp。實(shí)際上是源碼解析中的演示程
序第46號。
?????
為了使用好位塊傳輸,你需要掌握圖形光柵方面的知識。其內(nèi)容和上一期給出的位操作運(yùn)算在其原理上相同。只是需要進(jìn)行的并不是一個(gè)、兩個(gè)或四個(gè)直接等的數(shù)
據(jù),而是一個(gè)位圖數(shù)據(jù)。位圖數(shù)據(jù)也是以字節(jié)來構(gòu)成的,只是長度要長得多。為了使用光柵運(yùn)算,你需要有一個(gè)自己的表格,以便從中查找并計(jì)劃光柵方案。以下是
Dan的書中的,也是我最喜歡的一個(gè)表(詳細(xì)的中文說明格式的表不如它好用)∶
SRCCOPY?????? Destination = Source
SRCPAINT????? Destination = Source OR Destination
SRCAND??????? Destination = Source AND Destination
SRCINVERT???? Destination = Source XOR Destination
SRCERASE????? Destination = Source AND (NOT Destination)
NOTSRCCOPY??? Destination = NOT Source
NOTSRCERASE?? Destination = (NOT Source) AND (NOT Destination)
MERGECOPY???? Destination = Source AND Pattern
MERGEPAINT??? Destination = (NOT Source) OR Destination
PATCOPY?????? Destination = Pattern
PATPAINT????? Destination = (NOT Source) OR Pattern OR Destination
PATINVERT???? Destination = Pattern XOR Destination
DSTINVERT???? Destination = NOT Destination
BLACKNESS???? Destination = 0
WHITENESS???? Destination = All bits set to 1
?????? 可惜,少一個(gè)很重要的。可別擔(dān)心,本老師有辦法∶
&H220326???? Destination=(NOT Source) AND Detination
注∶以上,Destination指的是目標(biāo)位圖(BitBlt中前一個(gè)DC中的),Source是來源位圖(BitBlt中的后一個(gè)DC中的)。
???????以下給出一個(gè)例子,是我常用的一個(gè)函數(shù)。它用來實(shí)現(xiàn)透明復(fù)制位圖。你可以在你的程序中直接粘貼使用。它總結(jié)了本節(jié)給出的內(nèi)容。
作者(xing)? 1999年10月17日 整理備用
功能∶
? 透明復(fù)制位圖
參數(shù)表∶
? hDestDC --------? Long,目標(biāo)設(shè)備場景
? x,y ------------? Long,對目標(biāo)DC中目標(biāo)矩形左上角位置進(jìn)行描述的那個(gè)點(diǎn)。用目標(biāo)DC的邏輯坐標(biāo)表示
? nWidth,nHeight -? Long,欲傳輸圖象的寬度和高度
? hSrcDC ---------? Long,源設(shè)備場景。如光柵運(yùn)算未指定源,則應(yīng)設(shè)為0
? xSrc,ySrc ------? Long,對源DC中源矩形左上角位置進(jìn)行描述的那個(gè)點(diǎn)。用源DC的邏輯坐標(biāo)表示
? TransColor -----? OLE_COLOR,被透明處理的顏色
Sub TransBlt(ByVal
hDestDC As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As
Long,???????????????? ByVal nHeight As Long, ByVal hSrcDC As Long,
ByVal xSrc As Long, ByVal ySrc As Long, ByVal TransColor As OLE_COLOR)
?????? Dim dl????????????? As Long
??????
?????? Dim OrigColor?????? As Long
?????? Dim OrigMode??????? As Long
??????
?????? Dim saveDC????????? As Long
?????? Dim maskDC????????? As Long
?????? Dim invDC?????????? As Long
?????? Dim resultDC??????? As Long
??????
?????? Dim hSaveBmp??????? As Long
?????? Dim hMaskBmp??????? As Long
?????? Dim hInvBmp???????? As Long
?????? Dim hResultBmp????? As Long
??????
?????? Dim hSavePrevBmp??? As Long
?????? Dim hMaskPrevBmp??? As Long
?????? Dim hInvPrevBmp???? As Long
?????? Dim hDestPrevBmp??? As Long
??????
?????? saveDC = CreateCompatibleDC(hDestDC)
?????? maskDC = CreateCompatibleDC(hDestDC)
?????? invDC = CreateCompatibleDC(hDestDC)
?????? resultDC = CreateCompatibleDC(hDestDC)
??????
?????? '按照規(guī)定的格式創(chuàng)建一幅與設(shè)備有關(guān)位圖
?????? hMaskBmp = CreateBitmap(nWidth, nHeight, 1, 1, ByVal 0&)
?????? hInvBmp = CreateBitmap(nWidth, nHeight, 1, 1, ByVal 0&)
???
?????? '創(chuàng)建一幅與設(shè)備有關(guān)位圖
?????? hResultBmp = CreateCompatibleBitmap(hDestDC, nWidth, nHeight)
?????? hSaveBmp = CreateCompatibleBitmap(hDestDC, nWidth, nHeight)
??????
?????? hSavePrevBmp = SelectObject(saveDC, hSaveBmp)
?????? hMaskPrevBmp = SelectObject(maskDC, hMaskBmp)
?????? hInvPrevBmp = SelectObject(invDC, hInvBmp)
?????? hDestPrevBmp = SelectObject(resultDC, hResultBmp)
??????
?????? '產(chǎn)生Mask圖象
?????? OrigColor = SetBkColor(hSrcDC, TransColor)
?????? dl& = BitBlt(maskDC, 0, 0, nWidth, nHeight, hSrcDC, xSrc, ySrc, vbSrcCopy)??????
?????? TransColor = SetBkColor(hSrcDC, OrigColor)
?????? 'invDC的圖象將與maskDC圖象相反
?????? dl& = BitBlt(invDC, 0, 0, nWidth, nHeight, maskDC, 0, 0, vbNotSrcCopy)
?????? 'resultDC的圖象將成為被寫位置的圖象
?????? dl& = BitBlt(resultDC, 0, 0, nWidth, nHeight, hDestDC, x, y, vbSrcCopy)
?????? 'resultDC中,需要新寫的位置將變?yōu)楹谏?/font>
?????? dl& = BitBlt(resultDC, 0, 0, nWidth, nHeight, maskDC, 0, 0, vbSrcAnd)
?????? dl& = BitBlt(saveDC, 0, 0, nWidth, nHeight, hSrcDC, xSrc, ySrc, vbSrcCopy)
?????? 'resultDC中不被寫入的顏色成為黑色
?????? dl& = BitBlt(saveDC, 0, 0, nWidth, nHeight, invDC, 0, 0, vbSrcAnd)
?????? '將兩幅圖合并起來
?????? dl& = BitBlt(resultDC, 0, 0, nWidth, nHeight, saveDC, 0, 0, vbSrcInvert)
?????? '完工后輸出
?????? dl& = BitBlt(hDestDC, x, y, nWidth, nHeight, resultDC, 0, 0, vbSrcCopy)
??????
?????? SelectObject saveDC, hSavePrevBmp
?????? SelectObject resultDC, hDestPrevBmp
?????? SelectObject maskDC, hMaskPrevBmp
?????? SelectObject invDC, hInvPrevBmp
??????
?????? DeleteObject hSaveBmp
?????? DeleteObject hMaskBmp
?????? DeleteObject hInvBmp
?????? DeleteObject hResultBmp
??????
?????? DeleteDC saveDC
?????? DeleteDC maskDC
?????? DeleteDC invDC
?????? DeleteDC resultDC
End Sub
函數(shù)需要的API
Private Declare Function
BitBlt& Lib "gdi32" (ByVal hDestDC As Long, ByVal x As Long,???????
ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal
hSrcDC As Long,???????? ByVal xSrc As Long, ByVal ySrc As Long, ByVal
dwRop As Long)
Private Declare Function
CreateCompatibleBitmap& Lib "gdi32" (ByVal hdc As Long,???????
ByVal nWidth As Long, ByVal nHeight As Long)
Private Declare Function CreateCompatibleDC& Lib "gdi32" (ByVal hdc As Long)
Private Declare Function DeleteObject& Lib "gdi32" (ByVal hObject As Long)
Private Declare Function SelectObject& Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long)
Private Declare Function SetBkColor& Lib "gdi32" (ByVal hdc As Long, ByVal crColor As Long)
Private Declare Function
CreateBitmap& Lib "gdi32" ( ByVal nWidth As Long, ByVal nHeight As
Long, ByVal nPlanes As Long, ByVal nBitCount As Long, lpBits As Any)??
?四、關(guān)于ImageIo.dll
?????
在前一段時(shí)間,有很多朋友來信問我如何把BMP圖象或圖片框中的圖象保存為JPG或GIF格式文件。我還沒有回答過一次。因?yàn)槲沂冀K沒有找到稱心的DLL
(動(dòng)態(tài)連接庫)。最近3個(gè)月,我一直學(xué)了VC,也算是勉強(qiáng)入門。我發(fā)現(xiàn)VC界的很多人都在使用一個(gè)叫ImageLoad.dll的動(dòng)態(tài)連接庫來完成這些工
作。可惜,這個(gè)動(dòng)態(tài)連接庫到VB就不好使了。因?yàn)槠浜瘮?shù)使用了指針(而不是句柄)。然而,可但是,只要本老師在,你就可以用這個(gè)動(dòng)態(tài)連接庫了。本老師寫了
一個(gè)叫ImageIo.dll的動(dòng)態(tài)連接庫,用VC編寫的。此連接庫完全面對VB編寫的,自然參數(shù)上完全考慮了VB用戶。該連接庫可以把你在VB中傳送的
句柄適當(dāng)?shù)馗某芍羔槻⒃僬{(diào)用ImageLoad.dll這個(gè)動(dòng)態(tài)連接庫,然后把處理結(jié)果以VB能夠接受的方式返回給VB用戶。另外一點(diǎn)是,
ImageLoad.dll的函數(shù)比較低級的,用起來也不方面。為此,我在ImageIo.dll中寫了一些代碼,使得使用起來更加方便,更加可靠。
?????
然而,也出現(xiàn)了一個(gè)接著一個(gè)的讓我傷心的事情。第一個(gè)是,函數(shù)不能讀取部分GIF格式的文件(有些可以讀出,但不是動(dòng)畫,只有一張靜態(tài)畫面);另一個(gè)是無
法存成GIF格式,最后一點(diǎn)就古怪了,當(dāng)像素位不等于24的時(shí)候位圖文件不能存成JPG格式。該連接庫可以操縱6中格式文件,有BMP,GIF,JPG,
PCX,TGA,TIF。我對TGA和TIF沒有怎么考慮過,因?yàn)樵贕IF中出現(xiàn)一些問題后感到這個(gè)ImageIo.dll不能使其功能齊全了,而且
TGA和TIF也并不是常用文件,也就不大測試了。那么,是因?yàn)槲疫€不懂VC嗎?不,并不是我寫錯(cuò)了代碼,而是該動(dòng)態(tài)連接庫提供的演示程序同樣不能解決我
遇到的問題。據(jù)圖象處理方面的書上說,一個(gè)JPG庫代碼,需要一個(gè)人用半年的時(shí)間才能寫完。我總不能花半年去寫一個(gè)這種庫文件吧。其實(shí)我手里有一個(gè),而我
卻懶得去用它。
?????
我考慮了,你能拿ImageIo.dll能做什么?想編寫一個(gè)圖象處理軟件?如果你這樣想的話,那么我建議你不要用它,如果你真的想編一個(gè)圖象處理軟件,
最好還是自己從頭編寫一個(gè)完整的DLL文件。我想,算是出了著名的圖象處理軟件的公司都是如此。而且我建議你不要用VB來編寫這種軟件,不適合。我想,對
絕大部分VB屆的朋友來講,對JPG的需求無疑是將一些圖象文件壓縮起來,以節(jié)約磁盤空間。比如,你需要編寫一個(gè)人事檔案管理系統(tǒng),把人員照片掃描后保存
成JGP文件。那么我想,你用這個(gè)ImageIo.dll文件,算是很不錯(cuò)的。我曾經(jīng)在網(wǎng)絡(luò)上下載過一個(gè)DLL文件,但其壓縮質(zhì)量實(shí)在差,而
ImageLoad.dll卻做得很出色。
?? 你在本教程的打包文件中可以找到一個(gè)Test.vbp文件,正是對這個(gè)ImageIo.dll寫的。有關(guān)ImageIo.dll的技術(shù)文檔,現(xiàn)給出如下,歡迎提一些寶貴意見∶
ImageIo.dll
????
?本動(dòng)態(tài)連接庫是為了解決VB用戶不能使用ImageLoad.dll動(dòng)態(tài)連接庫文件而編寫的。它可以作為一個(gè)橋梁,使VB用戶能夠使用
ImageLoad.dll。但它并不是對ImageLoad.dll的簡單轉(zhuǎn)化,而是添加了諸多代碼,使其用起來更加簡單、方便。它可以幫助用戶完成
Bmp,Gif,Jpg 等多種圖象文件的數(shù)據(jù)的讀取和存盤工作。
制作者∶Xing
編程環(huán)境∶VC6.0
日期∶1999,12,20
常量∶
1∶文件格式
Const IMAGETYPE_NONE = 0???? '未知格式
Const IMAGETYPE_BMP = 1????? 'bmp 文件格式
Const IMAGETYPE_GIF = 2????? 'gif 文件格式
Const IMAGETYPE_PCX = 3????? 'pcx 文件格式
Const IMAGETYPE_TGA = 4????? 'tga 文件格式?
Const IMAGETYPE_JPG = 5????? 'jpg 文件格式
Const IMAGETYPE_TIF = 6????? 'tif 文件格式
Const IMAGETYPE_FIRSTTYPE = IMAGETYPE_BMP???? '第一個(gè)文件格式?? (bmp) = 1
Const IMAGETYPE_LASTTYPE = IMAGETYPE_TIF????? '最后一個(gè)文件格式 (tif) = 6
函數(shù)∶
GetImageType
VB∶
?? Private Declare Function GetImageType Lib "ImageIo.dll" (lpsFilename As String) As Long
VC∶
?? __API int GetImageType(LPTSTR & pszFilename);
說 明∶
??? 獲取圖形文件類型
返回值∶
??? 文件類型 0--6,0 表示出錯(cuò)或未知文件類型
參數(shù)∶
??? lpsFilename? String 文件名
ImageLoad
VB∶
??? Declare Function
ImageLoad Lib "ImageIo.dll" (lpsFilename As String, ByVal hDC As Long,
ByVal nX As Long, ByVal nY As Long) As Long
VC∶
?? _API HGLOBAL ImageLoad( LPTSTR & pszFilename,HDC hDC, int nX, int nY);
說明∶
??? 裝載一個(gè)圖象文件,并必要時(shí)輸出到指定的DC
返回∶
??? 返回指向裝有圖象文件數(shù)據(jù)的一個(gè)緩沖區(qū)的指針。不管你打開任何文件,裝載到緩沖區(qū)的數(shù)據(jù)都已經(jīng)是轉(zhuǎn)換為DIB格式,也就是說和BMP文件一樣。在不在使用該緩沖區(qū)的時(shí)候,應(yīng)該用API函數(shù)GlobalFree或者本動(dòng)態(tài)連接庫提供的KillImage函數(shù)釋放掉。
參數(shù)∶
KillImage:
VB∶
??? Declare Function KillImage Lib "ImageIo.dll" (ByVal hDib As Long) As Long
VC∶
??? __API HGLOBAL KillImage(HGLOBAL hDib);
說明∶
??? 本函數(shù)與API函數(shù)GlobalFree在其功能上完全一樣,當(dāng)然可以改用GlobalFree 。VC代碼如下∶
HGLOBAL CImageIoApp::KillImage(HGLOBAL hDib)
{
?return(GlobalFree(hDib));
}
注∶如果成功返回0,否則返回hDib。
ImageDraw:
VB∶
???
Declare Function ImageDraw Lib "ImageIo.dll" (ByVal hDib As Long, ByVal
hDC As Long, ByVal nX As Long, ByVal nY As Long) As Long
VC∶
??? __API BOOL ImageDraw(HGLOBAL hDib,HDC hDC, int nX, int nY );
說明∶
?? 將圖象輸出到指定DC。
返回值∶
?? 成功返回1,否則返回0。
參數(shù)∶
?? hDib 一個(gè)指向緩沖區(qū)的指針。該緩沖區(qū)中裝有DIB位圖數(shù)據(jù)。可以用LoadImage函數(shù)獲取它。也可以自己創(chuàng)建。
?? hDC 需要繪制圖象的設(shè)備常場景句柄
?? nX,nY 設(shè)備場景中被繪制圖象的左上角的坐標(biāo)。
GetInfo:
VB:
??? Declare Function GetInfo Lib "ImageIo.dll" (ByVal hDib As Long, BIH As BITMAPINFOHEADER) As Long
VC:
??? __API BOOL GetInfo(HGLOBAL hDib,BITMAPINFOHEADER & BIH);
說明∶
?? 本來是想用GetImageInfo作為函數(shù)名,但VC中用此名發(fā)生一些沖突,改用了GetInfo。此函數(shù)用于獲取DIB文件中的BITMAPINFOHEADER結(jié)構(gòu)信息。
返回值∶
?? 成功返回1,失敗返回0。
參數(shù)∶
?? hDib 一個(gè)指向緩沖區(qū)的指針。該緩沖區(qū)中裝有DIB位圖數(shù)據(jù)。
?? BIH 一個(gè)準(zhǔn)備裝載信息的BITMAPINFOHEADER結(jié)構(gòu)。傳遞前沒有必要設(shè)置其biSize(結(jié)構(gòu)長度)成員變量。
__API BOOL ImageSave(LPTSTR & pszFilename,HGLOBAL hDib,int nType,int nQuality);
只適用于BMP,JPG,TGA
五、圖表與指針
??????
圖表是一幅小位圖,圖表文件的常用附加名是ICO。它的一項(xiàng)特殊能力就是不僅允許任何像素使用某種位圖顏色,也允許它們使用屏幕或反轉(zhuǎn)的屏幕顏色。
Windows3.x中的圖表通常是32×32像素大小,但在Windows95(98)和NT4.0中,對16×16和64×64像素圖標(biāo)的支持已非常
普遍了。圖標(biāo)實(shí)際包含兩幅獨(dú)立位圖。第一幅位圖可能是單色的或彩色的,其中包含了要與顯示屏幕圖象合并到一起的一幅圖象。這種合并是通過異或(XOR)操
作實(shí)現(xiàn)的。我們將其稱為XOR位圖,其中包含了一個(gè)掩模。在顯示屏幕圖象與XOR位圖合并到一起之前,這個(gè)掩模會(huì)先與屏幕圖象進(jìn)行AND操作,然后才是
XOR位圖和屏幕進(jìn)行XOR。
????? 鼠標(biāo)指針在其內(nèi)部結(jié)構(gòu)上和圖表完全一致,只是它多出一個(gè)斎鵲銛,即該指針位圖中代表指針位置的準(zhǔn)確坐標(biāo)點(diǎn)。該文件的附加名一般是CUR。能對圖標(biāo)進(jìn)行操作的大部分函數(shù)也能對(鼠標(biāo))指針。
????? 圖標(biāo)被裝入到內(nèi)存之后將產(chǎn)生一個(gè)句柄。但需要注意的是,圖表并不屬于GDI對象,而是屬于USER對象。所以有可能在應(yīng)用程序間共享圖表資源。
??? 以下給出了有關(guān)圖標(biāo)和指針的API函數(shù)
函??? 數(shù)????????????????????????????? 說??? 明
CopyCursor???????????????? 復(fù)制指針,使用CopyIcon
CopyIcon???????????????????? 復(fù)制圖表。
CreateCursor????????????? 創(chuàng)建指針
CreateIcon???????????????? 創(chuàng)建圖表
CreateIconIndirect????? 在一個(gè)ICONINFO結(jié)構(gòu)的基礎(chǔ)上創(chuàng)建一個(gè)圖標(biāo)
DestroyCursor??????????? 清除指針
DestroyIcon?????????????? 清除圖表
DrawIcon??????????????????? 向指定設(shè)備場景繪制圖標(biāo)
DrawIconEx??????????????? 用附加的選項(xiàng)描繪圖標(biāo)
ExtractIcon???????????????? 從一個(gè)可執(zhí)行文件或DLL中載入圖標(biāo)
ExtractAssociatedIcon?載入指定文件內(nèi)部或與它相關(guān)的一個(gè)圖標(biāo)
GetIconInfo???????????????? 取得關(guān)于圖標(biāo)的信息
LoadCursor???????????????? 從一個(gè)資源文件中載入指針,或者裝載一個(gè)固有系統(tǒng)指針。
LoadCursorFromFile??? 通過讀取標(biāo)準(zhǔn)指針文件(.cur)或動(dòng)畫指針(.ani)創(chuàng)建指針
LoadIcon??????????????????? 從一個(gè)資源文件中載入圖表,或載入一個(gè)固有系統(tǒng)圖標(biāo)。
六、顏色區(qū)間??
????
RGB
????
我們已經(jīng)知道IBM兼容視頻系統(tǒng)上的顏色由RGB三色體現(xiàn)。RGB三色中的每一種顏色都包含顏色中的紅色、綠色和藍(lán)色成分的值。這三部分的值組合在一起確
定屏幕上所顯示的顏色。RGB是常見顏色空間,紅色、綠色和藍(lán)色被認(rèn)為是基本色,不能夠被進(jìn)一步的分解。而色彩系統(tǒng)可以分為兩類,加法色彩系統(tǒng)和減法色彩
系統(tǒng)。加法色彩系統(tǒng),例如RGB系統(tǒng)中的顏色可通過將顏色添加到黑色中創(chuàng)建新顏色。添加的顏色越多,結(jié)果顏色也就越趨向于白色。足夠的主要顏色可以創(chuàng)建出
純白色,而缺少所有主要顏色只能得出純黑色。
???
????
?一個(gè)RGB是用四個(gè)字節(jié)來構(gòu)成的。好象該字節(jié)中除了表示紅、綠、藍(lán)三個(gè)字節(jié)以外的剩余一個(gè)字節(jié)是多余的,其實(shí)不然。從一個(gè)API繪圖函數(shù)引用一種顏色,
都不只是用容納紅、綠、藍(lán)顏色值的三個(gè)字節(jié),而且還用第四個(gè)字節(jié)(最高字節(jié))。這個(gè)字節(jié)包含一個(gè)標(biāo)志值。順序排列的最低字節(jié)包含紅色值,其次兩個(gè)字節(jié)分別
是綠色和藍(lán)色,而最后位字節(jié)則包含一個(gè)標(biāo)志。此標(biāo)志用于指示是否引用一個(gè)高頻脈動(dòng)色,或者一個(gè)調(diào)色板匹配色,或者一個(gè)明顯的調(diào)色板索引。這個(gè)字節(jié)的值決定
順序排列的三個(gè)低位字節(jié)如何選擇一種顏色。當(dāng)你想指定一個(gè)建立這些對象的API函數(shù)中的筆或刷子的顏色時(shí),可以把高位字節(jié)設(shè)置為如下表中的三個(gè)值之一。
高字節(jié)值?????????? 結(jié)果
&H00????????????? 在對象繪出后,Windows高頻脈動(dòng)20個(gè)保留色,這叫RGB顏色引用。(普通情況)
&H01????????????? 最低字節(jié)不是紅色的值,而是指一個(gè)調(diào)色板項(xiàng)的數(shù)目或索引值,因此Windows使用在那個(gè)調(diào)色板項(xiàng)中的顏色,中間的兩個(gè)字節(jié)(字節(jié)1和2,原綠色和藍(lán)色字節(jié))應(yīng)保持值&H00,這叫調(diào)色板索引引用。
&H02????????????? Windows定位與由紅、綠、藍(lán)決定的在三個(gè)順序排列的低位字節(jié)指定的顏色最相匹配的調(diào)色板項(xiàng)。這叫作調(diào)色板RGB引用。
因此,嚴(yán)密地講,一個(gè)RGB值是通過以下方式構(gòu)成的。
Dim RGBColor As Long
RGBColor=RedValue + (GreenValue * 256) + (BlueValue? * 65536) + _
???????? (FlagValue * 16777216)
??? 這種技術(shù)的使用請參考本教程附帶的程序Palette.vbp。??
????? CMY
?????
在計(jì)算機(jī)領(lǐng)域還存在幾種RGB顏色區(qū)間的變種。一個(gè)是叫做CMY的顏色區(qū)間。指的是藍(lán)綠色、紫紅色以及黃色。打印機(jī)和照相機(jī)通過加墨乳劑渲染顏色時(shí),采用
的是減法色彩系統(tǒng)。它由將顏色色素沉淀到白色紙上的大部分硬拷貝設(shè)備使用,例如激光打印機(jī)和噴墨打印機(jī)。當(dāng)被現(xiàn)實(shí)時(shí),這三種顏色都吸收與自身互補(bǔ)的淺顏
色。藍(lán)綠色吸收紅色,紫紅色吸收綠色,黃色吸收藍(lán)色。例如,可增加黃色墨的量,圖象中的藍(lán)色量減少。RGB模式和
?????
CMY模式之間的轉(zhuǎn)換是非常簡單的。為了計(jì)算藍(lán)綠色,請從255中減去RGB中的紅色值;對于紫紅色,則從255中減去RGB中的綠色值;至于黃色嘛,
喔!你已經(jīng)猜測到了,即從255中減去RGB中的藍(lán)色值。例如,RGB(240,12,135)的CMY值分別是15,243和120。然而,這種顏色區(qū)
間大概并不是我們所感興趣的,當(dāng)然也有人感興趣的。但下面給出的顏色區(qū)間更吸引我們,起碼是我。
????? HSV(顏色,濃度,亮度)
?????
HSV是眾多色彩系統(tǒng)中的一個(gè)。如果你打開了通用對話框的顏色對話框家會(huì)發(fā)現(xiàn),除了RGB調(diào)解器,還有一個(gè)類似的但其值很古怪的調(diào)解器。很多3D動(dòng)畫軟件
和圖象處理軟件中,這種HSV顏色空間也被廣泛采用。它的基本原理是更改顏色屬性值以創(chuàng)建新顏色,而不是使用顏色本身的混合色。色調(diào)般指顏色,例如紅色、
桔黃色、藍(lán)色等等。飽和度(也被稱為濃度)指示指示色調(diào)中的白色量∶全飽和和色調(diào)不包含白色,顯示純色。而部分包含色調(diào)根據(jù)所混合的白色的情況,顯示的顏
色淺。例如,50%的飽和度的紅色色調(diào)顯示為粉紅色。值(也被稱為亮度)是顏色自身的發(fā)光度,也就是它所發(fā)出的光的多少。高飽和度色調(diào)非常亮,而低飽和度
色調(diào)非常暗。
?????
HSV與畫家以及其他通過將白色、黑色和灰色增加到純色素中以創(chuàng)建色彩、底紋、以及色調(diào)的藝術(shù)家們使用的色彩系統(tǒng)非常相似。色彩是純色,是組合了白色的全
飽和度顏色,而底紋是組合了黑色的全飽和度顏色。如果使用這兩種顏色混合,那么HSV的飽和度是白色量,值是黑色量,色調(diào)是增加了黑白兩色后的顏色。
?更詳細(xì)的情況不再這里說了,說起來也費(fèi)勁,也不常用,以后做個(gè)程序放到站點(diǎn)里。
七、關(guān)于調(diào)色板的基礎(chǔ)知識
?????
關(guān)于調(diào)色板,很少書談得深入,Dan的書上講得也就那么一點(diǎn)點(diǎn),少得實(shí)在可憐,連一個(gè)有關(guān)調(diào)色板的API都沒有給出。為了弄懂它,我也付出了不少時(shí)間。當(dāng)
然,現(xiàn)在就給你講解講解。把各個(gè)角落中學(xué)來的知識總結(jié)在這里,想來也是滿不錯(cuò)的主意感興趣的朋友可以看,不看也無訪。。
?????
用戶顯示于簡報(bào)的圖片很可能需要大量不同的顏色。可以想象∶艷綠色可滾動(dòng)的愛爾蘭小山的照片會(huì)比火星的圖片需要更多的套不同的顏色、若用戶想同時(shí)顯示這兩
幅圖象,會(huì)碰到一個(gè)很現(xiàn)實(shí)的問題。即使不限制逼真度,甚至用256種顏色,用戶都不可能有足夠的濃淡級別同時(shí)提供給兩個(gè)圖象。值得慶幸的是,若一次只有一
幅圖片顯示,用戶可以要求Windows交換顏色,使每個(gè)圖象能激活自己的256種顏色選擇--它自己的顏色調(diào)色板。
??????? 那么,什么是調(diào)色板?它是一種Windows對象,但適當(dāng)?shù)兀绻惆阉胂蟪梢环N特定長度的結(jié)構(gòu)數(shù)組,對理解會(huì)有幫助的。還記得RECT結(jié)構(gòu)吧,如果聲明
???
??? Dim MyRect(0 to 255) as RECT
???
????? 以上,我們定義了一個(gè)RECT結(jié)構(gòu)數(shù)組。當(dāng)然,所有的笨蛋都知道調(diào)色板并不是RECT的。調(diào)色板結(jié)構(gòu)比RECT要復(fù)雜一點(diǎn)。首先調(diào)色板是一種叫LOGPALETTE 的結(jié)構(gòu)來構(gòu)成的。如下∶
??? Type LOGPALETTE
???????? palVersion????????? As Integer????
???????? palNumEntries?????? As Integer????
???????? palPalEntry ( 1 )?? As PALETTEENTRY
??? End Type
??????? palVersion
指Windows的版本號,數(shù)值300代表Windows3.0或3.1,不過請記住,在Windows9X中仍然使用該數(shù)值,即300。第二個(gè)參數(shù)值調(diào)
色板登錄項(xiàng)的數(shù)目。也就是說,它指的是第三個(gè)參數(shù)palPalEntry的元素個(gè)數(shù)。這個(gè)例子中顯然是1嘍!那么在以下的情況呢?
??? Type LOGPALETTE
???????? palVersion????????? As Integer????
???????? palNumEntries?????? As Integer????
???????? palPalEntry ( 255 )?? As PALETTEENTRY
??? End Type
???
??????? 當(dāng)然是256。但這也不一定,比255小也無妨。比如在4位模式中,應(yīng)當(dāng)是16,就算你聲明為palPalEntry ( 255 ),創(chuàng)建調(diào)色板時(shí),函數(shù)只使用其前面的0到15的數(shù)組元素。
??????? 接下來,正如你已經(jīng)看到的那樣,第三個(gè)參數(shù)是一種結(jié)構(gòu)數(shù)組。當(dāng)然不是RECT嘍,而是 PALETTEENTRY。它的內(nèi)容如下∶
??? Type PALETTEENTRY
???????? peRed?????????????? As Byte??????
???????? peGreen???????????? As Byte??????
???????? peBlue????????????? As Byte??????
???????? peFlags???????????? As Byte??????
??? End Type
?????? Red,Green,Blue,不用我說了吧?最后一個(gè)peFlags是什么呢? 它是包含用來描述調(diào)色板項(xiàng)類型的一個(gè)或多個(gè)顏色標(biāo)志的值。如下表顯示了個(gè)些標(biāo)志∶
標(biāo)志???????????????????????? ? 值??????????????????????? 說???? 明
PC_EXPLICIT?????????? &H2????? 創(chuàng)建一個(gè)調(diào)色板條目,該調(diào)色板條目在系統(tǒng)調(diào)色板中指定一個(gè)索引而不是顏色。有顯示系統(tǒng)調(diào)色板內(nèi)容的程序使用。
PC_NOCOLLAPSE??? &H4???? ?創(chuàng)建一個(gè)被影射到系統(tǒng)調(diào)色板中未用條目上的調(diào)色板條目,即使該顏色條目已經(jīng)存在。用于當(dāng)兩個(gè)條目影射到同一顏色上時(shí)確保調(diào)色板顏色的唯一性。
PC_RESERVED???????? &H1????? 創(chuàng)建一個(gè)某應(yīng)用程序?qū)S械恼{(diào)色板條目。當(dāng)PC_RESERVED條目被添加到系統(tǒng)調(diào)色板時(shí),它不被影射到其他邏輯調(diào)色板中的顏色上,即使這些顏色匹配。由執(zhí)行調(diào)色板動(dòng)畫的程序使用。
???
?????
在8位顯示器上,一個(gè)像素的顏色是通過查看一個(gè)色彩表中自己的8位像素值來決定的。一個(gè)調(diào)色板包含一套(數(shù)組)24位RGB色彩值(peRed,
peGreen,peBlue)。一個(gè)調(diào)色板中的顏色數(shù)目最多256個(gè)(從0到255)。每個(gè)顯示內(nèi)存的像素選項(xiàng)包含一個(gè)從0到255的值,這個(gè)像素指定
哪一個(gè)調(diào)色板項(xiàng)被用來為這個(gè)像素著色。要改變一個(gè)像素的顏色,用戶有兩個(gè)選擇∶其一,可改變這個(gè)像素的顏色索引值;其二,改變調(diào)色板登錄項(xiàng)上的顏色值
(RGB值,24位)。在后一種情況下,這種改變使得所有引用這個(gè)調(diào)色板登錄項(xiàng)的像素均同時(shí)引起顏色變化。
????? 然而你應(yīng)當(dāng)清楚,初始化一個(gè)LOGPALETTE變量,并不能說它是一個(gè)調(diào)色板。因?yàn)檎{(diào)色板是GDI對象的一員,就像畫筆、畫刷之類的GDI對象一樣,必須通過特定的函數(shù)創(chuàng)建它。創(chuàng)建后,我們可以獲得一個(gè)調(diào)色板句柄,那么這個(gè)句柄所代表的就是真正的調(diào)色板了。
?????
每個(gè)用戶顯示的圖象能帶自身的色彩調(diào)色板。另外,每個(gè)活動(dòng)窗口能根據(jù)自身目的來操作調(diào)色板。但是,記住,應(yīng)用于全屏幕,具有256種顏色的限制,這并不是
對于每個(gè)窗口或應(yīng)用程序而言。對于這些,還得用Windows調(diào)色板管理器來進(jìn)行管理。Windows調(diào)色板管理器決定在給定時(shí)間哪個(gè)窗口擁有調(diào)色板控
制。在前臺活動(dòng)的窗口常常擁有優(yōu)先權(quán)。若此窗口不使用調(diào)色板,優(yōu)先權(quán)則傳給Z向(Z-order,聽說過吧?它垂直于屏幕。因?yàn)槲覀兂0哑聊蛔鴺?biāo)用X和Y
來表示。而三維坐標(biāo)是用X,Y,Z來表示的。這里指的Z并不是說真的三維坐標(biāo)中的那個(gè)Z軸,而只是一個(gè)形象的比喻中被命名的,是指桌面窗口垂直方向)的下
一個(gè)窗口。一旦帶最高優(yōu)先權(quán)的窗口認(rèn)清自己的調(diào)色板作為前臺調(diào)色板,其他窗口將被依次通知為背景調(diào)色板,這是由調(diào)色板管理器來通知。在一般情況下調(diào)色板管
理器是自動(dòng)工作的。
??????
剛才我說了一句“認(rèn)清”這么一個(gè)單詞。那么,什么叫“認(rèn)清一個(gè)調(diào)色板”呢?每個(gè)用戶顯示的圖象能帶自己的調(diào)色板(幾個(gè)調(diào)色板能被同時(shí)放在內(nèi)存中)。存放在
內(nèi)存的調(diào)色板叫做邏輯調(diào)色板。在用戶顯示系統(tǒng)的調(diào)色板決定著哪一種顏色實(shí)際出現(xiàn)在屏幕上,這個(gè)調(diào)色板被叫做系統(tǒng)調(diào)色板或者硬件調(diào)色板(集中精力!系統(tǒng)調(diào)色
板是硬件層的)只有一個(gè)硬件調(diào)色板并且調(diào)色板管理器保留一個(gè)它的復(fù)制調(diào)色板。當(dāng)一個(gè)應(yīng)用程序激活自己的顏色時(shí),它必須在設(shè)備場景(device
context)中選入邏輯調(diào)色板并認(rèn)清它。這意味著,應(yīng)用程序必須要求調(diào)色板管理器裝載它的邏輯調(diào)色板給系統(tǒng)(硬件)調(diào)色板。所以,所謂“認(rèn)清”實(shí)際上
是一種“拜托”,或者就象日本人常說的“多多關(guān)照”∶請多多關(guān)照,使用我的調(diào)色板吧。因?yàn)橹挥羞@樣,應(yīng)用程序才能顯示出符合自己口味的顏色。
?????
由于調(diào)色板的大小會(huì)發(fā)生改變,調(diào)色板管理器不會(huì)傻呵呵地硬著從邏輯調(diào)色板復(fù)制一個(gè)固定大小的256種顏色元素的塊給硬件調(diào)色板。而是,這個(gè)調(diào)色板管理器只
裝載它在每個(gè)邏輯調(diào)色板中找到的那么多的顏色。系統(tǒng)調(diào)色板可以容納多個(gè)邏輯調(diào)色板,只要顏色總數(shù)不超過256即可。Windows為它靜態(tài)顏色(用于畫按
鈕、邊框、文本、圖表等等,叫做系統(tǒng)保留色。可以用GetSystemColor函數(shù)獲取的那幾種)保留了20種調(diào)色板登錄項(xiàng)。因此,只為我們剩下236
個(gè)可變顏色位置。但這并不意味著一個(gè)調(diào)色板將只包括它需要的236種顏色來支持它的位圖。把8位圖象調(diào)色板轉(zhuǎn)化為Windows本體調(diào)色板是明智的。
Windows本體調(diào)色板包括20種保留色,特別是用戶打算用調(diào)色板繪圖時(shí),否則不能用保留的顏色。本教程附帶的Palette.vbp程序如果在16位
或24位模式下運(yùn)行,將只顯示這20種顏色。若用戶愿意,可以用系統(tǒng)靜態(tài)顏色把可定義的顏色范圍擴(kuò)展到256。GDI就是為這個(gè)目的提供了特殊函數(shù)。但這
將干擾其他活動(dòng)程序的基本命令,因?yàn)樗鼘?dòng)態(tài)改變活動(dòng)程序的外觀。因此除非你的程序獨(dú)占整個(gè)屏幕(比如一些游戲就是這樣的,不與其他窗口程序同時(shí)顯示),
否則這種做法是不應(yīng)采取的。
?????
一個(gè)邏輯調(diào)色板中的顏色在系統(tǒng)調(diào)色板中一般不占據(jù)與在邏輯調(diào)色板中相同的位置。這是因?yàn)樵谡J(rèn)清一個(gè)調(diào)色板的時(shí)候,調(diào)色板管理器建立一個(gè)交叉引用表,也叫做
調(diào)色板映像(palette
mapping),正如它把一個(gè)邏輯調(diào)色板加載給系統(tǒng)調(diào)色板。這個(gè)表用于GDI繪圖函數(shù)把像素從邏輯調(diào)色板翻譯到系統(tǒng)調(diào)色板索引。很好理解的。可以把它想
象為一種兩列的表,左列中填寫有邏輯調(diào)色板的顏色索引值,對應(yīng)的右列中填寫有系統(tǒng)調(diào)色板的索引值。就像英語詞典里一個(gè)英語單詞對應(yīng)著一個(gè)中文單詞。記住,
計(jì)算機(jī)中所謂映像都是指這種情況。比如消息映像,它為各種消息值調(diào)用相應(yīng)的消息處理函數(shù)。接著來。正如前面所解釋的,一個(gè)像素的顏色是通過在顏色表中查看
它的值來決定的。在設(shè)備相關(guān)位圖和DIB(設(shè)備無關(guān)位圖)的情況下,這是256種顏色位圖文件的最通用窗體,組成位圖像素?cái)?shù)據(jù)的字節(jié)包含在文件的顏色表中
的登錄項(xiàng)的值。當(dāng)GDI把圖象從文件轉(zhuǎn)移到屏幕時(shí),也就是說,從一個(gè)設(shè)備相關(guān)位圖轉(zhuǎn)移到另一個(gè)依賴于設(shè)備的位圖或DDB(設(shè)備相關(guān)位圖)時(shí),它用調(diào)色板映
像來改變像素值,使它能引用系統(tǒng)調(diào)色板中當(dāng)前正確的顏色。為一個(gè)邏輯調(diào)色板建立的調(diào)色板映像叫做前臺映像。
??????
若從活動(dòng)窗口沒有獲取所有調(diào)色板登錄項(xiàng),則剩下的位置被填入從非活動(dòng)窗口拿來的顏色,直到或者所有的位置占據(jù),或者沒有其他的窗口要求識別自己的調(diào)色板位
置。若前臺窗口需要所有的236個(gè)自由顏色位置,則所有的非活動(dòng)窗口必須受前臺活動(dòng)調(diào)色板的支配。調(diào)色板管理器還能自動(dòng)地執(zhí)行這個(gè)服務(wù),這是通過把非活動(dòng)
窗口中的顏色映像給當(dāng)前被辨認(rèn)出的調(diào)色板中的最匹配的顏色來實(shí)現(xiàn)的。這就是所謂的后臺影像。這有時(shí)會(huì)產(chǎn)生有趣的但對大多數(shù)人來講是糟糕的結(jié)果。比如,最匹
配色=很不匹配,其他色=更不匹配的情況下。另外,你需要記住的一點(diǎn)是,當(dāng)焦點(diǎn)每次從一個(gè)基于調(diào)色板的應(yīng)用程序更換到另一個(gè)基于調(diào)色板的應(yīng)用程序時(shí),整個(gè)
辨認(rèn)過程重復(fù)執(zhí)行一次。
????
DIB中的像素包含與位圖一起的邏輯調(diào)色板中的顏色索引,通常一個(gè)顏色表存在于DIB文件中;DDB中的像素包含系統(tǒng)調(diào)色板中的顏色索引。當(dāng)用戶是根據(jù)索
引值來訪問一種顏色時(shí),用戶是在邏輯調(diào)色板中根據(jù)它的位置來訪問顏色的,而不是在系統(tǒng)調(diào)色板中的它的位置。也就是說,你只能用邏輯調(diào)色板的索引值。因此,
假如邏輯調(diào)色板中不存在20中系統(tǒng)顏色,那么我們就無法訪問它,這并不是說它不存在。因此,如果你需要系統(tǒng)顏色,就應(yīng)當(dāng)把這些顏色值裝載的邏輯調(diào)色板。