日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

15.1 DIB 文件格式

發(fā)布時(shí)間:2023/12/20 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 15.1 DIB 文件格式 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

摘錄于《Windows程序(第5版,珍藏版).CHarles.Petzold 著》P569

??????? 有趣的是,DIB 格式并不是從 Windows 起源的。它最早是在微軟和 IBM 于上世紀(jì) 80 年代中期開(kāi)始合作開(kāi)發(fā)的操作系統(tǒng) OS/2 1.1 版本中定義的。OS/2 1.1 于 1988 年發(fā)布,是第一個(gè)擁有像 Windows 一樣的圖形用戶界面(叫 Presentation Manager,PM)的 OS/2 版本。PM 包含一套圖形編程接口,其中定義了位圖的格式。

??????? OS/2 的這種位圖格式后來(lái)被用到 Windows 3.0(1990 年發(fā)布)中,從此被稱作 DIB。Windows 3.0 還包含了最初 DIB 格式的一個(gè)變形,它最后成為了 Windows 環(huán)境下的標(biāo)準(zhǔn)。Windows 95(和 Windows NT 4.0)以及后來(lái)的 Windows 98(和 Windows NT 5.0)又在此基礎(chǔ)之上做了一些改進(jìn),我會(huì)在本章的稍后部分討論。

??????? 最好是先把 DIB 想象為一種文件格式。DIB 文件的擴(kuò)展名是 BMP,在極個(gè)別情況下也可以是 DIB。Windows 應(yīng)用程序中用到的位圖(例如一個(gè)按鈕的圖像)一般都是作為 DIB 文件存儲(chǔ)在可執(zhí)行文件的只讀資源中。圖標(biāo)和鼠標(biāo)指針也是 DIB 文件,只不過(guò)有一些微小的不同。

??????? 程序可以把 DIB 文件除去開(kāi)始的 14 個(gè)字節(jié)外,整個(gè)載入到一塊連續(xù)的內(nèi)存區(qū)域中這有時(shí)也被稱為“緊湊 DIB 格式的位圖”(packed-DIB format)。Windows 下的應(yīng)用程序可以用這種格式創(chuàng)建畫(huà)刷或者通過(guò) Windows 剪貼板來(lái)交換圖像。程序還擁有對(duì)該 DIB 內(nèi)容的完全控制,可以對(duì)此 DIB 進(jìn)行任意修改。

??????? 程序也可以在內(nèi)存中創(chuàng)建自己的 DIB,然后把它們存到文件中。這些 DIB 中的圖像可以用 GDI 函數(shù)繪制。程序也可以直接對(duì)像素操作,在此過(guò)程中可以使用其他基于內(nèi)存的 DIB。

??????? 一個(gè) DIB 被載入到內(nèi)存后,程序可以對(duì)該 DIB 數(shù)據(jù)調(diào)用一些 Windows API 函數(shù),這些函數(shù)我會(huì)在本章中討論。與 DIB 相關(guān)的 API 調(diào)用并不多,主要都是用來(lái)在屏幕或者打印機(jī)上顯示圖像,還有一些是用來(lái)在 DIB 和 GDI 位圖對(duì)象之間進(jìn)行互相轉(zhuǎn)換。

??????? 盡管有以上提到的這些功能,但還有很多很多和 DIB 相關(guān)的任務(wù) Windows 操作系統(tǒng)并不支持。例如,一個(gè)程序可以存取一個(gè) 24 位的 DIB,現(xiàn)在它想把這個(gè) DIB 轉(zhuǎn)換成一個(gè)擁有最佳匹配的 256 色調(diào)色板的 8 位 DIB。Windows 并不能替你做到這一點(diǎn)。而本章和第 16 章會(huì)演示怎樣完成一些 Windows API 并不支持的 DIB 相關(guān)任務(wù)。

15.1.1? OS/2 風(fēng)格的 DIB

??????? 在尚未涉及太多細(xì)節(jié)之前,先來(lái)看一下和最早在 OS/2 1.1 版本中引入的位圖文件相兼容的 Windows DIB 文件格式。

??????? DIB 文件有四個(gè)主要部分

  • 文件頭
  • 信息頭
  • RGB 顏色表(有時(shí)可能沒(méi)有)
  • 位圖的像素位

??????? 可以把前兩部分看做是 C 數(shù)據(jù)結(jié)構(gòu),把第三部分看做一個(gè)數(shù)組結(jié)構(gòu)的數(shù)組。這些數(shù)據(jù)結(jié)構(gòu)定義在 Windows 的頭文件 WINGDI.H 中。在內(nèi)存中緊湊格式的 DIB 有三個(gè)部分:

  • 信息頭
  • RGB 顏色表(有時(shí)可能沒(méi)有)
  • 位圖的像素位

它和存在與文件中的 DIB 一模一樣,唯一的區(qū)別在于沒(méi)有文件頭。

??????? DIB 文件——不是內(nèi)存中緊湊格式 DIB——以一個(gè) 14 字節(jié)的文件頭開(kāi)始,該文件頭的定義如下:

typedef struct tagBITMAPFILEHEADER // bmfh {WORD bfType; // 文件簽名,是 "BM" 或者 0x4D42DWORD bfSize; // 整個(gè)文件的長(zhǎng)度WORD bfReserved1; // 必須是 0WORD bfReserved2; // 必須是 0DWORD bfOffsetBits; // 到位圖像素位的位移 } BITMAPFILEHEADER, * PBITMAPFILEHEADER; 這也許和在 WINGDI.H 中的定義不完全一樣,例如,注釋是我加的,但是二者功能上時(shí)一樣的。第一個(gè)注釋,"bmfh",是我推薦使用的這個(gè)數(shù)據(jù)結(jié)構(gòu)的縮寫(xiě)名稱。如果你在我的程序中看到一個(gè)變量叫做 pbmfh,那就是一個(gè)指向 BITMAPFILEHEADER 類(lèi)型的結(jié)構(gòu)的指針,或者是一個(gè) PBITMAPFILEHEADER 類(lèi)型的變量。

??????? 這個(gè)結(jié)構(gòu)有 14 字節(jié)長(zhǎng)。它以兩個(gè)字母 "BM" 打頭,說(shuō)明這是一個(gè)位圖文件。如果用十六進(jìn)制的 WORD 值來(lái)表示,它的數(shù)值就是 0X4D42。“BM”之后是一個(gè) DWORD,記錄了整個(gè)文件的大小,以字節(jié)為單位,該大小包含了文件頭的長(zhǎng)度。在這之后的兩個(gè) WORD 字段必須設(shè)置為零。(在鼠標(biāo)指針文件中,采用非常相似于 DIB 的文件格式,而這兩個(gè)字段表示指針的“熱點(diǎn)”位置。)這個(gè)結(jié)構(gòu)的最后一個(gè)字段是一個(gè) DWORD,它指示了圖像的像素位在文件中的起始位置。這個(gè)值可以從 DIB 信息頭中推導(dǎo)出來(lái),這里提供它只是為了方便。

??????? 在 OS/2 風(fēng)格的 DIB 中,緊接著 BITMAPFILEHEADER 結(jié)構(gòu)之后是一個(gè) BITMAPCOREHEADER 結(jié)構(gòu),它提供了 DIB 圖像的基本信息。緊湊格式的 DIB 就是這個(gè) BITMAPCOREHEADER 結(jié)構(gòu)開(kāi)頭,如下所示:

typedef struct tagBITMAPCOREHEADER // bmch {DWORD bcSize; // 結(jié)構(gòu)大小 = 12WORD bcWidth; // 以像素計(jì)的圖像的寬度WORD bcHeight; // 以像素計(jì)的圖像的高度WORD bcPlanes; // = 1WORD bcBitCount; // 每個(gè)像素的位數(shù) (1, 4, 8 or 24) } BITMAPCOREHEADER, * PBITMAPCOREHEADER; 對(duì)于這個(gè)結(jié)構(gòu),其名稱中的單詞“core”(核心)聽(tīng)起來(lái)有一些奇怪,事實(shí)上也正是如此。它其實(shí)表面這是其他位圖格式的基礎(chǔ)(所以叫 “core”),其他的格式都由此衍生而來(lái)。

??????? 在 BITMAPCOREHEADER 結(jié)構(gòu)中的 bcSize 字段表明了這個(gè)結(jié)構(gòu)本身的大小,在這里,它是 12 字節(jié)。

??????? bcWidth 和 bcHeight 兩個(gè)字段按像素為單位,給出了位圖的大小。盡管這兩個(gè)字段是 WORD 類(lèi)型,所以能表示有 65535 個(gè)像素寬或者高的 DIB,但是你可能很少會(huì)遇到那樣大的圖像。

??????? bcPlanes 字段總是 1。從它開(kāi)始被定義一直到現(xiàn)在始終是 1。它是我們?cè)诘?14 章遇到的早期 Windows GDI 位圖對(duì)象的遺留產(chǎn)物。

??????? bcBitCount 字段指明每一個(gè)像素的位數(shù)。對(duì) OS/2 風(fēng)格的 DIB 來(lái)說(shuō),它可以是 1, 4, 8 或者 24。DIB 中的顏色數(shù)等于 2^(bmch.bcBitCount),或者,用 C 語(yǔ)言的語(yǔ)法,就是:

1 << bmch.bcBitCount因此,bcBitCount 字段就等于:

  • 1 對(duì)應(yīng)于雙色 DIB
  • 4 對(duì)應(yīng)于 16 色 DIB
  • 8 對(duì)應(yīng)于 256 色 DIB
  • 24 對(duì)應(yīng)于全彩 DIB

在提到“一個(gè) 8 位 DIB”時(shí),我指的是一個(gè) DIB 的每個(gè)像素有 8 位。

??????? 對(duì)于前三種情形(位數(shù)為 1、4 和 8),BITMAPCOREHEADER 后面是一個(gè)顏色表24 位 DIB 沒(méi)有顏色表顏色表是一個(gè)數(shù)組,數(shù)組中的元素是一個(gè) 3 字節(jié)長(zhǎng)的 RGBTRIPLE 結(jié)構(gòu),對(duì)應(yīng)于圖像中的每一種顏色:

typedef struct tagRGBTRIPLE // rgbt {BYTE rbgtBlue; // 藍(lán)色值BYTE rbgtGreen; // 綠色值BYTE rgbtRed; // 紅色值 } RGBTRIPLE; 一般都推薦把圖像中最重要的顏色排在顏色表的前面。我們?cè)诘?16 章會(huì)看到原因。

??????? WINGDI.H 頭文件中還定義了下面的結(jié)構(gòu):

