/*
RGB24转BMP
BMP图像内部实际上存储的就是RGB数据,
BMP格式:
|---------------------------------|
| bmp文件头bitmap_file_header(14) |
|-------------------------------- |
|位图信息头bitmap_info_header(40) |
|---------------------------------|
| 位图数据 |
|---------------------------------|
*/
#ifdef WIN32
#pragma pack(1)
#define PACKED_ALIGN
#else
#define PACKED_ALIGN __attribute__((packed))
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// bitmap file header
typedef struct BitmapFileHeader
{
char identifier[2]; //文件类型,必须为BM
unsigned int fileSize; //整个BMP文件大小,单位字节
unsigned short reserved1; //保留,必须为0
unsigned short reserved2; //保留,必须为0
unsigned int dataOffset; //文件头到实际图像数据之间的偏移
} PACKED_ALIGN BITMAP_FILE_HEADER;
// bitmap info header
typedef struct BitmapInfoHeader
{
unsigned int infoSize; //BitmapInfoHeader结构体大小,单位字节
unsigned int width; //图形宽度,单位像素
unsigned int height; //图形高度,单位像素
short planes; //目标设备级别,必须为1
short bitsPerPixel; //颜色深度,每个像素所需的位数
unsigned int compression; //位图的压缩类型
unsigned int dataSize; //位图的大小,单位字节
unsigned int hresolution; //位图水平分辨率,每米像素数
unsigned int vresolution; //位图垂直分辨率,每米像素数
unsigned int useColors; //位图实际使用的颜色表中的颜色数
unsigned int importantColors; //位图显示过程中重要的颜色数
} PACKED_ALIGN BITMAP_INFO_HEADER;
// 将rgb24图片转成bmp图像
int rgb24_to_bmp(const char *file_rgb24, int width, int height, const char *file_bmp)
{
int i = 0;
int j = 0;
BITMAP_FILE_HEADER bmpFileHeader = {0};
BITMAP_INFO_HEADER bmpInfoHeader = {0};
FILE *fp_rgb24 = NULL;
FILE *fp_bmp = NULL;
unsigned int headSize = sizeof(BITMAP_FILE_HEADER) + sizeof(BITMAP_INFO_HEADER);
if ((fp_rgb24 = fopen(file_rgb24, "rb")) == NULL)
{
printf("fopen error: cannot open file %s\n", file_rgb24);
return -1;
}
if ((fp_bmp = fopen(file_bmp, "wb")) == NULL)
{
printf("fopen error: cannot open file %s\n", file_bmp);
fclose(fp_rgb24);
return -1;
}
unsigned int rgb24_size = width * height * 3;
unsigned char *rgb24_buf = (unsigned char *)malloc(rgb24_size);
if (rgb24_buf == NULL)
{
printf("malloc error: alloc rgb24 memory fail\n");
fclose(fp_rgb24);
fclose(fp_bmp);
return -1;
}
fread(rgb24_buf, 1, rgb24_size, fp_rgb24);
// 填充bmpFileHeader结构体
bmpFileHeader.identifier[0] = 'B';
bmpFileHeader.identifier[1] = 'M';
bmpFileHeader.fileSize = headSize + rgb24_size;
bmpFileHeader.reserved1 = 0;
bmpFileHeader.reserved2 = 0;
bmpFileHeader.dataOffset = headSize;
// 填充bmpInfoHeader结构体
bmpInfoHeader.infoSize = sizeof(BITMAP_INFO_HEADER);
bmpInfoHeader.width = width;
// BMP在Y轴的相反方向(从下到上)存储像素数据, height取反
bmpInfoHeader.height = -height;
bmpInfoHeader.planes = 1;
bmpInfoHeader.bitsPerPixel = 24;
bmpInfoHeader.dataSize = rgb24_size;
//printf("sizeof(BITMAP_FILE_HEADER): %d\n", sizeof(BITMAP_FILE_HEADER));
//printf("sizeof(BITMAP_INFO_HEADER): %d\n", sizeof(BITMAP_INFO_HEADER));
// 写bitmap file header
fwrite(&bmpFileHeader, 1, sizeof(BITMAP_FILE_HEADER), fp_bmp);
// 写bitmap info header
fwrite(&bmpInfoHeader, 1, sizeof(BITMAP_INFO_HEADER), fp_bmp);
/* 写rgb数据 */
/* BMP是按小端格式存储RGB数据,低地址存低位数据,高地址存高位数据
RGB一个像素是按R|G|B排列,转BMP时需要按小端格式存储RGB值,小端格式需要将
RGB排列转换成BGR排列
二进制高低位
00011000 00011100 00000000
高位 低位
内存地址
-------------------------->
低地址 往右地址增加 高地址
所以Little Endian小端格式需要将右端的低位与左端的高位调换
*/
for (j = 0; j < height; j++)
{
for (i = 0; i < width; i++)
{
// 获取第j行的第i个像素的B
char temp = rgb24_buf[(j * width + i) * 3 + 2];
// 将第j行的第i个像素的R值赋值给第j行的第i个像素的B
rgb24_buf[(j * width + i) * 3 + 2] = rgb24_buf[(j * width + i) * 3 + 0];
// 将原第j行的第i个像素的B值赋值给第j行的第i个像素的R
rgb24_buf[(j * width + i) * 3 + 0] = temp;
}
}
// 写RGB数据
fwrite(rgb24_buf, 1, rgb24_size, fp_bmp);
fclose(fp_rgb24);
fclose(fp_bmp);
free(rgb24_buf);
return 0;
}
int main(void)
{
const char *file_rgb24 = "lena_256x256_rgb24.rgb";
const char *file_bmp = "output_lena.bmp";
return rgb24_to_bmp(file_rgb24, 256, 256, file_bmp);
}