我很早就学习了BMP位图。印象中,那时应该是在研究AVI视频文件格式时顺便研究的,或者是研究YUV转RGB时顺便研究的。但未写文章出来,我一直以为我的学习只有在发表了文章才算是完结,否则不能算是我做过了这个事。在这里补上当初读、写BMP的函数代码。

头文件如下:

/*** @file   bmp_utils.h* @author Late Lee * @date   2012-7-2 13:21:53* @brief  *          BMP相关工具函数,目前只针对24位图片测试**        1、在VS003及GCC下编译测试通过;*        2、解决了BMP图片倒立、偏色、倾斜等问题。*        3、BMP图像每行数据需要4字节对齐,即一行数据不足4的倍数,以0补。*           解决此问题方法:设置2个变量:*           width_byte:实际的RGB每一行的字节数*           stride_byte:4字节对齐的每一行的字节数(已对齐时两者相等)*           保存时,另外开辟一个考虑了4字节对齐的缓冲区,每拷贝一行数据(width_byte),*           跳过stride_byte个字节,即跳到4字节对齐的下一行。*           读取时,只读width_byte,并且跳过每行最后补的0。*        4、图像倒立:读取与保存BMP时,将数据倒过来:*           读取时,将读到的数据由下往上存放到缓冲区*           保存时,将数据由下往上拷贝到缓冲区*        5、偏色:BMP排序为BGR,将RGB数据的G、B调换位置即可。*        6、倾斜:读取BMP时,未跳过补充的0。**       笔记:BMP图片结构,基中第1、第2部分占54字节,真彩色图没有第三部分_______________________________|        BITMAPFILEHEADER       ||_______________________________||        BITMAPINFOHEADER       ||_______________________________||          n * RGBQUAD          ||_______________________________||          image  data          ||_______________________________|*对于2色位图,用1位表示该象素的颜色(一般0表示黑,1表示白),一个字节可以表示8个象素。调色板:2*4=8对于16色位图,用4位表示一个象素的颜色,以一个字节可以表示2个象素。调色板:16*4=64对于256色位图,一个字节表示1个象素。调色板:256*4=1024对于真彩色图,三个字节表示1个象素。无调色板*      单色BMP图:调色板占8字节,故头部占用54+8=62字节,后面为像素字节,注意每行字节需要4字节对齐,举例:16*16像素单色位图,一行占16/8 = 2字节,需要补2字节。实际像素字节:16*16/2 = 32字节,补齐字节:2*16 = 32,共64字节头部共62字节,故该图片总大小为64+62=126字节*/#ifndef _BMP_UTILS_H
#define _BMP_UTILS_H#ifdef __cplusplus
extern "C" {
#endif#ifdef WIN32
#include <Windows.h>
#else
typedef unsigned char   BYTE;
typedef unsigned short  WORD;
typedef unsigned long   DWORD;
typedef long            LONG;#pragma pack(push)
// 2字节对齐,共14
#pragma pack(2)
typedef struct tagBITMAPFILEHEADER {WORD    bfType;             // 文件类型, 0x4d42DWORD   bfSize;             // 文件总大小WORD    bfReserved1;WORD    bfReserved2;DWORD   bfOffBits;          // 实际位图数据偏移
} BITMAPFILEHEADER; //__attribute__ ((packed));// 40
typedef struct tagBITMAPINFOHEADER{DWORD      biSize;          // 本结构体长度LONG       biWidth;         // 宽(单位像素)LONG       biHeight;        // 高(单位像素)WORD       biPlanes;        // 为1WORD       biBitCount;      // 像素占用位数 1(2^1=2黑白二色), 4(2^4=16色),8(2^8=256色),24(真彩色),32DWORD      biCompression;   // 压缩类型,不压缩:BI_RGB(0)DWORD      biSizeImage;     // 位图数据大小,如果是不压缩类型,可以为0LONG       biXPelsPerMeter; // 水平分辨率,单位是每米的象素个数LONG       biYPelsPerMeter; // 垂直分辨率DWORD      biClrUsed;       // 位图实际使用的颜色表中的颜色数DWORD      biClrImportant;  // 位图显示过程中重要的颜色数
} BITMAPINFOHEADER; //__attribute__ ((aligned(2)));typedef struct tagRGBQUAD {BYTE    rgbBlue;BYTE    rgbGreen;BYTE    rgbRed;BYTE    rgbReserved;
} RGBQUAD;typedef struct tagBITMAPINFO{BITMAPINFOHEADER    bmiHeader;RGBQUAD             bmiColors[1];
} BITMAPINFO;   // __attribute__ ((aligned(2)));#pragma pack(pop)#endif#undef  ALIGN
#define ALIGN(x, n) (((x)+(n)-1)&~((n)-1))/*** RGB互换R、B顺序* * @param[IN]  rgb_buffer RGB缓冲区* @param[IN]  len        缓冲区大小* * @return none** @note*        缓冲区数据可以是RGB,也可以是BGR,该函数只是将B、G进行互换*/
void swap_rgb(unsigned char* rgb_buffer, int len);/*** 分析BMP文件头部* * @param[IN]  bmp_file  BMP图片文件名称* * @return *         0:  成功*         -1: 文件不存在或不是BMP文件*/
int analyse_bmp_file(const char* bmp_file);/*** 读取BMP图片文件* * @param[IN]   bmp_file    BMP图片文件名称* * @param[OUT]  rgb_buffer RGB数据(实际为BGR)* @param[OUT]  size       RGB数据大小* @param[OUT]  width      图片宽* @param[OUT]  height     图片高** @return *         0:成功*         -1:读取文件失败,或不是BMP文件,或申请内存失败* @note*         rgb_buffer为二级指针,内存由该函数分配,需要自行释放*         rgb_buffer数据排列顺序为BGR,因此,处理时可能需要转换成RGB顺序*/
int read_bmp_file(const char* bmp_file, unsigned char** rgb_buffer,int* size, int* width, int* height);int read_bmp_file_1(const char* bmp_file, unsigned char** rgb_buffer, int* rgb_size,unsigned char** palette_buf, int* palette_len,int* width, int* height);
/*** 保存BMP文件* * @param[IN]  bmp_file   BMP图片文件名称* * @param[IN]  rgb_buffer RGB数据(实际为BGR)* @param[IN]  width      图片宽* @param[IN]  height     图片高** @return *         0:成功*         -1:打开文件失败* @note*         BMP图片颜色分量实际为BGR,因此,需要事先将rgb_buffer数据排列顺序转换成BGR。*/
int write_bmp_file(const char* bmp_file, unsigned char* rgb_buffer, int width, int height);int write_bmp_file_1(const char* bmp_file, unsigned char* rgb_buffer,unsigned char* palette_buf, int* palette_len,int width, int height);
#ifdef __cplusplus
};
#endif#endif /* _BMP_UTILS_H */

