MFC基础知识(三)——用DIB位图显示图像

本文主要介绍: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
				
			}
		}
	}

<3>显示图像:

在***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类,该类可直接获取图像的基本信息。

你可能感兴趣的:(DIB,MFC显示图像,显示图像,位图显示图像,DIB显示图像)