typedef struct tagBITMAPCOREINFO // bmci {BITMAPCOREHEADER bmciHeader; // 基本信息頭RGBTRIPLE bmciColors[1]; // 顏色表數(shù)組 } BITMAPCOREINFO, * PBITMAPCOREINFO; 這個(gè)結(jié)構(gòu)把信息頭和顏色表結(jié)合在一起。盡管在這里 RGBTRIPLE 的數(shù)目看起來(lái)只有 1,但是你實(shí)際上在 DIB 文件中總會(huì)看到不止一個(gè) RGBTRIPLE 結(jié)構(gòu)。根據(jù)每一像素的位數(shù),顏色表的大小總是 2、16 或者 256 個(gè) RGBTRIPLE 結(jié)構(gòu)。如果需要為一個(gè) 8 位 DIB 的 PBITMAPCOREINFO 結(jié)構(gòu)分配內(nèi)存,可以像下面這樣:

pbmci = malloc(sizeof(BITMAPCOREINFO) + 255 * sizeof(RGBTRIPLE)); 然后可以像下面這樣對(duì)任何一個(gè) RGBTRIPLE 結(jié)構(gòu)進(jìn)行訪問(wèn):

pbmci->bmciColors[i]

??????? 因?yàn)?RGBTRIPLE 結(jié)構(gòu)有 3 字節(jié)長(zhǎng),因此有些 RGBTRIPLE 結(jié)構(gòu)可能會(huì)從 DIB 內(nèi)一個(gè)奇數(shù)地址開(kāi)始。但是,由于 DIB 文件總是有偶數(shù)個(gè) RGBTRIPLE 結(jié)構(gòu),所以在顏色表后面的數(shù)據(jù)塊總是從一個(gè) WORD 地址邊界開(kāi)始。

??????? 顏色表之后的數(shù)據(jù)(對(duì) 24 位 DIB 來(lái)說(shuō),就是信息頭之后的數(shù)據(jù))就是像素位本身了

15.1.2? 自下而上存儲(chǔ)!

??????? 像大多數(shù)位圖格式一樣,DIB 中的像素位是以水平的行來(lái)排列的在視頻顯示硬件的術(shù)語(yǔ)中,也叫“掃描線”。圖像中的行數(shù)等于 BITMAPCOREHEADER 結(jié)構(gòu)中 bcHeight 字段的值。但是和其他大多數(shù)位圖格式不同的是,DIB 是從圖像的最下面一行開(kāi)始,然后逐漸向上來(lái)存儲(chǔ)整個(gè)圖像的

??????? 這里先解釋一些術(shù)語(yǔ)。當(dāng)我說(shuō)“頂行”“底行”時(shí),我指的是視覺(jué)上圖像的頂部和底部,就像在顯示器或打印頁(yè)上正確顯示時(shí)的樣子。一幅人頭肖像畫(huà)的頂行是頭發(fā);底行是下巴。當(dāng)我說(shuō)“第一行”時(shí),我指的是 DIB 文件中直接跟在顏色表中之后的像素行。當(dāng)我說(shuō)“最后一行”時(shí),我指的是在文件最后的像素行。

??????? 因此,在 DIB 中,圖像的底行是文件的第一行,圖像的頂行是文件的最后一行。這是一種由下往上的組織方式。由于這種組織方式不直觀,你可能會(huì)問(wèn)為什么要這樣安排。

??????? 一切都要從 OS/2 的 Presentation Manager 說(shuō)起。在 IBM,有人認(rèn)為在 PM 中的所有坐標(biāo)系都應(yīng)該保持一致,包括窗口、圖形以及位圖。這引發(fā)了一場(chǎng)辯論:多數(shù)人,包括哪些與整個(gè)屏幕的文本或窗口環(huán)境打交道的程序員,認(rèn)為垂直坐標(biāo)應(yīng)該隨著屏幕向下增加。然而,那些發(fā)燒級(jí)的計(jì)算機(jī)圖形程序員更愿意從解析幾何的角度來(lái)看圖形顯示系統(tǒng)。這就牽涉到了一個(gè)直角(或笛卡爾)坐標(biāo)系,其中垂直坐標(biāo)是隨著空間向上而增加的。

??????? 簡(jiǎn)而言之,數(shù)學(xué)家贏了。在 PM 中,所有坐標(biāo)系都以左下角為源點(diǎn),包括窗口坐標(biāo)。這就是 DIB 為什么這樣安排的原因

15.1.3? DIB 像素位

??????? DIB 文件的最后部分是真正的像素位組成的,在大多數(shù)情況下,這也是 DIB 文件的主體。像素位按水平行排列,從圖像的最下一行開(kāi)始逐漸向上包括整個(gè)圖像。

??????? DIB 中的行數(shù)等于 BITMAPCOREHEADER 中的 bcHeight 字段。每一行編碼的像素?cái)?shù)等于 bcWidth 字段。每一行的像素是從左向右安排的。每一個(gè)像素的位數(shù)由 bcBitCount 字段定義,可以是 1、4、8 或者 24。

??????? 每一行所用的字節(jié)數(shù)總是 4 的倍數(shù),可以用以下方法計(jì)算出來(lái)。

RowLength = 4 * ((bmch.bcWidth * bmch.bcBitCount + 31) / 32);或者,用更高效一點(diǎn)的 C 語(yǔ)言,可以這樣寫(xiě):
RowLength = ((bmch.bcWidth * bmch.bcBitCount + 31) & ~31) >> 3;

如果需要的話,每一行的結(jié)尾要補(bǔ)充一些字節(jié)(一般用 0)來(lái)達(dá)到這個(gè)長(zhǎng)度。所有像素?cái)?shù)據(jù)所占用的字節(jié)數(shù)就是 RowLength 和 bmch.bcHeight 的乘積。

??????? 要想知道像素是怎么編碼的,讓我們分別看一下這四種情況。在下面的圖中,每一個(gè)字節(jié)的二進(jìn)制位顯示在格子中,7 代表最高位,0 代表最低位。像素也從 0 開(kāi)始編號(hào),從一行中最左邊的像素開(kāi)始。

??????? 對(duì)于每像素 1 位的 DIB 來(lái)說(shuō),每個(gè)字節(jié)表示了 8 個(gè)像素。最左邊的像素就是第一個(gè)字節(jié)的最高位

每一個(gè)像素?cái)?shù)值不是 0 就是 1。0 代表這個(gè)像素的顏色是由顏色表第一個(gè) RGBTRIPLE 字段指定。1 代表這個(gè)像素的顏色是由顏色表第二個(gè) RGBTRIPLE 字段指定。

??????? 對(duì)于每像素 4 位的 DIB 來(lái)說(shuō),每個(gè)字節(jié)表示了 2 個(gè)像素。最左邊的像素就是第一個(gè)字節(jié)的高 4 位,以此類(lèi)推:

每一個(gè) 4 位像素的值在 0 到 15 之間。這個(gè)數(shù)值就是顏色表中 16 個(gè)元素的下標(biāo)。

??????? 對(duì)于每像素 8 位的 DIB 來(lái)說(shuō),每個(gè)字節(jié)表示了 1 個(gè)像素:


每一個(gè)像素(字節(jié))的數(shù)值在 0 到 255 之間。同樣,這個(gè)數(shù)值就是顏色表中 256 個(gè)元素的下標(biāo)。

??????? 對(duì)于每像素 24 位的 DIB 來(lái)說(shuō),每個(gè)像素需要 3 個(gè)字節(jié)來(lái)代表紅、綠和藍(lán)色的數(shù)值。每一行像素基本上就是一個(gè) RGBTRIPLE 結(jié)構(gòu)的數(shù)組,只是可能在每一行的結(jié)尾添加一些 0 字節(jié)以保證每行的字節(jié)數(shù)都是 4 的倍數(shù)。

再次說(shuō)明,每像素 24 位的 DIB 沒(méi)有顏色表。

15.1.4? Windows 擴(kuò)展 DIB

??????? 現(xiàn)在我們已經(jīng)掌握了在 Windows 3.0 中引入的和 OS/2 兼容的 DIB 格式,讓我們來(lái)看一下同時(shí)在 Windows 3.0 中引入的擴(kuò)展版本的 DIB 格式吧。

??????? 這個(gè)格式的 DIB 和前面提到的格式一樣,也是以一個(gè) BITMAPFILEHEADER 結(jié)構(gòu)開(kāi)頭,但是,接在后面的是一個(gè) BITMAPINFOHEADER 結(jié)構(gòu),而不是 BITMAPCOREHEADER 結(jié)構(gòu):

typedef struct tagBITMAPINFOHEADER // bmih {DWORD biSize; // 結(jié)構(gòu)大小 == 40LONG biWidth; // 以像素計(jì)的圖像的寬度LONG biHeight; // 以像素計(jì)的圖像的高度WORD biPlanes; // = 1WORD biBitCount; // 每個(gè)像素的位數(shù) (1, 4, 8, 16, 24 或 32)DWORD biCompression; // 壓縮編碼DWORD biSizeImage; // 圖像的字節(jié)數(shù)LONG biXPelsPerMeter; // 水平分辨率LONG biYPelsPerMeter; // 垂直分辨率DWORD biClrUsed; // 用到的顏色數(shù)DWORD biClrImportant; // 重要顏色的數(shù)目 } BITMAPINFOHEADER, * PBITMAPINFOHEADER;

??????? 你可以從這個(gè)結(jié)構(gòu)的第一個(gè)字段來(lái)區(qū)別 OS/2 兼容的 DIB 和 Windows DIB: 前者是 12,后者是 40

??????? 你可能注意到了,這個(gè)結(jié)構(gòu)有六個(gè)新字段,但是 BITMAPINFOHEADER 結(jié)構(gòu)并不是簡(jiǎn)單的 BITMAPCOREHEADER 結(jié)構(gòu)再加上一些新東西。再仔細(xì)看一下:在 BITMAPCOREHEADER 結(jié)構(gòu)中 bcWidth 和 bcHeight 字段是 16 位的 WORD 型數(shù)值。而在這個(gè)結(jié)構(gòu)中,它們是 32 位的 LONG 型數(shù)值。這個(gè)討厭的小改動(dòng)肯定會(huì)讓你發(fā)瘋

??????? 還有一個(gè)改動(dòng):對(duì)于 1 位、4 位 和 8 位的 DIB,如果用 BITMAPINFOHEADER 結(jié)構(gòu)的話,顏色表不再是一個(gè) RGBTRIPLE 結(jié)構(gòu)的數(shù)組。接在 BITMAPINFOHEADER 結(jié)構(gòu)后面的是一個(gè) RGBQUAD 結(jié)構(gòu)的數(shù)組:

typedef struct tagRGBQUAD // rgb {BYTE rgbBlue; // 藍(lán)色值BYTE rgbGreen; // 綠色值BYTE rgbRed; // 紅色值BYTE rbgReserved; // = 0 } RGBQUAD; 除了第 4 個(gè)字段總是設(shè)置為 0 以外,這個(gè)結(jié)構(gòu)幾乎和 RGBTRIPLE 結(jié)構(gòu)一樣。在 WINGDI.H 頭文件中,還定義了下面的結(jié)構(gòu):

