青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆 - 132  文章 - 51  trackbacks - 0
<2025年11月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

常用鏈接

留言簿(7)

隨筆分類

隨筆檔案

文章分類

文章檔案

cocos2d-x

OGRE

OPenGL

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

原文地址:

http://blog.csdn.net/ghost129/archive/2009/08/04/4409565.aspx

1、       BMP文件格式簡單介紹

BMP文件是一種像素文件,它保存了一幅圖象中所有的像素。這種文件格式可以保存單色位圖、16色或256色索引模式像素圖、24位真彩色圖象,每種模式種單一像素的大小分別為1/8字節(jié),1/2字節(jié),1字節(jié)和3字節(jié)。目前最常見的是256色BMP和24位色BMP。這種文件格式還定義了像素保存的幾種方法,包括不壓縮、RLE壓縮等。常見的BMP文件大多是不壓縮的。
這里為了簡單起見,我們僅討論24位色、不使用壓縮的BMP。(如果你使用Windows自帶的畫圖程序,很容易繪制出一個符合以上要求的BMP)
Windows所使用的BMP文件,在開始處有一個文件頭,大小為54字節(jié)。保存了包括文件格式標識、顏色數(shù)、圖象大小、壓縮方式等信息,因為我們僅討論24位色不壓縮的BMP,所以文件頭中的信息基本不需要注意,只有“大小”這一項對我們比較有用。圖象的寬度和高度都是一個32位整數(shù),在文件中的地址分別為0x0012和0x0016,于是我們可以使用以下代碼來讀取圖象的大小信息:


GLint width, height; // 使用OpenGL的GLint類型,它是32位的。
                                     // 而C語言本身的int則不一定是32位的。
FILE* pFile;
// 在這里進行“打開文件”的操作
fseek(pFile, 0x0012, SEEK_SET);         // 移動到0x0012位置
fread(&width, sizeof(width), 1, pFile); // 讀取寬度
fseek(pFile, 0x0016, SEEK_SET);         // 移動到0x0016位置
                                        // 由于上一句執(zhí)行后本就應(yīng)該在0x0016位置
                                        // 所以這一句可省略
fread(&height, sizeof(height), 1, pFile); // 讀取高度

54個字節(jié)以后,如果是16色或256色BMP,則還有一個顏色表,但24位色BMP沒有這個,我們這里不考慮。接下來就是實際的像素數(shù)據(jù)了。24位色的BMP文件中,每三個字節(jié)表示一個像素的顏色。
注意,OpenGL通常使用RGB來表示顏色,但BMP文件則采用BGR,就是說,順序被反過來了。
另外需要注意的地方是:像素的數(shù)據(jù)量并不一定完全等于圖象的高度乘以寬度乘以每一像素的字節(jié)數(shù),而是可能略大于這個值。原因是BMP文件采用了一種“對齊”的機制,每一行像素數(shù)據(jù)的長度若不是4的倍數(shù),則填充一些數(shù)據(jù)使它是4的倍數(shù)。這樣一來,一個17*15的24位BMP大小就應(yīng)該是834字節(jié)(每行17個像素,有51字節(jié),補充為52字節(jié),乘以15得到像素數(shù)據(jù)總長度780,再加上文件開始的54字節(jié),得到834字節(jié))。分配內(nèi)存時,一定要小心,不能直接使用“圖象的高度乘以寬度乘以每一像素的字節(jié)數(shù)”來計算分配空間的長度,否則有可能導(dǎo)致分配的內(nèi)存空間長度不足,造成越界訪問,帶來各種嚴重后果。
一個很簡單的計算數(shù)據(jù)長度的方法如下:

int LineLength, TotalLength;
LineLength = ImageWidth * BytesPerPixel; // 每行數(shù)據(jù)長度大致為圖象寬度乘以
                                         // 每像素的字節(jié)數(shù)
while( LineLength % 4 != 0 )             // 修正LineLength使其為4的倍數(shù)
    ++LineLenth;
TotalLength = LineLength * ImageHeight;  // 數(shù)據(jù)總長 = 每行長度 * 圖象高度

