- UID
- 1
- 精华
- 积分
- 76361
- 威望
- 点
- 宅币
- 个
- 贡献
- 次
- 宅之契约
- 份
- 最后登录
- 1970-1-1
- 在线时间
- 小时
|
我这里只是讲怎么读取BMP文件,这个帖子并不讲BMP文件的详细格式。
BMP文件的详细格式请到这里收看:
http://www.0xaa55.com/thread-271-1-1.html
首先BMP的文件头很简单,一个BITMAPFILEHEADER和一个BITMAPINFOHEADER,和可能出现的调色板,和颜色数据。(如果是图种的话,颜色数据后面会有个压缩包。这个这个……偏题了)
读取BMP的大致流程是先读取BITMAPFILEHEADER和BITMAPINFOHEADER,然后再调用CreateDIBSection创建一个HBITMAP,就可以载入完整的位图了。
或者用更简单的方法,直接StretchDIBits把位图画到HDC上。
我这里就讲讲两个常用的方法:
1、用CreateDIBSection创建一个HBITMAP,然后把HBITMAP选入HDC,再用BitBlt来画。
好处是你想画的时候随时可以画。画的时候效率较高而且速度快。
坏处是麻烦。如果你的程序足够大,用了很多HBITMAP、HDC,你会觉得一团糟。
2、直接用StretchDIBits来画。
好处是很直接,不需要创建HBITMAP、HDC,而且支持图像的缩放,还支持PNG、JPG的显示(这个以后我会教大家怎么显示JPG和PNG的)
坏处是你需要专门分配、管理一个用来存储位图信息的内存块。而且这个方法慢。
方法1的C语言实现:(这里只讲了读取的流程,我并没有测试代码,请不要照抄代码免得出BUG。)- //需要用到的头文件:
- #include<stdio.h>
- #include<windows.h>
- //定义一个包含256个调色板的结构体,来读取带调色板的BITMAPINFO
- typedef struct
- {
- BITMAPINFOHEADER bmiHeader;
- RGBQUAD bmiColors[256];
- }BITMAPINFO_;//类似于BITMAPINFO
- //读取BMP的函数
- HBITMAP ReadBMPFile(HDC hDC,char*pszBMPFile,void**ppBits)
- {
- BITMAPFILEHEADER BMFH;
- BITMAPINFO_ BMIF;
- HBITMAP hBMPRet=NULL;//要返回的HBITMAP
- UINT uPaletteColors=0;//调色板颜色数
- UINT iUsage=DIB_RGB_COLORS;//是否为调色板颜色
- FILE *fp=fopen(pszBMPFile,"rb");//打开文件
-
- if(!fp)//如果没打开文件直接返回NULL。
- return NULL;
- if(fread(&BMFH,1,sizeof(BMFH),fp)!=sizeof(BMFH))//首先读取BITMAPFILEHEADER结构
- goto ErrHandler;//不能读取完整的结构体返回NULL。
- if(BMFH.bfType!=0x4D42)
- goto ErrHandler;//文件标识不是"BM",返回NULL。
- if(fread(&(BMIF.bmiHeader),1,sizeof(BMIF.bmiHeader),fp)!=sizeof(BMIF.bmiHeader))//然后读取BITMAPINFOHEADER结构
- goto ErrHandler;//不能读取完整的结构体返回NULL。
- if(BMIF.bmiHeader.biSize!=sizeof(BMIF.bmiHeader))//结构体大小必须为40个字节
- goto ErrHandler;//否则文件不合法
- if(BMIF.bmiHeader.biBitCount<=8)//八位以下的位图有调色板数据
- {
- iUsage=DIB_PAL_COLORS;//使用调色板颜色
- uPaletteColors=1<<BMIF.bmiHeader.biBitCount;//调色板颜色数
- if(fread(BMIF.bmiColors,sizeof(RGBQUAD),uPaletteColors,fp)!=uPaletteColors)//读取调色板
- goto ErrHandler;//不能读取完整的调色板则返回NULL。
- }
- if(!(hBMPRet=CreateDIBSection(hDC,(BITMAPINFO*)&BMIF,iUsage,ppBits,NULL,0)))
- goto ErrHandler;//如果无法创建DIB Section则返回NULL
-
- if(!*ppBits)
- goto ErrHandler;//如果没有得到像素数据的指针返回NULL
-
- if(!BMIF.bmiHeader.biSizeImage)//如果没有读取出位图数据的大小则自动计算位图数据的大小
- {
- UINT uPitch=((BMIF.bmiHeader.biWidth-1)*BMIF.bmiHeader.biBitCount/32+1)*4;//每行字节数
- BMIF.bmiHeader.biSizeImage=uPitch*BMIF.bmiHeader.biHeight;//计算出实际尺寸
- }
-
- if(fread(*ppBits,1,BMIF.bmiHeader.biSizeImage,fp)!=BMIF.bmiHeader.biSizeImage)//读取位图的颜色数据
- goto ErrHandler;//如果读取不了完整的数据则返回NULL
-
- fclose(fp);
- return hBMPRet;
- ErrHandler://出错处理
- if(hBMPRet)
- DeleteObject(hBMPRet);
- if(fp)
- fclose(fp);
- return NULL;
- }
复制代码 方法2的C语言实现:- //需要用到的头文件:
- #include<stdio.h>
- #include<malloc.h>
- #include<windows.h>
- //定义一个包含256个调色板的结构体,来读取带调色板的BITMAPINFO
- typedef struct
- {
- BITMAPINFOHEADER bmiHeader;
- RGBQUAD bmiColors[256];
- }BITMAPINFO_;//类似于BITMAPINFO
- BITMAPINFO_ g_BMIF;
- void *g_pBits;
- //读取BMP的函数
- BOOL ReadBMPFile(HDC hDC,char*pszBMPFile)
- {
- BITMAPFILEHEADER BMFH;
- UINT uPaletteColors=0;//调色板颜色数
- UINT iUsage=DIB_RGB_COLORS;//是否为调色板颜色
- FILE *fp=fopen(pszBMPFile,"rb");//打开文件
-
- if(!fp)//如果没打开文件直接返回FALSE。
- return FALSE;
- if(fread(&BMFH,1,sizeof(BMFH),fp)!=sizeof(BMFH))//首先读取BITMAPFILEHEADER结构
- goto ErrHandler;//不能读取完整的结构体返回FALSE。
- if(BMFH.bfType!=0x4D42)
- goto ErrHandler;//文件标识不是"BM",返回FALSE。
- if(fread(&(g_BMIF.bmiHeader),1,sizeof(g_BMIF.bmiHeader),fp)!=sizeof(g_BMIF.bmiHeader))//然后读取BITMAPINFOHEADER结构
- goto ErrHandler;//不能读取完整的结构体返回FALSE。
- if(g_BMIF.bmiHeader.biSize!=sizeof(g_BMIF.bmiHeader))//结构体大小必须为40个字节
- goto ErrHandler;//否则文件不合法
- if(g_BMIF.bmiHeader.biBitCount<=8)//八位以下的位图有调色板数据
- {
- iUsage=DIB_PAL_COLORS;//使用调色板颜色
- uPaletteColors=1<<g_BMIF.bmiHeader.biBitCount;//调色板颜色数
- if(fread(g_BMIF.bmiColors,sizeof(RGBQUAD),uPaletteColors,fp)!=uPaletteColors)//读取调色板
- goto ErrHandler;//不能读取完整的调色板则返回FALSE。
- }
- if(!g_BMIF.bmiHeader.biSizeImage)//如果没有读取出位图数据的大小则自动计算位图数据的大小
- {
- UINT uPitch=((g_BMIF.bmiHeader.biWidth-1)*g_BMIF.bmiHeader.biBitCount/32+1)*4;//每行字节数
- g_BMIF.bmiHeader.biSizeImage=uPitch*g_BMIF.bmiHeader.biHeight;//计算出实际尺寸
- }
- g_pBits=malloc(g_BMIF.bmiHeader.biSizeImage);//分配内存来读取BMP位图数据
- if(!g_pBits)
- goto ErrHandler;//内存不足,返回
-
- if(fread(g_pBits,1,g_BMIF.bmiHeader.biSizeImage,fp)!=g_BMIF.bmiHeader.biSizeImage)//读取位图的颜色数据
- goto ErrHandler;//如果读取不了完整的数据则返回FALSE
-
- fclose(fp);
- return TRUE;
- ErrHandler://出错处理
- if(g_pBits)
- {
- free(g_pBits);
- g_pBits=NULL;
- }
- if(fp)
- fclose(fp);
- return FALSE;
- }
- /******************************************************************************
- 读取了BMP文件之后,调用StretchDIBits就能画图。
- StretchDIBits函数原型:
- int StretchDIBits(
- HDC hdc, // handle to DC
- int XDest, // x-coord of destination upper-left corner
- int YDest, // y-coord of destination upper-left corner
- int nDestWidth, // width of destination rectangle
- int nDestHeight, // height of destination rectangle
- int XSrc, // x-coord of source upper-left corner
- int YSrc, // y-coord of source upper-left corner
- int nSrcWidth, // width of source rectangle
- int nSrcHeight, // height of source rectangle
- CONST VOID *lpBits, // bitmap bits
- CONST BITMAPINFO *lpBitsInfo, // bitmap data
- UINT iUsage, // usage options
- DWORD dwRop // raster operation code
- );
- 函数运行成功的话,返回实际绘制的扫描线数目。
- 否则返回GDI_ERROR。
- 调用范例:
- StretchDIBits(hDC,X坐标,Y坐标,宽度,高度,
- 原图裁剪开始X,原图裁剪开始Y,原图裁剪宽度,原图裁剪高度,
- g_pBits,(BITMAPINFO*)&g_BMIF,
- (g_BMIF.bmiHeader.biBitCount<=8)?DIB_PAL_COLORS:DIB_RGB_COLORS,
- SRCCOPY);
- 其中的“hDC”是你要绘制的目标的设备句柄,
- “X坐标,Y坐标,宽度,高度”是你要绘制到的位置,
- “原图裁剪开始X,原图裁剪开始Y,原图裁剪宽度,原图裁剪高度”是你要裁剪的尺寸,
- “g_pBits”是刚才读取到的位图数据,
- “SRCCOPY”是绘图方式:“直接复制”(详见MSDN的BitBlt文档)
- ******************************************************************************/
复制代码 |
|