點陣圖(Bitmap)當然是最簡單的,它Windows顯示圖片的基本格式,其文件擴展名為*.BMP。在Windows下,任何各式的圖片文件(包括視頻播放)都要轉化為點陣圖個時候才能顯示出來,各種格式的圖片文件也都是在點陣圖格式的基礎上採用不同的壓縮演算法生成的(Flash中使用了適量圖,是按相同顏色區域存儲... ...
點陣圖(Bitmap)當然是最簡單的,它Windows顯示圖片的基本格式,其文件擴展名為*.BMP。在Windows下,任何各式的圖片文件(包括視頻播放)都要轉化為點陣圖個時候才能顯示出來,各種格式的圖片文件也都是在點陣圖格式的基礎上採用不同的壓縮演算法生成的(Flash中使用了適量圖,是按相同顏色區域存儲的)。
一、下麵我們來看看點陣圖文件(*.BMP)的格式。
點陣圖文件主要分為如下3個部分:
1、 文件信息頭BITMAPFILEHEADER
結構體定義如下:
typedef struct tagBITMAPFILEHEADER { /* bmfh */
UINT bfType;
DWORD bfSize;
UINT bfReserved1;
UINT bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
其中:
2、點陣圖信息頭BITMAPINFOHEADER
結構體定義如下:
typedef struct tagBITMAPINFOHEADER { /* bmih */
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
其中:
3、RGB顏色陣列
有關RGB三色空間我想大家都很熟悉,這裡我想說的是在Windows下,RGB顏色陣列存儲的格式其實BGR。也就是說,對於24位的RGB點陣圖像素數據格式是:
藍色B值 綠色G值 紅色R值
對於32位的RGB點陣圖像素數據格式是:
藍色B值 綠色G值 紅色R值 透明通道A值
透明通道也稱Alpha通道,該值是該像素點的透明屬性,取值在0(全透明)到255(不透明)之間。對於24位的圖像來說,因為沒有Alpha通道,故整個圖像都不透明。
二、搞清了文件格式,下一步我們要實現載入。
載入文件的目的是要得到圖片屬性,以及RGB數據,然後可以將其繪製在DC上(GDI),或是生成紋理對象(3D:OpenGL/Direct3D)。這兩種用途在數據處理上有點區別,我們主要按前一種用法講,在和3D有不同的地方,我們再提出來。
1、載入文件頭
//Load the file header
BITMAPFILEHEADER header;
memset(&header, 0, sizeof(header));
inf.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各重要的圖形屬性:寬,高,以及每個像素顏色所占用的位數。
3、行對齊
由於Windows在進行行掃描的時候最小的單位為4個位元組,所以當
圖片寬 X 每個像素的位元組數 != 4的整數倍
時要在每行的後面補上缺少的位元組,以0填充(一般來說當圖像寬度為2的冪時不需要對齊)。點陣圖文件里的數據在寫入的時候已經進行了行對齊,也就是說載入的時候不需要再做行對齊。但是這樣一來圖片數據的長度就不是:寬 X 高 X 每個像素的位元組數 了,我們需要通過下麵的方法計算正確的數據長度:
//Calculate the image data size
int iLineByteCnt = (((m_iImageWidth*m_iBitsPerPixel) + 31) >> 5) << 2;
m_iImageDataSize = iLineByteCnt * m_iImageHeight;
4、載入圖片數據
對於24位和32位的點陣圖文件,點陣圖數據的偏移量為sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER),也就是說現在我們可以直接讀取圖像數據了。
if(m_pImageData) delete []m_pImageData;
m_pImageData = new unsigned char[m_iImageDataSize];
inf.read((char*)m_pImageData, m_iImageDataSize);
如果你足夠細心,就會發現記憶體m_pImageData里的數據的確是BGR格式,可以用個純藍色或者是純紅色的圖片測試一下。
5、繪製
好了,數據和屬性我們都有了,現在就可以拿來隨便用了,就和吃饅頭一樣,愛粘白糖粘白糖,愛粘紅糖粘紅糖。下麵是我的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)的不同之處
如果你是想用剛纔我們得到的數據生成紋理對象,那麼你還要請出下麵的問題。
首先,用來生成紋理的數據不需要對齊,也就是說不能在每行的後面加上對齊的位元組。當然在OpenGL里要求紋理圖片的尺寸為2的冪,所以這個問題實際上不存在;
其次,我們得到的圖形數據格式是BGR(BGRA),所以在生成紋理的時候,需指定格式為GL_BGR_EXT(GL_BGRA_EXT);否則需要做BGR->RGB(BGRA->RGBA)的轉化。