這并不是效率最高的方法,但由于這個修正本身運算量并不大,使用頻率也不高,我們就不需要再考慮更快的方法了。
2、簡單的OpenGL像素操作
OpenGL提供了簡潔的函數(shù)來操作像素:
glReadPixels:讀取一些像素。當前可以簡單理解為“把已經(jīng)繪制好的像素(它可能已經(jīng)被保存到顯卡的顯存中)讀取到內(nèi)存”。
glDrawPixels:繪制一些像素。當前可以簡單理解為“把內(nèi)存中一些數(shù)據(jù)作為像素數(shù)據(jù),進行繪制”。
glCopyPixels:復(fù)制一些像素。當前可以簡單理解為“把已經(jīng)繪制好的像素從一個位置復(fù)制到另一個位置”。雖然從功能上看,好象等價于先讀取像素再繪制像素,但實際上它不需要把已經(jīng)繪制的像素(它可能已經(jīng)被保存到顯卡的顯存中)轉(zhuǎn)換為內(nèi)存數(shù)據(jù),然后再由內(nèi)存數(shù)據(jù)進行重新的繪制,所以要比先讀取后繪制快很多。
這三個函數(shù)可以完成簡單的像素讀取、繪制和復(fù)制任務(wù),但實際上也可以完成更復(fù)雜的任務(wù)。當前,我們僅討論一些簡單的應(yīng)用。由于這幾個函數(shù)的參數(shù)數(shù)目比較多,下面我們分別介紹。

3、glReadPixels的用法和舉例
3.1 函數(shù)的參數(shù)說明
該函數(shù)總共有七個參數(shù)。前四個參數(shù)可以得到一個矩形,該矩形所包括的像素都會被讀取出來。(第一、二個參數(shù)表示了矩形的左下角橫、縱坐標,坐標以窗口最左下角為零,最右上角為最大值;第三、四個參數(shù)表示了矩形的寬度和高度)
第五個參數(shù)表示讀取的內(nèi)容,例如:GL_RGB就會依次讀取像素的紅、綠、藍三種數(shù)據(jù),GL_RGBA則會依次讀取像素的紅、綠、藍、alpha四種數(shù)據(jù),GL_RED則只讀取像素的紅色數(shù)據(jù)(類似的還有GL_GREEN,GL_BLUE,以及GL_ALPHA)。如果采用的不是RGBA顏色模式,而是采用顏色索引模式,則也可以使用GL_COLOR_INDEX來讀取像素的顏色索引。目前僅需要知道這些,但實際上還可以讀取其它內(nèi)容,例如深度緩沖區(qū)的深度數(shù)據(jù)等。
第六個參數(shù)表示讀取的內(nèi)容保存到內(nèi)存時所使用的格式,例如:GL_UNSIGNED_BYTE會把各種數(shù)據(jù)保存為GLubyte,GL_FLOAT會把各種數(shù)據(jù)保存為GLfloat等。
第七個參數(shù)表示一個指針,像素數(shù)據(jù)被讀取后,將被保存到這個指針所表示的地址。注意,需要保證該地址有足夠的可以使用的空間,以容納讀取的像素數(shù)據(jù)。例如一幅大小為256*256的圖象,如果讀取其RGB數(shù)據(jù),且每一數(shù)據(jù)被保存為GLubyte,總大小就是:256*256*3 = 196608字節(jié),即192千字節(jié)。如果是讀取RGBA數(shù)據(jù),則總大小就是256*256*4 = 262144字節(jié),即256千字節(jié)。

注意:glReadPixels實際上是從緩沖區(qū)中讀取數(shù)據(jù),如果使用了雙緩沖區(qū),則默認是從正在顯示的緩沖(即前緩沖)中讀取,而繪制工作是默認繪制到后緩沖區(qū)的。因此,如果需要讀取已經(jīng)繪制好的像素,往往需要先交換前后緩沖。

再看前面提到的BMP文件中兩個需要注意的地方:
3.2 解決OpenGL常用的RGB像素數(shù)據(jù)與BMP文件的BGR像素數(shù)據(jù)順序不一致問題
可以使用一些代碼交換每個像素的第一字節(jié)和第三字節(jié),使得RGB的數(shù)據(jù)變成BGR的數(shù)據(jù)。當然也可以使用另外的方式解決問題:新版本的OpenGL除了可以使用GL_RGB讀取像素的紅、綠、藍數(shù)據(jù)外,也可以使用GL_BGR按照相反的順序依次讀取像素的藍、綠、紅數(shù)據(jù),這樣就與BMP文件格式相吻合了。即使你的gl/gl.h頭文件中沒有定義這個GL_BGR,也沒有關(guān)系,可以嘗試使用GL_BGR_EXT。雖然有的OpenGL實現(xiàn)(尤其是舊版本的實現(xiàn))并不能使用GL_BGR_EXT,但我所知道的Windows環(huán)境下各種OpenGL實現(xiàn)都對GL_BGR提供了支持,畢竟Windows中各種表示顏色的數(shù)據(jù)幾乎都是使用BGR的順序,而非RGB的順序。這可能與IBM-PC的硬件設(shè)計有關(guān)。

