理解分辨率數組
咱們常說的屏幕分辨率爲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個字節。
bfReserved1,bfReserved2
爲保留字,不用考慮
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函數得到的內存塊指針(輸出的第四個參數)所指向的地址中是沒有內容的,咱們必須向裏面寫入圖像數據,而後纔可以顯示圖像。