typedef struct tagBITMAPINFO // bmi {BITMAPINFOHEADER bmiHeader; // 信息頭RGBQUAD bmiColors[1]; // 顏色表數(shù)組 } BITMAPINFO, * PBITMAPINFO; 請(qǐng)注意,BITMAPINFO 結(jié)構(gòu)從一個(gè) 32 位地址邊界開(kāi)始,每一個(gè) RGBQUAD 字段也都從 32 位地址邊界開(kāi)始,這是因?yàn)?BITMAPINFOHEADER 結(jié)構(gòu)是 40 個(gè)字節(jié)長(zhǎng)。這保證了 32 位的處理器可以很有效地對(duì)顏色表數(shù)據(jù)進(jìn)行尋址

??????? 盡管 BITMAPINFOHEADER 最初是在 Windows 3.0 中定義的,但有些字段在 Windows 95 和 Windows NT 4.0 中又被重新定義了,并被帶到了 Windows 98 和 Windows NT 5.0 里。例如,現(xiàn)在的文檔中注明:“如果 biHeight 是負(fù)數(shù),那么這個(gè) DIB 就是從上到下的,原點(diǎn)為左上角。”知道這個(gè)確實(shí)不錯(cuò),但如果 1990 年最初定義這個(gè) DIB 格式的時(shí)候有人就這么決定就更好了。我的建議是不要從上到下的 DIB。因?yàn)槟切┎恢肋@個(gè)“功能”的程序在遇到一個(gè)負(fù)的 biHeight 字段時(shí)會(huì)崩潰或者出錯(cuò)。例如,微軟 Word 97 中包括的 Microsoft Photo Editor 就會(huì)在遇到一個(gè)從上到下的 DIB 時(shí)報(bào)告“非法圖像高度”(Word 97 自己倒是沒(méi)有這個(gè)問(wèn)題)。

??????? biPlanes 字段仍然是 1,但是 biBitCount 字段現(xiàn)在除了可以是1、4、8、24 之外,還可以是 16 或者 32。這也是在 Windows 95 和 Windows NT 4.0 中的新功能。我一會(huì)兒就會(huì)討論到這兩個(gè)新格式是怎么工作的。

??????? 讓我先跳過(guò) biCompression 和 biSizeImage 字段。我很快就會(huì)對(duì)此展開(kāi)討論。

??????? biXPelsPerMeter 和 biYPelsPerMeter 字段指示圖像在真實(shí)世界中的大小,以一個(gè)拙劣的每米像素?cái)?shù)作為單位。(“Pel”,Picture Element,是 IBM 對(duì)像素的稱呼。)Windows 內(nèi)部并不用這個(gè)信息。但是,應(yīng)用程序可以用它來(lái)顯示 DIB 真正的大小。有些 DIB 是從不具備正方形像素的設(shè)備中得來(lái)的,這兩個(gè)字段對(duì)這些 DIB 也是有用的。在絕大多數(shù) DIB 里,這兩個(gè)字段都是 0,說(shuō)明圖像沒(méi)有一個(gè)推薦的真實(shí)世界的大小。72 DPI(每英寸點(diǎn)數(shù))的分辨率(一般用在顯示器上,但是真正的分辨率要取決于顯示器的大小)大約等于每米 2835 個(gè)像素,而一般打印機(jī)的 300 DPI 分辨率大約等于每米 11811 個(gè)像素。

??????? biClrUsed 字段是一個(gè)非常重要的字段,因?yàn)樗绊懙筋伾碇性氐臄?shù)目。對(duì) 4 位和 8 位的 DIB 來(lái)說(shuō),這說(shuō)明顏色表中可能會(huì)有少于 16 或 256 個(gè)元素。這是一種減少 DIB 大小的辦法,盡管并沒(méi)有減少多少。例如,假如一個(gè) DIB 圖像中只有 64 級(jí)灰度,biClrUsed 字段就會(huì)設(shè)置為 64,顏色表中就會(huì)只有 64 個(gè) RGBQUAD 結(jié)構(gòu),而不是 256 個(gè)。每個(gè)像素的數(shù)值就會(huì)是 0x00 到 0x3F。這個(gè) DIB 仍然需要用 1 個(gè)字節(jié)來(lái)表示一個(gè)像素,但是每個(gè)字節(jié)的最高兩位為 0。如果 biClrUsed 字段為 0,則說(shuō)明顏色表中包括了由 biBitCount 字段指明的全部數(shù)目的元素。

??????? 從 Windows 95 開(kāi)始,biClrUsed 字段對(duì)于 16、24 和 32 位的 DIB 來(lái)說(shuō)也可以不是 0。在這種情況下,顏色表不再被 Windows 用來(lái)解釋像素位數(shù)據(jù),而是被用作在一個(gè) 256 色的顯示器上顯示這個(gè) DIB 時(shí)所用的調(diào)色板。你可能還記得在 OS/2 兼容的格式中,一個(gè) 24 位的 DIB 沒(méi)有顏色表。在 Windows 3.0 中,擴(kuò)展的 DIB 格式也是如此。但是在 Windows 95 中,24 位的 DIB 也可以有一個(gè)顏色表,其大小由 biClrUsed 字段指明。

??????? 簡(jiǎn)單總結(jié)如下:

  • 1 位 DIB 中,biClrUsed 總是 0 或 2.顏色表總是有 2 個(gè)元素。
  • 4 位 DIB 中,如果 biClrUsed 是 0 或 16,那么顏色表有 16 個(gè)元素。如果 biClrUsed 的數(shù)值是 2~15,那么它指明了顏色表的元素?cái)?shù),每一個(gè)像素的最大值為這個(gè)數(shù)減 1。
  • 8 位 DIB 中,如果 biClrUsed 是 0 或 256,那么顏色表有 256 個(gè)元素。如果 biClrUsed 的數(shù)值是 2 ~255,那么它指明了顏色表的元素?cái)?shù),每一個(gè)像素的最大值為這個(gè)數(shù)減 1。
  • 16、24 和 32 位 DIB 中,biClrUsed 一般為 0。如果不是,那么它指明了顏色表中的元素?cái)?shù)。應(yīng)用程序可以用這個(gè)顏色表為該 DIB 在 256 色的顯示適配器上設(shè)置調(diào)色板

??????? 另一個(gè)警告:根據(jù)較早的 DIB 文檔開(kāi)發(fā)的程序不會(huì)預(yù)料到 24 位 DIB 會(huì)有顏色表。如果已加入一個(gè),那么自己要對(duì)此負(fù)責(zé)。

??????? 和它的名稱不同,biClrImportant 字段其實(shí)遠(yuǎn)沒(méi)有 biClrUsed 字段重要。它通常是 0,說(shuō)明顏色表里的所有顏色都很重要。或者,它可以被設(shè)成和 biClrUsed 同樣的值,兩者是一回事。如果它是從 0 到 biClrUsed 中間的某個(gè)數(shù),那么它意味著這個(gè) DIB 可以用顏色表中的前 biClrImportant 種顏色差不多地顯示出來(lái)。如果需要在一個(gè) 256 色的顯示適配器上并列顯示兩個(gè) 8 位的 DIB,這可以是很有用的。

??????? 對(duì) 1、4、8 和 24 位的 DIB 來(lái)說(shuō),像素位的組織和 OS/2 兼容的 DIB 是一樣的。我會(huì)很快討論到 16 位和 32 位的 DIB。

15.1.5? 現(xiàn)實(shí)情況

??????? 如果遇到一個(gè)由別人或程序創(chuàng)建的 DIB,你可以期待發(fā)現(xiàn)什么呢?

??????? 盡管 OS/2 風(fēng)格的 DIB 在 Windows 3.0? 發(fā)布時(shí)是很常見(jiàn)的,但最近幾年它們已經(jīng)變得非常罕見(jiàn)了。有些程序在要快速編寫(xiě)一些 DIB 程序時(shí)干脆就把它們忽略不計(jì)了。你能遇到的 4 位 DIB 很可能是由 Windows 的畫(huà)圖程序在 16 色的顯示適配器上創(chuàng)建的,其中的顏色表就是那些顯示器上的標(biāo)準(zhǔn) 16 色。

??????? 你能找到的最常見(jiàn)的 DIB 可能就是 8 位的了(譯注:這是本書(shū)英文版成書(shū)時(shí)的情況。現(xiàn)在,隨著數(shù)碼硬件的普及,最常見(jiàn)的 DIB 已經(jīng)是 24 位的“真彩”的圖像了)。8 位 DIB 圖像可以分為兩類(lèi):灰度 DIB 和調(diào)色板化的彩色 DIB。可惜,文件頭中沒(méi)有任何信息可以告訴你具體是哪一種。

??????? 有些灰度 DIB 的 biClrUsed 字段等于 64,說(shuō)明顏色表中有 64 個(gè)元素。這些元素一般都以灰度遞增的順序排列。也就是說(shuō),顏色表開(kāi)始的 RGB 值是 00-00-00, 04-04-04, 08-08-08, 0C-0C-0C,最后的 RGB 值是 F0-F0-F0,F4-F4-F4,F8-F8-F8,FC-FC-FC。這種顏色表示用下面的公式計(jì)算出來(lái)的:

rgb[i].rgbRed = rgb[i].rgbGreen = rgb[i].rgbBlue = i * 256 / 64;其中 rgb 是一個(gè) RGBQUAD 結(jié)構(gòu)的數(shù)組,i 的范圍是 0~63。或者,灰度顏色表中的數(shù)值是用下面的公式計(jì)算出來(lái)的:

rgb[i].rgbRed = rgb[i].rgbGreen = rgb[i].rgbBlue = i * 255 / 63;這樣顏色表就會(huì)以 FF-FF-FF 結(jié)尾。

??????? 其實(shí)用哪一些公式并不重要。很多顯示適配器和顯示器本來(lái)也沒(méi)有超過(guò) 6 位的顏色精度。第一個(gè)公式認(rèn)識(shí)到了這一點(diǎn),而第二個(gè)公式在產(chǎn)生少于 64 級(jí)灰度時(shí)要更合適,例如要產(chǎn)生 16 或者 32 級(jí)灰度(在第二個(gè)公式末尾的分母就分別是 15 或 31)的時(shí)候。這是因?yàn)榈诙€(gè)公式保證了顏色表的最后一個(gè)元素時(shí) FF-FF-FF,也就是白色。

