CreateDIBSection和位圖結構

理解分辨率數組

咱們常說的屏幕分辨率爲640×480,刷新頻率爲70Hz,意思是說每行要掃描640個象素,一共有480行,每秒重複掃描屏幕70次。app

 

理解調色板函數

有一個長寬各爲200個象素,顏色數爲16色的彩色圖,每個象素都用R、G、B三個份量表示。由於每一個份量有256個級別,要用8位(bit),即一個字節(byte)來表示,因此每一個象素須要用3個字節。整個圖象要用200×200×3,約120k字節,可不是一個小數目呀!若是咱們用下面的方法,就能省的多。spa

由於是一個16色圖,也就是說這幅圖中最多隻有16種顏色,咱們能夠用一個表:表中的每一行記錄一種顏色的R、G、B值。這樣當咱們表示一個象素的顏色時,只須要指出該顏色是在第幾行,即該顏色在表中的索引值。舉個例子,若是表的第0行爲255,0,0(紅色),那麼當某個象素爲紅色時,只須要標明0便可。指針

讓咱們再來計算一下:16種狀態能夠用4位(bit)表示,因此一個象素要用半個字節。整個圖象要用200×200×0.5,約20k字節,再加上表佔用的字節爲3×16=48字節.整個佔用的字節數約爲前面的1/6,省不少吧?code

這張R、G、B的表,就是咱們常說的調色板(Palette),另外一種叫法是顏色查找表LUT(Look Up Table),彷佛更確切一些。Windows位圖中便用到了調色板技術。其實不光是Windows位圖,許多圖象文件格式如pcx、tif、gif等都用到了。因此很好地掌握調色板的概念是十分有用的。orm

有一種圖,它的顏色數高達256×256×256種,也就是說包含咱們上述提到的R、G、B顏色表示方法中全部的顏色,這種圖叫作真彩色圖(true color)。真彩色圖並非說一幅圖包含了全部的顏色,而是說它具備顯示全部顏色的能力,即最多能夠包含全部的顏色。表示真彩色圖時,每一個象素直接用R、G、B三個份量字節表示,而不採用調色板技術。緣由很明顯:若是用調色板,表示一個象素也要用24位,這是由於每種顏色的索引要用24位(由於總共有224種顏色,即調色板有224行),和直接用R,G,B三個份量表示用的字節數同樣,不但沒有任何便宜,還要加上一個256×256×256×3個字節的大調色板。因此真彩色圖直接用R、G、B三個份量表示,它又叫作24位色圖。索引

bmp文件格式內存

介紹完位圖和調色板的概念,下面就讓咱們來看一看Windows的位圖文件(.bmp文件)的格式是什麼樣子的。ci

bmp文件大致上分紅四個部分,如圖1.3所示。

位圖文件頭BITMAPFILEHEADER

位圖信息頭BITMAPINFOHEADER

調色板Palette

實際的位圖數據ImageDate

1.3     Windows位圖文件結構示意圖

第一部分爲位圖文件頭BITMAPFILEHEADER,是一個結構,其定義以下:

typedef struct tagBITMAPFILEHEADER {

WORD           bfType;

DWORD bfSize;

WORD           bfReserved1;

WORD           bfReserved2;

DWORD bfOffBits;

} BITMAPFILEHEADER;

這個結構的長度是固定的,爲14個字節(WORD爲無符號16位整數,DWORD爲無符號32位整數),各個域的說明以下:

bfType

指定文件類型,必須是0x424D,即字符串「BM」,也就是說全部.bmp文件的頭兩個字節都是「BM」。

bfSize

指定文件大小,包括這14個字節。

bfReserved1bfReserved2     

爲保留字,不用考慮

bfOffBits

爲從文件頭到實際的位圖數據的偏移字節數,即圖1.3中前三個部分的長度之和。

第二部分爲位圖信息頭BITMAPINFOHEADER,也是一個結構,其定義以下:

