找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 6777|回复: 3

【C】Windows下C语言读取BMP文件并通过GDI显示的方法

[复制链接]

1110

主题

1651

回帖

7万

积分

用户组: 管理员

一只技术宅

UID
1
精华
244
威望
743 点
宅币
24221 个
贡献
46222 次
宅之契约
0 份
在线时间
2296 小时
注册时间
2014-1-26
发表于 2014-2-27 22:20:20 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
我这里只是讲怎么读取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。)
  1. //需要用到的头文件:
  2. #include<stdio.h>
  3. #include<windows.h>

  4. //定义一个包含256个调色板的结构体,来读取带调色板的BITMAPINFO
  5. typedef struct
  6. {
  7.         BITMAPINFOHEADER        bmiHeader;
  8.         RGBQUAD                                bmiColors[256];
  9. }BITMAPINFO_;//类似于BITMAPINFO

  10. //读取BMP的函数
  11. HBITMAP ReadBMPFile(HDC hDC,char*pszBMPFile,void**ppBits)
  12. {
  13.         BITMAPFILEHEADER        BMFH;
  14.         BITMAPINFO_                        BMIF;
  15.         HBITMAP                                hBMPRet=NULL;//要返回的HBITMAP
  16.         UINT                                uPaletteColors=0;//调色板颜色数
  17.         UINT                                iUsage=DIB_RGB_COLORS;//是否为调色板颜色
  18.         FILE                                *fp=fopen(pszBMPFile,"rb");//打开文件
  19.        
  20.         if(!fp)//如果没打开文件直接返回NULL。
  21.                 return NULL;
  22.         if(fread(&BMFH,1,sizeof(BMFH),fp)!=sizeof(BMFH))//首先读取BITMAPFILEHEADER结构
  23.                 goto ErrHandler;//不能读取完整的结构体返回NULL。

  24.         if(BMFH.bfType!=0x4D42)
  25.                 goto ErrHandler;//文件标识不是"BM",返回NULL。

  26.         if(fread(&(BMIF.bmiHeader),1,sizeof(BMIF.bmiHeader),fp)!=sizeof(BMIF.bmiHeader))//然后读取BITMAPINFOHEADER结构
  27.                 goto ErrHandler;//不能读取完整的结构体返回NULL。

  28.         if(BMIF.bmiHeader.biSize!=sizeof(BMIF.bmiHeader))//结构体大小必须为40个字节
  29.                 goto ErrHandler;//否则文件不合法

  30.         if(BMIF.bmiHeader.biBitCount<=8)//八位以下的位图有调色板数据
  31.         {
  32.                 iUsage=DIB_PAL_COLORS;//使用调色板颜色
  33.                 uPaletteColors=1<<BMIF.bmiHeader.biBitCount;//调色板颜色数
  34.                 if(fread(BMIF.bmiColors,sizeof(RGBQUAD),uPaletteColors,fp)!=uPaletteColors)//读取调色板
  35.                         goto ErrHandler;//不能读取完整的调色板则返回NULL。
  36.         }

  37.         if(!(hBMPRet=CreateDIBSection(hDC,(BITMAPINFO*)&BMIF,iUsage,ppBits,NULL,0)))
  38.                 goto ErrHandler;//如果无法创建DIB Section则返回NULL
  39.        
  40.         if(!*ppBits)
  41.                 goto ErrHandler;//如果没有得到像素数据的指针返回NULL
  42.        
  43.         if(!BMIF.bmiHeader.biSizeImage)//如果没有读取出位图数据的大小则自动计算位图数据的大小
  44.         {
  45.                 UINT uPitch=((BMIF.bmiHeader.biWidth-1)*BMIF.bmiHeader.biBitCount/32+1)*4;//每行字节数
  46.                 BMIF.bmiHeader.biSizeImage=uPitch*BMIF.bmiHeader.biHeight;//计算出实际尺寸
  47.         }
  48.        
  49.         if(fread(*ppBits,1,BMIF.bmiHeader.biSizeImage,fp)!=BMIF.bmiHeader.biSizeImage)//读取位图的颜色数据
  50.                 goto ErrHandler;//如果读取不了完整的数据则返回NULL
  51.        
  52.         fclose(fp);
  53.         return hBMPRet;
  54. ErrHandler://出错处理
  55.         if(hBMPRet)
  56.                 DeleteObject(hBMPRet);
  57.         if(fp)
  58.                 fclose(fp);
  59.         return NULL;
  60. }
