如何使用XLib将Windows格式Bitmap文件读取为Pixmap对象
实现方案总览
整个操作分为BMP文件解析、像素格式适配、XLib Pixmap创建三个核心阶段,不需要引入额外重型依赖,以下是具体实现步骤和示例代码:
1 前置依赖说明
- 仅依赖X11核心库即可实现,编译时添加
-lX11参数链接X11库 - 以下示例为原生实现BMP解析,也可以使用libbmp等轻量库简化解析逻辑
2 具体实现步骤
2.1 解析BMP文件头与像素数据
Windows BMP的核心结构包含两个固定头:
BITMAPFILEHEADER:14字节,包含文件标识(固定为0x4D42,对应字符串"BM")、文件总大小、像素数据的偏移地址BITMAPINFOHEADER:常用版本为40字节,包含位图宽高、位深、压缩方式、像素数据总大小等信息
注意:BMP像素存储为行倒序,文件里的第一行像素对应位图最底部的内容;每行像素数据按4字节对齐,不足的位置用0填充。24位BMP像素顺序为BGR无Alpha通道,32位BMP像素顺序为BGRA。
2.2 适配X11像素格式
X11 Pixmap的像素格式和当前显示设备的Visual强绑定,默认24位真彩模式下像素顺序为RGB,32位带Alpha模式下为ARGB,需要对BMP读取到的像素做顺序转换:
- 24位BMP:交换每个像素的B和R分量位置
- 32位BMP:交换每个像素的B和R分量位置,同时调整Alpha通道位置适配当前Visual的要求
2.3 创建Pixmap并写入像素数据
调用XLib的XCreatePixmap接口创建对应尺寸的Pixmap对象,再通过XPutImage把转换完成的像素数据写入Pixmap即可。
3 完整代码示例
#include <X11/Xlib.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> // BMP头结构定义,禁用结构体对齐 typedef struct { uint16_t type; uint32_t size; uint16_t reserved1; uint16_t reserved2; uint32_t off_bits; } __attribute__((packed)) BitMapFileHeader; typedef struct { uint32_t size; int32_t width; int32_t height; uint16_t planes; uint16_t bit_count; uint32_t compression; uint32_t size_image; int32_t x_pels_per_meter; int32_t y_pels_per_meter; uint32_t clr_used; uint32_t clr_important; } __attribute__((packed)) BitMapInfoHeader; /** * @param dpy X11 Display连接句柄 * @param root 根窗口句柄 * @param bmp_path BMP文件路径 * @return 转换完成的Pixmap对象,创建失败返回0 */ Pixmap bmp_to_pixmap(Display *dpy, Window root, const char *bmp_path) { FILE *fp = fopen(bmp_path, "rb"); if (!fp) return 0; BitMapFileHeader file_hdr; BitMapInfoHeader info_hdr; // 读取BMP文件头 fread(&file_hdr, sizeof(BitMapFileHeader), 1, fp); if (file_hdr.type != 0x4D42) { fclose(fp); return 0; } // 读取BMP信息头 fread(&info_hdr, sizeof(BitMapInfoHeader), 1, fp); // 示例仅支持无压缩的24/32位BMP if ((info_hdr.bit_count != 24 && info_hdr.bit_count != 32) || info_hdr.compression != 0) { fclose(fp); return 0; } int width = info_hdr.width; int height = abs(info_hdr.height); // 计算BMP行步长(4字节对齐) int stride = (width * info_hdr.bit_count / 8 + 3) & ~3; uint8_t *bmp_data = malloc(stride * height); fseek(fp, file_hdr.off_bits, SEEK_SET); fread(bmp_data, stride * height, 1, fp); fclose(fp); // 获取X11默认24位真彩视觉信息 XVisualInfo vinfo; XMatchVisualInfo(dpy, DefaultScreen(dpy), 24, TrueColor, &vinfo); XImage *img = XCreateImage(dpy, vinfo.visual, 24, ZPixmap, 0, malloc(width * height * 4), width, height, 32, 0); // 转换像素格式,处理BMP行倒序 for (int y = 0; y < height; y++) { int bmp_y = info_hdr.height > 0 ? (height - 1 - y) : y; uint8_t *bmp_row = bmp_data + bmp_y * stride; for (int x = 0; x < width; x++) { uint8_t b = bmp_row[x * info_hdr.bit_count/8 + 0]; uint8_t g = bmp_row[x * info_hdr.bit_count/8 + 1]; uint8_t r = bmp_row[x * info_hdr.bit_count/8 + 2]; uint32_t pixel = (r << 16) | (g << 8) | b; XPutPixel(img, x, y, pixel); } } // 创建Pixmap并写入像素数据 Pixmap pix = XCreatePixmap(dpy, root, width, height, 24); GC gc = XCreateGC(dpy, pix, 0, NULL); XPutImage(dpy, pix, gc, img, 0, 0, 0, 0, width, height); // 释放临时资源 XFreeGC(dpy, gc); XDestroyImage(img); free(bmp_data); return pix; } // 编译命令:gcc 源文件名.c -o 输出文件名 -lX11
4 注意事项
- 上述代码默认适配24位真彩X11显示,如需支持32位带Alpha通道的Pixmap,调整
XMatchVisualInfo的位深参数和像素转换逻辑即可 - 示例仅支持无压缩BMP,如需支持RLE压缩的BMP需要额外实现压缩解码逻辑
- Pixmap使用完成后需要调用
XFreePixmap释放X服务器端的资源
内容的提问来源于stack exchange,提问作者Stewart Tunbridge
相关产品推荐
相关产品推荐