typedef struct tagBITMAPINFOHEADER{

DWORD  biSize;

LONG            biWidth;

LONG            biHeight;

WORD           biPlanes;

WORD           biBitCount

DWORD  biCompression;

DWORD  biSizeImage;

LONG            biXPelsPerMeter;

LONG            biYPelsPerMeter;

DWORD  biClrUsed;

DWORD  biClrImportant;

} BITMAPINFOHEADER;

這個結構的長度是固定的,爲40個字節(LONG爲32位整數),各個域的說明以下:

biSize

指定這個結構的長度,爲40。

biWidth

指定圖象的寬度,單位是象素。

biHeight

指定圖象的高度,單位是象素。

biPlanes

必須是1,不用考慮。

biBitCount

指定表示顏色時要用到的位數,經常使用的值爲1(黑白二色圖), 4(16色圖), 8(256色), 24(真彩色圖)(新的.bmp格式支持32位色,這裏就不作討論了)。

biCompression

指定位圖是否壓縮,有效的值爲BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS(都是一些Windows定義好的常量)。要說明的是,Windows位圖能夠採用RLE4,和RLE8的壓縮格式,但用的很少。咱們從此所討論的只有第一種不壓縮的狀況,即biCompression爲BI_RGB的狀況。

biSizeImage

指定實際的位圖數據佔用的字節數,其實也能夠從如下的公式中計算出來:

biSizeImage=biWidth’ × biHeight

要注意的是:上述公式中的biWidth’必須是4的整倍數(因此不是biWidth,而是biWidth’,表示大於或等於biWidth的,最接近4的整倍數。舉個例子,若是biWidth=240,則biWidth’=240;若是biWidth=241,biWidth’=244)。

若是biCompression爲BI_RGB,則該項可能爲零

biXPelsPerMeter

指定目標設備的水平分辨率,單位是每米的象素個數,關於分辨率的概念,咱們將在第4章詳細介紹。

biYPelsPerMeter

指定目標設備的垂直分辨率,單位同上。

biClrUsed

指定本圖象實際用到的顏色數,若是該值爲零,則用到的顏色數爲2biBitCount。

biClrImportant

指定本圖象中重要的顏色數,若是該值爲零,則認爲全部的顏色都是重要的。

第三部分爲調色板Palette,固然,這裏是對那些須要調色板的位圖文件而言的。有些位圖,如真彩色圖,前面已經講過,是不須要調色板的,BITMAPINFOHEADER後直接是位圖數據。

調色板其實是一個數組,共有biClrUsed個元素(若是該值爲零,則有2biBitCount個元素)。數組中每一個元素的類型是一個RGBQUAD結構,佔4個字節,其定義以下:

typedef struct tagRGBQUAD {

BYTE    rgbBlue; //該顏色的藍色份量

BYTE    rgbGreen; //該顏色的綠色份量

BYTE    rgbRed; //該顏色的紅色份量

BYTE    rgbReserved; //保留值

} RGBQUAD;

第四部分就是實際的圖象數據了。對於用到調色板的位圖,圖象數據就是該象素顏在調色板中的索引值。對於真彩色圖,圖象數據就是實際的R、G、B值。下面針對2色、16色、256色位圖和真彩色位圖分別介紹。

對於2色位圖,用1位就能夠表示該象素的顏色(通常0表示黑,1表示白),因此一個字節能夠表示8個象素。

對於16色位圖,用4位能夠表示一個象素的顏色,因此一個字節能夠表示2個象素。

對於256色位圖,一個字節恰好能夠表示1個象素。

對於真彩色圖,三個字節才能表示1個象素,哇,好費空間呀!沒辦法,誰叫你想讓圖的顏色顯得更亮麗呢,有得必有失嘛。

要注意兩點:

(1)    每一行的字節數必須是4的整倍數,若是不是,則須要補齊。這在前面介紹biSizeImage時已經提到了。