复制代码
方法2的C语言实现:
  1. //需要用到的头文件:
  2. #include<stdio.h>
  3. #include<malloc.h>
  4. #include<windows.h>

  5. //定义一个包含256个调色板的结构体,来读取带调色板的BITMAPINFO
  6. typedef struct
  7. {
  8.         BITMAPINFOHEADER        bmiHeader;
  9.         RGBQUAD                                bmiColors[256];
  10. }BITMAPINFO_;//类似于BITMAPINFO


  11. BITMAPINFO_        g_BMIF;
  12. void                *g_pBits;


  13. //读取BMP的函数
  14. BOOL ReadBMPFile(HDC hDC,char*pszBMPFile)
  15. {
  16.         BITMAPFILEHEADER        BMFH;
  17.         UINT                                uPaletteColors=0;//调色板颜色数
  18.         UINT                                iUsage=DIB_RGB_COLORS;//是否为调色板颜色
  19.         FILE                                *fp=fopen(pszBMPFile,"rb");//打开文件
  20.        
  21.         if(!fp)//如果没打开文件直接返回FALSE。
  22.                 return FALSE;
  23.         if(fread(&BMFH,1,sizeof(BMFH),fp)!=sizeof(BMFH))//首先读取BITMAPFILEHEADER结构
  24.                 goto ErrHandler;//不能读取完整的结构体返回FALSE。

  25.         if(BMFH.bfType!=0x4D42)
  26.                 goto ErrHandler;//文件标识不是"BM",返回FALSE。

  27.         if(fread(&(g_BMIF.bmiHeader),1,sizeof(g_BMIF.bmiHeader),fp)!=sizeof(g_BMIF.bmiHeader))//然后读取BITMAPINFOHEADER结构
  28.                 goto ErrHandler;//不能读取完整的结构体返回FALSE。

  29.         if(g_BMIF.bmiHeader.biSize!=sizeof(g_BMIF.bmiHeader))//结构体大小必须为40个字节
  30.                 goto ErrHandler;//否则文件不合法

  31.         if(g_BMIF.bmiHeader.biBitCount<=8)//八位以下的位图有调色板数据
  32.         {
  33.                 iUsage=DIB_PAL_COLORS;//使用调色板颜色
  34.                 uPaletteColors=1<<g_BMIF.bmiHeader.biBitCount;//调色板颜色数
  35.                 if(fread(g_BMIF.bmiColors,sizeof(RGBQUAD),uPaletteColors,fp)!=uPaletteColors)//读取调色板
  36.                         goto ErrHandler;//不能读取完整的调色板则返回FALSE。
  37.         }

  38.         if(!g_BMIF.bmiHeader.biSizeImage)//如果没有读取出位图数据的大小则自动计算位图数据的大小
  39.         {
  40.                 UINT uPitch=((g_BMIF.bmiHeader.biWidth-1)*g_BMIF.bmiHeader.biBitCount/32+1)*4;//每行字节数
  41.                 g_BMIF.bmiHeader.biSizeImage=uPitch*g_BMIF.bmiHeader.biHeight;//计算出实际尺寸
  42.         }

  43.         g_pBits=malloc(g_BMIF.bmiHeader.biSizeImage);//分配内存来读取BMP位图数据
  44.         if(!g_pBits)
  45.                 goto ErrHandler;//内存不足,返回
  46.        
  47.         if(fread(g_pBits,1,g_BMIF.bmiHeader.biSizeImage,fp)!=g_BMIF.bmiHeader.biSizeImage)//读取位图的颜色数据
  48.                 goto ErrHandler;//如果读取不了完整的数据则返回FALSE
  49.        
  50.         fclose(fp);
  51.         return TRUE;
  52. ErrHandler://出错处理
  53.         if(g_pBits)
  54.         {
  55.                 free(g_pBits);
  56.                 g_pBits=NULL;
  57.         }
  58.         if(fp)
  59.                 fclose(fp);
  60.         return FALSE;
  61. }

  62. /******************************************************************************
  63. 读取了BMP文件之后,调用StretchDIBits就能画图。
  64. StretchDIBits函数原型:
  65. int StretchDIBits(
  66.   HDC hdc,                      // handle to DC
  67.   int XDest,                    // x-coord of destination upper-left corner
  68.   int YDest,                    // y-coord of destination upper-left corner
  69.   int nDestWidth,               // width of destination rectangle
  70.   int nDestHeight,              // height of destination rectangle
  71.   int XSrc,                     // x-coord of source upper-left corner
  72.   int YSrc,                     // y-coord of source upper-left corner
  73.   int nSrcWidth,                // width of source rectangle
  74.   int nSrcHeight,               // height of source rectangle
  75.   CONST VOID *lpBits,           // bitmap bits
  76.   CONST BITMAPINFO *lpBitsInfo, // bitmap data
  77.   UINT iUsage,                  // usage options
  78.   DWORD dwRop                   // raster operation code
  79. );

  80. 函数运行成功的话,返回实际绘制的扫描线数目。
  81. 否则返回GDI_ERROR。

  82. 调用范例:

  83. StretchDIBits(hDC,X坐标,Y坐标,宽度,高度,
  84.         原图裁剪开始X,原图裁剪开始Y,原图裁剪宽度,原图裁剪高度,
  85.         g_pBits,(BITMAPINFO*)&g_BMIF,
  86.         (g_BMIF.bmiHeader.biBitCount<=8)?DIB_PAL_COLORS:DIB_RGB_COLORS,
  87.         SRCCOPY);

  88. 其中的“hDC”是你要绘制的目标的设备句柄,
  89. “X坐标,Y坐标,宽度,高度”是你要绘制到的位置,
  90. “原图裁剪开始X,原图裁剪开始Y,原图裁剪宽度,原图裁剪高度”是你要裁剪的尺寸,
  91. “g_pBits”是刚才读取到的位图数据,
  92. “SRCCOPY”是绘图方式:“直接复制”(详见MSDN的BitBlt文档)
  93. ******************************************************************************/