3.3 消除BMP文件中“對齊”帶來的影響
實際上OpenGL也支持使用了這種“對齊”方式的像素數(shù)據(jù)。只要通過glPixelStore修改“像素保存時對齊的方式”就可以了。像這樣:
int alignment = 4;
glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
第一個參數(shù)表示“設(shè)置像素的對齊值”,第二個參數(shù)表示實際設(shè)置為多少。這里像素可以單字節(jié)對齊(實際上就是不使用對齊)、雙字節(jié)對齊(如果長度為奇數(shù),則再補一個字節(jié))、四字節(jié)對齊(如果長度不是四的倍數(shù),則補為四的倍數(shù))、八字節(jié)對齊。分別對應(yīng)alignment的值為1, 2, 4, 8。實際上,默認的值是4,正好與BMP文件的對齊方式相吻合。
glPixelStorei也可以用于設(shè)置其它各種參數(shù)。但我們這里并不需要深入討論了。

現(xiàn)在,我們已經(jīng)可以把屏幕上的像素讀取到內(nèi)存了,如果需要的話,我們還可以將內(nèi)存中的數(shù)據(jù)保存到文件。正確的對照BMP文件格式,我們的程序就可以把屏幕中的圖象保存為BMP文件,達到屏幕截圖的效果。
我們并沒有詳細介紹BMP文件開頭的54個字節(jié)的所有內(nèi)容,不過這無傷大雅。從一個正確的BMP文件中讀取前54個字節(jié),修改其中的寬度和高度信息,就可以得到新的文件頭了。假設(shè)我們先建立一個1*1大小的24位色BMP,文件名為dummy.bmp,又假設(shè)新的BMP文件名稱為grab.bmp。則可以編寫如下代碼:

