You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于X11与jpeglib的C语言Linux图片查看器JPEG显示异常排查

修复X11+JPEG图片查看器的显示异常

一、解决图像显示不全+重复片段问题

这个问题核心是X11的行对齐要求未满足,其次可能是窗口/图像尺寸不匹配、像素深度不统一。

1. 处理X11的行对齐

X11的XPutImage要求图像每行字节数(stride)必须对齐到4字节边界(针对24/32位深度)。jpeglib读取的RGB数据每行是width*3字节,若这个值不是4的倍数,会导致后续行数据偏移,出现重复错位。

修复代码:

// 计算对齐后的行字节数(24位RGB,对齐到4字节)
int stride = (img_width * 3 + 3) & ~3;
// 创建对齐后的图像缓冲区
JSAMPLE* aligned_buf = malloc(stride * img_height);

// 读取JPEG行并复制到对齐缓冲区
while (cinfo.output_scanline < cinfo.output_height) {
    JSAMPLE* row = jpeg_read_scanline(&cinfo, NULL);
    int dest_idx = cinfo.output_scanline * stride;
    memcpy(aligned_buf + dest_idx, row, img_width * 3);
    // 填充对齐的空白字节(可填0)
    memset(aligned_buf + dest_idx + img_width*3, 0, stride - img_width*3);
}

创建XImage时指定正确的stride:

XImage* ximg = XCreateImage(display, vinfo.visual, vinfo.depth, ZPixmap, 0, 
                            (char*)aligned_buf, img_width, img_height, 32, stride);

2. 匹配窗口与图像尺寸

确保创建窗口时使用JPEG实际的宽高:

// 读取JPEG后获取真实尺寸
int img_width = cinfo.image_width;
int img_height = cinfo.image_height;
// 创建对应尺寸的窗口
Window win = XCreateSimpleWindow(display, root, 0, 0, img_width, img_height, 
                                 1, black_pixel, white_pixel);

3. 统一图像深度

X11窗口和XImage的深度必须一致,先获取窗口的visual信息:

XVisualInfo vinfo;
XMatchVisualInfo(display, DefaultScreen(display), 24, TrueColor, &vinfo);
// 创建XImage时使用该depth
XImage* ximg = XCreateImage(display, vinfo.visual, vinfo.depth, ZPixmap, 0, ...);

如果窗口是24位深度,不要用带alpha的32位格式,避免像素解析错误。

二、解决宽高比>1时图像旋转90度问题

这个问题是JPEG的EXIF方向标记未被处理导致的。竖拍照片通常会在EXIF中标记旋转方向,jpeglib默认不会解析这个标记,导致读取的宽高是交换后的,直接绘图就会显示旋转。

1. 解析EXIF方向标记

手动读取JPEG文件头中的EXIF方向信息(简化实现):

int get_jpeg_orientation(const char* path) {
    FILE* fp = fopen(path, "rb");
    if (!fp) return 1;
    unsigned char buf[20];
    fread(buf, 1, 20, fp);
    int orientation = 1; // 默认正常
    // 检查是否有EXIF段
    if (buf[0] == 0xFF && buf[1] == 0xD8 && buf[2] == 0xFF && buf[3] == 0xE1) {
        int exif_len = (buf[4] << 8) + buf[5];
        fseek(fp, 6, SEEK_SET);
        unsigned char* exif_buf = malloc(exif_len);
        fread(exif_buf, 1, exif_len, fp);
        // 查找Orientation标签(0x0112)
        for (int i = 0; i < exif_len - 4; i++) {
            if (exif_buf[i] == 0x01 && exif_buf[i+1] == 0x12) {
                orientation = exif_buf[i+3];
                break;
            }
        }
        free(exif_buf);
    }
    fclose(fp);
    return orientation;
}

2. 根据旋转标记调整图像数据

根据orientation值处理旋转:

int orientation = get_jpeg_orientation("test.jpg");
JSAMPLE* final_data = original_data;
int final_w = img_width, final_h = img_height;

if (orientation == 6) { // 顺时针90度
    // 交换宽高
    final_w = img_height;
    final_h = img_width;
    final_data = malloc(final_w * final_h * 3);
    // 旋转图像
    for (int y = 0; y < img_height; y++) {
        for (int x = 0; x < img_width; x++) {
            int orig_idx = (img_height - 1 - y) * img_width * 3 + x * 3;
            int new_idx = x * final_h * 3 + y * 3;
            final_data[new_idx] = original_data[orig_idx];
            final_data[new_idx+1] = original_data[orig_idx+1];
            final_data[new_idx+2] = original_data[orig_idx+2];
        }
    }
    free(original_data);
}
// orientation=8(逆时针90度)、orientation=3(180度)的处理逻辑类似

之后用final_data、final_w、final_h进行绘图即可。

3. 检查代码中的宽高顺序

确认创建窗口、XImage时没有把宽高参数写反,比如不要把img_height当作宽度传入。

内容的提问来源于stack exchange,提问作者user18419131

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 21:34:59