??????? 有些 8 位的灰度 DIB 的顏色表有 64 個(gè)元素,另外一些灰度 DIB 的顏色表也可能有 256 個(gè)元素。biClrUsed 字段可以是 0(說(shuō)明顏色表有 256 個(gè)元素),也可以是 2 到 256 的任何一個(gè)值。當(dāng)然了,如果它是 2,就沒(méi)有什么必要了(因?yàn)檫@樣的一個(gè) 8 位 DIB 可以重新編碼為 1 位的 DIB)。同樣,如果它小于 16,這個(gè) DIB 就可以重新編碼為 4 位 DIB。不管是哪種情況,顏色表中的元素個(gè)數(shù)一定要等于 biClrUsed(或者在 biClrUsed 為 0 時(shí)等于 256),而且每個(gè)像素的數(shù)值不能大于顏色表元素個(gè)數(shù)減一,因?yàn)橄袼氐臄?shù)值就是顏色表數(shù)組的下標(biāo)。對(duì)于 biClrUsed 為 64 的 8 位 DIB 來(lái)說(shuō),像素的數(shù)值是 0x00 到 0x3F。

??????? 有一件很重要的事情值得記住當(dāng)一個(gè) 8 位 DIB 的顏色表中的元素全部是灰度(也就是說(shuō),紅、綠、藍(lán)的級(jí)別一樣),而且灰度的等級(jí)是均勻的升高時(shí)(像我剛才描述的情況),那么像素的值就代表了灰度的比例。例如,biClrUsed 是 64,那么一個(gè)值為 0 的像素是黑色,一個(gè)值為 0x20 的像素為 50% 灰色,值為 3F 的像素值為白色。

??????? 這一點(diǎn)對(duì)某些圖像處理的任務(wù)很重要,因?yàn)槟憧梢酝耆雎灶伾矶缓拖袼刂荡蚪坏?/span>。這太有用了,以至于如果給我一個(gè)機(jī)會(huì)回到過(guò)去對(duì) BITMAPINFOHEADER 結(jié)構(gòu)作一個(gè)修改的話,我就會(huì)在其中加上一個(gè)標(biāo)志位,說(shuō)明這個(gè) DIB 是灰度的,沒(méi)有顏色表,每個(gè)像素的值直接代表灰度等級(jí)。

??????? 調(diào)色板化的 8 位彩色 DIB 一般都會(huì)用到整個(gè)顏色表,于是其 biClrUsed 字段值為 0 或者 256。然而,你也可能遇到更少顏色的情況——例如,236 色。這和如下事實(shí)有關(guān):程序一般只能改變 Windows 調(diào)色板中 236 個(gè)元素的值以準(zhǔn)確顯示這個(gè) DIB。我會(huì)在第 16 章討論到這個(gè)問(wèn)題。

??????? biXPelsPerMeter 和 biYPelsPerMeter 值不為 0 的情況很少見(jiàn)另一種極少見(jiàn)的情況是 biClrImportant 不等于 0 或者不等于 biClrUsed

15.1. 6? DIB 壓縮

??????? 前面我推遲了對(duì) BITMAPINFOHEADER 結(jié)構(gòu)中 biCompression 和 biSizeImage 字段的討論。現(xiàn)在是時(shí)候來(lái)檢視它們了。

??????? biCompression 字段可以是 4 個(gè)常數(shù)值之一:BI_RGB,BI_RLE8,BI_RLE4 或 BI_BITFIELDS,它們?cè)?WINGDI.H 頭文件中定義為 0 到 3。這個(gè)字段有兩個(gè)意義:對(duì) 4 位和 8 位的 DIB 來(lái)說(shuō),它說(shuō)明像素的值以一種行程長(zhǎng)度編碼(run-length encoding, RLE)壓縮;對(duì) 16 位或 32 位 DIB 來(lái)說(shuō),它說(shuō)明是否使用顏色遮罩(color mask)來(lái)對(duì)像素位進(jìn)行編碼。第二個(gè)功能是在 Windows 95 中被引入的。

??????? 讓我們來(lái)先看一下行程長(zhǎng)度編碼(RLE)壓縮。

  • 對(duì) 1 位 DIB 來(lái)說(shuō),biCompression 字段總是 BI_RGB。
  • 對(duì) 4 位 DIB 來(lái)說(shuō),biCompression 字段可以是 BI_RGB 或 BI_RLE4。
  • 對(duì) 8 位 DIB 來(lái)說(shuō),biCompression 字段可以是 BI_RGB 或 BI_RLE8。
  • 對(duì) 24 位 DIB 來(lái)說(shuō),biCompression 字段總是 BI_RGB。

??????? 如果這個(gè)值是 BI_RGB,那么像素位就和 OS/2 兼容的 DIB 一樣存儲(chǔ)。否則的話,像素位就是用行程長(zhǎng)度編碼來(lái)壓縮的。

??????? 行程長(zhǎng)度編碼(RLE)是最簡(jiǎn)單的數(shù)據(jù)壓縮方法之一它基于這樣一個(gè)知識(shí):DIB 圖像中經(jīng)常在一行中會(huì)有一連串相同的像素。行程長(zhǎng)度編碼通過(guò)記錄下重復(fù)像素的值和它重復(fù)的次數(shù)來(lái)節(jié)省空間。DIB 使用的行程長(zhǎng)度編碼算法有時(shí)還超出這個(gè)范圍,它可以用來(lái)定義一個(gè)稀疏的矩形 DIB 圖像。也就是說(shuō),矩形中有些區(qū)域是沒(méi)有定義的。這可以用來(lái)繪制非矩形的圖像。

??????? 對(duì) 8 位 DIB 進(jìn)行行程長(zhǎng)度編碼從概念上來(lái)說(shuō)是最簡(jiǎn)單的,那就讓我們從這里開(kāi)始吧。下表有助于理解在 biCompression 字段等于 BI_RLE8 時(shí)像素是怎么編碼的。

字節(jié) 1字節(jié) 2字節(jié) 3字節(jié) 4含? 義
?00?00???一行的結(jié)尾
?00?01???圖像的結(jié)尾
?00?02?dx?dy?移動(dòng)到(x+dx, y+dy)
?00?n = 03 到 FF???使用接下來(lái)的 n 個(gè)像素
?n = 01 到 FF?像素???重復(fù)像素 n 次
??????? 在對(duì)一個(gè)壓縮的 DIB 解碼時(shí),要把 DIB 字節(jié)數(shù)據(jù)成對(duì)看,就像上表中的“字節(jié) 1”和“字節(jié) 2”一樣。這個(gè)表格是按照字節(jié)的值從小到大排列的,但是從底向上地討論更方便一些。

??????? 如果第一個(gè)字節(jié)不為 0(如表中最后一行所示),那么它就是行程長(zhǎng)度編碼的重復(fù)因子它表示后面的像素?cái)?shù)據(jù)就重復(fù)這么多次。例如,以下這對(duì)字節(jié):

0x05 0x27就被解碼為如下像素值:
0x27 0x27 0x27 0x27 0x27

??????? DIB 中也會(huì)有很多不重復(fù)的像素值。這種情況由表格的倒數(shù)第二行來(lái)處理。它表明其后跟隨的若干像素就應(yīng)該直接使用。例如,以下序列:

0x00 0x06 0x45 0x32 0x77 0x34 0x59 0x90就被解碼為如下像素值:

0x45 0x32 0x77 0x34 0x59 0x90

??????? 這樣的序列總是與兩個(gè)字節(jié)的邊界對(duì)齊的。如果第二個(gè)字節(jié)是奇數(shù),那么在這個(gè)序列的結(jié)尾就會(huì)有一個(gè)不同的多余字節(jié)。例如,以下序列:

0x00 0x05 0x45 0x32 0x77 0x34 0x59 0x00就被解碼為如下像素值:

0x45 0x32 0x77 0x34 0x59

??????? 行程長(zhǎng)度編碼就是這么工作的。很明顯,如果 DIB 圖像中沒(méi)有重復(fù)像素,用這種壓縮技術(shù)反而會(huì)增加 DIB 文件的大小

??????? 表格的前三行指出了一個(gè)矩形 DIB 圖像中有些部分是可以沒(méi)有定義的。現(xiàn)在,假設(shè)要寫(xiě)一個(gè) DIB 解壓縮程序。在解壓縮過(guò)程中,會(huì)維護(hù)一對(duì)從(0, 0)開(kāi)始的數(shù)值(y, x)。每次解碼出一個(gè)像素,便使 x 加 1。每結(jié)束一行的解碼,你會(huì)把 y 加 1 并且把 x 重置為 0。

??????? 在遇到一個(gè) 0x00 字節(jié),后跟一個(gè) 0x02 字節(jié)時(shí),應(yīng)該讀入后面緊跟的兩個(gè)字節(jié)然后把它們作為無(wú)符號(hào)整數(shù)加到現(xiàn)在的 x 和 y 值上。在遇到一個(gè) 0x00 字節(jié)后面跟著一個(gè) 0x00 字節(jié)時(shí),這一行的解碼就結(jié)束了。應(yīng)該把 x 設(shè)置為 0 并把 y 加 1。在遇到一個(gè) 0x00 字節(jié)后面跟著一個(gè) 0x01 字節(jié)時(shí),整個(gè)圖像的解碼就算完成了。這樣的代碼可以讓 DIB 中包括未定義的區(qū)域。有時(shí)候這會(huì)很有用,例如說(shuō)定義一個(gè)非矩形的圖像,或者制作數(shù)字動(dòng)畫(huà)或電影的時(shí)候(因?yàn)槊恳粠袝?huì)包含很多在上一幀中已有的信息,這些信息沒(méi)有必要再重新編碼)。

??????? 對(duì) 4 位 DIB 來(lái)說(shuō),編碼的過(guò)程基本上是一樣的,但是會(huì)復(fù)雜一些,因?yàn)橄袼睾妥止?jié)不是一一對(duì)應(yīng)的

??????? 如果第一個(gè)字節(jié)不是 0,那么它就是重復(fù)因子 n。第二個(gè)字節(jié)包括兩個(gè)像素,它們會(huì)在解碼后的序列中交替重復(fù)至 n 個(gè)像素。例如,以下這對(duì)數(shù)據(jù):

0x07 0x35就被解碼為如下像素值:

0x35 0x35 0x35 0x3?其中的問(wèn)號(hào)表面這個(gè)像素還是未知的。如果前面 0x07 0x35 數(shù)據(jù)對(duì)之后是以下這對(duì)數(shù)據(jù):

0x05 0x24 那么整個(gè)解碼后的序列將如下所示:

0x35 0x35 0x35 0x32 0x42 0x42

??????? 如果一對(duì)數(shù)據(jù)的第一個(gè)字節(jié)是 0x00 而第二個(gè)字節(jié)為 0x03 或更大時(shí),解碼就要使用由第二個(gè)字節(jié)指定的數(shù)目的像素。例如,下面的序列:

0x00 0x05 0x23 0x57 0x10 0x00 就被解碼為:

