【C】Windows下C语言读取BMP文件并通过GDI显示的方法
我这里只是讲怎么读取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;
}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;
}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文档)
******************************************************************************/ fclose(fp);
return hBMPRet;
ErrHandler://出错处理
if(hBMPRet)
DeleteObject(hBMPRet);
if(fp)
fclose(fp);
return NULL;
表示这里看不懂,已经return了,后面的代码还有用?是不是应该先处理错误最后才返回?
葡萄成熟时 发表于 2014-3-24 15:10
fclose(fp);
return hBMPRet;
你没看到ErrHandler:这个标号吗?你不知道goto跳转语句? 0xAA55 发表于 2014-3-25 00:46
你没看到ErrHandler:这个标号吗?你不知道goto跳转语句?
嗯嗯,明白了:lol
页:
[1]