(2)    通常來講,.bMP文件的數據從下到上,從左到右的。也就是說,從文件中最早讀到的是圖象最下面一行的左邊第一個象素,而後是左邊第二個象素……接下來是倒數第二行左邊第一個象素,左邊第二個象素……依次類推 ,最後獲得的是最上面一行的最右一個象素。

下面的函數將pBuffer指向的內存塊中的位圖數據寫入文件中,lBufferLen參數爲pBuffer指向的內存塊的大小,注意必須先指定位圖的BITMAPFILEHEADER結構和BITMAPINFOHEADER結構。

STDMETHODIMP CSampleGrabberCallback::BufferCB(double time,BYTE* pBuffer,long lBufferLen)
{
    if(!g_bSnap)
        return E_FAIL;
    BOOL bWrite=FALSE;
    HANDLE hFile=CreateFile("E:\\Test.bmp",GENERIC_WRITE,
        FILE_SHARE_READ, NULL, CREATE_ALWAYS, NULL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        return E_FAIL;
}
//首先初始化位圖文件頭結構(BITMAPFILEHEADER),並將其寫入文件。
    BITMAPFILEHEADER bmpFileHeader;
    //memset(&bmpFileHeader,0,sizeof(bmpFileHeader));
    ZeroMemory(&bmpFileHeader,sizeof(bmpFileHeader));
 
    bmpFileHeader.bfType='MB';
    bmpFileHeader.bfSize=sizeof(bmpFileHeader)+lBufferLen+sizeof(BITMAPINFOHEADER);
    bmpFileHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
 
    DWORD dwWritten=0;
    bWrite=WriteFile(hFile,&bmpFileHeader,sizeof(bmpFileHeader),&dwWritten,NULL);
    if(!bWrite)
    {
        MessageBox(0,TEXT("fail to write"),TEXT("Error"),MB_OK);
    }
 
    //初始化BITMAPINFOHEADER結構並將其寫入文件。
    //VIDEOINFOHEADER*viInfoHeader=(VIDEOINFOHEADER*) g_media_type.pbFormat;
    //FreeMediaType(g_media_type);
    BITMAPINFOHEADER bmpInfoHeader;
    ZeroMemory(&bmpInfoHeader,sizeof(bmpInfoHeader));
    //memset(&bmpInfoHeader,0,sizeof(bmpInfoHeader));
 
    bmpInfoHeader.biSize=sizeof(bmpInfoHeader);
    bmpInfoHeader.biWidth=lWidth;
    bmpInfoHeader.biHeight=lHeight;
    bmpInfoHeader.biPlanes=1;
    bmpInfoHeader.biBitCount=16;//???24 8
 
    dwWritten=0;
    bWrite=WriteFile(hFile,&bmpInfoHeader,sizeof(bmpInfoHeader),&dwWritten,NULL);
    if(!bWrite)
    {
        MessageBox(0,TEXT("fail to write"),TEXT("Error"),MB_OK);
    }
 
    //最後將位圖的主要數據寫入文件。
    dwWritten=0;
    bWrite=WriteFile(hFile,pBuffer,lBufferLen,&dwWritten,NULL);
    if(!bWrite)
    {
        MessageBox(0,TEXT("fail to write"),TEXT("Error"),MB_OK);
    }
 
    CloseHandle(hFile);
 
    CWnd* pMainWnd=theApp.GetMainWnd();
    CDfgDlg* pDfg=(CDfgDlg*)pMainWnd;
    HWND hwnd=pDfg->m_picture.GetSafeHwnd();
   
    RECT rc;
    ::GetWindowRect(hwnd,&rc);
    long lStillWidth=rc.right-rc.left;
    long lStillHeight=rc.bottom-rc.top;
    
    HDC hdcStill=GetDC(hwnd);
    PAINTSTRUCT ps;
    BeginPaint(hwnd,&ps);
 
SetStretchBltMode(hdcStill,COLORONCOLOR);
 StretchDIBits(hdcStill,0,0,lStillWidth,lStillHeight,0,0,lWidth,lHeight,pBuffer,(BITMAPINFO*)&bmpInfoHeader,DIB_RGB_COLORS,SRCCOPY);
    EndPaint(hwnd,&ps);
    ReleaseDC(hwnd,hdcStill);
 
    g_bSnap=!g_bSnap;
    return S_OK;   
}

 