FILE* pOriginFile = fopen("dummy.bmp", "rb);
FILE* pGrabFile = fopen("grab.bmp", "wb");
char  BMP_Header[54];
GLint width, height;

/* 先在這里設(shè)置好圖象的寬度和高度,即width和height的值,并計算像素的總長度 */

// 讀取dummy.bmp中的頭54個字節(jié)到數(shù)組
fread(BMP_Header, sizeof(BMP_Header), 1, pOriginFile);
// 把數(shù)組內(nèi)容寫入到新的BMP文件
fwrite(BMP_Header, sizeof(BMP_Header), 1, pGrabFile);

// 修改其中的大小信息
fseek(pGrabFile, 0x0012, SEEK_SET);
fwrite(&width, sizeof(width), 1, pGrabFile);
fwrite(&height, sizeof(height), 1, pGrabFile);

// 移動到文件末尾,開始寫入像素數(shù)據(jù)
fseek(pGrabFile, 0, SEEK_END);

/* 在這里寫入像素數(shù)據(jù)到文件 */

fclose(pOriginFile);
fclose(pGrabFile);

我們給出完整的代碼,演示如何把整個窗口的圖象抓取出來并保存為BMP文件。

#define WindowWidth  400
#define WindowHeight 400

#include <stdio.h>
#include <stdlib.h>

/* 函數(shù)grab
 * 抓取窗口中的像素
 * 假設(shè)窗口寬度為WindowWidth,高度為WindowHeight
 */
#define BMP_Header_Length 54
void grab(void)
{
    FILE*    pDummyFile;
    FILE*    pWritingFile;
    GLubyte* pPixelData;
    GLubyte  BMP_Header[BMP_Header_Length];
    GLint    i, j;
    GLint    PixelDataLength;

    // 計算像素數(shù)據(jù)的實際長度
    i = WindowWidth * 3;   // 得到每一行的像素數(shù)據(jù)長度
    while( i%4 != 0 )      // 補充數(shù)據(jù),直到i是的倍數(shù)
        ++i;               // 本來還有更快的算法,
                           // 但這里僅追求直觀,對速度沒有太高要求
    PixelDataLength = i * WindowHeight;

    // 分配內(nèi)存和打開文件
    pPixelData = (GLubyte*)malloc(PixelDataLength);
    if( pPixelData == 0 )
        exit(0);

    pDummyFile = fopen("dummy.bmp", "rb");
    if( pDummyFile == 0 )
        exit(0);

    pWritingFile = fopen("grab.bmp", "wb");
    if( pWritingFile == 0 )
        exit(0);

    // 讀取像素
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
    glReadPixels(0, 0, WindowWidth, WindowHeight,
        GL_BGR_EXT, GL_UNSIGNED_BYTE, pPixelData);

    // 把dummy.bmp的文件頭復(fù)制為新文件的文件頭
    fread(BMP_Header, sizeof(BMP_Header), 1, pDummyFile);
    fwrite(BMP_Header, sizeof(BMP_Header), 1, pWritingFile);
    fseek(pWritingFile, 0x0012, SEEK_SET);
    i = WindowWidth;
    j = WindowHeight;
    fwrite(&i, sizeof(i), 1, pWritingFile);
    fwrite(&j, sizeof(j), 1, pWritingFile);

    // 寫入像素數(shù)據(jù)
    fseek(pWritingFile, 0, SEEK_END);
    fwrite(pPixelData, PixelDataLength, 1, pWritingFile);

    // 釋放內(nèi)存和關(guān)閉文件
    fclose(pDummyFile);
    fclose(pWritingFile);
    free(pPixelData);
}

 

把這段代碼復(fù)制到以前任何課程的樣例程序中,在繪制函數(shù)的最后調(diào)用grab函數(shù),即可把圖象內(nèi)容保存為BMP文件了。(在我寫這個教程的時候,不少地方都用這樣的代碼進行截圖工作,這段代碼一旦寫好,運行起來是很方便的。)
4、glDrawPixels的用法和舉例
glDrawPixels函數(shù)與glReadPixels函數(shù)相比,參數(shù)內(nèi)容大致相同。它的第一、二、三、四個參數(shù)分別對應(yīng)于glReadPixels函數(shù)的第三、四、五、六個參數(shù),依次表示圖象寬度、圖象高度、像素數(shù)據(jù)內(nèi)容、像素數(shù)據(jù)在內(nèi)存中的格式。兩個函數(shù)的最后一個參數(shù)也是對應(yīng)的,glReadPixels中表示像素讀取后存放在內(nèi)存中的位置,glDrawPixels則表示用于繪制的像素數(shù)據(jù)在內(nèi)存中的位置。
注意到glDrawPixels函數(shù)比glReadPixels函數(shù)少了兩個參數(shù),這兩個參數(shù)在glReadPixels中分別是表示圖象的起始位置。在glDrawPixels中,不必顯式的指定繪制的位置,這是因為繪制的位置是由另一個函數(shù)glRasterPos*來指定的。glRasterPos*函數(shù)的參數(shù)與glVertex*類似,通過指定一個二維/三維/四維坐標,OpenGL將自動計算出該坐標對應(yīng)的屏幕位置,并把該位置作為繪制像素的起始位置。
很自然的,我們可以從BMP文件中讀取像素數(shù)據(jù),并使用glDrawPixels繪制到屏幕上。我們選擇Windows XP默認的桌面背景Bliss.bmp作為繪制的內(nèi)容(如果你使用的是Windows XP系統(tǒng),很可能可以在硬盤中搜索到這個文件。當然你也可以使用其它BMP文件來代替,只要它是24位的BMP文件。注意需要修改代碼開始部分的FileName的定義),先把該文件復(fù)制一份放到正確的位置,我們在程序開始時,就讀取該文件,從而獲得圖象的大小后,根據(jù)該大小來創(chuàng)建合適的OpenGL窗口,并繪制像素。
繪制像素本來是很簡單的過程,但是這個程序在骨架上與前面的各種示例程序稍有不同,所以我還是打算給出一份完整的代碼。

#include <gl/glut.h>

#define FileName "Bliss.bmp"

static GLint    ImageWidth;
static GLint    ImageHeight;
static GLint    PixelLength;
static GLubyte* PixelData;

#include <stdio.h>
#include <stdlib.h>

void display(void)
{
    // 清除屏幕并不必要
    // 每次繪制時,畫面都覆蓋整個屏幕
    // 因此無論是否清除屏幕,結(jié)果都一樣
    // glClear(GL_COLOR_BUFFER_BIT);

    // 繪制像素
    glDrawPixels(ImageWidth, ImageHeight,
        GL_BGR_EXT, GL_UNSIGNED_BYTE, PixelData);

    // 完成繪制
    glutSwapBuffers();
}

int main(int argc, char* argv[])
{
    // 打開文件
    FILE* pFile = fopen("Bliss.bmp", "rb");
    if( pFile == 0 )
        exit(0);

    // 讀取圖象的大小信息
    fseek(pFile, 0x0012, SEEK_SET);
    fread(&ImageWidth, sizeof(ImageWidth), 1, pFile);
    fread(&ImageHeight, sizeof(ImageHeight), 1, pFile);

    // 計算像素數(shù)據(jù)長度
    PixelLength = ImageWidth * 3;
    while( PixelLength % 4 != 0 )
        ++PixelLength;
    PixelLength *= ImageHeight;

    // 讀取像素數(shù)據(jù)
    PixelData = (GLubyte*)malloc(PixelLength);
    if( PixelData == 0 )
        exit(0);

    fseek(pFile, 54, SEEK_SET);
    fread(PixelData, PixelLength, 1, pFile);

    // 關(guān)閉文件
    fclose(pFile);

    // 初始化GLUT并運行
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(ImageWidth, ImageHeight);
    glutCreateWindow(FileName);
    glutDisplayFunc(&display);
    glutMainLoop();

    // 釋放內(nèi)存
    // 實際上,glutMainLoop函數(shù)永遠不會返回,這里也永遠不會到達
    // 這里寫釋放內(nèi)存只是出于一種個人習慣
    // 不用擔心內(nèi)存無法釋放。在程序結(jié)束時操作系統(tǒng)會自動回收所有內(nèi)存
    free(PixelData);

    return 0;
}

 

這里僅僅是一個簡單的顯示24位BMP圖象的程序,如果讀者對BMP文件格式比較熟悉,也可以寫出適用于各種BMP圖象的顯示程序,在像素處理時,它們所使用的方法是類似的。
OpenGL在繪制像素之前,可以對像素進行若干處理。最常用的可能就是對整個像素圖象進行放大/縮小。使用glPixelZoom來設(shè)置放大/縮小的系數(shù),該函數(shù)有兩個參數(shù),分別是水平方向系數(shù)和垂直方向系數(shù)。例如設(shè)置glPixelZoom(0.5f, 0.8f);則表示水平方向變?yōu)樵瓉淼?0%大小,而垂直方向變?yōu)樵瓉淼?0%大小。我們甚至可以使用負的系數(shù),使得整個圖象進行水平方向或垂直方向的翻轉(zhuǎn)(默認像素從左繪制到右,但翻轉(zhuǎn)后將從右繪制到左。默認像素從下繪制到上,但翻轉(zhuǎn)后將從上繪制到下。因此,glRasterPos*函數(shù)設(shè)置的“開始位置”不一定就是矩形的左下角)。
5、glCopyPixels的用法和舉例
從效果上看,glCopyPixels進行像素復(fù)制的操作,等價于把像素讀取到內(nèi)存,再從內(nèi)存繪制到另一個區(qū)域,因此可以通過glReadPixels和glDrawPixels組合來實現(xiàn)復(fù)制像素的功能。然而我們知道,像素數(shù)據(jù)通常數(shù)據(jù)量很大,例如一幅1024*768的圖象,如果使用24位BGR方式表示,則需要至少1024*768*3字節(jié),即2.25兆字節(jié)。這么多的數(shù)據(jù)要進行一次讀操作和一次寫操作,并且因為在glReadPixels和glDrawPixels中設(shè)置的數(shù)據(jù)格式不同,很可能涉及到數(shù)據(jù)格式的轉(zhuǎn)換。這對CPU無疑是一個不小的負擔。使用glCopyPixels直接從像素數(shù)據(jù)復(fù)制出新的像素數(shù)據(jù),避免了多余的數(shù)據(jù)的格式轉(zhuǎn)換,并且也可能減少一些數(shù)據(jù)復(fù)制操作(因為數(shù)據(jù)可能直接由顯卡負責復(fù)制,不需要經(jīng)過主內(nèi)存),因此效率比較高。
glCopyPixels函數(shù)也通過glRasterPos*系列函數(shù)來設(shè)置繪制的位置,因為不需要涉及到主內(nèi)存,所以不需要指定數(shù)據(jù)在內(nèi)存中的格式,也不需要使用任何指針。
glCopyPixels函數(shù)有五個參數(shù),第一、二個參數(shù)表示復(fù)制像素來源的矩形的左下角坐標,第三、四個參數(shù)表示復(fù)制像素來源的舉行的寬度和高度,第五個參數(shù)通常使用GL_COLOR,表示復(fù)制像素的顏色,但也可以是GL_DEPTH或GL_STENCIL,分別表示復(fù)制深度緩沖數(shù)據(jù)或模板緩沖數(shù)據(jù)。
值得一提的是,glDrawPixels和glReadPixels中設(shè)置的各種操作,例如glPixelZoom等,在glCopyPixels函數(shù)中同樣有效。
下面看一個簡單的例子,繪制一個三角形后,復(fù)制像素,并同時進行水平和垂直方向的翻轉(zhuǎn),然后縮小為原來的一半,并繪制。繪制完畢后,調(diào)用前面的grab函數(shù),將屏幕中所有內(nèi)容保存為grab.bmp。其中WindowWidth和WindowHeight是表示窗口寬度和高度的常量。

