读取BMP图片
之前说到LoadBitmap
这个函数用来加载一幅位图,现在正好复习到内存映射,就用内存映射来写这么一个相关的操作(这个我是照着书上写的,但是不知道那本书有点老还是QQ截图有问题,有地方我纠结了好一阵,后面说)。
首先我们想要原生态的打开BMP,也就是通过文件映射的方法打开BMP文件,那么我们至少要了解BMP文件的一个文件格式,这个文件格式和PE文件的很像,但是没有那个复杂,无非就是一些图片大小之类的一些信息。
我们要了解的结构体:
BITMAPFILEHEADER
BMP图片头文件1
2
3
4
5
6
7typedef struct tagBITMAPFILEHEADER { // bmfh
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;结构体比较简单,比较重要的就是第一个和最后一个,第一个就是表示这个文件的一个类型,其实就和PE文件基本上一模一样,PE是MZ,BMP是BM,就是一个模子刻出来的。最后一个就是说真实的图片数据的RVA。
BITMAPINFO
BMP 图片信息1
2
3
4typedef struct tagBITMAPINFO { // bmi
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO;这个的话呢里面比较重要的就是
bmiHeader
这个,里面还有一个结构体,这个结构体就是我们的信息了:1
2
3
4
5
6
7
8
9
10
11
12
13typedef 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;这些信息就比较通俗易懂了,看名字就能看懂了。
程序编写
首先在我们的程序窗口OnCreate
的时候我们我们先去创建一个兼容的DC。
1 | int CStudy_BMPDlg::OnCreate(LPCREATESTRUCT lpCreateStruct) |
然后我们将图片的宽高都设置为0。
对了,还是先看看我是怎么声明一些的变量的得吧:
1 | private: |
这个的话呢,一个是存放我们客户去大小的兼容DC,一个是BMP图片的宽和高。
创建写好之后,我们在销毁的时候不要忘记删除掉我们的DC对象,但是不是普通的delete
而是用DeleteDC
这个函数进行删除。
1 | void CStudy_BMPDlg::OnDestroy() |
这个点击完毕之后,我们可以加一个按钮或者说加一个菜单,这里我为了简单,我就加了一个按钮,但效果的话呢没有加菜单好一些。
按钮点击:
1 | void CStudy_BMPDlg::OnBnClickedButton1() |
这里我们就需要说一下,首先我们先用通用对话框打开一个文件,但是不知道是我电脑还是什么情况,书上写的是GetFileName
,但是这个的话呢只是获取文件的名字啊,不在一个目录下的话呢相对路径是找不到的,所以我就用了GetPathName
这个函数,然后我们就用CreateFile
打开一个文件,常规的打开属性就可以了,打开之后我们就开始我们的正题了,就是进行文件的内存映射操作,首先可定时创建一个文件内存映射,由于我们不需要进行内存的共享,所以最后一个参数可以设置为NULL,然后将映射的这块进行分配内存,分配完毕之后我们将这块内存给我们之前说的第一个BMP结构体,这个时候我们就需要用bfType判断一下是不是开头BM了,这个我在注释上详细说了,大家看注释就好了,然后如果不是BMP文件我们就关闭打开的东西,确定是BMP文件之后,我们给一个BYTE
的指针,主要是用来保存我们的真是图片数据的,这个图片数据是基地址+相对偏移进行计算出来的,然后我们计算我们的文件信息的地址,这个地址比较有意思,就是说我们第一个结构体紧随其后的就是他,我们只需要用基地址加上第一个BMP结构体的大小就可以计算出来他的一个内存地址了,计算出来之后,在我们获取宽和高的时候问题就来了,在用QQ进行截图生成的BMP文件的时候我在测试过程中,一直出错,不知道原因,慢慢多测试了几次,往上回溯了几次,发现为什么我的高是个负数?我现在也没明白,所以不得不我加上了一个取绝对值的函数,这样就就算可以了,之后我们在创建一个兼容的位图,用来存储我们的图片数据,和我们的客户区DC相兼容,然后将获取到的BMP句柄选入DC,之后我们需要将数据进行拷贝,用到了SetDIBitsToDevice
这个函数,就是将数据传输给我们的设备对象:
1 | int SetDIBitsToDevice( |
很好理解,倒数第二个参数就是我们的BMP结构信息,最后一个参数:
Value | Meaning |
---|---|
DIB_PAL_COLORS | The color table consists of an array of 16-bit indexes into the currently selected logical palette. |
DIB_RGB_COLORS | The color table contains literal RGB values |
我们就选择RGB,之后我们就需要关闭各种东西啦,然后使客户区无效。
然后在OnPaint中编写:
1 | void CStudy_BMPDlg::OnPaint() |
不要忘记一点就好了要写在外面,判断不一定进去的,这样我们就写好了我们的BMP浏览。BitBlt
就是一个数据拷贝的函数:
1 | BOOL BitBlt( |