代码:
/* 标准输入输出头文件,提供文件操作和输入输出函数(如printf)*/
#include <stdio.h>
/* 文件控制操作头文件,提供文件打开模式(如O_RDWR)和文件控制函数 */
#include <fcntl.h>
/* 设备I/O控制头文件,提供ioctl函数用于设备控制 */
#include <sys/ioctl.h>
/* 内存映射操作头文件,提供mmap/munmap内存映射相关函数 */
#include <sys/mman.h>
/* Framebuffer设备驱动头文件,定义fb_var_screeninfo等结构体 */
#include <linux/fb.h>
/* 标准整数类型头文件,定义uint16_t、uint32_t等跨平台类型 */
#include <stdint.h>
/* 网络字节序转换头文件,提供htonl等字节序转换函数 */
#include <arpa/inet.h>
/* UNIX标准函数头文件,提供close、lseek等系统调用 */
#include <unistd.h>
/* 标准库头文件,提供exit等通用函数 */
#include <stdlib.h>
/* 文件状态头文件,提供fstat等文件信息函数 */
#include <sys/stat.h>
/*----------------------------------------------------------
全局变量定义
----------------------------------------------------------*/
char *fbp; // 映射到framebuffer的指针(使用char*便于字节级操作)
int fb_device; // Framebuffer设备文件描述符
struct fb_var_screeninfo vinfo; // 可变屏幕参数(分辨率、色深等)
struct fb_fix_screeninfo finfo; // 固定屏幕参数(物理地址、长度等)
long screensize; // 屏幕缓冲区总大小
/*----------------------------------------------------------
BMP文件头结构定义
----------------------------------------------------------*/
#pragma pack(push, 1) // 设置字节对齐为1字节,避免结构体填充
typedef struct {
uint16_t bfType; // 文件类型,必须为"BM"(0x4D42)
uint32_t bfSize; // 文件总大小(字节)
uint16_t bfReserved1; // 保留字段1
uint16_t bfReserved2; // 保留字段2
uint32_t bfOffBits; // 像素数据偏移量(从头开始到数据的字节数)
} BITMAPFILEHEADER;
typedef struct {
uint32_t biSize; // 本结构体大小(通常40字节)
int32_t biWidth;
int32_t biHeight;
uint16_t biPlanes;
uint16_t biBitCount;
uint32_t biCompression;
uint32_t biSizeImage;
int32_t biXPelsPerMeter;
int32_t biYPelsPerMeter;
uint32_t biClrUsed;
uint32_t biClrImportant;
} BITMAPINFOHEADER;
#pragma pack(pop) // 恢复默认字节对齐
// BMP显示函数
void show_bmp(const char* img_path) {
// 打开文件
int fd = open(img_path, O_RDONLY);
if (fd < 0) {
perror("Error opening BMP file");
return;
}
// 读取文件头
BITMAPFILEHEADER bmfh;
if (read(fd, &bmfh, sizeof(bmfh)) != sizeof(bmfh)) {
perror("Error reading BMP header");
close(fd);
return;
}
// 验证BMP格式
if (bmfh.bfType != 0x4D42) { // "BM"
printf("Not a valid BMP file\n");
close(fd);
return;
}
// 读取信息头
BITMAPINFOHEADER bmih;
if (read(fd, &bmih, sizeof(bmih)) != sizeof(bmih)) {
perror("Error reading BMP info");
close(fd);
return;
}
// 验证图像尺寸
if (bmih.biWidth != vinfo.xres || abs(bmih.biHeight) != vinfo.yres) {
printf("BMP尺寸不匹配: %dx%d,屏幕: %dx%d\n",
bmih.biWidth, abs(bmih.biHeight),
vinfo.xres, vinfo.yres);
close(fd);
return;
}
// 验证位深度
if (bmih.biBitCount != 24) {
printf("仅支持24位BMP格式\n");
close(fd);
return;
}
// 计算行填充字节
int row_size = ((bmih.biWidth * 3 + 3) & ~3);
uint8_t* row_buffer = malloc(row_size);
// 读取像素数据(BMP存储顺序为从下到上)
for (int y = bmih.biHeight > 0 ? (bmih.biHeight-1) : 0;
bmih.biHeight > 0 ? (y >= 0) : (y < vinfo.yres);
bmih.biHeight > 0 ? y-- : y++) {
lseek(fd, bmfh.bfOffBits + y * row_size, SEEK_SET);
read(fd, row_buffer, row_size);
// 屏幕Y坐标计算
int screen_y = bmih.biHeight > 0 ? (bmih.biHeight - 1 - y) : y;
for (int x = 0; x < vinfo.xres; x++) {
uint8_t* pixel = &row_buffer[x * 3];
uint32_t color = 0;
uint16_t color_16 = 0;
switch (vinfo.bits_per_pixel) {
case 16:
color_16 = ((pixel[2] >> 3) << 11) |
((pixel[1] >> 2) << 5) |
(pixel[0] >> 3);
break;
case 32:
color = (0xFF << 24) |
(pixel[2] << vinfo.red.offset) |
(pixel[1] << vinfo.green.offset) |
(pixel[0] << vinfo.blue.offset);
break;
default:
printf("不支持的像素格式\n");
free(row_buffer);
close(fd);
return;
}
// 修改location计算中的y为screen_y
long location = (x + vinfo.xoffset) * (vinfo.bits_per_pixel / 8)
+ (screen_y + vinfo.yoffset) * finfo.line_length;
if (vinfo.bits_per_pixel == 16) {
*((uint16_t*)(fbp + location)) = color_16;
} else {
*((uint32_t*)(fbp + location)) = color;
}
}
}
free(row_buffer);
close(fd);
}
int main(int argc, char *argv[]) {
// 参数检查
if (argc < 2) {
printf("用法: %s <图片路径>\n", argv[0]);
return 1;
}
// 1. 打开FrameBuffer设备
fb_device = open("/dev/fb0", O_RDWR);
if (fb_device == -1) {
perror("Error opening framebuffer");
return 1;
}
// 2. 获取屏幕信息
if (ioctl(fb_device, FBIOGET_FSCREENINFO, &finfo)) {
perror("Error reading fixed info");
close(fb_device);
return 1;
}
if (ioctl(fb_device, FBIOGET_VSCREENINFO, &vinfo)) {
perror("Error reading variable info");
close(fb_device);
return 1;
}
// 3. 屏幕信息打印
printf("\nFrameBuffer 信息:\n");
printf("可视分辨率: %dx%d\n", vinfo.xres, vinfo.yres);
printf("虚拟分辨率: %dx%d\n", vinfo.xres_virtual, vinfo.yres_virtual);
printf("像素深度: %d bits/像素\n", vinfo.bits_per_pixel);
printf("颜色格式: R%u-%u G%u-%u B%u-%u\n", vinfo.red.offset, vinfo.red.length,
vinfo.green.offset, vinfo.green.length,
vinfo.blue.offset, vinfo.blue.length);
printf("行长度: %d 字节\n\n", finfo.line_length);
// 4. 计算显存大小并映射到用户空间
screensize = vinfo.xres_virtual * vinfo.yres_virtual * vinfo.bits_per_pixel / 8;
fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb_device, 0);
if ((void *)fbp == MAP_FAILED) {
perror("Error mmap framebuffer");
close(fb_device);
return 1;
}
show_bmp(argv[1]);
sleep(3);
// 6. 释放资源
munmap(fbp, screensize);
close(fb_device);
return 0;
}
运行效果:
RGB屏幕显示neko.bmp图像。