实现代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <limits.h>
#include "bmp_utils.h"
//#include "debug.h"// 注:只针对24位图片
int analyse_bmp_file(const char* bmp_file)
{
#if 0FILE* fp;BITMAPFILEHEADER bmpHeader;BITMAPINFOHEADER bmpInfo;int rgb_size1 = 0;int rgb_size2 = 0;int width = 0;int height = 0;int padding = 0;int stride_byte = 0;int color_num = 0;int paltette_len = 0;char* palette = NULL;fp = fopen(bmp_file, "rb");if (fp == NULL){printf("open file %s failed.\n", bmp_file);return -1;}fread(&bmpHeader, 1, sizeof(BITMAPFILEHEADER), fp);fread(&bmpInfo, 1, sizeof(BITMAPINFOHEADER), fp);if (bmpHeader.bfType != (('M' << 8) | 'B')){printf("Sorry, not bmp picture.\n");return -1;}width = bmpInfo.biWidth;height = (int)fabs((double)bmpInfo.biHeight);switch(bmpInfo.biBitCount) {case 1:color_num = 2;break;case 4:color_num = 16;break;case 8:color_num = 256;break;case 24:default:color_num = 0;break;}stride_byte = ALIGN(width*bmpInfo.biBitCount/8, 4);padding = stride_byte - width*bmpInfo.biBitCount/8;paltette_len = color_num * sizeof(RGBQUAD);rgb_size1 = bmpHeader.bfSize - sizeof(BITMAPFILEHEADER) - sizeof(BITMAPINFOHEADER) - paltette_len;rgb_size2 = stride_byte*height;// 打印结构体中每个成员printf("file name: %s\n", bmp_file);printf("file type: %c%c %x\n", (bmpHeader.bfType)>>8, (bmpHeader.bfType)&0xff, bmpHeader.bfType);printf("file size: %d(B) = %0.2f(KB) = %0.2f(MB)\n", bmpHeader.bfSize, (float)bmpHeader.bfSize/1024.00, (float)bmpHeader.bfSize/1024.00/1024.00);printf("offset of image data: %d\n", bmpHeader.bfOffBits);//printf("biSize: %d\n", bmpInfo.biSize);printf("width: %d\n", bmpInfo.biWidth);printf("height: %d\n", bmpInfo.biHeight);printf("Plane: %d\n", bmpInfo.biPlanes);printf("BitCount: %d\n", bmpInfo.biBitCount);printf("biCompression: %d\n", bmpInfo.biCompression);printf("biSizeImage: %d\n", bmpInfo.biSizeImage);printf("XPelsPerMeter: %d\n", bmpInfo.biXPelsPerMeter);printf("YPelsPerMeter: %d\n", bmpInfo.biYPelsPerMeter);printf("biClrUsed: %d\n", bmpInfo.biClrUsed);printf("biClrImportant: %d\n", bmpInfo.biClrImportant);printf("width*3: %d stride byte: %d padding: %d\n", width*3, stride_byte, padding);printf("rgb buffer size: %d %d\n", rgb_size1,rgb_size2);if (color_num != 0){palette = (char *)malloc(paltette_len * sizeof(char));fread(palette, paltette_len, 1, fp);printf("palette:\n");//dump(palette, paltette_len);}
#endifreturn 0;
}int read_bmp_file(const char* bmp_file, unsigned char** rgb_buffer,int* size, int* width, int* height)
{int ret = 0;FILE* fp;BITMAPFILEHEADER bmpHeader;BITMAPINFOHEADER bmpInfo;int tmp_width = 0;int tmp_height = 0;int rgb_size = 0;int stride_byte = 0; // 每行占用字节数(4字节对齐)int width_byte = 0;  // 每行真正有效字节数int padding = 0;    // 需要对齐的字节数unsigned char* tmp_buf = 0;int color_num = 0;int palette_len = 0;int i = 0;fp = fopen(bmp_file, "rb");if (fp == NULL){printf("open file %s failed.\n", bmp_file);return -1;}ret = fread(&bmpHeader, 1, sizeof(BITMAPFILEHEADER), fp);if (ret != sizeof(BITMAPFILEHEADER)){printf("read BITMAPFILEHEADER failed.\n");return -1;}ret = fread(&bmpInfo, 1, sizeof(BITMAPINFOHEADER), fp);if (ret != sizeof(BITMAPINFOHEADER)){printf("read BITMAPINFOHEADER failed read: %d %d.\n", ret, sizeof(BITMAPINFOHEADER));return -1;}if (bmpHeader.bfType != (('M' << 8) | 'B')){printf("Sorry, not bmp picture.\n");return -1;}tmp_width = bmpInfo.biWidth;tmp_height = (int)fabs((double)bmpInfo.biHeight);   // 预防高为负数的情况// 真正RGB数据大小rgb_size = tmp_width * tmp_height * bmpInfo.biBitCount/8;*width = tmp_width;*height = tmp_height;*size = rgb_size;/*** 每行占用字节数,与下式结果相同* stride_byte = (width * bmpInfo.biBitCount/8+3)/4*4;*/stride_byte = ALIGN(tmp_width*bmpInfo.biBitCount/8, 4);width_byte = tmp_width * bmpInfo.biBitCount/8;/*** 补齐字节,与下式结果相同* padding = (4 - width * 3 % 4) % 4;* 实现未使用*/padding = stride_byte - width_byte;// 判断调色板switch(bmpInfo.biBitCount) {case 1:color_num = 2;break;case 4:color_num = 16;break;case 8:color_num = 256;break;case 24:default:color_num = 0;break;}// todo:读取调色板palette_len = color_num * sizeof (RGBQUAD);// 计算偏移量与实际偏移量比较,如不等,颜色数出错if (bmpHeader.bfOffBits != sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + palette_len){return -1;}printf("debug--:\nfile size: %d rgb size: %d %d stride byte: %d padding: %d BitCount: %d\n", (int)bmpHeader.bfSize, rgb_size, stride_byte*tmp_height, stride_byte, padding, bmpInfo.biBitCount);if (color_num != 0){// 跳到图像数据处fseek(fp, palette_len, SEEK_CUR);}// 申请合适的内存*rgb_buffer = (unsigned char *)malloc(sizeof(char) * rgb_size);if (*rgb_buffer == NULL){return -1;}// 将读取的数据倒着存放到缓冲区(即BMP图像第一行数据放到缓冲区最后一行,等等),// 这样图像才是正常的,否则图像是倒立的tmp_buf = *rgb_buffer + rgb_size;for (i = 0; i < tmp_height; i++){tmp_buf -= width_byte;ret = fread(tmp_buf, 1, width_byte, fp);if (ret != width_byte){free(*rgb_buffer);return -1;}fseek(fp, padding, SEEK_CUR);}#if 0// 顺序读文件,读到的图像是倒立的unsigned char* tmp_buf = *rgb_buffer;size_t readByte = 0;for (int i = 0; i < tmp_height; i++){readByte += fread(tmp_buf, 1, width_byte, fp);fseek(fp, padding, SEEK_CUR);tmp_buf += width_byte;}
#endifreturn 0;
}int write_bmp_file(const char* bmp_file, unsigned char* rgb_buffer, int width, int height)
{
#define BPP 24  // 目前只考虑24色位图BITMAPFILEHEADER bmpHeader;BITMAPINFOHEADER bmpInfo;FILE* fp = NULL;int offset = 0;int stride_byte = 0;    // 每行占用字节数(4字节对齐)int width_byte = 0;     // 每行真正有效字节数int rgb_size = 0;int padding = 0;unsigned char* tmp_buf = NULL;int i = 0;fp = fopen(bmp_file, "wb");if (fp == NULL){printf("open %s failed\n", bmp_file);return -1;}offset = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);  //54字节// 4字节对齐 ((width * 24 + 31) / 32) * 4// 如已经对齐,则rowStride与实际宽一致,如不对齐rowStride会比宽大一些// stride_byte = ((width * 24 + 31) >> 5) << 2;stride_byte = ALIGN(width*BPP/8, 4);width_byte = width*BPP/8;rgb_size = stride_byte * height;  // 已考虑对齐bmpHeader.bfType = ('M' << 8) | 'B';bmpHeader.bfSize = offset + rgb_size;    // BMP文件总大小bmpHeader.bfReserved1 = 0;bmpHeader.bfReserved2 = 0;bmpHeader.bfOffBits = offset;bmpInfo.biSize = sizeof(BITMAPINFOHEADER);bmpInfo.biWidth  = width;bmpInfo.biHeight = height;bmpInfo.biPlanes = 1;bmpInfo.biBitCount = BPP;bmpInfo.biCompression = 0;bmpInfo.biSizeImage = rgb_size;bmpInfo.biXPelsPerMeter = 0;bmpInfo.biYPelsPerMeter = 0;bmpInfo.biClrUsed = 0;bmpInfo.biClrImportant = 0;// 需要填充字节,BMP要求每一行数据必须4字节对齐,不足以0补。//padding = (4 - width * 3 % 4) % 4;// 实际未使用到padding = stride_byte - width_byte;printf("debug--:\nwidth: %d height: %d padding: %d rgb_size: %d, stride_byte: %d\n",width, height, padding, rgb_size, stride_byte);tmp_buf = (unsigned char *)malloc(sizeof(char) * rgb_size);if (tmp_buf == NULL){return -1;}memset(tmp_buf, '\0', sizeof(char) * rgb_size);// 倒着拷贝到缓冲区for (i = 0; i < height; i++){// 每一行的实际数据为width * 3(R、G、B)memcpy(tmp_buf + i * stride_byte, rgb_buffer + (height - i - 1) * width_byte, width_byte);}fwrite(&bmpHeader, 1, sizeof(BITMAPFILEHEADER), fp);fwrite(&bmpInfo, 1, sizeof(BITMAPINFOHEADER), fp);fwrite(tmp_buf, 1, rgb_size, fp);free(tmp_buf);return 0;
}// rgb --> bgr or
// bgr --> rgb
void swap_rgb(unsigned char* rgb_buffer, int len)
{int i = 0;for (i = 0; i < len; i += 3){unsigned char tmp;tmp = rgb_buffer[i];rgb_buffer[i] = rgb_buffer[i + 2];rgb_buffer[i + 1] = rgb_buffer[i + 1];rgb_buffer[i + 2] = tmp;}
}