0x23 0x57 0x1? 注意,編碼序列必須填補(bǔ)成偶數(shù)個(gè)字節(jié)。

??????? 只要 biCompression 字段是 BI_RLE4 或者 BI_RLE8,biSizeImage 字段就會(huì)存儲(chǔ)以字節(jié)為單位的 DIB 像素?cái)?shù)據(jù)大小。如果 biCompression 字段是 BI_RGB,biSizeImage 通常為 0,但是它也可以設(shè)置成 biHeight 和每一行字節(jié)數(shù)的乘積,就像我們?cè)诒菊虑懊嬗?jì)算過(guò)的一樣。

??????? 現(xiàn)在的文檔中注明:“從上到下的 DIB 不能被壓縮。所謂從上到下的 DIB 指的是那些 biHeight 字段為負(fù)值的圖像。

15.1.7? 顏色遮罩

??????? biCompression 字段還可以用在 Windows 95 新加入的 16 位和 32 位的 DIB 中。在這樣的 DIB 中,biCompression 字段可以是 BI_RGB 或者 BI_BITFIELDS(定義值為 3)。

??????? 讓我們先來(lái)回顧一下 24 位 DIB 的像素格式,其 biCompression 字段總是 BI_RGB:

每一行基本上就是一個(gè) RGBTRIPLE 結(jié)構(gòu)的數(shù)組, 只是在每一行結(jié)束時(shí)有可能會(huì)有一些填充的數(shù)據(jù)以使每行的字節(jié)數(shù)都是 4 的倍數(shù)

??????? 對(duì)一個(gè) biCompression 為 BI_RGB 的 16 位 DIB 來(lái)說(shuō),每個(gè)像素需要兩個(gè)字節(jié)。顏色編碼如下:

每個(gè)顏色用 5 位來(lái)表示。對(duì)每行的第一個(gè)像素來(lái)說(shuō),藍(lán)色值是第一個(gè)字節(jié)的最低 5 位。綠色值需要用到兩個(gè)字節(jié)中的二進(jìn)制位:它的最高兩位是第二個(gè)字節(jié)的最低兩位,它的低三位是第一個(gè)字節(jié)的最高三位。紅色值是第二個(gè)字節(jié)的 2 到 6 位。第二個(gè)字節(jié)的最高位為 0。

??????? 在用一個(gè) 16 位的 WORD 型整數(shù)來(lái)存取像素值時(shí),這就非常清楚了。因?yàn)橐粋€(gè)多字節(jié)的數(shù)值存儲(chǔ)總是把最低位的字節(jié)存在前面,這個(gè)像素的數(shù)值就會(huì)是下面這樣:

??????? 假如有一個(gè) 16 位的像素儲(chǔ)存在 wPixel 變量中,可以按照下面的公式來(lái)計(jì)算紅、綠、藍(lán)色的值:

Red = ((0x7C00 & wPixel) >> 10) << 3; Greeen = ((0x03E0 & wPixel) >> 5) << 3; Blue = ((0x001F & wPixel) >> 0) << 3;

??????? 首先,這個(gè)像素要和一個(gè)遮罩作一個(gè)按位與操作,結(jié)果再向右位移。對(duì)紅色來(lái)說(shuō),右移 10 位,綠色右移 5 位,藍(lán)色右移 0 位。我會(huì)把這些位移值叫做“右移”值。這樣產(chǎn)生的顏色值在 0x00~0x1F 之間。這個(gè)值還需要左移三位以使其顏色值在 0x00 到 0xF8 之間。我會(huì)把這些位移值稱為“左移”值。

??????? 還有一點(diǎn)要記住:如果一個(gè) 16 位 DIB 的像素寬度是奇數(shù),那么每一行就會(huì)有 2 個(gè)字節(jié)填充在最后以使每一行的字節(jié)數(shù)是 4 的倍數(shù)。

??????? 對(duì) 32 位的 DIB 來(lái)說(shuō),如果 biCompression 等于 BI_RGB,那么每一個(gè)像素就需要 4 字節(jié)。藍(lán)色值是第一個(gè)字節(jié),綠色是第二個(gè),紅色是第三個(gè),第四個(gè)字節(jié)為 0。換言之,像素就是一個(gè) RGBQUAD 結(jié)構(gòu)的數(shù)組。因?yàn)槊總€(gè)像素都是 4 字節(jié),所以每一行的結(jié)尾永遠(yuǎn)不需要填充。

??????? 如果把每個(gè)像素視為一個(gè) 32 位的雙字,則會(huì)是下面這樣:

??????? 或者,如果 dwPixel 是一個(gè) 32 位的雙字,則像下面這樣:

Red = ((0x00FF0000 & dwPixel) >> 16) << 0; Green = ((0x0000FF00 & dwPixel) >> 8) << 0; Blue = ((0x000000ff & dwPixel) >> 0) << 0; 左移位全部是 0,這是因?yàn)轭伾狄呀?jīng)可以到最大的 0xFF 了。注意,這個(gè) 32 位值和 GDI 函數(shù)中用于指定 RGB 顏色的 32 位 COLORREF 并不一樣。 在 COLORREF 值中,紅色在最低字節(jié)

??????? 到現(xiàn)在為止,我們已經(jīng)介紹了 16 位和 32 位 DIB 當(dāng) biCompression 字段為 BI_RGB 時(shí)的默認(rèn)情況。如果 biCompression 字段是 BI_BITFIELDS,那么在 BITMAPINFOHEADER 結(jié)構(gòu)之后緊跟著的是 3 個(gè) 32 位的顏色遮罩,第一個(gè)用于紅色,第二個(gè)用于綠色,第三個(gè)用于藍(lán)色。可以用 C 語(yǔ)言中的按位與操作符(&)來(lái)把這些遮罩加到 16 位或 32 位的像素值上。然后可以右移來(lái)得到最終結(jié)果。可惜,右移的位數(shù)必須從遮罩中得到。仔細(xì)想一下,顏色遮罩的規(guī)則應(yīng)該是很明顯的:每一個(gè)顏色遮罩中 1 的位必須是連續(xù)的,而且 3 個(gè)遮罩中為 1 的位不能互相重疊。

??????? 讓我們來(lái)看一個(gè)例子吧。假設(shè)有一個(gè) 16 位的 DIB,而且 biCompression 字段為 BI_BITFIELDS。檢查一下 BITMAPINFOHEADER 結(jié)構(gòu)之后的 3 個(gè) 32 位數(shù):

0x0000F800 0x000007E0 0X0000001F

??????? 注意,只有在最低的 16 位中才可以設(shè)置為 1,因?yàn)檫@時(shí)一個(gè) 16 位的 DIB。把這三個(gè)值保存到變量 dwMask[0]、dwMask[1]和 dwMask[2]中。現(xiàn)在,需要寫(xiě)兩個(gè)函數(shù)來(lái)從這些遮罩中計(jì)算出右移和左移的位數(shù):

int MaskToRShift (DWORD dwMask) {int iShift ;if ( dwMask == 0)return 0 ;for ( iShift = 0 ; !(dwMask & 1) ; iShift++)dwMask >>= 1 ;return iShift ; }int MaskToLShift (DWORD dwMask) {int iShift ;if ( dwMask == 0)return 0 ;while (!(dwMask & 1))dwMask >>= 1 ;for (iShift = 0 ; dwMask & 1 ; iShift++)dwMask >>= 1 ;return 8 - iShift ; } 然后調(diào)用 MaskToRShift 函數(shù) 3 次來(lái)得到右移的位數(shù):

iRShift[0] = MaskToRShift (dwMask[0]) ; iRShift[1] = MaskToRShift (dwMask[1]) ; iRShift[2] = MaskToRShift (dwMask[2]) ;分別得到 11、5 和 0。然后可以類(lèi)似地調(diào)用 MaskToLShift:

iLShift[0] = MaskToLShift (dwMask[0]) ; iLShift[1] = MaskToLShift (dwMask[1]) ; iLShift[2] = MaskToLShift (dwMask[2]) ; 分別得到 3、2 和 3。現(xiàn)在便可以從像素值從得到顏色值了:

Red = ((dwMask[0] & wPixel) >> iRShift[0]) << iLShift[0] ; Green = ((dwMask[1] & wPixel) >> iRShift[1]) << iLShift[1] ; Blue = ((dwMask[2] & wPixel) >> iRShift[2]) << iLShift[2] ; 對(duì) 32 位 DIB 來(lái)說(shuō),過(guò)程一樣,只是顏色遮罩可以大于 16 位 DIB 所能允許的最大值 0x0000FFFF。

??????? 注意,不管是 16 位還是 32 位的 DIB,紅、綠、藍(lán)的顏色值都可以大于 255。事實(shí)上,在 32 位 DIB 中,如果兩個(gè)顏色的遮罩都是 0,那么第三個(gè)可以是 0xFFFFFFFF,也就是一個(gè) 32 位的顏色值!當(dāng)然,這在某種意義上說(shuō)有些可笑,所以我不會(huì)太為此而擔(dān)心。

??????? 與 Windows NT 不同,Windows 95 和 Windows 98 對(duì)顏色遮罩有更嚴(yán)格的規(guī)定。允許的數(shù)值如下表所示。

?16 位 DIB16 位 DIB32 位 DIB
?紅色遮罩?0x00007C00?0x0000F800?0x00FF0000
?綠色遮罩?0x000003E0?0x000007E0?0x0000FF00
?藍(lán)色遮罩?0x0000001F?0x0000001F?0x000000FF
?簡(jiǎn)寫(xiě)?5-5-5?5-6-5?8-8-8
??????? 換言之,當(dāng) biCompression 是 BI_RGB 時(shí),可以用默認(rèn)的兩套遮罩,再加上我在上面例子中所用的那一套遮罩。表的最下一行給出了一個(gè)簡(jiǎn)寫(xiě)形式,指明了每個(gè)像素種紅、綠和藍(lán)色所用的位數(shù)。

15.1.8? 版本 4 的文件頭

??????? 我們還遠(yuǎn)未結(jié)束呢。正如我提到的,Windows 95 改變了最早的 BITMAPINFOHEADER 結(jié)構(gòu)中有些字段的含義,而且還包括了一個(gè)擴(kuò)展的信息頭,叫做 BITMAPV4HEADER。當(dāng)你意識(shí)到 Windows 95 可能會(huì)被命名為 Windows 4.0,而且 Windows NT 4.0 也支持這個(gè)結(jié)構(gòu)時(shí),你就會(huì)明白為什么這個(gè)結(jié)構(gòu)會(huì)有這樣的名稱。

