#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <sys/mman.h> | |
#include <sys/ioctl.h> | |
#include <linux/fb.h> | |
#pragma pack(1) | |
typedef struct { | |
unsigned short bfType; | |
unsigned int bfSize; | |
unsigned short bfReserved1; | |
unsigned short bfReserved2; | |
unsigned int bfOffBits; | |
} BMPFILEHEADER; | |
typedef struct { | |
unsigned int biSize; | |
int biWidth; | |
int biHeight; | |
unsigned short biPlanes; | |
unsigned short biBitCount; | |
unsigned int biCompression; | |
unsigned int biSizeImage; | |
int biXPelsPerMeter; | |
int biYPelsPerMeter; | |
unsigned int biClrUsed; | |
unsigned int biClrImportant; | |
} BMPINFOHEADER; | |
#pragma pack() | |
#define SWAP_ENDIAN_USHORT(value) ((unsigned short)((((unsigned short)(value) & 0xFF) << 8) | (((unsigned short)(value) & 0xFF00) >> 8))) | |
// Convert 24-bit RGB to 16-bit RGB565 | |
unsigned short rgb24_to_rgb565(unsigned char r, unsigned char g, unsigned char b) { | |
unsigned short color = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); | |
unsigned short swapped = SWAP_ENDIAN_USHORT(color); | |
return swapped; | |
} | |
int main(int argc, char *argv[]) { | |
FILE *fp = NULL; | |
unsigned char *line = NULL; | |
BMPFILEHEADER fileHeader; | |
BMPINFOHEADER infoHeader; | |
if (argc < 2) { | |
printf("No para. Please input bmp filepath.\n"); | |
return; | |
} | |
// Open BMP file | |
fp = fopen(argv[1], "rb"); | |
if (!fp) { | |
perror("Cannot open BMP file"); | |
return -1; | |
} | |
// Read BMP headers | |
if (fread(&fileHeader, sizeof(BMPFILEHEADER), 1, fp) != 1) { | |
perror("Failed to read file header"); | |
fclose(fp); | |
return -1; | |
} | |
printf("fileHeader: bfType=%x, bfSize=%u, bfOffBits=%u\n", | |
fileHeader.bfType, fileHeader.bfSize, fileHeader.bfOffBits); | |
if (fread(&infoHeader, sizeof(BMPINFOHEADER), 1, fp) != 1) { | |
perror("Failed to read info header"); | |
fclose(fp); | |
return -1; | |
} | |
printf("infoHeader: biSize=%u, biWidth=%d, biHeight=%d\n", | |
infoHeader.biSize, infoHeader.biWidth, infoHeader.biHeight); | |
printf("infoHeader: biPlanes=%u, biBitCount=%u, biCompression=%u\n", | |
infoHeader.biPlanes, infoHeader.biBitCount, infoHeader.biCompression); | |
printf("infoHeader: biSizeImage=%u, biXPelsPerMeter=%d, biYPelsPerMeter=%d\n", | |
infoHeader.biSizeImage, infoHeader.biXPelsPerMeter, infoHeader.biYPelsPerMeter); | |
printf("infoHeader: biClrUsed=%u, biClrImportant=%d\n", | |
infoHeader.biClrUsed, infoHeader.biClrImportant); | |
// Check BMP format | |
if (fileHeader.bfType != 0x4D42) { // ASCII code for "BM" | |
printf("Not a valid BMP file\n"); | |
fclose(fp); | |
return -1; | |
} | |
// Check bit depth | |
if (infoHeader.biBitCount != 24 && infoHeader.biBitCount != 16) { | |
printf("Only 16-bit or 24-bit BMP supported, current image is %d-bit\n", infoHeader.biBitCount); | |
fclose(fp); | |
return -1; | |
} | |
// Open framebuffer device | |
int fbfd = open("/dev/fb0", O_RDWR); | |
if (fbfd < 0) { | |
perror("Cannot open framebuffer device"); | |
fclose(fp); | |
return -1; | |
} | |
// Get screen information | |
struct fb_var_screeninfo vinfo; | |
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo) < 0) { | |
perror("Cannot get screen info"); | |
close(fbfd); | |
fclose(fp); | |
return -1; | |
} | |
// Check screen color depth | |
if (vinfo.bits_per_pixel != 16) { | |
printf("This program only supports 16-bit color depth display\n"); | |
close(fbfd); | |
fclose(fp); | |
return -1; | |
} | |
// Calculate screen size and map framebuffer | |
long screensize = vinfo.xres * vinfo.yres * 2; // 16-bit = 2 bytes | |
unsigned char *fbp = mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0); | |
if (fbp == MAP_FAILED) { | |
perror("Cannot map framebuffer"); | |
close(fbfd); | |
fclose(fp); | |
return -1; | |
} | |
// Move file pointer to image data | |
fseek(fp, fileHeader.bfOffBits, SEEK_SET); | |
// Prepare to read image data | |
unsigned short *fb16 = (unsigned short *)fbp; | |
int bytes_per_pixel = infoHeader.biBitCount / 8; | |
int padding = (4 - (infoHeader.biWidth * bytes_per_pixel) % 4) % 4; | |
line = malloc(infoHeader.biWidth * bytes_per_pixel + padding); | |
if (line == NULL) { | |
perror("Memory allocation failed"); | |
munmap(fbp, screensize); | |
close(fbfd); | |
fclose(fp); | |
return -1; | |
} | |
// Read and display image data | |
int y = 0; | |
for (y = infoHeader.biHeight - 1; y >= 0; y--) { | |
if (fread(line, infoHeader.biWidth * bytes_per_pixel + padding, 1, fp) != 1) { | |
printf("Failed to read image data\n"); | |
free(line); | |
munmap(fbp, screensize); | |
close(fbfd); | |
fclose(fp); | |
return -1; | |
} | |
int x = 0; | |
for (x = 0; x < infoHeader.biWidth; x++) { | |
unsigned short pixel; | |
int location = x + y * vinfo.xres; | |
if (infoHeader.biBitCount == 24) { | |
// 24-bit BMP (BGR format) | |
unsigned char b = line[x * 3]; | |
unsigned char g = line[x * 3 + 1]; | |
unsigned char r = line[x * 3 + 2]; | |
pixel = rgb24_to_rgb565(r, g, b); | |
} else { | |
// 16-bit BMP (already in RGB565 format) | |
pixel = *(unsigned short*)&line[x * 2]; | |
} | |
// Check if within display bounds | |
if (x < vinfo.xres && y < vinfo.yres) { | |
fb16[location] = pixel; | |
} | |
} | |
} | |
// To display | |
write(fbfd, "1", 1); | |
usleep(1000*100); | |
// Cleanup resources | |
free(line); | |
fclose(fp); | |
munmap(fbp, screensize); | |
close(fbfd); | |
printf("Image display completed\n"); | |
return 0; | |
} |