开发过程中有时候需要解析bmp数据,下面先简单介绍bmp数据组成,后面附上C语言读取和存储bmp格式图片代码。
BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息。
这部分是识别信息,典型的应用程序会首先普通读取这部分数据以确保的确是位图文件并且没有损坏。
位图头结构体定义如下:
typedef struct { uint16_t type; //位图文件的类型,必须为BM(1-2字节) uint32_t size; //位图文件的大小,以字节为单位(3-6字节,低位在前) uint16_t reserved1; //位图文件保留字,必须为0(7-8字节) uint16_t reserved2; //位图文件保留字,必须为0(9-10字节) uint32_t off_bits; //位图数据位置的地址偏移,即起始位置,以相对于位图(11-14字节,低位在前) }__attribute__ ((packed)) bmp_file_header_t;
这部分告诉应用程序图像的详细信息,在屏幕上显示图像将会使用这些信息,它从文件的第15个字节开始。
位图信息头结构体定义如下:
typedef struct { uint32_t size; int32_t width; int32_t height; uint16_t planes; uint16_t bit_count; uint32_t compression; uint32_t size_image; uint32_t x_pels_permeter; uint32_t y_pels_permeter; uint32_t clr_used; uint32_t clr_important; } bmp_info_header_t;
结构体变量解析如下:
BMP调色板结构体定义如下:
typedef struct _tagRGBQUAD { BYTE rgbBlue; //指定蓝色强度 BYTE rgbGreen; //指定绿色强度 BYTE rgbRed; //指定红色强度 BYTE rgbReserved; //保留,设置为0 } RGBQUAD;
1,4,8位图像才会使用调色板数据,16,24,32位图像不需要调色板数据,即调色板最多只需要256项(索引0 - 255)。
颜色表的大小根据所使用的颜色模式而定:2色图像为8字节;16色图像位64字节;256色图像为1024字节。其中,每4字节表示一种颜色,并以B(蓝色)、G(绿色)、R(红色)、alpha(32位位图的透明度值,一般不需要)。即首先4字节表示颜色号1的颜色,接下来表示颜色号2的颜色,依此类推。
颜色表中RGBQUAD结构数据的个数有biBitCount来确定,当biBitCount=1,4,8时,分别有2,16,256个表项:
位图数据记录了位图的每一个像素值。
像素是从下到上、从左到右保存的。
每个像素使用一个或者多个字节表示。
如果一个图像水平线的字节数不是4的倍数,这行就使用空字节补齐,通常是ASCII码0。
例如:
一张5 * 6的图片,有30个pixels,因为列数6不是4的倍数,所以会显示成:
xxxxxx00 xxxxxx00 xxxxxx00 xxxxxx00 xxxxxx00
其中,x代表调色盘的编号,0代表补齐的空字节
一张4 * 4的图片,有16个pixels,因为列数刚好是4的倍数,所以会显示成:
xxxx xxxx xxxx xxxx
#include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <math.h> #include <string.h> typedef struct { uint16_t type; uint32_t size; uint16_t reserved1; uint16_t reserved2; uint32_t off_bits; }__attribute__ ((packed)) bmp_file_header_t; typedef struct { uint32_t size; int32_t width; int32_t height; uint16_t planes; uint16_t bit_count; uint32_t compression; uint32_t size_image; uint32_t x_pels_permeter; uint32_t y_pels_permeter; uint32_t clr_used; uint32_t clr_important; } bmp_info_header_t; static bmp_file_header_t s_bmp_file_header = { 0x4d42, 0, 0, 0, 0 }; static bmp_info_header_t s_bmp_info_header = { 0, 0, 0, 1, 8, 0, 0, 0, 0, 0, 0 }; static uint8_t s_bmpdata[200 * 200] = { 0 }; static uint32_t s_bmp_col = 0; static uint32_t s_bmp_row = 0; char in_file_path[256] = "in.bmp"; char out_file_path[256] = "out.bmp"; int32_t bmp_file_to_image(const char *file_path, uint8_t *image, uint32_t *col, uint32_t *row) { FILE *file = NULL; uint32_t line_width = 0; uint32_t width = 0; uint32_t height = 0; int32_t err = 0; uint8_t buf[200 * 200] = { 0 }; char temp[2048] = { 0 }; int i = 0; do { if (NULL == file_path || NULL == image) { err = -1; break; } printf("[%s] file_path = %s\n", __func__, file_path); file = fopen(file_path, "rb"); if (NULL == file) { err = -1; break; } fread(&s_bmp_file_header, sizeof(s_bmp_file_header), 1, file); fread(&s_bmp_info_header, sizeof(s_bmp_info_header), 1, file); fread(temp, 4*256, 1, file); width = s_bmp_info_header.width; height = s_bmp_info_header.height; *col = width; *row = height; line_width = (width + 3) / 4 * 4; printf("[%s] line_width = %d, width = %d, height = %d\n", __func__, line_width, width, height); for (i = height - 1; i >= 0; i--) { if (line_width == width) { fread(buf + i * width, width, 1, file); } else if (line_width > width) { fread(buf + i * width, width, 1, file); fread(temp, line_width-width, 1, file); } } memcpy(image, buf, width * height); } while (0); if (file != NULL) { fclose(file); } return err; } int32_t dump_image_to_bmp_file(const char *file_path, uint8_t *image, uint32_t width, uint32_t height) { FILE *file = NULL; int32_t err = 0; do { if (NULL == file_path || NULL == image) { err = -1; break; } uint32_t line_width = (width + 3) / 4 * 4; s_bmp_file_header.off_bits = sizeof(bmp_file_header_t) + sizeof(bmp_info_header_t) + 4 * 256; s_bmp_file_header.size = s_bmp_file_header.off_bits + line_width * height; s_bmp_info_header.size = sizeof(bmp_info_header_t); s_bmp_info_header.width = width; s_bmp_info_header.height = height; s_bmp_info_header.size_image = line_width * height; printf("[%s] line_width = %d, width = %d, height = %d\n", __func__, line_width, width, height); file = fopen(file_path, "wb"); if (NULL == file) { err = -1; break; } fwrite(&s_bmp_file_header.type, 1, sizeof(s_bmp_file_header.type), file); fwrite(&s_bmp_file_header.size, 1, sizeof(s_bmp_file_header.size), file); fwrite(&s_bmp_file_header.reserved1, 1, sizeof(s_bmp_file_header.reserved1), file); fwrite(&s_bmp_file_header.reserved2, 1, sizeof(s_bmp_file_header.reserved2), file); fwrite(&s_bmp_file_header.off_bits, 1, sizeof(s_bmp_file_header.off_bits), file); fwrite(&s_bmp_info_header, 1, sizeof(bmp_info_header_t), file); uint8_t alpha = 0; int32_t i; for (i = 0; i < 256; i++) { fwrite(&i, 1, sizeof(uint8_t), file); fwrite(&i, 1, sizeof(uint8_t), file); fwrite(&i, 1, sizeof(uint8_t), file); fwrite(&alpha, 1, sizeof(uint8_t), file); } for (i = height - 1; i >= 0; i--) { fwrite(image + i * width, 1, width, file); if (line_width > width) { uint8_t line_align[4] = { 0 }; fwrite(line_align, 1, line_width - width, file); } } fflush(file); } while (0); if (file != NULL) { fclose(file); } return err; } int main() { int32_t err = 0; err = bmp_file_to_image(in_file_path, s_bmpdata, &s_bmp_col, &s_bmp_row); if (err != 0) { return -1; } printf("[%s] s_bmp_col = %d, s_bmp_row = %d\n", __func__, s_bmp_col, s_bmp_row); dump_image_to_bmp_file(out_file_path, s_bmpdata, s_bmp_col, s_bmp_row); return 0; }