3.3.2 创建图形画刷来实现位图加载
1、首先在Resource View中导入一幅位图,位图大小96×96像素;
2、其次在主对话框中添加一个静态文本资源,ID号是IDC_BITMAPAREA,添加一个按钮资源,ID号是IDB_LOADBITMAP;
3、然后编辑按钮(IDB_LOADBITMAP)的消息关联函数,如图3.6:
此处需要注意一点是:如图3.5在默认情况下,设定的位图大小为96×96像素,而在编辑位图显示区域时,也就是图3.5中标记出来的静态文本区域,其属性大小显示是64×64,这里的单位本人认为是逻辑单位,量纲为1。在图3.6调试模式下证实了逻辑坐标下绘制的显示区域与实际大小成一定比例关系(本人测试的是2/3关系),也就是示例代码中mywnd->GetClientRect(&rc)获得的区域大小是像素坐标。
之所以提到这点注意细节是由于本节是通过创建图形画刷来实现位图显示,如果显示区域是任意大小,会发现显示出来的效果是如下图3.7所示:
3.3.3创建DIB(device-independent bitmap)图形画刷
通过CBrush::CreateDIBPatternBrush函数来创建DIB图形画刷。首先典型的DIB图像的数据结构由以下4部分组成:
1、位图文件头数据结构(BITMAPFILEHEADER),其数据结构定义如下:
typedef struct tagBITMAPFILEHEADER { // bmfh
WORD bfType; //文件类型,必须是”BM”(0x4D42)
DWORD bfSize; //位图文件大小,单位:字节
WORD bfReserved1; //保留,必须为0
WORD bfReserved2; //保留,必须为0
DWORD bfOffBits; //实际图像数据距文件头起始的偏离量
} BITMAPFILEHEADER;
2、位图信息头数据结构(BITMAPINFOHEADER),其数据结构定义如下:
typedef struct tagBITMAPINFOHEADER{ // bmih
DWORD biSize; //本结构体占用的字节数
LONG biWidth; //位图图像宽度,单位:像素
LONG biHeight; //位图图像高度,单位:像素
WORD biPlanes; //设备上颜色平面数目,必须为1
WORD biBitCount //存储每个像素占用的二进制位数,可取1、4、8、16、24、32
DWORD biCompression; //是否压缩存储图像数据
DWORD biSizeImage; //指定图像的大小,单位:字节
LONG biXPelsPerMeter; //图像的水平分辨率,单位:像素每米
LONG biYPelsPerMeter; //图像的垂直分辨率,单位:像素每米
DWORD biClrUsed; //图像中实际使用了颜色索引表中的多少中颜色
DWORD biClrImportant; //图像中重要的颜色数,如果该值为0,则认为所有颜色均重要
} BITMAPINFOHEADER;
具体解释:biSizeImage:biSizeImage=biWidth' * biHeight * 每个像素占用字节数,对于位图数据,每一行的字节数必须是4的倍数,即“biWidth' * 每个像素占用字节数”是4的倍数。因此对于实际图像数据宽度biWidth不是4的倍数时,需要对位图数据进行补齐。
biCompression: 对于bottom-up bitmap位图(位图读取方式是从下向上)压缩类型有(对于从上向下读取方式的位图不能被压缩):
BI_RGB:无压缩模式
BI_RLE8 、BI_RLE4:具体查看MSDN的Bitmap Compression。
BI_BITFIELDS:对于16bits或32bits位图,biCompression = BI_BITFIELDS,其中调色板包含3个DWORD类型的颜色掩码用以指定像素颜色中的R、G、B成分。
biClrUsed:指定位图图像中实际使用的颜色数目。如果该值为0,则图像中实际使用的颜色数目是和biBitCount成员中规定的值相同的最大数目。如果位图是一个packed bitmap(在BITMAPINFO数据结构体之后紧接着位图数据组,该位图数据组由一个指针引用。)biClrUsed必须是0或者调色板的实际颜色数目。
3、调色板数据结构,其结构体定义如下:
typedef struct tagRGBQUAD { // rgbq
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved; //必须为0
} RGBQUAD;
对于真彩图(24bits位图,即biBitCount = 24)没有调色板。
4、第四部分就是位图实际数据。
接下来解释CBrush::CreateDIBPatternBrush:
BOOL CreateDIBPatternBrush( const void* lpPackedDIB, UINT nUsage );
lpPackedDIB:指向一个packed DIB
nUsage:在BITMAPINFO结构体中成员bmiColors[]表示的是明确的RGB值还是逻辑调色板的索引值。如果nUsage = DIB_RGB_COLORS表示颜色表包含了明确的RGB值,如果nUsage = DIB_PAL_COLORS表示颜色表组成了一个16bit的索引数组。
具体实现的示例代码如下:
void CFDlg::OnLoadbitmap() { // TODO: Add your control notification handler code here unsigned char* ImageBuffer=new unsigned char[ sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256 ]; unsigned char* ImageInfo = NULL; BITMAPFILEHEADER m_bitmapfileheader; BITMAPINFOHEADER* infoheader; RGBQUAD* palette; BITMAPINFO* bmi;; FILE* ImageFile; unsigned int BytesSize=0; if (NULL==(ImageFile=fopen("D:\\10.bmp","rb")))//从D盘中读取一幅位图 { printf("can not open the image file!"); return ; } else { fread(&m_bitmapfileheader,sizeof(BITMAPFILEHEADER),1,ImageFile);//读取文件头 //读取信息头与调色板数据块 fread(ImageBuffer , m_bitmapfileheader.bfOffBits - sizeof(BITMAPFILEHEADER) , 1 , ImageFile); bmi = (BITMAPINFO*)ImageBuffer; infoheader = &(bmi->bmiHeader); palette = bmi->bmiColors; //位图信息头和实际数据的总字节数 BytesSize = m_bitmapfileheader.bfOffBits - sizeof(BITMAPFILEHEADER) + (infoheader->biWidth * infoheader->biHeight * (infoheader->biBitCount/8));
ImageInfo = new unsigned char[BytesSize]; fseek(ImageFile , sizeof(BITMAPFILEHEADER) , SEEK_SET);//跳过文件头 fread(ImageInfo , BytesSize , 1 , ImageFile); CWnd* mywnd = GetDlgItem(IDC_BITMAPAREA); CClientDC dc(mywnd); RECT rc; CBrush mybrush; CBrush* oldbrush; mywnd->GetClientRect(&rc); mybrush.CreateDIBPatternBrush(ImageInfo , DIB_RGB_COLORS); oldbrush = dc.SelectObject(&mybrush); dc.FillRect(&rc,&mybrush); dc.SelectObject(oldbrush); mybrush.DeleteObject(); } delete[] ImageBuffer; delete[] ImageInfo;}