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

egui epaint图片加载维度错误排查及快速缩放实现咨询

图片加载崩溃问题及后续疑问

问题描述

遇到图片加载错误,推测与维度相关——修改图片加载尺寸变量时,报错的左右数值会随之变化。程序可编译但启动即崩溃。

代码片段

use std::include_bytes;

const ICONB: &[u8; 3860] = include_bytes!("2.png");

(.. egui boilerplate ..)

impl eframe::App for MyApp {
  fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
    egui::CentralPanel::default().show(ctx, |ui| {
      egui::Grid::new("")
        .striped(true)
        .show(ui, |ui| {
            ui.label("name");
            ui.add(egui::Image::new(
                egui::TextureId::from(
                    &egui::Context::load_texture(
                        &ctx,
                        "",
                        egui::ColorImage::from_rgba_unmultiplied(
                            [109usize, 462usize], //those are factual dimensions
                            ICONB
                        ),
                        egui::TextureFilter::Linear
                    )
                ), [109.0, 462.0]
            ));

崩溃回溯信息

thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `201432`,
 right: `3860`', /home/tommy/.cargo/registry/src/github.com-1ecc6299db9ec823/epaint-0.19.0/src/image.rs:96:9
stack backtrace:
   0: rust_begin_unwind
             at /rustc/ca122c7ebb3ab50149c9d3d24ddb59c252b32272/library/std/src/panicking.rs:584:5
   1: core::panicking::panic_fmt
             at /rustc/ca122c7ebb3ab50149c9d3d24ddb59c252b32272/library/core/src/panicking.rs:142:14
   2: core::panicking::assert_failed_inner
   3: core::panicking::assert_failed
   4: epaint::image::ColorImage::from_rgba_unmultiplied
   5: core::ops::function::FnOnce::call_once{{vtable.shim}}
   6: core::ops::function::FnOnce::call_once{{vtable.shim}}
   7: egui::ui::Ui::allocate_ui_at_rect
   8: core::ops::function::FnOnce::call_once{{vtable.shim}}
   9: egui::containers::frame::Frame::show
  10: egui::containers::panel::CentralPanel::show
  11: <win::MyApp as eframe::epi::App>::update
  12: eframe::native::epi_integration::EpiIntegration::update
  13: <eframe::native::run::glow_integration::GlowWinitApp as eframe::native::run::WinitApp>::paint
  14: eframe::native::run::run_and_return::{{closure}}
  15: winit::platform_impl::platform::x11::EventLoop<T>::run_return::single_iteration
  16: winit::platform_impl::platform::x11::EventLoop<T>::run_return
  17: eframe::native::run::run_and_return
  18: std::thread::local::LocalKey<T>::with
  19: eframe::native::run::glow_integration::run_glow
  20: eframe::run_native
  21: win::main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

触发崩溃的epaint源码

pub fn from_rgba_unmultiplied(size: [usize; 2], rgba: &[u8]) -> Self {
        assert_eq!(size[0] * size[1] * 4, rgba.len());
        let pixels = rgba
            .chunks_exact(4)
            .map(|p| Color32::from_rgba_unmultiplied(p[0], p[1], p[2], p[3]))
            .collect();
        Self { size, pixels }
    }

疑问

  1. 能否通过该方式加载图片?
  2. 如何更好地理解图片加载的内部逻辑?
  3. 如何在此处实现快速图片缩放?

解答

1. 能否通过该方式加载图片?

不能直接用当前方式加载。核心问题是:from_rgba_unmultiplied要求传入原始RGBA像素数据(每个像素占4字节:红、绿、蓝、透明度各1字节),但你传入的是PNG格式的压缩字节流。

从报错能看出:109*462*4=201432是计算出的预期像素数据长度,而ICONB实际只有3860字节,两者不匹配触发了断言失败。PNG是经过压缩的图片格式,并非直接的RGBA像素数组,所以不能直接传给from_rgba_unmultiplied。

2. 理解图片加载的内部逻辑

egui的图片加载流程分两步核心操作:

  • 解码压缩格式:把PNG/JPG等压缩字节流,转换成原始RGBA像素数组,这一步需要依赖图片解码库(比如image crate)完成。
  • 创建渲染纹理:将解码后的ColorImage(包含尺寸和像素数据)交给egui上下文,加载成GPU可直接渲染的纹理,之后才能通过Image组件显示。

from_rgba_unmultiplied只是个包装工具,负责把已经解码好的RGBA字节数组转换成egui能识别的ColorImage,它不具备解码压缩图片的能力。你现在跳过了解码步骤,直接传入PNG压缩数据,必然触发长度校验崩溃。

另外,你当前代码还有性能问题:每次update都会重新加载纹理,应该把纹理加载放在App的setup初始化方法中,只执行一次,后续update直接复用纹理ID。

3. 实现快速图片缩放

有两种高效方式可选:

  • 解码时缩放:用image crate解码PNG时直接缩放到目标尺寸,再转换成ColorImage。适合静态图片,一次处理完成,性能开销低。
  • 渲染时缩放:加载原始尺寸纹理后,在egui::Image::new中传入更小的显示尺寸(比如原尺寸一半的[54.5, 231.0]),egui会自动在GPU层面完成缩放。这种方式灵活,适合动态调整尺寸的场景,GPU缩放效率极高。

修正后的示例代码

先在Cargo.toml中添加依赖:

[dependencies]
eframe = "0.19"
image = { version = "0.24", features = ["png"] }

修改后的核心代码:

use eframe::egui;
use image::GenericImageView;
use std::include_bytes;

struct MyApp {
    texture_handle: Option<egui::TextureHandle>,
}

impl Default for MyApp {
    fn default() -> Self {
        Self { texture_handle: None }
    }
}

impl eframe::App for MyApp {
    fn setup(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame, _storage: Option<&dyn eframe::Storage>) {
        // 初始化阶段仅加载一次纹理
        const ICONB: &[u8] = include_bytes!("2.png");
        
        // 解码PNG为RGBA像素数据
        let img = image::load_from_memory(ICONB).expect("Failed to load image");
        let rgba_image = img.to_rgba8();
        let (width, height) = rgba_image.dimensions();
        
        // 转换为egui的ColorImage
        let color_image = egui::ColorImage::from_rgba_unmultiplied(
            [width as usize, height as usize],
            rgba_image.as_flat_samples().as_slice(),
        );
        
        // 加载纹理并保存句柄
        self.texture_handle = Some(ctx.load_texture(
            "icon_texture",
            color_image,
            egui::TextureFilter::Linear,
        ));
    }

    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            egui::Grid::new("icon_grid")
                .striped(true)
                .show(ui, |ui| {
                    ui.label("name");
                    if let Some(texture) = &self.texture_handle {
                        // 渲染图片,修改第二个参数即可实现缩放
                        ui.add(egui::Image::new(texture, [109.0, 462.0]));
                        // 示例:缩放到原尺寸一半
                        // ui.add(egui::Image::new(texture, [54.5, 231.0]));
                    }
                    ui.end_row();
                });
        });
    }
}

fn main() -> Result<(), eframe::Error> {
    eframe::run_native(
        "Image Loader Demo",
        eframe::NativeOptions::default(),
        Box::new(|_cc| Box::new(MyApp::default())),
    )
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 00:50:21