typedef struct {DWORD bV4Size ; ?? ??? ? // 結(jié)構(gòu)的大小 = 120LONG bV4Width ; ?? ??? ?// 以像素為單位的圖像寬度LONG bV4Height ; ?? ??? // 以像素為單位的圖像高度WORD bV4Planes ; ?? ??? // = 1WORD bV4BitCount ; ?? ? // 每像素位數(shù) (1, 4, 8, 16, 24, or 32)DWORD bV4Compression ;? // 壓縮編碼DWORD bV4SizeImage ; ?? // 圖像字節(jié)數(shù)LONG bV4XPelsPerMeter ; // 水平分辨率LONG bV4YPelsPerMeter ; // 垂直分辨率DWORD bV4ClrUsed ; ?? ? // 用到的顏色數(shù)DWORD bV4ClrImportant ; // 重要的顏色數(shù)DWORD bV4RedMask ; ?? ? // 紅色遮罩DWORD bV4GreenMask ; ?? // 綠色遮罩DWORD bV4BlueMask ; ?? ? // 藍(lán)色遮罩DWORD bV4AlphaMask ; ?? // 阿爾法遮罩DWORD bV4CSType ; ?? ?? // 色彩空間類(lèi)型CIEXYZTRIPLE bV4Endpoints ; // XYZ 值DWORD bV4GammaRed ; ?? ? // 紅色伽瑪值DWORD bV4GammaGreen ; ? // 綠色伽瑪值DWORD bV4GammaBlue ; ?? // 藍(lán)色伽瑪值 } BITMAPV4HEADER, * PBITMAPV4HEADER ;

??????? 請(qǐng)注意,前面的 11 個(gè)字段和 BITMAPINFOHEADER 結(jié)構(gòu)一樣。最后 5 個(gè)字段用于支持 Windows 95 和 Windows NT 4.0 的顏色匹配(color-matching)技術(shù)。如果用不到 BITMAPV4HEADER 結(jié)構(gòu)的最后 4 個(gè)字段,便應(yīng)該用 BITMAPINFOHEADER(或 BITMAPV5HEADER)。

??????? bV4RedMask、bV4GreenMask 和 bV4BlueMask 只適用于 bV4Compression 字段為 BI_BITFIELDS 的 16 位或 32 位 DIB。它們和 BITMAPINFOHEADER 結(jié)構(gòu)之后的顏色遮罩的功能一樣,而且事實(shí)上也出現(xiàn)在 DIB 文件的同樣位置,只不過(guò)這里它們是明確命名的字段而已。而 bV4AlphaMask 字段就我所知沒(méi)有被用到。

??????? BITMAPV4HEADER 結(jié)構(gòu)的其余字段牽涉到 Windows 的圖像顏色管理系統(tǒng)(Windows Image Color Management),這恐怕超出了本書(shū)的范圍。但是,一些背景知識(shí)也許可以幫你入門(mén)。

??????? 用 RGB 來(lái)表示顏色有一個(gè)缺陷,就是它和顯示器、彩色照相機(jī)和彩色掃描儀的技術(shù)密切相關(guān)。如果一個(gè)顏色的 RGB 值為(255, 0, 0),它僅僅意味著在一個(gè)陰極射線管中的紅色電子槍?xiě)?yīng)該施加最大的電壓。一個(gè)為(128, 0, 0)的 RGB 值意味著應(yīng)該施加一半的電壓。但是不同的顯示器反應(yīng)卻是可以不一樣的。更重要的是,打印機(jī)用的顏色模式是不一樣的,它是由青色(Cyan)、品紅色(Magenta)、黃色(Yellow)和黑色(Black)混合成的。這個(gè)顏色模式也稱為CMY(cyan-magenta-yellow)和CMYK(cyan-magenta-yellow-black)。有數(shù)學(xué)公式可以把 RGB 值轉(zhuǎn)換成 CMY 和 CMYK 值但是沒(méi)法保證打印出來(lái)的顏色會(huì)和顯示出來(lái)的顏色一樣。圖像顏色管理系統(tǒng)就是要把顏色和一個(gè)設(shè)備無(wú)關(guān)的標(biāo)準(zhǔn)聯(lián)系起來(lái)。

??????? 顏色現(xiàn)象和可見(jiàn)光的波長(zhǎng)有關(guān),其范圍在 380nm(藍(lán)色)到 780nm(紅色)之間。我們能夠看見(jiàn)的光都是由在可見(jiàn)光光譜內(nèi)的不同數(shù)量、不同波長(zhǎng)的光組成的。1931 年,國(guó)際照明委員會(huì)(Commission Internationale de l′Eclairage,CIE)發(fā)布了一個(gè)科學(xué)的定量顏色的方法。這個(gè)方法使用了三個(gè)顏色匹配函數(shù)(稱為x, y, z)來(lái)描述一般人對(duì)不同光的波長(zhǎng)的反應(yīng),其精簡(jiǎn)形式(每隔 5nm 一個(gè)值)出版在 CIE 刊物 15.2-1986,《比色法,第 2 版》,表 2.1 中。

??????? 一個(gè)顏色的頻譜(Spectrum, S)是一組值,表明了每個(gè)波長(zhǎng)的強(qiáng)度。如果知道了頻譜,那么前面提到的三個(gè)顏色匹配函數(shù)就可以用來(lái)計(jì)算出 X, Y 和 Z:

這三個(gè)值稱為大 X、大 Y 和大 Z。y 顏色匹配函數(shù)相當(dāng)于人眼對(duì)可見(jiàn)光譜內(nèi)的光線強(qiáng)度的反應(yīng)。(它看起來(lái)像一個(gè)鈴鐺狀的曲線,在 380nm 和 780nm 時(shí)為 0。)因?yàn)樗砹斯饩€的總體強(qiáng)度,所以 Y 被稱為 CIE 亮度(CIE Luminance)

??????? 如果要用 BITMAPV5HEADER 結(jié)構(gòu),bV4CSType 字段就必須設(shè)置為 LCS_CALIBRATED_RGB,其值定義為 0。接下來(lái)的 4 個(gè)字段必須設(shè)置為合理的值。

??????? CIEXYZTRIPLE 結(jié)構(gòu)定義如下:

typedef struct tagCIEXYZTRIPLE {CIEXYZ ciexyzRed ;CIEXYZ ciexyzGreen ;CIEXYZ ciexyzBlue ; } CIEXYZTRIPLE, * LPCIEXYZTRIPLE ;其中的 CIEXYZ 結(jié)構(gòu)如下:

typedef struct tagCIEXYZ {FXPT2DOT30 ciexyzX ;FXPT2DOT30 ciexyzY ;FXPT2DOT30 ciexyzZ ; } CIEXYZ, * LPCIEXYZ ;它的三個(gè)字段定義為 FXPT2DOT30 類(lèi)型也就是說(shuō)它們是定點(diǎn)(fixed-point)數(shù)值,整數(shù)部分是 2 位而小數(shù)部分為 30 位。所以,0x40000000 就表示 1.0, 0x48000000 是 1.125。最大值是 0xFFFFFFFF,比 4.0 小一點(diǎn)點(diǎn)。

??????? bV4Endpoints 字段提供了三個(gè) X、Y 和 Z 的值,對(duì)應(yīng)于 RGB 顏色的 (255, 0, 0),(0, 255, 0)和(0, 0, 255)。這三個(gè)值應(yīng)該在創(chuàng)建 DIB 時(shí),由創(chuàng)建它的應(yīng)用程序加入進(jìn)來(lái),用以指明獨(dú)立于設(shè)備的 RGB 顏色的含義。

??????? BITMAPV4HEADER 剩下的三個(gè)字段是“伽瑪”值(Gamma)。伽瑪(小寫(xiě)希臘字母 γ)指的是顏色值的非線性屬性。在 DIB 中,紅、綠、藍(lán)色的范圍是 0 到 255。在顯示適配器上,這三個(gè)數(shù)值被轉(zhuǎn)換成三個(gè)模擬電壓輸出到顯示器上。這個(gè)電壓決定了每個(gè)像素的強(qiáng)度。但是,由于陰極射線管中電子槍發(fā)射出的電子的特點(diǎn),像素的強(qiáng)度(intensity)I 和電壓(voltage)V 的關(guān)系并不是線性的,而是

其中,ε 是顯示器的黑電平(Black Level),由顯示器的亮度控制器決定(最好為 0)指數(shù) γ 由顯示器的對(duì)比度或圖像控制器決定對(duì)絕大多數(shù)顯示器來(lái)說(shuō),γ 大約是 2.5

??????? 為了補(bǔ)償這種非線性關(guān)系,攝像機(jī)傳統(tǒng)上都要包括一個(gè)“伽瑪校正”的功能。進(jìn)入攝像機(jī)的光線要被一個(gè) 0.45 的指數(shù)修改。這意味著顯示器的伽瑪值大約是 2.2(1/0.45)。(更高的伽瑪值會(huì)增加對(duì)比度,但通常這不是我們想要的,因?yàn)楸尘肮鈺?huì)降低對(duì)比度。)

??????? 實(shí)際上,顯示器的這個(gè)非線性特征比它看起來(lái)要恰當(dāng)?shù)枚?/span>。這是因?yàn)槿藢?duì)光的反應(yīng)也是非線性的。我前面提到 Y 被稱為 CIE 亮度。這是對(duì)光的一個(gè)線性度量。CIE 也定義了一個(gè)接近人感知的光強(qiáng)數(shù)值 L*(英語(yǔ)發(fā)音為 “ell star”),它可以用下面的公式從 Y 計(jì)算出來(lái):

其中,Yn 是白電平(White Level)。公式的第一部分是一個(gè)線性關(guān)系。總體來(lái)說(shuō),人對(duì)光的感知與光亮度的立方根是線性關(guān)系,這是由第二個(gè)公式表示的。L* 的范圍從 0 到 100。L* 的每個(gè)整數(shù)級(jí)的變化,一般被認(rèn)為是人能察覺(jué)的最小光亮變化

??????? 在程序中,我們最好是根據(jù)人對(duì)光的感應(yīng)而不是線性光亮來(lái)調(diào)節(jié)光的強(qiáng)度。這樣我們可以把像素位數(shù)降至一個(gè)合理的水平并且可以減少模擬電路中的噪音。

??????? 讓我們來(lái)看一看整個(gè)過(guò)程吧。像素值(P)的范圍是從 0 到 255。這個(gè)數(shù)值被線性地轉(zhuǎn)換成電壓,我們可以假設(shè)它被規(guī)范化到 0.0 至 1.0 之間。假設(shè)顯示器的黑電平被設(shè)置為 0,則該像素點(diǎn)的強(qiáng)度如下:

其中,γ 大約為 2.5。人的感應(yīng)光亮(L*)基于這個(gè)強(qiáng)度的立方根,而且范圍從 0 至 100,那么近似地:

那個(gè)指數(shù)約為 0.85 左右。如果指數(shù)是 1,那么 CIE 亮度就和像素值完美匹配了,盡管事實(shí)并非如此,但它還是比用像素值代表線性光亮更接近一些。

