From e5295d640513da5926ca06ee6ecad1d466e0e3ce Mon Sep 17 00:00:00 2001 From: Frost-ZX Date: Fri, 12 Jun 2026 14:55:05 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E6=B7=BB=E5=8A=A0=20bmp=5Fto=5Fframeb?= =?UTF-8?q?uffer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- C++/bmp_to_framebuffer.cpp | 288 +++++++++++++++++++++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 C++/bmp_to_framebuffer.cpp diff --git a/C++/bmp_to_framebuffer.cpp b/C++/bmp_to_framebuffer.cpp new file mode 100644 index 0000000..d2f96be --- /dev/null +++ b/C++/bmp_to_framebuffer.cpp @@ -0,0 +1,288 @@ +// 将 BMP 图片写入到 FrameBuffer +// 示例:sudo bmp_to_framebuffer ./image.bmp /dev/fb0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma pack(push, 1) +struct BMPFileHeader { + uint16_t type; // 文件类型 "BM" (0x4D42) + uint32_t size; // 文件大小 + uint16_t reserved1; // 保留 + uint16_t reserved2; // 保留 + uint32_t offset; // 像素数据偏移量 +}; + +struct BMPInfoHeader { + uint32_t size; // 信息头大小 + int32_t width; // 宽度 + int32_t height; // 高度 (正数表示倒序,负数表示正序) + uint16_t planes; // 色彩平面数 + uint16_t bitCount; // 位深度 (1, 4, 8, 16, 24, 32) + uint32_t compression; // 压缩类型 (0:BI_RGB, 1:BI_RLE8, 2:BI_RLE4, 3:BI_BITFIELDS) + uint32_t sizeImage; // 像素数据大小 + int32_t xPelsPerMeter; // 水平分辨率 + int32_t yPelsPerMeter; // 垂直分辨率 + uint32_t clrUsed; // 使用的颜色数 + uint32_t clrImportant; // 重要颜色数 +}; +#pragma pack(pop) + +// 辅助结构:用于解析 BI_BITFIELDS 掩码 +struct BitFieldInfo { + uint32_t mask; + int shift; + int width; +}; + +// 计算掩码的移位和宽度 +BitFieldInfo getBitFieldInfo(uint32_t mask) { + BitFieldInfo info{}; + info.mask = mask; + if (mask == 0) return info; + + // 计算右移位数 + info.shift = 0; + uint32_t temp = mask; + while ((temp & 1) == 0) { + temp >>= 1; + info.shift++; + } + + // 计算位宽 + info.width = 0; + while ((temp & 1) == 1) { + temp >>= 1; + info.width++; + } + return info; +} + +// RLE8 解压函数 +std::vector decodeRLE8(const uint8_t* data, size_t dataSize, int width, int height) { + std::vector output(width * height, 0); + int x = 0, y = 0; + size_t pos = 0; + + while (pos < dataSize) { + uint8_t count = data[pos++]; + if (count == 0) { + uint8_t escape = data[pos++]; + if (escape == 0) { // 行结束 + x = 0; + y++; + } else if (escape == 1) { // 图像结束 + break; + } else if (escape == 2) { // Delta 偏移 + x += data[pos++]; + y += data[pos++]; + } else { // 绝对模式 + for (int i = 0; i < escape; i++) { + if (x < width && y < height) { + output[y * width + x] = data[pos]; + x++; + } + pos++; + } + if (escape % 2 != 0) pos++; // 2字节对齐 + } + } else { // 编码模式 + uint8_t color = data[pos++]; + for (int i = 0; i < count; i++) { + if (x < width && y < height) { + output[y * width + x] = color; + x++; + } + } + } + } + return output; +} + +// RLE4 解压函数 +std::vector decodeRLE4(const uint8_t* data, size_t dataSize, int width, int height) { + std::vector output(width * height, 0); + int x = 0, y = 0; + size_t pos = 0; + + while (pos < dataSize) { + uint8_t count = data[pos++]; + if (count == 0) { + uint8_t escape = data[pos++]; + if (escape == 0) { // 行结束 + x = 0; + y++; + } else if (escape == 1) { // 图像结束 + break; + } else if (escape == 2) { // Delta 偏移 + x += data[pos++]; + y += data[pos++]; + } else { // 绝对模式 + bool highNibble = true; + uint8_t byte = 0; + for (int i = 0; i < escape; i++) { + if (highNibble) byte = data[pos++]; + uint8_t idx = highNibble ? (byte >> 4) : (byte & 0x0F); + if (x < width && y < height) { + output[y * width + x] = idx; + x++; + } + highNibble = !highNibble; + } + // 对齐检查:如果读取的字节数是奇数,跳过填充 + if (((escape + 1) / 2) % 2 != 0) pos++; + } + } else { // 编码模式 + uint8_t byte = data[pos++]; + bool highNibble = true; + for (int i = 0; i < count; i++) { + uint8_t idx = highNibble ? (byte >> 4) : (byte & 0x0F); + if (x < width && y < height) { + output[y * width + x] = idx; + x++; + } + highNibble = !highNibble; + } + } + } + return output; +} + +int main(int argc, char* argv[]) { + const char* filename = "image.bmp"; + const char* fbname = "/dev/fb0"; + if (argc > 1) filename = argv[1]; + if (argc > 2) fbname = argv[2]; + + // 1. 打开 Framebuffer + int fbfd = open(fbname, O_RDWR); + if (fbfd == -1) { perror("打开 fb 失败"); return 1; } + + // 2. 获取屏幕信息 + struct fb_var_screeninfo vinfo; + struct fb_fix_screeninfo finfo; + ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo); + ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo); + + uint8_t* fbp = (uint8_t*)mmap(nullptr, finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0); + if (fbp == MAP_FAILED) { perror("内存映射失败"); close(fbfd); return 1; } + + // 3. 读取 BMP 文件 + std::ifstream file(filename, std::ios::binary); + if (!file) { std::cerr << "无法打开文件" << std::endl; munmap(fbp, finfo.smem_len); close(fbfd); return 1; } + + BMPFileHeader fileHeader; + BMPInfoHeader infoHeader; + file.read(reinterpret_cast(&fileHeader), sizeof(fileHeader)); + file.read(reinterpret_cast(&infoHeader), sizeof(infoHeader)); + + if (fileHeader.type != 0x4D42) { std::cerr << "非 BMP 文件" << std::endl; return 1; } + + int imgWidth = infoHeader.width; + int imgHeight = abs(infoHeader.height); + bool isTopDown = (infoHeader.height < 0); + + // 4. 读取颜色表 (如果是索引色) + std::vector colorTable; + if (infoHeader.bitCount <= 8) { + int colorCount = infoHeader.clrUsed == 0 ? (1 << infoHeader.bitCount) : infoHeader.clrUsed; + colorTable.resize(colorCount); + file.read(reinterpret_cast(colorTable.data()), colorCount * 4); + } + + // 5. 读取并解压像素数据 + std::vector pixelData; // 存储解压后的索引或原始数据 + bool isIndexed = (infoHeader.bitCount <= 8); + + file.seekg(fileHeader.offset, std::ios::beg); + std::vector rawData(infoHeader.sizeImage); + file.read(reinterpret_cast(rawData.data()), rawData.size()); + + if (infoHeader.compression == 0) { // BI_RGB (无压缩) + int rowSize = ((imgWidth * infoHeader.bitCount + 31) / 32) * 4; + pixelData.resize(rowSize * imgHeight); + file.seekg(fileHeader.offset, std::ios::beg); + file.read(reinterpret_cast(pixelData.data()), pixelData.size()); + } + else if (infoHeader.compression == 1) { // BI_RLE8 + pixelData = decodeRLE8(rawData.data(), rawData.size(), imgWidth, imgHeight); + } + else if (infoHeader.compression == 2) { // BI_RLE4 + pixelData = decodeRLE4(rawData.data(), rawData.size(), imgWidth, imgHeight); + } + else { + std::cerr << "不支持的压缩格式: " << infoHeader.compression << std::endl; + return 1; + } + + // 6. 绘制到 Framebuffer + int drawW = std::min(imgWidth, (int)vinfo.xres); + int drawH = std::min(imgHeight, (int)vinfo.yres); + + for (int y = 0; y < drawH; ++y) { + // 处理 BMP 上下翻转 + int srcY = isTopDown ? y : (imgHeight - 1 - y); + + for (int x = 0; x < drawW; ++x) { + uint8_t r, g, b, a = 255; + + if (isIndexed) { + uint8_t idx = 0; + // 从解压数据或无压缩数据中获取索引 + if (infoHeader.compression == 1 || infoHeader.compression == 2) { + idx = pixelData[srcY * imgWidth + x]; + } else { + int rowSize = ((imgWidth * infoHeader.bitCount + 31) / 32) * 4; + uint8_t* srcRow = pixelData.data() + srcY * rowSize; + if (infoHeader.bitCount == 8) { + idx = srcRow[x]; + } else if (infoHeader.bitCount == 4) { + idx = (x % 2 == 0) ? (srcRow[x/2] >> 4) : (srcRow[x/2] & 0x0F); + } + } + // 从颜色表获取 RGB (BMP 颜色表格式为 BGRX) + uint32_t color = colorTable[idx]; + b = color & 0xFF; + g = (color >> 8) & 0xFF; + r = (color >> 16) & 0xFF; + } else { + // 直接读取 RGB (24/32位) + int rowSize = ((imgWidth * infoHeader.bitCount + 31) / 32) * 4; + uint8_t* srcRow = pixelData.data() + srcY * rowSize; + if (infoHeader.bitCount == 24) { + b = srcRow[x*3+0]; g = srcRow[x*3+1]; r = srcRow[x*3+2]; + } else if (infoHeader.bitCount == 32) { + b = srcRow[x*4+0]; g = srcRow[x*4+1]; r = srcRow[x*4+2]; a = srcRow[x*4+3]; + } + } + + // 写入 Framebuffer (假设是 32位 BGRA 模式,这是最常见的) + long fbIdx = (x + vinfo.xoffset) * (vinfo.bits_per_pixel/8) + + (y + vinfo.yoffset) * finfo.line_length; + + if (vinfo.bits_per_pixel == 32) { + fbp[fbIdx+0] = b; + fbp[fbIdx+1] = g; + fbp[fbIdx+2] = r; + fbp[fbIdx+3] = a; + } else if (vinfo.bits_per_pixel == 16) { // RGB565 + uint16_t c = ((r>>3)<<11) | ((g>>2)<<5) | (b>>3); + *(uint16_t*)(&fbp[fbIdx]) = c; + } + } + } + + // 清理 + munmap(fbp, finfo.smem_len); + close(fbfd); + std::cout << "显示完成" << std::endl; + return 0; +}