最近学习雷神的博客, RGB24转换成BMP位图, 发现一些问题与心得, 记录下来
1.位图知识:(会列出两篇比较好的博文, 这里简单总结一下)
位图结构:
位图文件头(14字节)
位图信息头(40字节)
彩色表(RGB24真彩不存在彩色表)
位图数据(实际上就是RGB数据)
【注意1】:位图文件头与位图信息头一共是54字节, 雷神在博文里写的结构体不正确, 正确的结构体如下:
贴图中的位图文件头为12字节, 因为有两个字节的BM用字符数组表示了, 详见最后的完整代码
/*12Bytes*/
typedef struct /**** BMP file header structure ****/
{
unsigned int bfSize; /* Size of file */
unsigned short bfReserved1; /* Reserved */
unsigned short bfReserved2; /* ... */
unsigned int bfOffBits; /* Offset to bitmap data */
}BITMAPFILEHEADER;
/*40Bytes*/
typedef struct /**** BMP file info structure ****/
{
unsigned int biSize; /* Size of info header */
int biWidth; /* Width of image */
int biHeight; /* Height of image */
unsigned short biPlanes; /* Number of color planes */
unsigned short biBitCount; /* Number of bits per pixel */
unsigned int biCompression; /* Type of compression to use */
unsigned int biSizeImage; /* Size of image data */
int biXPelsPerMeter; /* X pixels per meter */
int biYPelsPerMeter; /* Y pixels per meter */
unsigned int biClrUsed; /* Number of colors used */
unsigned int biClrImportant; /* Number of important colors */
}BITMAPINFOHEADER;
【注意2】:BMP的存储方式是小端存储, 即低字节放低地址, 高字节放高地址
关于大小端存储, 我一直有误区, 我错误理解为"地位放在低地址, 高位放在高地址",这是不对的, 应该是将整个低字节的内容都存储到低地址去
例如内存中存储0x12345678的小端存法应该是0x78 0x56 0x34 0x12(可以写代码进行验证)
关于大小端, 比较好的博客是:
https://blog.csdn.net/ce123_zhouwei/article/details/6971544
【注意3】:C语言结构体对齐的相关知识《C语言结构体对齐》
rules one:数据成员对齐规则:
结构(struct)或联合(union)的数据成员, 第一个数据成员放在offset为0的地方, 以后每个数据成员存储的起始位置要从该成员大小的整数倍开始
rules two:
结构体的总大小, 也就是sizeof的结果, 必须其内部最大成员的整数倍, 不足的要补齐
【注意4】:BMP图像的RGB数据存储方式
part1:RGB文件存储性质-大端存储
我个人理解RGB文件的存储方式是大端性质的, 即低地址存高字节, 高地址存低字节
因为从RGB文件读出来的数据可知是按照R-G-B R-G-B R-G-B顺序的, 而在RGB编码中R为高字节, B为低字节
所以在BMP存储时要将R和B的顺序调换, 方法如下:
/*change R-G-B to B-G-R*/
for(i = 0; i < (w*h); i++)
{
temp = *(readBuff + i*3);
*(readBuff + i*3) = *(readBuff + i*3 + 2);
*(readBuff + i*3 + 2) = temp;
}
part2:BMP文件数据存储方式
若BITMAPINFOHEADER的成员biHeight为负值, 则BMP数据存储方向为从左到右, 从上到下
若BITMAPINFOHEADER的成员biHeight为正值, 则BMP数据存储方向为从左到右, 从下到上
我在程序中写入宏#define POSITIVE_HEIGHT,并将biHeight的值填为正数
如上四点, 比较好的博文为:【转】
https://www.cnblogs.com/liulijin/p/9108099.html
http://wojiaolongyinong.iteye.com/blog/1896092
https://blog.csdn.net/mlfcjob/article/details/78918235
https://blog.csdn.net/ce123_zhouwei/article/details/6971544
2.RGB24 to BMP代码
#include "common_head.h"
#define MAX_LEN (1*1024*1024)
#define POSITIVE_HEIGHT (1)
/*12Bytes*/
typedef struct /**** BMP file header structure ****/
{
unsigned int bfSize; /* Size of file */
unsigned short bfReserved1; /* Reserved */
unsigned short bfReserved2; /* ... */
unsigned int bfOffBits; /* Offset to bitmap data */
}BITMAPFILEHEADER;
/*40Bytes*/
typedef struct /**** BMP file info structure ****/
{
unsigned int biSize; /* Size of info header */
int biWidth; /* Width of image */
int biHeight; /* Height of image */
unsigned short biPlanes; /* Number of color planes */
unsigned short biBitCount; /* Number of bits per pixel */
unsigned int biCompression; /* Type of compression to use */
unsigned int biSizeImage; /* Size of image data */
int biXPelsPerMeter; /* X pixels per meter */
int biYPelsPerMeter; /* Y pixels per meter */
unsigned int biClrUsed; /* Number of colors used */
unsigned int biClrImportant; /* Number of important colors */
}BITMAPINFOHEADER;
HI_S32 simplest_rgb24_to_bmp(const char* rgb24Path, int w, int h, const char* bmpPath)
{
HI_S32 s32Ret = 0;
int fd_ori = -1;
int fd_bmp = -1;
int headerSize = 0;
int i = 0;//for circle
int j = 0;//for circle
unsigned char temp = 0;
unsigned char readBuff[MAX_LEN] = {'\0'};
memset(readBuff, 0, sizeof(readBuff));
#ifdef POSITIVE_HEIGHT
unsigned char readBuff4Ph[MAX_LEN] = {'\0'};
memset(readBuff4Ph, 0, sizeof(readBuff4Ph));
#endif
char bfType[2] = {'B', 'M'};
BITMAPFILEHEADER myHead;
BITMAPINFOHEADER myHeadInfo;
memset(&myHead, 0, sizeof(myHead));
memset(&myHeadInfo, 0, sizeof(myHeadInfo));
printf("sizeof(myHead) = %d\n", sizeof(myHead));
printf("sizeof(myHeadInfo) = %d\n", sizeof(myHeadInfo));
/*myHead*/
headerSize = sizeof(bfType) + sizeof(myHead) + sizeof(myHeadInfo);
myHead.bfSize = headerSize + w*h*3;
myHead.bfOffBits = headerSize;
/*myHeadInfo*/
myHeadInfo.biSize = sizeof(myHeadInfo);
myHeadInfo.biWidth = w;
#ifndef POSITIVE_HEIGHT
myHeadInfo.biHeight = -1 * h;
#else
myHeadInfo.biHeight = h;
#endif
myHeadInfo.biPlanes = 1;
myHeadInfo.biBitCount = 24;
myHeadInfo.biSizeImage = w*h*3;
/*open files*/
fd_ori = open(rgb24Path, O_RDONLY);
if(fd_ori < 0)
{
printf("open rgb24 failed!\n");
return -1;
}
printf("open rgb24 success!\n");
fd_bmp = open(bmpPath, O_WRONLY|O_CREAT|O_TRUNC|O_APPEND, 777);
if(fd_bmp < 0)
{
printf("open bmp failed!\n");
close(fd_ori);
return -1;
}
printf("open bmp success!\n");
/*read*/
memset(readBuff, 0, sizeof(readBuff));
s32Ret = read(fd_ori, readBuff, sizeof(readBuff));
if((s32Ret < 0) || (s32Ret != w*h*3))
{
printf("read RGB file failed!\n");
close(fd_bmp);
close(fd_ori);
return -1;
}
printf("read RGB file success!\n");
/*change R-G-B to B-G-R*/
for(i = 0; i < (w*h); i++)
{
temp = *(readBuff + i*3);
*(readBuff + i*3) = *(readBuff + i*3 + 2);
*(readBuff + i*3 + 2) = temp;
}
/*positive height storage sequence:left-right down-up*/
#ifdef POSITIVE_HEIGHT
for(i = (h - 1), j = 0; i >= 0; i--, j++)
{
memcpy(readBuff4Ph + j*w*3, readBuff + i*w*3, w*3);
}
#endif
/*write-4 parts*/
s32Ret = write(fd_bmp, bfType, sizeof(bfType));
if(s32Ret < 0)
{
printf("write bfType failed!\n");
close(fd_bmp);
close(fd_ori);
return -1;
}
s32Ret = write(fd_bmp, &myHead, sizeof(myHead));
if(s32Ret < 0)
{
printf("write myHead failed!\n");
close(fd_bmp);
close(fd_ori);
return -1;
}
s32Ret = write(fd_bmp, &myHeadInfo, sizeof(myHeadInfo));
if(s32Ret < 0)
{
printf("write myHeadInfo failed!\n");
close(fd_bmp);
close(fd_ori);
return -1;
}
#ifdef POSITIVE_HEIGHT
s32Ret = write(fd_bmp, readBuff4Ph, w*h*3);
if(s32Ret < 0)
{
printf("write readBuff4Ph failed!\n");
close(fd_bmp);
close(fd_ori);
return -1;
}
printf("write readBuff4Ph success!\n");
#else
s32Ret = write(fd_bmp, readBuff, w*h*3);
if(s32Ret < 0)
{
printf("write readBuff failed!\n");
close(fd_bmp);
close(fd_ori);
return -1;
}
printf("write readBuff success!\n");
#endif
close(fd_bmp);
close(fd_ori);
return 0;
}
最后得到的BMP图为:(CSDN传不上, 只有截图一下了)