??????? BITMAPV4HEADER 的最后三個(gè)字段為創(chuàng)建 DIB 的程序提供了一個(gè)方式來(lái)指明像素值應(yīng)該具有的伽瑪值。這個(gè)值有 16 位整數(shù)部分和 16 位小數(shù)部分。例如,0x10000 是 1.0。如果 DIB 是一個(gè)由真實(shí)世界拍攝出的圖像,這個(gè)伽瑪值很可能由拍攝硬件隱含指定,并且大概是 2.2(編碼為 0x23333)如果 DIB 是由程序算法生成的,這個(gè)程序一般會(huì)用一個(gè)指數(shù)函數(shù)把所有線性光亮轉(zhuǎn)換成 CIE 光亮。該指數(shù)的倒數(shù)就是 DIB 中的伽瑪值

15.1.9? 版本 5 的頭文件

??????? 為 Windows 98 和 Windows NT 5.0 寫(xiě)的程序可以使用一個(gè)新的 BITMAPV5HEADER 結(jié)構(gòu),如下所示:

typedef struct {DWORD bV5Size; // 結(jié)構(gòu)的大小= 120LONG bV5Width; // 以像素為單位的圖像寬度LONG bV5Height; // 以像素為單位的圖像高度WORD bV5Planes; // = 1WORD bV5BitCount; // 每像素位數(shù)(1, 4, 8, 16, 24 或 32)DWORD bV5Compression; // 壓縮編碼DWORD bV5SizeImage; // 圖像字節(jié)數(shù)LONG bV5XPelsPerMeter; // 水平分辨率LONG bV5YPelsPerMeter; // 垂直分辨率DWORD bV5ClrUsed; // 用到的顏色數(shù)DWORD bV5ClrImpportant; // 重要顏色數(shù)DWORD bV5RedMask; // 紅色遮罩DWORD bV5GreenMask; // 綠色遮罩DWORD bV5BlueMask; // 藍(lán)色遮罩DWORD bV5AlphaMask; // 阿爾法遮罩CIEXYZTRIPLE bV5Endpoints; // XYZ 值DWORD bV5GammaRed; // 紅色伽瑪值DWORD bV5GammaGreen; // 綠色伽瑪值DWORD bV5GammaBlue; // 藍(lán)色伽瑪值DWORD bV5Intent; // 渲染意圖DWORD bV5ProfileData; // 顏色配置數(shù)據(jù)或文件名DWORD bV5ProfileSize; // 內(nèi)嵌數(shù)據(jù)或文件名的大小DWORD bV5Reserved; } BITMAPV5HEADER, * PBITMAPV5HEADER 它有 4 個(gè)新字段,實(shí)際上只用到三個(gè)。這些字段支持由國(guó)際色彩協(xié)會(huì)(International Color Consortium,ICC,由 Adobe、Agfa、Apple、Kodak、Microsoft、Silicon Graphics 和 Sun Microsystems 等創(chuàng)立)提出的一個(gè)叫 ICC 顏色配置文件格式規(guī)范(ICC Profile Format Specification)的提案。可以從? http://www.icc.org 得到這個(gè)提案的副本。 簡(jiǎn)單地講,每個(gè)輸入設(shè)備(掃描器或照相機(jī))、輸出設(shè)備(打印機(jī)或膠片記錄器)和顯示設(shè)備(顯示器)都和一個(gè)顏色配置文件(profile)聯(lián)系在一起。這個(gè)顏色配置文件把設(shè)備相關(guān)的顏色值(一般是 RGB 或 CMYK)與一個(gè)設(shè)備無(wú)關(guān)的顏色規(guī)范(最終基于 CIE 的 XYZ 值)聯(lián)系在一起。顏色配置文件的文件擴(kuò)展名是 .ICM(Image Color Management, 圖像顏色管理)。顏色配置文件可以內(nèi)嵌在 DIB 文件中或者鏈接到 DIB 文件中以表明 DIB 是怎么被創(chuàng)建出來(lái)的。 可以從 MSDN 文檔的“/Platform SDK/Graphics and Multimedia Service/Color Management”(譯注:MSDN Library/Win32 and COM Development/ Graphics and Multimedia/Windows Color System)部分進(jìn)一步了解 Windows 圖像顏色管理

??????? BITMAPV5HEADER 中的 bV5CSType 字段可以采用幾個(gè)不同的數(shù)值。如果它是 LCS_CALIBRATED_RGB,則它就是和 BITMAPV4HEADER 結(jié)構(gòu)兼容的。相應(yīng)的 bV5Endpoints 字段和伽瑪字段必須是合理值。

??????? 如果 bV5CSType 字段是 LCS_sRGB,則所有剩余的字段都不需要設(shè)置。這意味著這個(gè) DIB 的色彩空間是微軟和惠普提出的“標(biāo)準(zhǔn)”RGB色彩空間。這個(gè)標(biāo)準(zhǔn)色彩空間的目標(biāo)是要提供一定的設(shè)備無(wú)關(guān)性,不要顏色配置文件,特別適用于因特網(wǎng)。http://www.color.org/contrib/sRGB.html 有它的詳細(xì)文檔。

??????? 如果 bV5CSType 字段是 LCS_WINDOWS_COLOR_SPACE,那么所有剩余字段也都不需要設(shè)置。Windows 會(huì)使用顯示位圖的 API 函數(shù)所用到的色彩空間。

??????? 如果 bV5CSType 字段是 PROFILE_EMBEDDED,那么說(shuō)明 DIB 文件中包含一個(gè) ICC 顏色配置文件。如果該字段是 PROFILE_LINKED,那么說(shuō)明 DIB 文件
中包含一個(gè) ICC 顏色配置文件的完整文件名。無(wú)論是這兩種情況的哪一種,bV5ProfileData 都是從 BITMAPV5HEADER 的開(kāi)頭到顏色配置文件數(shù)據(jù)或文件名開(kāi)始的位移量。bV5ProfileSize 字段給出了顏色配置文件數(shù)據(jù)或文件名的大小。不需要設(shè)置 bV5Endpoints 和伽瑪字段。

15.1.10? 顯示 DIB 信息

??????? 現(xiàn)在是時(shí)候來(lái)看一下代碼了。我們還沒(méi)有足夠的知識(shí)來(lái)顯示 DIB,但是我們至少可以從文件頭結(jié)構(gòu)中得到 DIB 的信息并把這些信息顯示出來(lái)。DIBHEADS 程序就是為此設(shè)計(jì)的。

/*-------------------------------------------------DIBHEADS.C -- Displays DIB Header Information(c) Charles Petzold, 1998 --------------------------------------------------*/#include <windows.h> #include "resource.h"LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);TCHAR szAppName[] = TEXT("DibHeads");int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevinstance,PSTR szCmdLine, int iCmdShow) {HACCEL hAccel;HWND hwnd;MSG msg;WNDCLASS wndclass;wndclass.style = CS_HREDRAW | CS_VREDRAW;wndclass.lpfnWndProc = WndProc;wndclass.cbClsExtra = 0;wndclass.cbWndExtra = 0;wndclass.hInstance = hInstance;wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);wndclass.lpszMenuName = szAppName;wndclass.lpszClassName = szAppName;if (!RegisterClass(&wndclass)){MessageBox(NULL, TEXT("This program requires Windows NT!"),szAppName, MB_ICONERROR);return 0;}hwnd = CreateWindow(szAppName, TEXT("DIB Headers"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL);ShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd);hAccel = LoadAccelerators(hInstance, szAppName);while (GetMessage(&msg, NULL, 0, 0)){if (!TranslateAccelerator(hwnd, hAccel, &msg)){TranslateMessage(&msg);DispatchMessage(&msg);}}return msg.wParam; }void Printf(HWND hwnd, TCHAR * szFormat, ...) {TCHAR szBuffer[1024];va_list pArgList;va_start(pArgList, szFormat);wvsprintf(szBuffer, szFormat, pArgList);va_end(pArgList);SendMessage(hwnd, EM_SETSEL, (WPARAM)-1, (LPARAM)-1);SendMessage(hwnd, EM_REPLACESEL, FALSE, (LPARAM)szBuffer);SendMessage(hwnd, EM_SCROLLCARET, 0, 0); }void DisplayDibHeaders(HWND hwnd, TCHAR * szFileName) {static TCHAR * szInfoName[] = { TEXT("BITMAPCOREHEADER"),TEXT("BITMAPINFOHEADER"),TEXT("BITMAPV4HEADER"),TEXT("BITMAPV5HEADER") };static TCHAR * szCompression[] = { TEXT("BI_RGB"), TEXT("BI_RLE8"),TEXT("BI_RLE4"),TEXT("BI_BITFIELDS"),TEXT("unknown") };BITMAPCOREHEADER * pbmch;BITMAPFILEHEADER * pbmfh;BITMAPV5HEADER * pbmih;BOOL bSuccess;DWORD dwFileSize, dwHighSize, dwBytesRead;HANDLE hFile;int i;PBYTE pFile;TCHAR * szV;// Display the file namePrintf(hwnd, TEXT("File: %s\r\n\r\n"), szFileName);// Open the filehFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);if (hFile == INVALID_HANDLE_VALUE){Printf(hwnd, TEXT("Cannot open file.\r\n\r\n"));return;}// Get the size of the filedwFileSize = GetFileSize(hFile, &dwHighSize);if (dwHighSize){Printf(hwnd, TEXT("Cannot deal with > 4G files.\r\n\r\n"));CloseHandle(hFile);return;}// Allocate memory for the filepFile = (PBYTE)malloc(dwFileSize);if (!pFile){Printf(hwnd, TEXT("Cannot allocate memory.\r\n\r\n"));CloseHandle(hFile);return;}// Read the fileSetCursor(LoadCursor(NULL, IDC_WAIT));ShowCursor(TRUE);bSuccess = ReadFile(hFile, pFile, dwFileSize, &dwBytesRead, NULL);ShowCursor(FALSE);SetCursor(LoadCursor(NULL, IDC_ARROW));if (!bSuccess || (dwBytesRead != dwFileSize)){Printf(hwnd, TEXT("Could not read file.\r\n\r\n"));CloseHandle(hFile);free(pFile);return;}// Close the fileCloseHandle(hFile);// Display file sizePrintf(hwnd, TEXT("File size = %u bytes\r\n\r\n"), dwFileSize);// Display BITMAPFILEHEADER structurepbmfh = (BITMAPFILEHEADER *)pFile;Printf(hwnd, TEXT("BITMAPFILEHEADER\r\n"));Printf(hwnd, TEXT("\t.bfType = 0x%X\r\n"), pbmfh->bfType);Printf(hwnd, TEXT("\t.bfSize = %u\r\n"), pbmfh->bfSize);Printf(hwnd, TEXT("\t.bfReserved1 = %u\r\n"), pbmfh->bfReserved1);Printf(hwnd, TEXT("\t.bfReserved2 = %u\r\n"), pbmfh->bfReserved2);Printf(hwnd, TEXT("\t.bfOffBits = %u\r\n\r\n"), pbmfh->bfOffBits);// Determine which information structure we havepbmih = (BITMAPV5HEADER *)(pFile + sizeof(BITMAPFILEHEADER));switch (pbmih->bV5Size){case sizeof(BITMAPCOREHEADER) : i = 0; szV = TEXT(""); break;case sizeof(BITMAPINFOHEADER) : i = 1; szV = TEXT("i"); break;case sizeof(BITMAPV4HEADER) : i = 2; szV = TEXT("V4"); break;case sizeof(BITMAPV5HEADER) : i = 3; szV = TEXT("V5"); break;default:Printf(hwnd, TEXT("Unknown header size of %u.\r\n\r\n"), pbmih->bV5Size);free(pFile);return;}Printf(hwnd, TEXT("%s\r\n"), szInfoName[i]);// Display the BITMAPCOREHEADER fieldsif (pbmih->bV5Size == sizeof(BITMAPCOREHEADER)){pbmch = (BITMAPCOREHEADER *)pbmih;Printf(hwnd, TEXT("\t.bcSize = %u\r\n"), pbmch->bcSize);Printf(hwnd, TEXT("\t.bcWidth = %u\r\n"), pbmch->bcWidth);Printf(hwnd, TEXT("\t.bcHeight = %u\r\n"), pbmch->bcHeight);Printf(hwnd, TEXT("\t.bcPlanes = %u\r\n"), pbmch->bcPlanes);Printf(hwnd, TEXT("\t.bcBitCount = %u\r\n\r\n"), pbmch->bcBitCount);free(pFile);return;}// Display the BITMAPINFOHEADER fieldsPrintf(hwnd, TEXT("\t.b%sSize = %u\r\n"), szV, pbmih->bV5Size);Printf(hwnd, TEXT("\t.b%sWidth = %i\r\n"), szV, pbmih->bV5Width);Printf(hwnd, TEXT("\t.b%sHeight = %i\r\n"), szV, pbmih->bV5Height);Printf(hwnd, TEXT("\t.b%sPlanes = %u\r\n"), szV, pbmih->bV5Planes);Printf(hwnd, TEXT("\t.b%sBitCount=%u\r\n"), szV, pbmih->bV5BitCount);Printf(hwnd, TEXT("\t.b%sCompression = %s\r\n"), szV,szCompression[min(4, pbmih->bV5Compression)]);Printf(hwnd, TEXT("\t.b%sSizeImage= %u\r\n"), szV,pbmih->bV5SizeImage);Printf(hwnd, TEXT("\t.b%sXPelsPerMeter = %i\r\n"), szV,pbmih->bV5XPelsPerMeter);Printf(hwnd, TEXT("\t.b%sYPelsPerMeter = %i\r\n"), szV,pbmih->bV5YPelsPerMeter);Printf(hwnd, TEXT("\t.b%sClrUsed = %i\r\n"), szV,pbmih->bV5ClrUsed);Printf(hwnd, TEXT("\t.b%sClrImportant = %i\r\n\r\n"), szV,pbmih->bV5ClrImportant);if (pbmih->bV5Size == sizeof(BITMAPINFOHEADER)){if (pbmih->bV5Compression == BI_BITFIELDS){Printf(hwnd, TEXT("Red Mask = %08X\r\n"),pbmih->bV5RedMask);Printf(hwnd, TEXT("Green Mask = %08X\r\n"),pbmih->bV5GreenMask);Printf(hwnd, TEXT("Blue Mask = %08X\r\n\r\n"),pbmih->bV5BlueMask);}free(pFile);return;}// Display additional BITMAPV4HEADER fieldsPrintf(hwnd, TEXT("\t.b%sRedMask = %08X\r\n"), szV,pbmih->bV5RedMask);Printf(hwnd, TEXT("\t.b%sGreenMask = %08X\r\n"), szV,pbmih->bV5GreenMask);Printf(hwnd, TEXT("\t.b%sBlueMask = %08X\r\n"), szV,pbmih->bV5BlueMask);Printf(hwnd, TEXT("\t.b%sAlphaMask = %08X\r\n"), szV,pbmih->bV5AlphaMask);Printf(hwnd, TEXT("\t.b%sCSType = %u\r\n"), szV,pbmih->bV5CSType);Printf(hwnd, TEXT("\t.b%sEndpoints.ciexyzRed.ciexyzX = %08X\r\n"),szV, pbmih->bV5Endpoints.ciexyzRed.ciexyzX);Printf(hwnd, TEXT("\t.b%sEndpoints.ciexyzRed.ciexyzY = %08X\r\n"),szV, pbmih->bV5Endpoints.ciexyzRed.ciexyzY);Printf(hwnd, TEXT("\t.b%sEndpoints.ciexyzRed.ciexyzZ = %08X\r\n"),szV, pbmih->bV5Endpoints.ciexyzRed.ciexyzZ);Printf(hwnd, TEXT("\t.b%sEndpoints.ciexyzGreen.ciexyzX = %08X\r\n"),szV, pbmih->bV5Endpoints.ciexyzGreen.ciexyzX);Printf(hwnd, TEXT("\t.b%sEndpoints.ciexyzGreen.ciexyzY = %08X\r\n"),szV, pbmih->bV5Endpoints.ciexyzGreen.ciexyzY);Printf(hwnd, TEXT("\t.b%sEndpoints.ciexyzGreen.ciexyzZ = %08X\r\n"),szV, pbmih->bV5Endpoints.ciexyzGreen.ciexyzZ);Printf(hwnd, TEXT("\t.b%sEndpoints.ciexyzBlue.ciexyzX = %08X\r\n"),szV, pbmih->bV5Endpoints.ciexyzBlue.ciexyzX);Printf(hwnd, TEXT("\t.b%sEndpoints.ciexyzBlue.ciexyzY = %08X\r\n"),szV, pbmih->bV5Endpoints.ciexyzBlue.ciexyzY);Printf(hwnd, TEXT("\t.b%sEndpoints.ciexyzBlue.ciexyzZ = %08X\r\n"),szV, pbmih->bV5Endpoints.ciexyzBlue.ciexyzZ);Printf(hwnd, TEXT("\t.b%sGammaRed = %08X\r\n"), szV,pbmih->bV5GammaRed);Printf(hwnd, TEXT("\t.b%sGammaGreen = %08X\r\n"), szV,pbmih->bV5GammaGreen);Printf(hwnd, TEXT("\t.b%sGammaBlue = %08X\r\n\r\n"), szV,pbmih->bV5GammaBlue);if (pbmih->bV5Size == sizeof(BITMAPV4HEADER)){free(pFile);return;}// Display additional BITMAPV5HEADER fieldsPrintf(hwnd, TEXT("\t.b%sIntent = %u\r\n"), szV,pbmih->bV5Intent);Printf(hwnd, TEXT("\t.b%sProfileData = %u\r\n"), szV,pbmih->bV5ProfileData);Printf(hwnd, TEXT("\t.b%sProfileSize = %u\r\n"), szV,pbmih->bV5ProfileSize);Printf(hwnd, TEXT("\t.b%sReserved = %u\r\n\r\n"), szV,pbmih->bV5Reserved);free(pFile);return; }LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {static HWND hwndEdit;static OPENFILENAME ofn;static TCHAR szFileName[MAX_PATH], szTitleName[MAX_PATH];static TCHAR szFilter[] = TEXT("Bitmap Files (*.BMP)\0*.bmp\0")TEXT("All Files (*.*)\0*.*\0\0");switch (message){case WM_CREATE:hwndEdit = CreateWindow(TEXT("edit"), NULL,WS_CHILD | WS_VISIBLE | WS_BORDER |WS_VSCROLL | WS_HSCROLL |ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY,0, 0, 0, 0, hwnd, (HMENU)1,((LPCREATESTRUCT)lParam)->hInstance, NULL);ofn.lStructSize = sizeof(OPENFILENAME);ofn.hwndOwner = hwnd;ofn.hInstance = NULL;ofn.lpstrFilter = szFilter;ofn.lpstrCustomFilter = NULL;ofn.nMaxCustFilter = 0;ofn.nFilterIndex = 0;ofn.lpstrFile = szFileName;ofn.nMaxFile = MAX_PATH;ofn.lpstrFileTitle = szTitleName;ofn.nMaxFileTitle = MAX_PATH;ofn.lpstrInitialDir = NULL;ofn.lpstrTitle = NULL;ofn.Flags = 0;ofn.nFileOffset = 0;ofn.nFileExtension = 0;ofn.lpstrDefExt = TEXT("bmp");ofn.lCustData = 0;ofn.lpfnHook = NULL;ofn.lpTemplateName = NULL;return 0;case WM_SIZE:MoveWindow(hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);return 0;case WM_COMMAND:switch (LOWORD(wParam)){case IDM_FILE_OPEN:if (GetOpenFileName(&ofn))DisplayDibHeaders(hwndEdit, szFileName);return 0;}break;case WM_DESTROY:PostQuitMessage(0);return 0;}return DefWindowProc(hwnd, message, wParam, lParam); } DIBHEADS.RC (excerpts)// Microsoft Visual C++ generated resource script. // #include "resource.h"/ // // Menu //DIBHEADS MENU BEGINPOPUP "&File"BEGINMENUITEM "&Open\tCtrl+O",?? ??? ??? ??? ?IDM_FILE_OPENEND END/ // // Accelerator //DIBHEADS ACCELERATORS BEGIN"O",??????????? IDM_FILE_OPEN,????????? VIRTKEY, CONTROL, NOINVERT END RESOURCE.H (excerpts)// Microsoft Visual C++ 生成的包含文件。 // 供 DIBHeads.rc 使用 // #define IDM_FILE_OPEN?????????????????? 40001

??????? 這個(gè)程序有一個(gè)很短的 WndProc 函數(shù),它創(chuàng)建了一個(gè)只讀的編輯窗口,這個(gè)只讀窗口填滿了它的客戶區(qū),該函數(shù)還處理菜單的 File Open(打開(kāi)文件)命令。它使用 GetOpenFileName 函數(shù)調(diào)用標(biāo)準(zhǔn)的 File Open 對(duì)話框,然后調(diào)用 DisplayDibHeaders 這個(gè)函數(shù)。這個(gè)函數(shù)把整個(gè) DIB 文件讀入內(nèi)存,然后一個(gè)字段一個(gè)字段地顯示所有文件頭信息。

總結(jié)

以上是生活随笔為你收集整理的15.1 DIB 文件格式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。