關於CreateDIBSection函數:

HBITMAP CreateDIBSection(
 HDC hdc,                 // handle to DC
 CONST BITMAPINFO*pbmi, // bitmap data
 UINT iUsage,             // data type indicator
 VOID**ppvBits,          // bit values
 HANDLE hSection,         // handle to file mapping object
 DWORD dwOffset           // offset to bitmap bit values
);

CreateDIBSection函數會根據位圖結構信息(pbmi)分配內存空間,你不用爲它分配內存,這塊內存也不須要你釋放,系統會本身釋放的。   
  而後將位圖中的圖像數據讀入這個內存地址,顯示便可。   
  LPBYTE   lpBits; 
  HBITMAP   hBmp=::CreateDIBSection(dcMem.m_hDC,lpBitmap,DIB_PAL_COLORS,   &lpBits,NUL      L,0);

//將圖像數據填充到獲得的內存地址中
  file.ReadHuge(lpBits,dwBitlen);   
  pDC->StretchBlt(0,0,bmp.bmWidth,bmp.bmHeight,&dcMem,0,0,   
  bmp.bmWidth,bmp.bmHeight,SRCCOPY);  

 

首先讓咱們檢查一下如何簡化CreateDIBSection,並正確地使用它。首先,把最後兩個參數hSection和dwOffset,分別設定爲NULL和0,我將在本章最後討論這些參數的用法。第二,僅在fColorUse參數設定爲DIB_ PAL_COLORS時,才使用hdc參數,若是fColorUse爲DIB_RGB_COLORS(或0),hdc將被忽略(這與CreateDIBitmap不一樣,hdc參數用於取得與DDB相容的設備的色彩格式,CreateDIBitmap建立的是DDB(設備相關位圖,CreateDIBSection建立設備無關位圖),所以必須指定與位圖所關聯的設備,即hdc,位圖根據hdc所表明的設備來取得位圖的色彩格式)。

所以,CreateDIBSection最簡單的形式僅須要第二和第四個參數。第二個參數是指向BITMAPINFO結構的指標,

BITMAPINFOHEADER         bmih ;
BYTE                           * pBits ;
HBITMAP                          hBitmap ;
如今初始化BITMAPINFOHEADER結構的欄位

bmih->biSize                  = sizeof (BITMAPINFOHEADER) ;
bmih->biWidth                 = 384 ;
bmih->biHeight                = 256 ;
bmih->biPlanes                = 1 ;
bmih->biBitCount              = 24 ;
bmih->biCompression           = BI_RGB ;
bmih->biSizeImage             = 0 ;
bmih->biXPelsPerMeter         = 0 ;
bmih->biYPelsPerMeter         = 0 ;
bmih->biClrUsed               = 0 ;
bmih->biClrImportant          = 0 ;
在基本準備後,咱們呼叫該函式:

hBitmap = CreateDIBSection (NULL, (BITMAPINFO *)  &bmih, 0, &pBits, NULL, 0) ;

這是函式呼叫所作的:CreateDIBSection檢查BITMAPINFOHEADER結構並配置足夠的記憶體塊來載入DIB圖素位元。(在這個例子裏,記憶體塊的大小爲384×256×3位元組。)它在您提供的pBits參數中儲存了指向此記憶體塊的指標。

然而,咱們尚未作完,點陣圖圖素是未初始化的。若是正在讀取DIB檔案,能夠簡單地把pBits參數傳遞給ReadFile函式並讀取它們。或者可使用一些程式碼「人工」設定。

注意:使用CreateDIBSection函數得到的內存塊指針(輸出的第四個參數)所指向的地址中是沒有內容的,咱們必須向裏面寫入圖像數據,而後纔可以顯示圖像。

相關文章
相關標籤/搜索