说明:

最后写的那个RGB交换函数,是因为当初未了解libjpeg解压jpeg可以选择RGB的格式,以为只有是RGB,但BMP却只认BGR,因此就自己写了个R、B交换函数。但libjpeg本身是支持BGR的格式的。

看了几年前自己写的代码,觉得能写Doxygen风格的注释很不容易,希望自己能坚持下去,而不用理会其它的影响。

李迟 2015.7.9

BMP图片读写接口函数相关推荐

  1. C语言bmp图片读写,画点,画线

    bmp文件信息头.文件头结构体: #pragma pack(1)typedef struct {uint16_t bfType;//位图类别,根据不同的操作系统而不同,在Windows中,此字段的值总 ...

  2. bmp图片显示(任意位置任意大小)

    文章目录 前言 图片显示 1.普通全屏(800*480)显示bmp 2.在任意位置显示任意大小bmp图片 头文件 前言 Linux系统,GEC6818,800*480,bmp图片 图片显示 由文件IO ...

  3. ZYNQ系列之-----SD卡中BMP图片读取+ddr读写验证

    1.前提: sd卡挂载在ps测,并且使用的ZYNQ系统.和前文是一样的. ZYNQ系列之-----SD卡读写文件_hhh_fpga的博客-CSDN博客 2.设备与软件 软件: vivado 2021. ...

  4. bmp文件头_「正点原子FPGA连载」第十九章SD卡读BMP图片LCD显示

    1)摘自[正点原子]领航者 ZYNQ 之嵌入式开发指南 2)实验平台:正点原子领航者ZYNQ开发板 3)平台购买地址:https://item.taobao.com/item.htm?&id= ...

  5. activiti高亮显示图片_【正点原子FPGA连载】第二十章SD卡读BMP图片HDMI显示实验领航者 ZYNQ 之嵌入式开发指南...

    1)实验平台:正点原子领航者ZYNQ开发板 2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761 3)全套实验源码+手册+视频下 ...

  6. 基于ZYNQ的网页上传BMP图片至HDMI端口输出实例

    目录 Change Log 0 前言 0.1 系统实现 0.2 源码下载 0.3 项目信息 1 ZYNQ开发板HDMI输出BMP图片功能:FPGA相关 1.1 系统框图 1.2 RTL图 1.3 Bl ...

  7. c++jpg转bmp_用C++加速julia:BMP图片读取

    最近写了一个程序,需要大批量地读取8位的BMP格式的灰度图,程序写完profiler一看,竟然读图和处理的时间不相上下,这是万万不可接受的.经过一番折腾,最后决定用C++来加速8位BMP图片的读取.目 ...

  8. 图像处理-STB图片读写

    图片读写 stb 在matlab中利用imread和imwrite来对图片进行读写,在c++中推荐一个很好用的库–stb,这个库只需要一个头文件,就可以对多种类型的图片进行解析和保存,并且使用方便,接 ...

  9. C++实验六:继承,对BMP图片操作

    功能介绍: 图片翻转.旋转.剪切.任意缩放.图片相加.图片相减等等 复杂功能解释基本都写在注释中了,对于图片缩放,可谓本次实验之精华(搞人心态) 其次,关于resize函数的重载,应该private掉 ...

