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

使用Rust主流JPEG解码器解码后图像显示异常求助

问题根源与修复方案

你的代码主要存在两个核心问题,导致图像显示单色且损坏:

1. 像素格式不匹配

jpeg-decoder解码后的像素数据是RGB格式(每个像素3字节,顺序为R→G→B),但你调用CreateBitmap时指定了32位(4字节/像素)的位图格式。Windows GDI的32位位图要求像素为BGRA/BGRX格式(每个像素4字节,顺序为B→G→R→A/X,X为填充字节)。直接将3字节的RGB数据当作4字节的32位位图数据使用,会导致颜色通道错位、数据读取偏移,最终出现颜色混乱和图像损坏。

2. 行存储顺序不一致

GDI位图的像素数据是从图像底部第一行开始存储,而jpeg-decoder解码出的像素是从图像顶部第一行开始存储,如果不处理行顺序,图像会上下颠倒(这也是你当前显示异常的潜在因素之一)。


修复后的完整代码

use jpeg_decoder as jpeg;
use winsafe::{self, co, BITMAP, POINT, SIZE, HWND, HDC};

fn show_img(img_data: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
    let buff = std::io::Cursor::new(img_data);
    let mut decoder = jpeg::Decoder::new(buff);
    let pixels = decoder.decode().expect("failed to decode image");
    let metadata = decoder.info().unwrap();
    let width = metadata.width as i32;
    let height = metadata.height as i32;

    // 转换RGB为GDI要求的BGRA格式,同时反转行顺序以匹配GDI存储规则
    let mut bmp_data = Vec::with_capacity((width * height * 4) as usize);
    // 从最后一行开始遍历,反转行顺序
    for y in (0..height).rev() {
        let row_start = (y * width * 3) as usize;
        let row_end = row_start + (width * 3) as usize;
        let row_rgb = &pixels[row_start..row_end];
        
        // 将每个RGB三元组转为BGRA四元组(alpha设为不透明)
        for rgb_chunk in row_rgb.chunks_exact(3) {
            let r = rgb_chunk[0];
            let g = rgb_chunk[1];
            let b = rgb_chunk[2];
            bmp_data.extend_from_slice(&[b, g, r, 0xFF]);
        }
    }

    // 创建符合GDI要求的32位位图
    let h_bmp = HBITMAP::CreateBitmap(
        SIZE::new(width, height),
        1,
        32,
        bmp_data.as_mut_ptr(),
    )?;

    let mut bitmap = BITMAP {
        bmType: 0,
        bmWidth: 0,
        bmHeight: 0,
        bmWidthBytes: 0,
        bmPlanes: 0,
        bmBitsPixel: 0,
        bmBits: std::ptr::null_mut(),
    };
    h_bmp.GetObject(&mut bitmap)?;

    let h_dc = HDC::NULL.CreateCompatibleDC()?;
    let old_bmp = h_dc.SelectObject(&h_bmp)?;

    let dhwnd = HWND::GetDesktopWindow();
    let h_ddc = dhwnd.GetDC()?;
    let desk_rect = dhwnd.GetClientRect()?;

    // 居中显示图像
    let x_pos = (desk_rect.right - width) / 2;
    h_ddc.BitBlt(
        POINT::new(x_pos, 0),
        SIZE::new(width, height),
        &h_dc,
        POINT::new(0, 0),
        co::ROP::SRCCOPY,
    )?;

    // 清理GDI资源,避免泄漏
    h_dc.SelectObject(&old_bmp)?;
    dhwnd.ReleaseDC(h_ddc)?;

    Ok(())
}

关键修改说明

  • 像素格式转换:将每个RGB像素转为BGRA格式,确保每个像素占4字节,颜色通道顺序符合GDI要求。
  • 行顺序反转:通过倒序遍历解码后的像素行,让数据存储顺序匹配GDI位图的底部优先规则。
  • 资源清理:添加了选回旧位图、释放DC的操作,避免GDI资源泄漏。
  • 简化API调用:修正SelectObject的用法,直接传递&h_bmp即可,无需解引用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 17:59:52