本文主要介绍:DIB位图的一些基础知识和在MFC中如何利用DIB位图显示图像。
一、DIB位图结构及注意点:
1.DIB结构:
一个完整的DIB由两部分组成:一个BITMAPINFO结构和一个存储像素阵列的数组:
typedef struct tagBITMAPINFO { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[1]; //颜色表 } BITMAPINFO;由此可见BITMAPINFO结构包含两个部分,BITMAPINFOHEADER结构和RGBQUAD结构,其中两个结构定义如下:
typedef struct tagBITMAPINFOHEADER{ DWORD biSize; //该结构的大小 LONG biWidth; //位图的宽度(以像素为单位) LONG biHeight; //位图的高度(以像素为单位) WORD biPlanes; //必须为1 WORD biBitCount //每个像素的位数(1、4、8、16、24或32) DWORD biCompression; //压缩方式,一般为0或BI_RGB (未压缩) DWORD biSizeImage; //以字节为单位的图象大小(仅用于压缩位图) LONG biXPelsPerMeter; //以目标设备每米的像素数来说明位图的水平分辨率 LONG biYPelsPerMeter; //以目标设备每米的像素数来说明位图的垂直分辨率 DWORD biClrUsed; /*颜色表的颜色数,若为0则位图使用由biBitCount指定的最大颜色数*/ DWORD biClrImportant; //重要颜色的数目,若该值为0则所有颜色都重要 } BITMAPINFOHEADER;
typedef struct tagRGBQUAD { BYTE rgbBlue; //蓝色的强度 BYTE rgbGreen; //绿色的强度 BYTE rgbRed; //红色的强度 BYTE rgbReserved; //保留字节,为0 } RGBQUAD; //注意,RGBQUAD结构中的颜色顺序是BGR,而不是平常的RGB。由此可知,BITMAPINFO结构是由图像的基本信息和调色板组成,对于彩色图,没有调色板,所以BITMAPINFO只包含图像的基本信息结构BITMAPINFOHEADER:
灰度图 彩色图
BITMAPINFO包含: BITMAPINFOHEADER + RGBQUAD BITMAPINFOHEADER
2.DIB注意点:
<1>DIB位图每行数据必须是32bit(4个字节)的整数倍,如果图像数据为Byte型(0~255),即每个像素为一个字节(8bit),这样图像每行的宽必须是4的整数倍,不足4的整数倍的部分,以0补充。
<2>DIB数据存储顺序是:自左到右,自下到上,逆序存储。即:图像的第一行数据,存在DIB数据部分最后一行,最后一行存在第一行,因为显示的时候,DIB位图是,自下而上显示的,即读取的第一行数据,会显示在界面的最后一行,然后,第二行显示在界面倒数第二行。
<3>调色板顺序为BGR而不是RGB。
二、DIB结构赋值及显示:
1.先看一下显示函数:
int StretchDIBits(HDC hdc, int XDest , int YDest , int nDestWidth, int nDestHeight, int XSrc, int Ysrc, int nSrcWidth, int nSrcHeight, CONST VOID *lpBits, CONST BITMAPINFO * lpBitsInfo, hdc:指向目标设备环境的句柄。 XDest:指定目标矩形左上角位置的X轴坐标,按逻辑单位来表示坐标。 YDest:指定目标矩形左上角的Y轴坐标,按逻辑单位表示坐标。 nDestWidth:指定目标矩形的宽度。 nDestHeight:指定目标矩形的高度。 XSrc:指定DIB中源矩形(左上角)的X轴坐标,坐标以像素点表示。 YSrc:指定DIB中源矩形(左上角)的Y轴坐标,坐标以像素点表示。 nSrcWidth:按像素点指定DIB中源矩形的宽度。 nSrcHeight:按像素点指定DIB中源矩形的高度。 lpBits:指向DIB位的指针,这些位的值按字节类型数组存储,有关更多的信息,参考下面的备注一节。 lpBitsInfo:指向BITMAPINFO结构的指针,该结构包含有关DIB方面的信息。 iUsage:表示是否提供了BITMAPINFO结构中的成员bmiColors,如果提供了,那么该bmiColors是否包含了明确的RGB值或索引。参数iUsage必须取下列值,这些值的含义如下: DIB_PAL_COLORS:表示该数组包含对源设备环境的逻辑调色板进行索引的16位索引值。 DIB_RGB_COLORS:表示该颜色表包含原义的RGB值,若想了解更多的信息,请参考下面备注一节。 dwRop:指定源像素点、目标设备环境的当前刷子和目标像素点是如何组合形成新的图像。若想了解更多信息,请参考下面的备注一节。 返回值:如果函数执行成功,那么返回值是拷贝的扫描线数目,如果函数执行失败,那么返回值是GDI_ERROR。UINT iUsage, DWORD dwRop);由此可知:只要对BITMAPINFO结构和像素阵列的数组赋值即可:
2.DIB赋值:
<1>在 ***Doc.h中定义BITMAPINFO结构和像素阵列的数组变量:
public: BITMAPINFO *m_pBmInfo; //位图信息头 unsigned char *m_pImageData; //位图数据 void InitialDIB(unsigned char **InData8); //初始化位图信息头与数据<2>在***Doc.cpp中对变量赋值:
void C***Doc::InitialDIB(unsigned char **InData8) //InData8 包含图像数据数组 { int i,row,col,tIND,ImgIdx; if(m_nAllBandNum == 1) //如果图像只有一个波段,按灰度图显示 { m_pBmInfo = (BITMAPINFO *) new BYTE[sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*255]; //开辟BITMAPINFO结构大小 for (i=0; i<256; i++) { m_pBmInfo->bmiColors[i].rgbRed = i; m_pBmInfo->bmiColors[i].rgbGreen = i; m_pBmInfo->bmiColors[i].rgbBlue = i; } m_pBmInfo->bmiHeader.biBitCount = 8; } else //如果大于一个波段,按彩色图显示 { m_pBmInfo = (BITMAPINFO *) new BYTE[sizeof(BITMAPINFOHEADER)]; m_pBmInfo->bmiHeader.biBitCount = 24; //也可以是32,如果是32下面对彩色图赋值将不同,下面再说 } //设置图像基本信息 m_pBmInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); m_pBmInfo->bmiHeader.biWidth = m_nImageWidth; m_pBmInfo->bmiHeader.biHeight =m_nImageHeight; m_pBmInfo->bmiHeader.biCompression = BI_RGB; m_pBmInfo->bmiHeader.biPlanes = 1; m_pBmInfo->bmiHeader.biClrUsed = 0; m_pBmInfo->bmiHeader.biClrImportant = 0; m_pBmInfo->bmiHeader.biSizeImage = 4*((m_nImageWidth*m_pBmInfo->bmiHeader.biBitCount+31)/32)*m_nImageHeight; int nWideBytes = 4*((m_pBmInfo->bmiHeader.biWidth*m_pBmInfo->bmiHeader.biBitCount+31)/32); m_pImageData = new BYTE[nWideBytes * m_nImageHeight]; //开辟数据缓存 if(m_nAllBandNum == 1) //灰度图:对图像数据数组赋值 { for(row = 0;row<m_nImageHeight; row++) { for(col=0; col<m_nImageWidth; col++) { ImgIdx = m_nImageWidth * row + col; tIND = nWideBytes * (m_nImageHeight - row - 1) + col; m_pImageData[tIND] = InData8[0][ImgIdx]; } } } else //彩色图: { for(row = 0;row<m_nImageHeight; row++) { for(col=0; col<m_nImageWidth; col++) { ImgIdx =m_nImageWidth * row + col; tIND = nWideBytes * (m_nImageHeight - row - 1) + 3*col; m_pImageData[tIND] = InData8[2][ImgIdx]; //B tIND += 1; m_pImageData[tIND] = InData8[1][ImgIdx]; //G tIND += 1; m_pImageData[tIND] = InData8[0][ImgIdx]; //R } } } }如果 设置m_pBmInfo->bmiHeader.biBitCount = 32,则彩色图对数据赋值部分要改为:
else //彩色图: { for(row = 0;row<m_nImageHeight; row++) { for(col=0; col<m_nImageWidth; col++) { ImgIdx =m_nImageWidth * row + col; tIND = nWideBytes * (m_nImageHeight - row - 1) + 4*col; m_pImageData[tIND] = InData8[2][ImgIdx]; //B tIND += 1; m_pImageData[tIND] = InData8[1][ImgIdx]; //G tIND += 1; m_pImageData[tIND] = InData8[0][ImgIdx]; //R tIND +=1; m_pImageData[tIND] = 0; //保留字节 rgbReserved } } }
在***View的OnDraw()函数中调用显示函数:
::SetStretchBltMode(pDC->m_hDC,STRETCH_HALFTONE); //设置拉伸方式,避免缩小时图像失真 ::StretchDIBits(pDC->m_hDC, 0, 0, nImgWidth , nImgHeight ,0, 0, nImgWidth, nImgHeight, pDoc->m_pImageData, pDoc->m_pBmInfo, DIB_RGB_COLORS, SRCCOPY) ;可以调整显示区域大小,图像会发生相应的拉伸,具体见StretchDIBits参数。
注:
1.如果是对话框,可以OnPaint()中调用。
2.至于DIB对象的定义、赋值和显示调用位置,根据需要而定。
3.图像数据的获取,可以参看GDAL类,该类可直接获取图像的基本信息。