最新文章

  1. 【通知】有三AI新手入门群开放,欢迎新手们来加入
  2. 【图像处理】图像内插“最近邻插值 最近邻内插法(Nearest Neighbour Interpolate)”代码演示(调整图像大小、放大、缩小)
  3. 《系统集成项目管理工程师》必背100个知识点-75配置标识的基本内容
  4. 通过QuartzCore/CoreAnimation.h实现让玫瑰花飞舞
  5. html print 边距,css print
  6. 继承 java 1614784316
  7. Mvc检查图片格式后上传
  8. WRK-HTTP压力测试工的下载安装与使用方法
  9. 网络是怎样连接的笔记第5章 防火墙,缓存服务器
  10. Illustrator中文版教程,如何在 Illustrator中设置图标项目?
  11. LOLCC换肤盒子官网网站源码
  12. Java Web面试题及答案整理(2021年最新版,持续更新)
  13. Leader安排的三小时工作量,我如何用python十秒完成
  14. python小白的word转excel
  15. MATLAB使用教程(三)——慢慢练手做项目啦——新手来看
  16. 典型的人工神经网络由很多层构成,但不包括
  17. 【原创】《矩阵的史诗级玩法》连载十七:用矩阵研究二次贝塞尔曲线和抛物线的关系(上)
  18. 【转载】PyCharm 或者其他 Idea 官网打不开解决办法:
  19. 命令模式实现电视遥控器
  20. Eclipse neon版本下载安装

热门文章

  1. 阿里公布碳中和目标:2030年带动生态 15年减碳15亿吨
  2. 库克:iPhone决不妥协!不爽换安卓 iPhone 更有“安全性和隐私性”
  3. 特斯拉宣布总部将从加州硅谷迁到得州
  4. 苹果高管谈及近期员工担忧,呼吁其向管理层报告职场问题
  5. 陌陌宣布由总裁兼COO王力担任公司新任CEO
  6. 理想汽车已累计交付2万台 仅用时10个月
  7. 在公司群匿名吐槽后当场“掉马”?QQ回应:真这样程序猿要被祭天
  8. 金山云和金山办公均成功上市 雷军揭秘背后原因
  9. 发黄图再截图举报!这个社交软件运营合伙人被逮捕:“设局”恶意举报同行...
  10. iPhone 9真机谍照曝光:真没有什么悬念了