本文最后更新于 165 天前,其中的信息可能已经有所发展或是发生改变。
/**
* @filename: showbmp.c
* @brief: showbmp
* @author: philia
* @date: 2026/3/15
* @version: ver1.0
* @note none
* CopyRight (c) 2026 philia All Right Reseverd
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <stdlib.h>
// ...(保持你的结构体定义不变)...
#pragma pack(push, 1) // 禁用内存对齐
typedef struct
{
uint16_t bfType; // 文件标识,必须为 "BM"(0x4D42)
uint32_t bfSize; // 文件总大小(字节)
uint16_t bfReserved1; // 保留字段(必须为0)
uint16_t bfReserved2; // 保留字段(必须为0)
uint32_t bfOffBits; // 像素数据偏移量(从文件头到像素数据的字节数)
} BITMAPFILEHEADER;
#pragma pack(pop)
typedef struct
{
uint32_t biSize; // 本结构体大小(通常为40)
int32_t biWidth; // 图像宽度(像素)
int32_t biHeight; // 图像高度(像素),正数表示从下到上存储
uint16_t biPlanes; // 颜色平面数(必须为1)
uint16_t biBitCount; // 每像素位数(1/4/8/16/24/32)
uint32_t biCompression; // 压缩方式(0表示不压缩)
uint32_t biSizeImage; // 像素数据大小(字节),压缩时需填写
int32_t biXPelsPerMeter; // 水平分辨率(像素/米)
int32_t biYPelsPerMeter; // 垂直分辨率(像素/米)
uint32_t biClrUsed; // 实际使用的调色板颜色数(0表示全部)
uint32_t biClrImportant; // 重要颜色数(0表示所有颜色均重要)
} BITMAPINFOHEADER;
int main(int argc, const char *argv[])
{
// 【修复1】:必须校验传入的参数个数!
if (argc != 4) {
printf("用法错误!正确格式: %s <bmp文件> <起始X> <起始Y>\n", argv[0]);
return -1;
}
// 打开bmp文件
FILE *bmp = fopen(argv[1], "rb");
if (bmp == NULL) {
printf("bmp文件打开失败\n");
return -1;
}
// 读取bmp文件信息
BITMAPFILEHEADER fileheader;
BITMAPINFOHEADER infoheader;
fread(&fileheader, 14, 1, bmp);
fread(&infoheader, 40, 1, bmp);
int bmp_width = infoheader.biWidth;
int bmp_height = infoheader.biHeight;
// 处理高度为负数的情况(有些BMP是从上倒下存的)
if (bmp_height < 0) bmp_height = -bmp_height;
// 获取输入的起始坐标
int x_start = atoi(argv[2]);
int y_start = atoi(argv[3]);
// 【修复3】:计算带补齐的行字节数,并用此大小分配内存
int valid_bytes_per_row = bmp_width * 3;
int total_bytes_per_row = ((valid_bytes_per_row + 3) / 4) * 4;
int padding_bytes = total_bytes_per_row - valid_bytes_per_row;
// 图片数据的总大小(包含废字节)
int img_data_size = total_bytes_per_row * bmp_height;
// 使用 malloc 在堆区分配大内存,防止栈溢出段错误
unsigned char *bmp_buf = (unsigned char *)malloc(img_data_size);
if (bmp_buf == NULL) {
printf("内存分配失败\n");
fclose(bmp);
return -1;
}
// 将文件指针移动到真正的像素数据开始处(跳过中间可能的调色板等杂注)
fseek(bmp, fileheader.bfOffBits, SEEK_SET);
// 一次性读取包含废字节在内的所有数据
fread(bmp_buf, img_data_size, 1, bmp);
// 打开lcd屏幕
int fd = open("/dev/ubuntu_lcd", O_RDWR);
if (fd == -1) {
printf("lcd屏幕打开失败!\n");
free(bmp_buf);
fclose(bmp);
return -1;
}
// 申请内存空间内存映射
int *lcd_buf = (int *)mmap(NULL, 800 * 480 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (lcd_buf == MAP_FAILED) {
perror("mmap");
free(bmp_buf);
fclose(bmp);
close(fd);
return -1;
}
// 0xFF 也就是按字节全部填满 1,拼成 32 位就是 0xFFFFFFFF (纯白)
memset(lcd_buf, 0xFFFF0000, 800 * 480 * 4);
// 【修复2】:只保留一个统一的写入循环
int i = 0;
int data = 0;
// 开始映射:Y轴倒序,从图片的最后一行画到第一行
for (int y = y_start + bmp_height - 1; y >= y_start; y--)
{
for (int x = x_start; x < x_start + bmp_width; x++)
{
// 保护伞:只有在屏幕坐标范围内的像素才允许写入显存
if (x >= 0 && x < 800 && y >= 0 && y < 480)
{
data |= bmp_buf[i]; // B
data |= bmp_buf[i + 1] << 8; // G
data |= bmp_buf[i + 2] << 16; // R
lcd_buf[800 * y + x] = data;
}
i += 3; // 处理完一个像素
data = 0;
}
// 一行有效数据读完了,跳过文件里的废字节对齐
i += padding_bytes;
}
// 收尾工作:释放所有资源
free(bmp_buf);
fclose(bmp);
close(fd);
munmap(lcd_buf, 800 * 480 * 4);
printf("图片显示成功!\n");
return 0;
}



好