显示 BMP 图片
BMP 图像介绍BMPBitmap是 Windows 操作系统中的标准图像文件格式文件后缀名为 .bmp。它采用位映射存储格式图像数据没有进行任何压缩因此文件占用空间大但无失真、解析简单。BMP 图像深度可选1bit、4bit、8bit、16bit、24bit、32bit。典型的 BMP 文件由以下四部分组成数据段 大小 说明BMP 文件头 14 字节 文件格式、大小、位图数据偏移量位图信息头 通常 40 或 56 字节 尺寸、位深度、压缩方式、颜色索引调色板 由颜色索引数决定 可选索引到颜色的映射表位图数据 由图像尺寸决定 图像原始数据真彩色图像16位、24位、32位不需要调色板位图信息头后面紧跟位图数据。BMP 文件头结构typedef struct tagBITMAPFILEHEADER {UINT16 bfType; /* 文件类型“BM” 表示 Windows 位图/DWORD bfSize; /文件大小字节/UINT16 bfReserved1; /保留必须为 0/UINT16 bfReserved2; /保留必须为 0/DWORD bfOffBits; /从文件起始到位图数据的字节偏移量/} BITMAPFILEHEADER;位图信息头结构typedef struct tagBITMAPINFOHEADER {DWORD biSize; /位图信息头大小/LONG biWidth; /图像宽度像素/LONG biHeight; /图像高度像素正数为倒向位图负数为正向位图/WORD biPlanes; /色彩平面数总为 1/WORD biBitCount; /像素深度1/4/8/16/24/32/DWORD biCompression; /压缩类型0RGB, 3Bit-Fields/DWORD biSizeImage; /图像数据大小/LONG biXPelsPerMeter; /水平分辨率像素/米/LONG biYPelsPerMeter; /垂直分辨率像素/米/DWORD biClrUsed; /实际使用的颜色索引数/DWORD biClrImportant; /重要颜色索引数 */} BITMAPINFOHEADER;正向位图与倒向位图正向位图biHeight 为负数图像数据按左上角到右下角排列水平从左到右垂直从上到下。倒向位图biHeight 为正数图像数据按左下角到右上角排列水平从左到右垂直从下到上。大多数 BMP 图像为倒向位图。RGB 与 Bit-Fields 编码对于 16bpp 及以上的位图不使用调色板有两种编码格式RGB 编码R、G、B 三种颜色信息容量均分如 24bpp 各占 8 位。Bit-FieldsBF编码利用位域掩码确定 RGB 三分量的信息容量。当压缩方式为 BFbiCompression3时位图信息头大小为 56 字节多出的 16 字节为 R、G、B、A 四个分量的位域掩码。A 分量即 Alpha透明度 通道取值范围 0~2550 表示完全透明255 表示完全不透明。在 BMP 中通常无实际意义但 ARGB8888 格式会占 8 位。位域掩码的作用指出 R、G、B或 A各分量在一个像素值中分别占多少位、位于什么位置。通过掩码即可从像素值中提取或组合出各颜色分量从而动态判断像素格式。对于 RGB565 格式R、G、B 的位域掩码分别为 0xF800、0x07E0、0x001FR 通道高 5 位bit 15~11G 通道中间 6 位bit 10~5B 通道低 5 位bit 4~0在 LCD 上显示 BMP 图像显示 BMP 图像的核心思路解析 BMP 文件头和位图信息头读取位图数据然后写入 LCD 显存。示例代码#include stdio.h#include stdlib.h#include sys/types.h#include sys/stat.h#include fcntl.h#include unistd.h#include sys/ioctl.h#include string.h#include linux/fb.h#include sys/mman.h/**** BMP 文件头数据结构 ****/typedef struct {unsigned char type[2];unsigned int size;unsigned short reserved1;unsigned short reserved2;unsigned int offset;}attribute((packed)) bmp_file_header;/**** 位图信息头数据结构 ***/typedef struct {unsigned int size;int width;int height;unsigned short planes;unsigned short bpp; /bits per pixel每个像素占多少位 */unsigned int compression;unsigned int image_size;int x_pels_per_meter;int y_pels_per_meter;unsigned int clr_used;unsigned int clr_omportant;}attribute((packed)) bmp_info_header;static int width;static int height;static unsigned short *screen_base NULL;static unsigned long line_length;static int show_bmp_image(const char *path){bmp_file_header file_h;bmp_info_header info_h;unsigned short *line_buf NULL;unsigned long line_bytes;unsigned int min_h, min_bytes;int fd -1;int j;/* 打开文件 */ if (0 (fd open(path, O_RDONLY))) { perror(open error); return -1; } /* 读取 BMP 文件头 */ if (sizeof(bmp_file_header) ! read(fd, file_h, sizeof(bmp_file_header))) { perror(read error); close(fd); return -1; } if (0 ! memcmp(file_h.type, BM, 2)) { fprintf(stderr, its not a BMP file\n); close(fd); return -1; } /* 读取位图信息头 */ if (sizeof(bmp_info_header) ! read(fd, info_h, sizeof(bmp_info_header))) { perror(read error); close(fd); return -1; } printf(文件大小: %d\n 位图数据的偏移量: %d\n 位图信息头大小: %d\n 图像分辨率: %d*%d\n 像素深度: %d\n, file_h.size, file_h.offset, info_h.size, info_h.width, info_h.height, info_h.bpp); /* 将文件读写位置移动到图像数据开始处 */ if (-1 lseek(fd, file_h.offset, SEEK_SET)) { perror(lseek error); close(fd); return -1; } /* 申请一个 buf、暂存 bmp 图像的一行数据 */ line_bytes info_h.width * info_h.bpp / 8; // bpp ÷ 8 将位(bit)转为字节(Byte) line_buf malloc(line_bytes); if (NULL line_buf) { fprintf(stderr, malloc error\n); close(fd); return -1; } if (line_length line_bytes) // line_length: LCD一行字节数 min_bytes line_bytes; // line_bytes: BMP一行字节数 else // 取较小值防止memcpy越界 min_bytes line_length; /* 读取图像数据显示到 LCD */ if (0 info_h.height) { // 倒向位图 if (info_h.height height) { min_h height; lseek(fd, (info_h.height - height) * line_bytes, SEEK_CUR); screen_base width * (height - 1); } else { min_h info_h.height; screen_base width * (info_h.height - 1); } for (j min_h; j 0; screen_base - width, j--) { read(fd, line_buf, line_bytes); memcpy(screen_base, line_buf, min_bytes); } } else { // 正向位图 int temp 0 - info_h.height; if (temp height) min_h height; else min_h temp; for (j 0; j min_h; j, screen_base width) { read(fd, line_buf, line_bytes); memcpy(screen_base, line_buf, min_bytes); } } close(fd); free(line_buf); return 0;}int main(int argc, char *argv[]){struct fb_fix_screeninfo fb_fix;struct fb_var_screeninfo fb_var;unsigned int screen_size;int fd;if (2 ! argc) { fprintf(stderr, usage: %s bmp_file\n, argv[0]); exit(-1); } if (0 (fd open(/dev/fb0, O_RDWR))) { perror(open error); exit(EXIT_FAILURE); } ioctl(fd, FBIOGET_VSCREENINFO, fb_var); ioctl(fd, FBIOGET_FSCREENINFO, fb_fix); screen_size fb_fix.line_length * fb_var.yres; line_length fb_fix.line_length; width fb_var.xres; height fb_var.yres; screen_base mmap(NULL, screen_size, PROT_WRITE, MAP_SHARED, fd, 0); if (MAP_FAILED (void *)screen_base) { perror(mmap error); close(fd); exit(EXIT_FAILURE); } memset(screen_base, 0xFF, screen_size); show_bmp_image(argv[1]); munmap(screen_base, screen_size); close(fd); exit(EXIT_SUCCESS);}显示 BMP 的主要流程打开 BMP 文件读取文件头和位图信息头。校验文件类型是否为 “BM”。通过 lseek() 将文件读写位置移至图像数据起始处file_h.offset。根据 info_h.height 判断正向/倒向位图逐行读取图像数据并 memcpy 到显存映射区。关闭文件释放资源。注意示例代码默认传入的 BMP 图像为 RGB565 格式。如需兼容 RGB888 等其他格式需根据 bmp_info_header 中的 compression 和位域掩码进行判断处理。