BMP文件解析
一,、簡介
BMP(Bitmap-File)圖形文件是Windows采用的圖形文件格式,,在Windows環(huán)境下運行的所有圖象處理軟件都支持BMP圖像文件格式,。Windows系統(tǒng)內(nèi)部各圖像繪制操作都是以BMP為基礎(chǔ)的,。Windows 3.0以前的BMP圖文件格式與顯示設(shè)備有關(guān),,因此把這種BMP圖像文件格式稱為設(shè)備相關(guān)位圖DDB(device-dependent bitmap)文件格式。Windows 3.0以后的BMP圖象文件與顯示設(shè)備無關(guān),,因此把這種BMP圖像文件格式稱為設(shè)備無關(guān)位圖DIB(device-independent bitmap)格式(注:Windows 3.0以后,,在系統(tǒng)中仍然存在DDB位圖,,像BitBlt()這種函數(shù)就是基于DDB位圖的,只不過如果你想將圖像以BMP格式保存到磁盤文件中時,,微軟極力推薦你以DIB格式保存),,目的是為了讓Windows能夠在任何類型的顯示設(shè)備上顯示所存儲的圖象。BMP位圖文件默認的文件擴展名是BMP或者bmp(有時它也會以.DIB或.RLE作擴展名),。 Bmp文件是非常常用的位圖文件,。針對bmp文件的處理也有一堆現(xiàn)成的API進行調(diào)用,然而文件內(nèi)部究竟怎樣,,如何自己來解析這樣的文件呢,?在解析格式之前,將WINDOWS編程中使用的類型名解釋一下,。 typedef unsigned short WORD; typedef unsigned char BYTE; typedef unsigned long DWORD; (注:這里用到了無符號類型,,在進行運算時注意數(shù)據(jù)類型的匹配與轉(zhuǎn)換,否則將出現(xiàn)奇怪的結(jié)果,。) 二,、數(shù)據(jù)格式解析
第一塊是bmp的文件頭用于描述整個bmp文件的情況。結(jié)構(gòu)如下: typedef struct tagBITMAPFILEHEADER { WORD bfType; //指定文件類型 DWORD bfSize; //指定文件大小 WORD bfReserved1; //為保留字,,不用考慮 WORD bfReserved2; //為保留字,,不用考慮 DWORD bfOffBits; //為保留字,不用考慮 } BITMAPFILEHEADER, *PBITMAPFILEHEADER; 這些信息相當有用,,如果你想直接來解析bmp文件,。第一個bfType用于表示文件類型,如果它是bmp文件,,那么它這個位置的值一定是 ‘BM’ :Windows 3.1x, 95, NT, ‘BA’ :OS/2 Bitmap Array ‘CI’ :OS/2 Color Icon ‘CP’ :OS/2 Color Pointer ‘IC’ :OS/2 Icon ‘PT’ :OS/2 Pointer (注:因為OS/2系統(tǒng)并沒有被普及開,,所以在編程時,你只需判斷第一個標識“BM”就行,。)第二個bfSize表示整個文件的字節(jié)數(shù),。第三第四個則保留,目前無意義,。最后一個相當重要,,表示,位圖的數(shù)據(jù)信息離文件頭的偏移量,,以字節(jié)為單位,,通常是前三個部分的長度之和。信息頭的長度通常是14B(注:在Windows95,、98,、2000等操作系統(tǒng)中,位圖信息頭的長度并不一定是14B,,因為微軟已經(jīng)制定出了新的BMP文件格式,,其中的信息頭結(jié)構(gòu)變化比較大,,長度加長。所以最好不要直接使用常數(shù)14B,,而是應該從具體的文件中讀取這個值,。這樣才能確保程序的兼容性。)如果信息頭的長度是14B的話,,這個偏移量就是54B+調(diào)色板的長度或者顏色掩碼的長度。 第二塊是位圖信息頭,,即BITMAPINFOHEADER,,用于描述整個位圖文件的情況。
typedef struct tagBITMAPINFOHEADER{ DWORD biSize; //表示本結(jié)構(gòu)的大小,,40B LONG biWidth; //位圖的寬度 LONG biHeight; //位圖的高度 WORD biPlanes; //位面數(shù)永遠為1參見msdn解釋 WORD biBitCount;//位圖的位數(shù) 分為1 4 8 16 24 32 DWORD biCompression; //本以為壓縮類型,,但是卻另外有作用,稍候解釋 DWORD biSizeImage; //表示位圖數(shù)據(jù)區(qū)域的大小以字節(jié)為單位 LONG biXPelsPerMeter; //表示顯示設(shè)備的水平分辨率 LONG biYPelsPerMeter; //表示顯示設(shè)備的垂直分辨率 DWORD biClrUsed; //實際使用的顏色數(shù)目,,通常為0,,表示全部使用 DWORD biClrImportant; //重要的顏色數(shù)量,通常為0,,表示全部重要 } BITMAPINFOHEADER, *PBITMAPINFOHEADER; biBitCount可以取下列值: 1 – 雙色位圖(缺省情況下是黑色和白色,。你可以自己定義這兩種顏色) 4 - 16 色位圖 8 - 256 色位圖 16 - 16bit 高彩色位圖,(用16位表示三種顏色,,有555,,565兩種方式) 24 - 24bit 真彩色位圖 32 - 32bit 增強型真彩色位圖 biCompression可以取下列值: 0 - 不壓縮 (使用BI_RGB表示) 1 - RLE 8-使用8位RLE壓縮方式(用BI_RLE8表示) 2 - RLE 4-使用4位RLE壓縮方式(用BI_RLE4表示) 3 - Bitfields-位域存放方式(用BI_BITFIELDS表示) 第三塊就是調(diào)色板或者掩碼部分。如果是1,、4,、8則存放調(diào)色板;24與32位位圖則存放RGB顏色的掩碼,,有3個,,這些掩碼用DWORD大小來存放。16位有存放調(diào)色版的也有存放掩碼的,。調(diào)色板的結(jié)構(gòu)如下:
typedef struct tagRGBQUAD { BYTE rgbBlue; //藍色分量 BYTE rgbGreen; //綠色分量 BYTE rgbRed; //紅色分量 BYTE rgbReserved; //保留值,,通常為0 } RGBQUAD; 對于有調(diào)色板位圖,調(diào)色板數(shù)量的計算公式是: (pow(2,bmpInfoH.biBitCount)) ,。但是如果biClrUsed不是0,,調(diào)色板數(shù)目就是biClrUsed。 typedef struct bi_bitField{ DWORD redCode; DWORD greenCode DWORD blueCode; } bitField; bitField即顏色掩碼其實沒有什么用處,。 第四塊就是位圖的數(shù)據(jù)實體,。對與有調(diào)色板的位圖,表示顏色及亮度的位圖數(shù)據(jù)實體存放的實際是本位置應有的顏色值在調(diào)色板數(shù)組中的位置,,即索引值,。位圖像素區(qū)是按行存放的,,每行從左到右與顯示出來的圖像對應;但是上下確是倒置的,,即顯示在上邊的像素存放在后邊,。值得注意的一個問題是每行存放的字節(jié)數(shù)是4的整數(shù)倍。那么圖像每行顏色與亮度信息存放的字節(jié)數(shù)storewidth是大于等于biWidth* biBitCount/8的最小4的整數(shù)倍,。如果計算有誤讀入或者寫入的圖像將會是錯位的?,F(xiàn)在給出一個通用的公式: 4*((biWidth*biBitCoun+31)/32);
有了上面的知識,現(xiàn)在給出一c++中讀入位圖的函數(shù):
為了方便給出符合c++中結(jié)構(gòu)體特性的位圖五種結(jié)構(gòu)的定義,; typedef struct bi_bitField { DWORD redCode; DWORD greenCode; DWORD blueCode; const struct bi_bitField & operator = (const struct bi_bitField & F) { if (&F != this) { redCode = F.redCode; greenCode = F.greenCode; blueCode = F.blueCode; } return *this; } friend std::istream & operator>>(std::istream & is,struct bi_bitField & F) { is >> F.redCode >> F.greenCode >> F.blueCode ; return is; } friend std::ostream & operator<<(std::ostream & os,const struct bi_bitField & F)
{ os << F.redCode << F.greenCode << F.blueCode ; return os; } }bitField; //RGB顏色結(jié)構(gòu)
typedef bitField ColorRGB; //位圖文件頭
typedef struct tagBmpFile { WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits; const struct tagBmpFile & operator = (const struct tagBmpFile & F) { if (&F != this) { bfType = F.bfType; bfSize = F.bfSize; bfReserved1 = F.bfReserved1; bfReserved2 = F.bfReserved2; bfOffBits = F.bfOffBits; } return *this; } friend std::istream & operator>>(std::istream & is,struct tagBmpFile & F)
{ is >> F.bfType >> F.bfSize >> F.bfReserved1 >> F.bfReserved2 >> F.bfOffBits; return is; } friend std::ostream & operator<<(std::ostream & os,const struct tagBmpFile & F)
{ os << F.bfType << F.bfSize << F.bfReserved1 << F.bfReserved2 << F.bfOffBits; return os; } }bmpFile; //位圖信息頭
typedef struct tagBmpInfo { DWORD biSize; DWORD biWidth; DWORD biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; DWORD biXPelsPerMeter; DWORD biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; const struct tagBmpInfo & operator = (const struct tagBmpInfo & I) { if (&I != this) { biSize = I.biSize; biWidth = I.biWidth; biHeight = I.biHeight; biPlanes = I.biPlanes; biBitCount = I.biBitCount; biCompression = I.biCompression; biSizeImage = I.biSizeImage; biXPelsPerMeter = I.biXPelsPerMeter; biYPelsPerMeter = I.biYPelsPerMeter; biClrUsed = I.biClrUsed; biClrImportant = I.biClrImportant; } return *this; } friend std::istream & operator>>(std::istream & is,struct tagBmpInfo & F)
{ is >> F.biSize >> F.biWidth >> F.biHeight >> F.biPlanes >> F.biBitCount >> F.biCompression >> F.biSizeImage >> F.biXPelsPerMeter >> F.biYPelsPerMeter >> F.biClrUsed >> F.biClrImportant; return is; } friend std::ostream & operator<<(std::ostream & os,const struct tagBmpInfo & F)
{ os << F.biSize << F.biWidth << F.biHeight << F.biPlanes << F.biBitCount << F.biCompression << F.biSizeImage << F.biXPelsPerMeter << F.biYPelsPerMeter << F.biClrUsed << F.biClrImportant; return os; } }bmpInfo; //調(diào)色板 typedef struct tagRGBQuad { BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; const struct tagRGBQuad & operator = (const struct tagRGBQuad & Q) { if (&Q != this) { rgbBlue = Q.rgbBlue; rgbGreen = Q.rgbGreen; rgbRed = Q.rgbRed; rgbReserved = Q.rgbReserved; } return *this; } friend std::istream & operator>>(std::istream & is,struct tagRGBQuad & F)
{ is >> F.rgbBlue >> F.rgbGreen >> F.rgbRed >> F.rgbReserved; return is; } friend std::ostream & operator<<(std::ostream & os,const struct tagRGBQuad & F)
{ os << F.rgbBlue << F.rgbGreen << F.rgbRed << F.rgbReserved; return os; } } RGBQuad; bool openBmp(const std::string fname)
std::string fileName; bmpFile bmpFileH; bmpInfo bmpInfoH; DWORD storeWidth; bitField bmpBitField; RGBQuad * bmpRGB; BYTE * bmpData; { FILE * inbmpF; WORD colorCount; if ((inbmpF=fopen(fname.c_str(),"rb"))==NULL)
return false; else { fileName.assign(fname); fread(&bmpFileH.bfType,2,1,inbmpF);
fread(&bmpFileH.bfSize,4,1,inbmpF); fread(&bmpFileH.bfReserved1,2,1,inbmpF); fread(&bmpFileH.bfReserved2,2,1,inbmpF); fread(&bmpFileH.bfOffBits,4,1,inbmpF); //fread(&bmpFileH,14,1,inbmpF);
fread(&bmpInfoH,40,1,inbmpF); if (0<bmpInfoH.biBitCount&&bmpInfoH.biBitCount<=8) colorCount=(WORD)(pow(2,bmpInfoH.biBitCount)); else colorCount=0; storeWidth=4*((bmpInfoH.biWidth*bmpInfoH.biBitCount+31)/32);
bmpFileH.bfSize=54+4*colorCount+storeWidth*bmpInfoH.biHeight; if ( 0 != colorCount )
{ bmpRGB = new RGBQuad[colorCount]; fread(bmpRGB,4,colorCount,inbmpF); } else { fread(&bmpBitField,12,1,inbmpF); } bmpInfoH.biSizeImage = storeWidth*bmpInfoH.biHeight;
bmpData=new BYTE[bmpInfoH.biSizeImage]; fseek(inbmpF,0-bmpInfoH.biSizeImage,SEEK_END);
fread(bmpData,bmpInfoH.biSizeImage,1,inbmpF); fclose(inbmpF); return true; } } 三,、顏色及亮度信息解析 biBitCount=1 表示位圖最多有兩種顏色,缺省情況下是黑色和白色,,你也可以自己定義這兩種顏色,。圖像信息頭裝調(diào)色板中將有兩個調(diào)色板項,稱為索引0和索引1,。圖象數(shù)據(jù)陣列中的每一位表示一個象素,。如果一個位是0,顯示時就使用索引0的RGB值,,如果位是1,,則使用索引1的RGB值。 biBitCount=4 表示位圖最多有2^4=16種顏色,。每個象素用4位表示,,并用這4位作為彩色表的索引來查找該象素的顏色。例如,,如果位圖中的第一個字節(jié)為0x1A,,它表示兩個象素,第一象素的顏色就在彩色表的第2項中,,而第二個象素的顏色就在彩色表的第11項中,。調(diào)色板中缺省情況下會有16個RGB項,對應于索引0到索引15,。 biBitCount=8 表示位圖最多有2^8種顏色,。每個象素用8位表示,并用這8位作為彩色表的索引來查找該象素的顏色,。例如,,如果位圖中的第一個字節(jié)為0x1A,這個象素的顏色就在彩色表的第26項中,。此時,,缺省情況下,調(diào)色板中會有256個RGB,對應于索引0到索引255,。 biBitCount=16 表示位圖最多有2^16種顏色,。每個像素用16位(2個字節(jié))表示。這種格式叫作高彩色,,或叫增強型16位色,,或64K色。它的情況比較復雜,,當biCompression成員的值是BI_RGB時,,它沒有調(diào)色板。16位中,,最低的5位表示藍色分量,,中間的5位表示綠色分量,高的5位表示紅色分量,,一共占用了15位,最高的一位保留,,設(shè)為0,。這種格式也被稱作555 16位位圖。 如果biCompression成員的值是BI_BITFIELDS,,那么情況就復雜了,,首先是原來調(diào)色板的位置被三個DWORD變量占據(jù),稱為紅,、綠,、藍掩碼。分別用于描述紅,、綠,、藍分量在16位中所占的位置。在Windows 95(或98)中,,系統(tǒng)可接受兩種格式的位域:555和565,,在555格式下,紅,、綠,、藍的掩碼分別是:0x7C00、0x03E0,、0x001F,,而在565格式下,它們則分別為:0xF800,、0x07E0,、0x001F。你在讀取一個像素之后,,可以分別用掩碼“與”上像素值,,從而提取出想要的顏色分量(當然還要再經(jīng)過適當?shù)淖笥乙撇僮鳎?。在NT系統(tǒng)中,則沒有格式限制,,只不過要求掩碼之間不能有重疊,。(注:這種格式的圖像使用起來是比較麻煩的,不過因為它的顯示效果接近于真彩,,而圖像數(shù)據(jù)又比真彩圖像小的多,,所以,它更多的被用于游戲軟件),。我們只需要讀取其中的R或者G的掩碼,,來判斷是那種格式。以紅色掩碼為例 0111110000000000的時候就是555格式 1111100000000000就是565格式,。 555 格式 xrrrrrgggggbbbbb 565 格式 rrrrrggggggbbbbb 解析555格式的代碼: BYTE b= bmpData[i*storeWidth+j]&0x1F; BYTE g=((bmpData[i*storeWidth+j +1]<<6)>>3)+(buffer[i*storeWidth+j]>>5); BYTE r=(bmpData[i*storeWidth+j +1]<<1)>>3; 有一點值得提醒的是由于有較多的位操作 ,,所以在處理的時候在前一次操作的上面加上一對括號。 現(xiàn)在我們得到了55RGB各自的分量,,但是還有一個新的問題,,那就是由于兩字節(jié)表示了3個顏色 555下每個顏色最多到0x1F。所以我們需要一個轉(zhuǎn)換,,很簡單將得到的各顏色分量乘8就可以了,。 以下是565格式時的數(shù)據(jù)分離:
BYTE b= bmpData[i*storeWidth+j]&0x1F; BYTE g=((bmpData[i*storeWidth+j+1]<<5)>>2)+(buffer[i*storeWidth+j]>>5); BYTE r=bmpData[i*storeWidth+j +1]>>3; 現(xiàn)在我們得到了565RGB各自的分量,但是仍然還有一個新的問題,,565格式下最大的綠色分量也就0x3F,。所以我們需要一個轉(zhuǎn)換,很簡單將得到的綠色分量乘以4,,其余乘8就可以了,。
biBitCount=24 表示位圖最多有2^24種顏色。這種位圖沒有調(diào)色板,。每3個字節(jié)代表一個象素,,分別對應于顏色B、G,、R,。 biBitCount=32 表示位圖最多有2^32種顏色。這種位圖的結(jié)構(gòu)與16位位圖結(jié)構(gòu)非常類似,,當biCompression成員的值是BI_RGB時,,它也沒有調(diào)色板,32位中有24位用于存放RGB值,,順序是:最前一字節(jié)保留,,紅8位、綠8位、藍8位,。這種格式也被成為888 32位圖,。如果 biCompression成員的值是BI_BITFIELDS時,原來調(diào)色板的位置將被三個DWORD變量占據(jù),,成為紅,、綠、藍掩碼,,分別用于描述紅,、綠、藍分量在32位中所占的位置,。在Windows 95(or98)中,,系統(tǒng)只接受888格式,也就是說三個掩碼的值將只能是:0xFF0000,、0xFF00,、0xFF。而在NT系統(tǒng)中,,你只要注意使掩碼之間不產(chǎn)生重疊就行,。 在次,給出灰度與RGB顏色的關(guān)系公式:gray = 0.3*r+0.6*g+0.1*b,。有了這些的知識,現(xiàn)在給出用上述c++位圖讀入函數(shù)讀入的位圖的灰度數(shù)組轉(zhuǎn)換函數(shù);
//------------------------------------------------
//將一幅圖片的像素區(qū),,轉(zhuǎn)換為灰度值數(shù)組 //------------------------------------------------ template<typename T> bool tmBmp::tranToArray(T * pA) { DWORD i,j; WORD k,index=0,pixelPerB=0; if (bmpInfoH.biBitCount==1) { pixelPerB=8; for (i=0;i<bmpInfoH.biHeight;i++)
for (j=0;j<storeWidth;j++ ) { k=0; while (k<=7) { index=(grasp(i,j)>>(7-k))&1; fetch(pA,bmpInfoH.biWidth,i,j*pixelPerB+k)= (T)(0.3*bmpRGB[index].rgbRed + 0.6*bmpRGB[index].rgbGreen + 0.1*bmpRGB[index].rgbBlue); k++; } } } else if (bmpInfoH.biBitCount==4) { pixelPerB=2; for (i=0;i<bmpInfoH.biHeight;i++)
for (j=0;j<storeWidth;j++) { k=0; while (k<=1&&(j*pixelPerB+k)<bmpInfoH.biWidth) { index=(grasp(i,j)>>(1-k)*4)&15; fetch(pA,bmpInfoH.biWidth,i,j*pixelPerB+k) = (T)(0.3*bmpRGB[index].rgbRed + 0.6*bmpRGB[index].rgbGreen + 0.1*bmpRGB[index].rgbBlue); k++; } } } else if (bmpInfoH.biBitCount==8) { for (i=0;i<bmpInfoH.biHeight;i++) for (j=0;j<storeWidth;j++) { index=grasp(i,j); fetch(pA,bmpInfoH.biWidth,i,j)= (T)(0.3*bmpRGB[index].rgbRed + 0.6*bmpRGB[index].rgbGreen + 0.1*bmpRGB[index].rgbBlue); } } else if ( (bmpInfoH.biBitCount==16) && (BI_RGB == bmpInfoH.biCompression)) { BYTE r,g,b; for (i = 0; i < bmpInfoH.biHeight; i++) for (j = 0, k = 0; j < bmpInfoH.biWidth; j++, k += 2) { b = (grasp(i,k)&0x1F)*8; g = (grasp(i,k)>>5)*8 + ((grasp(i,k+1)<<5)>>2)*8; r = (grasp(i,k+1)>>3)*8; fetch(pA,bmpInfoH.biWidth,i,j) = (T)(0.3*r + 0.6*g + 0.1*b); } } else if (bmpInfoH.biBitCount==24) { for (i = 0; i < bmpInfoH.biHeight; i++) for (j = 0, k = 0; j < bmpInfoH.biWidth; j++ , k += 3) { fetch(pA,bmpInfoH.biWidth,i,j) = (T)( 0.1*grasp(i,k) + 0.6*grasp(i,k+1) + 0.3*grasp(i,k+2) ); } } else if ( (bmpInfoH.biBitCount==32) && (BI_RGB == bmpInfoH.biCompression)) { for (i = 0; i < bmpInfoH.biHeight; i++) for (j = 0, k = 0; j < bmpInfoH.biWidth; j++, k += 4) { fetch(pA,bmpInfoH.biWidth,i,j)= (T)( 0.1*grasp(i,k+1) + 0.6*grasp(i,k+2) + 0.3*grasp(i,k+3)); } } else return false; return true; } 四,、壓縮編碼解析 BI_RLE8(8位位圖的壓縮)
在這種情況下BITMAPINFOHEADER結(jié)構(gòu)中的biCompression設(shè)置為BI_RLE8,.使用256色位圖行程編碼格式將位圖進行壓縮。這種壓縮方式包括絕對方式和編碼方式,。 編碼方式:
在此方式下每兩個字節(jié)組成一個信息單元,。第一個字節(jié)給出其后面相連的象素的個數(shù)。第二個字節(jié)給出這些象素使用的顏色索引表中的索引,。例如:信息單元03 04,,03表示其后的象素個數(shù)是3個,04表示這些象素使用的是顏色索引表中的第五項的值,。壓縮數(shù)據(jù)展開后就是04 04 04 .同理04 05 可以展開為05 05 05 05. 信息單元的第一個字節(jié)也可以是00,,這種情況下信息單元并不表示數(shù)據(jù)單元,而是表示一些特殊的含義,。這些含義通常由信息單元的第二個字節(jié)的值來描述,。這些值在0x00到0x02之間。具體含義如下:00 本行結(jié)束 ,;01 位圖結(jié)束 ,;02 象素位置增量,表示緊跟在這個字節(jié)后面的信息單元里的兩個字節(jié)中所包含的無符號值指定了下個象素相對于當前象素的水平和垂直偏移量(相對方式的意義體現(xiàn)在此)。例如:00 02 06 08表示的含義是下一個象素的位值是從當前位置向右移動5個象素,,向下移動8個象素(不是字節(jié)),。 絕對方式:
絕對方式的標志是第一個字節(jié)是0,第二個字節(jié)是0x03到0xff之間的值,。第二個字節(jié)的值表示跟隨其后面的象素的字節(jié)數(shù)目,。每個字節(jié)都包含一個象素的顏色索引。 每個行程編碼都必須補齊到字的邊界,。 BI_RLE4(4位位圖壓縮)
這是BITMAPINFOHEADER的biCompression設(shè)置為BI_RLE4,,使用16位行程編碼格式進行位圖壓縮。壓縮方式也包括編碼方式和絕對方式,。 編碼方式:
4位壓縮的編碼方式跟8位的編碼的壓縮方式?jīng)]有什么區(qū)別,。每個信息單元也是由兩個字節(jié)表示,第一個字節(jié)表示其后面所跟隨的象素的個數(shù),。第二個字節(jié)表示象素在顏色索引表中的索引,。這個字節(jié)又分為上下兩個部分。第一個象素用上半部分指定的顏色表中的顏色畫出,。第二個象素用下半部分的顏色畫出,。第三個象素用下一個字節(jié) 的上半部分畫出,依次類推,。它只能壓縮的顏色數(shù)不超過16的圖像,。因而這種壓縮應用范圍有限。 其余的跟BI_RLE8一樣,。 發(fā)表于 @ 2009年07月01日 14:30:00 | 評論( 0 ) | 編輯| 舉報| 收藏
舊一篇:在Internet傳播聲音 | 新一篇:Gtk中調(diào)用Mplayer
查看最新精華文章 請訪問博客首頁相關(guān)文章 本文來自CSDN博客,,轉(zhuǎn)載請標明出處:file:///C:/Documents%20and%20Settings/goodoo/桌面/在中國能找到的最好的BMP文件結(jié)構(gòu)%20-%20KaKa的專欄%20-%20CSDN博客.mht
|
|