复制代码

本帖被以下淘专辑推荐:

回复

使用道具 举报

3

主题

13

回帖

112

积分

用户组: 小·技术宅

UID
75
精华
1
威望
1 点
宅币
88 个
贡献
1 次
宅之契约
0 份
在线时间
12 小时
注册时间
2014-2-21
发表于 2014-3-24 23:10:53 | 显示全部楼层
fclose(fp);
    return hBMPRet;

ErrHandler://出错处理
    if(hBMPRet)
        DeleteObject(hBMPRet);
    if(fp)
        fclose(fp);
    return NULL;

表示这里看不懂,已经return了,后面的代码还有用?是不是应该先处理错误最后才返回?
回复 赞! 靠!

使用道具 举报

1110

主题

1651

回帖

7万

积分

用户组: 管理员

一只技术宅

UID
1
精华
244
威望
743 点
宅币
24221 个
贡献
46222 次
宅之契约
0 份
在线时间
2296 小时
注册时间
2014-1-26
 楼主| 发表于 2014-3-25 00:46:36 | 显示全部楼层
葡萄成熟时 发表于 2014-3-24 15:10
fclose(fp);
    return hBMPRet;

你没看到ErrHandler:这个标号吗?你不知道goto跳转语句?
回复 赞! 靠!

使用道具 举报

3

主题

13

回帖

112

积分

用户组: 小·技术宅

UID
75
精华
1
威望
1 点
宅币
88 个
贡献
1 次
宅之契约
0 份
在线时间
12 小时
注册时间
2014-2-21
发表于 2014-3-25 09:28:57 | 显示全部楼层
0xAA55 发表于 2014-3-25 00:46
你没看到ErrHandler:这个标号吗?你不知道goto跳转语句?

嗯嗯,明白了
回复 赞! 靠!

使用道具 举报

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-4-19 07:13 , Processed in 0.039656 second(s), 33 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表