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. 能否通过该方式加载图片?
不能直接用当前方式加载。核心问题是:from_rgba_unmultiplied要求传入原始RGBA像素数据(每个像素占4字节:红、绿、蓝、透明度各1字节),但你传入的是PNG格式的压缩字节流。
从报错能看出:109*462*4=201432是计算出的预期像素数据长度,而ICONB实际只有3860字节,两者不匹配触发了断言失败。PNG是经过压缩的图片格式,并非直接的RGBA像素数组,所以不能直接传给from_rgba_unmultiplied。
2. 理解图片加载的内部逻辑
egui的图片加载流程分两步核心操作:
- 解码压缩格式:把PNG/JPG等压缩字节流,转换成原始RGBA像素数组,这一步需要依赖图片解码库(比如
imagecrate)完成。 - 创建渲染纹理:将解码后的
ColorImage(包含尺寸和像素数据)交给egui上下文,加载成GPU可直接渲染的纹理,之后才能通过Image组件显示。
from_rgba_unmultiplied只是个包装工具,负责把已经解码好的RGBA字节数组转换成egui能识别的ColorImage,它不具备解码压缩图片的能力。你现在跳过了解码步骤,直接传入PNG压缩数据,必然触发长度校验崩溃。
另外,你当前代码还有性能问题:每次update都会重新加载纹理,应该把纹理加载放在App的setup初始化方法中,只执行一次,后续update直接复用纹理ID。
3. 实现快速图片缩放
有两种高效方式可选:
- 解码时缩放:用
imagecrate解码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
相关产品推荐
相关产品推荐

