設計一個將24位色深的bmp圖片顯示到lcd屏幕上,可移植,該程序自己寫過之後,又經過了ai的完善和修復後,自己又再次添加一些語句註釋
/****************************************************************************
*
* file name: 2025-07-30_filecopy.c
* author : 15515376695@163.com
* date : 2025-07-30
* function : 該程序實現BMP圖片信息的讀取,且顯示在lcd屏幕上
* note : None
* CopyRight (c) 2025 15515376695@163.com Right Reseverd
*
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#pragma pack(1)
// BMP文件頭(14字節)
typedef struct {
unsigned short bfType; // 必須為0x4D42("BM"標識)
unsigned int bfSize; // 文件總大小(字節)
unsigned short bfReserved1; // 保留字段,0
unsigned short bfReserved2; // 保留字段,0
unsigned int bfOffBits; // 像素數據偏移量(從文件頭開始)
} BITMAPFILEHEADER;
// BMP信息頭(40字節,Windows格式)
typedef struct {
unsigned int biSize; // 信息頭大小(40字節)
int biWidth; // 圖像寬度(像素)
int biHeight; // 圖像高度(像素,負數表示從上到下存儲)
unsigned short biPlanes; // 必須為1
unsigned short biBitCount; // 色深(每像素位數)
unsigned int biCompression;// 壓縮方式(0表示不壓縮)
unsigned int biSizeImage; // 像素數據大小(字節,0表示不壓縮時可忽略)
int biXPelsPerMeter; // 水平分辨率(像素/米)
int biYPelsPerMeter; // 垂直分辨率(像素/米)
unsigned int biClrUsed; // 使用的顏色數(0表示全部)
unsigned int biClrImportant; // 重要顏色數(0表示全部)
} BITMAPINFOHEADER;
#pragma pack()
int main(int argc, char const *argv[]) {
// 1. 檢查命令行參數
if (argc != 2) {
fprintf(stderr, "用法: %s <24位無壓縮BMP文件路徑>\n", argv[0]);
return -1;
}
// 2. 打開BMP文件並驗證合法性
FILE *bmp_fp = fopen(argv[1], "rb");
if (bmp_fp == NULL) {
perror("無法打開BMP文件");
return -1;
}
// 3. 讀取並驗證BMP文件頭
BITMAPFILEHEADER file_header;
if (fread(&file_header, sizeof(BITMAPFILEHEADER), 1, bmp_fp) != 1) {
perror("讀取BMP文件頭失敗");
fclose(bmp_fp);
return -1;
}
if (file_header.bfType != 0x4D42) { // "BM"的十六進制表示
fprintf(stderr, "錯誤:不是BMP文件(標識不符)\n");
fclose(bmp_fp);
return -1;
}
// 4. 讀取並驗證BMP信息頭
BITMAPINFOHEADER info_header;
if (fread(&info_header, sizeof(BITMAPINFOHEADER), 1, bmp_fp) != 1) {
perror("讀取BMP信息頭失敗");
fclose(bmp_fp);
return -1;
}
// 僅支持24位色、不壓縮的BMP
if (info_header.biBitCount != 24) {
fprintf(stderr, "錯誤:僅支持24位色BMP(當前色深:%d位)\n", info_header.biBitCount);
fclose(bmp_fp);
return -1;
}
if (info_header.biCompression != 0) {
fprintf(stderr, "錯誤:僅支持無壓縮BMP(當前壓縮方式:%d)\n", info_header.biCompression);
fclose(bmp_fp);
return -1;
}
// 5. 獲取BMP寬高(處理高度可能為負數的情況)
int bmp_width = info_header.biWidth;
int bmp_height = abs(info_header.biHeight); // 取絕對值(高度符號表示存儲方向)
printf("BMP信息:寬=%dpx,高=%dpx,像素數據偏移量=%d字節\n",
bmp_width, bmp_height, file_header.bfOffBits);
// 6. 計算BMP每行實際字節數(必須是4的倍數,含填充字節)
int line_bytes = (bmp_width * 3 + 3) / 4 * 4; // 向上取4的倍數
int pixel_data_size = line_bytes * bmp_height; // 總像素數據大小(含填充)
// 7. 分配內存存儲像素數據(堆內存,避免棧溢出)
unsigned char *bmp_pixels = malloc(pixel_data_size);//pixels是像素的意思
if (bmp_pixels == NULL) {
perror("內存分配失敗");
fclose(bmp_fp);
return -1;
}
// 8. 定位到像素數據位置並讀取
if (fseek(bmp_fp, file_header.bfOffBits, SEEK_SET) != 0) {
perror("定位到像素數據失敗");
free(bmp_pixels);
fclose(bmp_fp);
return -1;
}
if (fread(bmp_pixels, 1, pixel_data_size, bmp_fp) != pixel_data_size) {
perror("讀取像素數據失敗");
free(bmp_pixels);
fclose(bmp_fp);
return -1;
}
fclose(bmp_fp); // 像素數據已讀取,關閉BMP文件
// 9. 打開LCD設備(framebuffer)
int lcd_fd = open("/dev/fb0", O_RDWR);
if (lcd_fd == -1) {
perror("無法打開LCD設備(/dev/fb0)");
free(bmp_pixels);
return -1;
}
// 10. 內存映射LCD(假設LCD分辨率與BMP一致,實際可通過ioctl獲取)
int lcd_size = bmp_width * bmp_height * 4; // 每個像素4字節(ARGB)
int *lcd_mp = (int *)mmap(NULL, lcd_size, PROT_READ | PROT_WRITE, MAP_SHARED, lcd_fd, 0);
if (lcd_mp == MAP_FAILED) {
perror("LCD內存映射失敗");
close(lcd_fd);
free(bmp_pixels);
return -1;
}
// 11. 像素轉換與顯示(處理BMP存儲方向)
for (int y = 0; y < bmp_height; y++) {
// BMP高度為正數時,像素數據從上到下存儲的是圖像的底部到頂部,需翻轉y軸
int bmp_y = (info_header.biHeight > 0) ? (bmp_height - 1 - y) : y;
//條件 ? 表達式1 : 表達式2 //實現了行索引的翻轉
//意思是:如果 “條件” 為真(非 0),就返回 “表達式 1” 的結果;否則返回 “表達式 2” 的結果。
for (int x = 0; x < bmp_width; x++) {
// 計算BMP每個像素在內存中所對應的位置(跳過每行填充字節)
int bmp_idx = bmp_y * line_bytes + x * 3;
// BMP像素格式:BGR,轉換為LCD的ARGB(Alpha=0xFF表示不透明)
unsigned char b = bmp_pixels[bmp_idx];//blue
unsigned char g = bmp_pixels[bmp_idx + 1];//green
unsigned char r = bmp_pixels[bmp_idx + 2];//red
//進行偏移成argb的色彩與lcd對應
int argb = 0xFF000000 | (r << 16) | (g << 8) | b; // 0xFF為Alpha通道(不透明)
// 寫入LCD對應位置
lcd_mp[y * bmp_width + x] = argb;
}
}
// 12. 清理資源
munmap(lcd_mp, lcd_size);
close(lcd_fd);
free(bmp_pixels);
printf("圖像顯示完成\n");
return 0;
}