void display(void)
{
    // 清除屏幕
    glClear(GL_COLOR_BUFFER_BIT);

    // 繪制
    glBegin(GL_TRIANGLES);
        glColor3f(1.0f, 0.0f, 0.0f);    glVertex2f(0.0f, 0.0f);
        glColor3f(0.0f, 1.0f, 0.0f);    glVertex2f(1.0f, 0.0f);
        glColor3f(0.0f, 0.0f, 1.0f);    glVertex2f(0.5f, 1.0f);
    glEnd();
    glPixelZoom(-0.5f, -0.5f);
    glRasterPos2i(1, 1);
    glCopyPixels(WindowWidth/2, WindowHeight/2,
        WindowWidth/2, WindowHeight/2, GL_COLOR);

    // 完成繪制,并抓取圖象保存為BMP文件
    glutSwapBuffers();
    grab();
}

 

 

 

小結(jié):
本課結(jié)合Windows系統(tǒng)常見的BMP圖象格式,簡單介紹了OpenGL的像素處理功能。包括使用glReadPixels讀取像素、glDrawPixels繪制像素、glCopyPixels復(fù)制像素。
本課僅介紹了像素處理的一些簡單應(yīng)用,但相信大家已經(jīng)可以體會到,圍繞這三個像素處理函數(shù),還存在一些“外圍”函數(shù),比如glPixelStore*,glRasterPos*,以及glPixelZoom等。我們僅使用了這些函數(shù)的一少部分功能。
本課內(nèi)容并不多,例子足夠豐富,三個像素處理函數(shù)都有例子,大家可以結(jié)合例子來體會。

 

