基于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
相关产品推荐
相关产品推荐

