Search code examples
rustegui

egui epaint loading image - dimensions problem


I am getting an image error that I'm guessing is dimension-related, since the left and right change whenever I change the image loading size variables. The program compiles but crashes on start.

This is my code, I'm inserting a PNG image into an existing Grid. [apologies for the verbose :: syntax, I use it while I'm learning a library]

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]
            ));

Backtrace:

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.

That's the source code of epaint's from_rgba_unmultiplied that fails:

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 }
    }

Can I load the image this way, how to understand the internals of loading an image better?

Follow-up issue: How can I implement rescaling in a fast way here?


Solution

  • The from_rgba_unmultiplied() function that is panicking expects raw RGBA data, but you're passing in PNG-encoded image data. The PNG format compresses the image data, which is why the size is off and the assert fails.

    To fix this, you need to decode the PNG data into raw RGBA before passing it to from_rgba_unmultiplied(). The documentation for that function shows exactly how to do that, with the help of the image crate:

    fn load_image_from_memory(image_data: &[u8]) -> Result<ColorImage, image::ImageError> {
        let image = image::load_from_memory(image_data)?;
        let size = [image.width() as _, image.height() as _];
        let image_buffer = image.to_rgba8();
        let pixels = image_buffer.as_flat_samples();
        Ok(ColorImage::from_rgba_unmultiplied(
            size,
            pixels.as_slice(),
        ))
    }