附錄(其它位色的BMP文件簡介):

BMP文件組成
  BMP文件由文件頭、位圖信息頭、顏色信息和圖形數(shù)據(jù)四部分組成。
   BMP文件頭
  BMP文件頭數(shù)據(jù)結(jié)構(gòu)含有BMP文件的類型、文件大小和位圖起始位置等信息。
  其結(jié)構(gòu)定義如下:  
  typedef struct tagBITMAPFILEHEADER
  {
  WORDbfType; // 位圖文件的類型,必須為BM
  DWORD bfSize; // 位圖文件的大小,以字節(jié)為單位
  WORDbfReserved1; // 位圖文件保留字,必須為0
  WORDbfReserved2; // 位圖文件保留字,必須為0
  DWORD bfOffBits; // 位圖數(shù)據(jù)的起始位置,以相對于位圖
  // 文件頭的偏移量表示,以字節(jié)為單位
  } BITMAPFILEHEADER;
  

位圖信息頭
  
  BMP位圖信息頭數(shù)據(jù)用于說明位圖的尺寸等信息。
  typedef struct tagBITMAPINFOHEADER{
  DWORD biSize; // 本結(jié)構(gòu)所占用字節(jié)數(shù)
  LONGbiWidth; // 位圖的寬度,以像素為單位
  LONGbiHeight; // 位圖的高度,以像素為單位
  WORD biPlanes; // 目標設(shè)備的級別,必須為1
  WORD biBitCount// 每個像素所需的位數(shù),必須是1(雙色),
  // 4(16色),8(256色)或24(真彩色)之一
  DWORD biCompression; // 位圖壓縮類型,必須是 0(不壓縮),
  // 1(BI_RLE8壓縮類型)或2(BI_RLE4壓縮類型)之一
  DWORD biSizeImage; // 位圖的大小,以字節(jié)為單位
  LONGbiXPelsPerMeter; // 位圖水平分辨率,每米像素數(shù)
  LONGbiYPelsPerMeter; // 位圖垂直分辨率,每米像素數(shù)
  DWORD biClrUsed;// 位圖實際使用的顏色表中的顏色數(shù)
  DWORD biClrImportant;// 位圖顯示過程中重要的顏色數(shù)
  } BITMAPINFOHEADER;
  

