位圖(Bitmap)當(dāng)然是最簡單的,它Windows顯示圖片的基本格式,其文件擴(kuò)展名為*.BMP。在Windows下,任何各式的圖片文件(包括視頻播放)都要轉(zhuǎn)化為位圖個(gè)時(shí)候才能顯示出來,各種格式的圖片文件也都是在位圖格式的基礎(chǔ)上采用不同的壓縮算法生成的(Flash中使用了適量圖,是按相同顏色區(qū)域存儲的)。
一、下面我們來看看位圖文件(*.BMP)的格式。
位圖文件主要分為如下3個(gè)部分:
1、 文件信息頭BITMAPFILEHEADER 結(jié)構(gòu)體定義如下: typedef struct tagBITMAPFILEHEADER { /* bmfh */ UINT bfType; } BITMAPFILEHEADER; 其中:
2、位圖信息頭BITMAPINFOHEADER 結(jié)構(gòu)體定義如下: typedef struct tagBITMAPINFOHEADER { /* bmih */ DWORD biSize; } BITMAPINFOHEADER; 其中:
3、RGB顏色陣列 有關(guān)RGB三色空間我想大家都很熟悉,這里我想說的是在Windows下,RGB顏色陣列存儲的格式其實(shí)BGR。也就是說,對于24位的RGB位圖像素?cái)?shù)據(jù)格式是:
對于32位的RGB位圖像素?cái)?shù)據(jù)格式是:
透明通道也稱Alpha通道,該值是該像素點(diǎn)的透明屬性,取值在0(全透明)到255(不透明)之間。對于24位的圖像來說,因?yàn)闆]有Alpha通道,故整個(gè)圖像都不透明。
二、下一步我們要實(shí)現(xiàn)加載。 加載文件的目的是要得到圖片屬性,以及RGB數(shù)據(jù),然后可以將其繪制在DC上(GDI),或是生成紋理對象(3D:OpenGL/Direct3D)。這兩種用途在數(shù)據(jù)處理上有點(diǎn)區(qū)別,我們主要按前一種用法講,在和3D有不同的地方,我們再提出來。 1、加載文件頭 //Load the file header BITMAPFILEHEADER header; memset(&header, 0, sizeof(header)); read((char*)&header, sizeof(header)); if(header.bfType != 0x4D42) return false; 2、加載位圖信息頭 //Load the image information header BITMAPINFOHEADER infoheader; memset(&infoheader, 0, sizeof(infoheader)); inf.read((char*)&infoheader, sizeof(infoheader)); m_iImageWidth = infoheader.biWidth; m_iImageHeight = infoheader.biHeight; m_iBitsPerPixel = infoheader.biBitCount; 這里我們得到了3各重要的圖形屬性:寬,高,以及每個(gè)像素顏色所占用的位數(shù)。
3、行對齊 由于Windows在進(jìn)行行掃描的時(shí)候最小的單位為4個(gè)字節(jié),所以當(dāng) 圖片寬 X 每個(gè)像素的字節(jié)數(shù) != 4的整數(shù)倍 時(shí)要在每行的后面補(bǔ)上缺少的字節(jié),以0填充(一般來說當(dāng)圖像寬度為2的冪時(shí)不需要對齊)。位圖文件里的數(shù)據(jù)在寫入的時(shí)候已經(jīng)進(jìn)行了行對齊,也就是說加載的時(shí)候不需要再做行對齊。但是這樣一來圖片數(shù)據(jù)的長度就不是:寬 X 高 X 每個(gè)像素的字節(jié)數(shù) 了,我們需要通過下面的方法計(jì)算正確的數(shù)據(jù)長度: //Calculate the image data size int iLineByteCnt = (((m_iImageWidth*m_iBitsPerPixel) + 31) >> 5) << 2; m_iImageDataSize = iLineByteCnt * m_iImageHeight;
4、加載圖片數(shù)據(jù) 對于24位和32位的位圖文件,位圖數(shù)據(jù)的偏移量為sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER),也就是說現(xiàn)在我們可以直接讀取圖像數(shù)據(jù)了。 if(m_pImageData) delete []m_pImageData; m_pImageData = new unsigned char[m_iImageDataSize]; inf.read((char*)m_pImageData, m_iImageDataSize); 如果你足夠細(xì)心,就會發(fā)現(xiàn)內(nèi)存m_pImageData里的數(shù)據(jù)的確是BGR格式,可以用個(gè)純藍(lán)色或者是純紅色的圖片測試一下。
5、繪制 好了,數(shù)據(jù)和屬性我們都有了,現(xiàn)在就可以拿來隨便用了,就和吃饅頭一樣,愛粘白糖粘白糖,愛粘紅糖粘紅糖。下面是我的GDI繪制代碼,僅作參考。 void CImage::DrawImage(HDC hdc, int iLeft, int iTop, int iWidth, int iHeight) { if(!hdc || m_pImageData == NULL) return; BITMAPINFO bmi; memset(&bmi, 0, sizeof(bmi)); bmi.bmiHeader.biSize = sizeof(BITMAPINFO); bmi.bmiHeader.biWidth = m_iImageWidth; bmi.bmiHeader.biHeight = m_iImageHeight; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = m_iBitsPerPixel; bmi.bmiHeader.biCompression = BI_RGB; bmi.bmiHeader.biSizeImage = m_iImageDataSize; StretchDIBits(hdc, iLeft, iTop, iWidth, iHeight, 0, 0, m_iImageWidth, m_iImageHeight, m_pImageData, &bmi, DIB_RGB_COLORS, SRCCOPY); }
6、3D(OpenGL)的不同之處 如果你是想用剛才我們得到的數(shù)據(jù)生成紋理對象,那么你還要請出下面的問題。 首先,用來生成紋理的數(shù)據(jù)不需要對齊,也就是說不能在每行的后面加上對齊的字節(jié)。當(dāng)然在OpenGL里要求紋理圖片的尺寸為2的冪,所以這個(gè)問題實(shí)際上不存在; 其次,我們得到的圖形數(shù)據(jù)格式是BGR(BGRA),所以在生成紋理的時(shí)候,需指定格式為GL_BGR_EXT(GL_BGRA_EXT);否則需要做BGR->RGB(BGRA->RGBA)的轉(zhuǎn)化。 |
|