顏色表
  顏色表用于說明位圖中的顏色,它有若干個表項,每一個表項是一個RGBQUAD類型的結(jié)構(gòu),定義一種顏色。RGBQUAD結(jié)構(gòu)的定義如下:
  
  typedef struct tagRGBQUAD {
  BYTErgbBlue;// 藍色的亮度(值范圍為0-255)
  BYTErgbGreen; // 綠色的亮度(值范圍為0-255)
  BYTErgbRed; // 紅色的亮度(值范圍為0-255)
  BYTErgbReserved;// 保留,必須為0
  } RGBQUAD;
  顏色表中RGBQUAD結(jié)構(gòu)數(shù)據(jù)的個數(shù)有biBitCount來確定:
  當biBitCount=1,4,8時,分別有2,16,256個表項;
  當biBitCount=24時,沒有顏色表項。
  位圖信息頭和顏色表組成位圖信息,BITMAPINFO結(jié)構(gòu)定義如下:
  typedef struct tagBITMAPINFO {
  BITMAPINFOHEADER bmiHeader; // 位圖信息頭
  RGBQUAD bmiColors[1]; // 顏色表
  } BITMAPINFO;

位圖數(shù)據(jù)
  位圖數(shù)據(jù)記錄了位圖的每一個像素值,記錄順序是在掃描行內(nèi)是從左到右,掃描行之間是從下到上。位圖的一個像素值所占的字節(jié)數(shù):
  
  當biBitCount=1時,8個像素占1個字節(jié);
  當biBitCount=4時,2個像素占1個字節(jié);
  當biBitCount=8時,1個像素占1個字節(jié);
  當biBitCount=24時,1個像素占3個字節(jié);
  Windows規(guī)定一個掃描行所占的字節(jié)數(shù)必須是
  4的倍數(shù)(即以long為單位),不足的以0填充,
  一個掃描行所占的字節(jié)數(shù)計算方法:
  DataSizePerLine= (biWidth* biBitCount+31)/8;
  // 一個掃描行所占的字節(jié)數(shù)
  DataSizePerLine= DataSizePerLine/4*4; // 字節(jié)數(shù)必須是4的倍數(shù)
  位圖數(shù)據(jù)的大小(不壓縮情況下):
  DataSize= DataSizePerLine* biHeight;

 

 

posted on 2010-06-02 11:02 風輕云淡 閱讀(2796) 評論(0)  編輯 收藏 引用 所屬分類: 圖像讀取
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美片在线观看| 欧美激情久久久久| 国产精品theporn| 亚洲欧洲三级电影| 狂野欧美性猛交xxxx巴西| 国产精品99久久久久久www| 欧美电影免费观看| 99亚洲一区二区| 亚洲精品国产精品久久清纯直播| 欧美在线一区二区三区| 在线亚洲一区| 亚洲免费激情| 亚洲视频观看| 亚洲一区二区三区三| 亚洲综合不卡| 国产亚洲精品aa| 久久亚洲欧洲| 欧美福利一区二区| 欧美成人黑人xx视频免费观看| 老司机免费视频一区二区三区| 亚洲电影有码| 亚洲免费观看| 国产日韩一区二区| 亚洲黄色尤物视频| 国产精品国产三级国产专区53| 亚欧成人在线| 久久综合亚州| 欧美护士18xxxxhd| 欧美日韩中文| 麻豆成人在线播放| 在线看视频不卡| 男女激情久久| 久久成人这里只有精品| 国产深夜精品福利| 久久er精品视频| av成人国产| 国产精品乱码人人做人人爱| 久热精品视频在线观看| 在线观看精品视频| 亚洲无线一线二线三线区别av| 亚洲高清一区二| 亚洲欧美在线一区| 亚洲一级片在线看| 国产美女精品视频| 亚洲人成网站在线观看播放| 在线亚洲欧美视频| 欧美黑人一区二区三区| 日韩视频欧美视频| 欧美一区二区高清| 国产精品国产三级国产aⅴ无密码| 亚洲香蕉网站| 欧美ab在线视频| 亚洲精品国偷自产在线99热| 久久青青草综合| 久久成人在线| 亚洲精品美女在线观看播放| 欧美性大战久久久久久久蜜臀| 欧美在线观看网址综合| 亚洲国产成人精品久久| 国内免费精品永久在线视频| 亚洲香蕉在线观看| 鲁鲁狠狠狠7777一区二区| 日韩视频第一页| 国产午夜精品在线| 欧美精品国产精品日韩精品| 亚洲二区视频在线| 先锋亚洲精品| 亚洲精品偷拍| 国产亚洲一区二区三区| 欧美日韩精品久久久| 亚洲毛片在线看| 一本色道久久综合狠狠躁的推荐| 国产日韩欧美在线| 欧美三级在线| 老妇喷水一区二区三区| 亚洲欧美综合| 99re8这里有精品热视频免费| 久久久亚洲欧洲日产国码αv| 国产日韩欧美日韩| 欧美日韩在线视频一区二区| 久久久在线视频| 欧美激情视频在线播放| 久久成人精品电影| 亚洲一级影院| 亚洲精品视频二区| 激情欧美一区| 欧美激情精品| 猛男gaygay欧美视频| 亚洲激情第一页| 美女主播一区| 这里是久久伊人| 亚洲欧洲精品成人久久奇米网| 国产色产综合色产在线视频| 国产精品家教| 欧美三区免费完整视频在线观看| 欧美成人在线影院| 免费亚洲婷婷| 欧美+日本+国产+在线a∨观看| 久久久久88色偷偷免费| 亚洲国产91色在线| 欧美成人资源| 亚洲在线视频观看| 国产免费观看久久| 国产精品一区免费视频| 国产精品你懂的在线| 国产精品久久99| 欧美午夜精品理论片a级大开眼界 欧美午夜精品理论片a级按摩 | 亚洲欧美日韩系列| 狠狠久久亚洲欧美| 欧美精品一卡二卡| 欧美激情偷拍| 欧美日韩精品三区| 欧美日韩色一区| 久久精品视频免费播放| 欧美中文字幕在线| 久久久久9999亚洲精品| 久久久精品tv| 欧美成人一品| 欧美三区在线观看| 国产精品午夜视频| 欧美高清不卡| 欧美日韩国产区| 国产精品久久久久久久久久久久久| 欧美三区美女| 国产亚洲欧洲997久久综合| 精品91在线| 日韩一二三区视频| 亚洲国产合集| 日韩亚洲精品在线| 亚洲一区二区高清| 久久国产手机看片| 亚洲欧美日韩一区二区| 欧美亚洲自偷自偷| 老司机成人在线视频| 欧美国产先锋| 亚洲社区在线观看| 久久国产精品一区二区三区| 亚洲天堂第二页| 欧美一区二视频在线免费观看| 99在线|亚洲一区二区| 91久久精品美女| 亚洲女性裸体视频| 你懂的视频欧美| 国产精品久久| 亚洲人成在线观看| 性伦欧美刺激片在线观看| 欧美成人有码| 亚洲一区欧美激情| 亚洲一级在线| 蜜桃久久精品乱码一区二区| 欧美天天视频| 亚洲黄色成人久久久| 最新成人av在线| 午夜影视日本亚洲欧洲精品| 欧美成人黑人xx视频免费观看| 中文一区二区| 欧美第一黄网免费网站| 国产一区二区精品丝袜| 一区二区av在线| 免费在线成人av| 亚洲欧美日韩国产| 欧美另类综合| 在线日韩欧美视频| 欧美中文在线观看国产| 亚洲精品免费电影| 久久久青草婷婷精品综合日韩| 国产精品久久久久久久久免费桃花| 亚洲国产欧美不卡在线观看| 久久爱91午夜羞羞| 亚洲视频一二区| 欧美激情1区2区| 亚洲高清视频中文字幕| 欧美自拍偷拍午夜视频| 宅男噜噜噜66一区二区| 欧美女同视频| 亚洲黄色有码视频| 浪潮色综合久久天堂| 午夜在线观看免费一区| 久久深夜福利免费观看| 国产日韩欧美三级| 欧美一级片在线播放| 在线亚洲电影| 欧美性开放视频| 亚洲亚洲精品三区日韩精品在线视频| 亚洲动漫精品| 欧美成人免费观看| 亚洲精选视频免费看| 欧美激情一区在线观看| 久久婷婷麻豆| 亚洲黑丝在线| 亚洲高清资源| 欧美精品二区| 亚洲视频中文| 狂野欧美一区| 久久先锋影音av| 亚洲国产欧美一区二区三区久久| 欧美成人精品福利| 免费亚洲网站| 一本久久综合亚洲鲁鲁五月天| 亚洲精品一区二区三区99|