iced-rs / iced

A cross-platform GUI library for Rust, inspired by Elm
https://iced.rs
MIT License
24.94k stars 1.18k forks source link

Cannot visualize a memory-mapped image #906

Closed Luni-4 closed 3 years ago

Luni-4 commented 3 years ago

When I try to visualize a memory-mapped image, the application does not render it

Below there is a simple minimal test to better explain my problem

Cargo.toml

[dependencies]
iced = { version = "0.3.0", features = ["image"]}

main.rs

use iced::{executor, image, Application, Clipboard, Command, Element, Length, Settings};

pub fn main() -> iced::Result {
    ImageViewer::run(Settings {
        antialiasing: true,
        ..Settings::default()
    })
}

#[derive(Default)]
struct ImageViewer {
    pub width: usize,
    pub height: usize,
    image_viewer: image::viewer::State,
}

#[derive(Debug, Clone)]
pub enum Message {
    Void,
}

impl Application for ImageViewer {
    type Message = Message;
    type Executor = executor::Default;
    type Flags = ();

    fn new(_: Self::Flags) -> (Self, Command<Message>) {
        (
            Self {
                width: 640,
                height: 360,
                image_viewer: image::viewer::State::new(),
            },
            Command::none(),
        )
    }

    fn title(&self) -> String {
        String::from("Image Viewer")
    }

    fn update(&mut self, _message: Message, _clipboard: &mut Clipboard) -> Command<Message> {
        Command::none()
    }

    fn view(&mut self) -> Element<Message> {
        let image_handle = image::Handle::from_pixels(
            self.width as u32,
            self.height as u32,
            vec![0u8; self.width * self.height * 4],
        );

        image::Viewer::new(&mut self.image_viewer, image_handle)
        .width(Length::Units(self.width as u16))
        .height(Length::Units(self.height as u16))
        .into()
    }
}

I also tried to create different versions of the view function:

Using an Image

    fn view(&mut self) -> Element<Message> {
        let image_handle = image::Handle::from_pixels(
            self.width as u32,
            self.height as u32,
            vec![0u8; self.width * self.height * 4],
        );
        Image::new(image_handle).into()
    }

Creating the image externally through the image crate

    fn view(&mut self) -> Element<Message> {
        extern crate image as create_image;
        use create_image::RgbaImage;
        let img = RgbaImage::new(self.width as u32, self.height as u32).into_raw();

        let image_handle = image::Handle::from_pixels(self.width as u32, self.height as u32, img);        

        image::Viewer::new(&mut self.image_viewer, image_handle)
            .width(Length::Units(self.width as u16))
            .height(Length::Units(self.height as u16))
            .into()
    }

If I instead try to load an external image

Image::new("my_image.png")

It works perfectly.

Thanks in advance for your help! :smiley:

yusdacra commented 3 years ago

The format of the image must be in BGRA. See here https://docs.rs/iced_native/0.4.0/iced_native/widget/image/enum.Data.html#variant.Pixels

Luni-4 commented 3 years ago

Oops, you're right, I missed that. Thank you! But I cannot understand why the Vector filled with 0 does not work though.

It would be helpful to add a RGBA/RGB APIs, more intuitive in my opinion. Or is there a particular reason not to have them?

yusdacra commented 3 years ago

I'm not sure why this format was chosen, but as to the image not displaying it's probably because the alpha value is also 0 so it's transparent.

Luni-4 commented 3 years ago

@yusdacra

I followed your advice but it does not seem to work either this method

    fn view(&mut self) -> Element<Message> {
        let pixel_values: Vec<u8> = [0, 0, 0, 255]
            .iter()
            .cycle()
            .take(4 * self.width * self.height)
            .copied()
            .collect();

        let image_handle =
            image::Handle::from_pixels(self.width as u32, self.height as u32, pixel_values);

        image::Viewer::new(&mut self.image_viewer, image_handle)
            .width(Length::Units(self.width as u16))
            .height(Length::Units(self.height as u16))
            .into()
    }
yusdacra commented 3 years ago

Try to make every pixel 255, or just the alpha value 255.

Luni-4 commented 3 years ago

Ouch, it's an u8, not a floating point, I don't know why I've written that value. Edited the message above, perhaps it could be useful for someone else. It works perfectly now.

Thanks a lot for your help @yusdacra, very appreciated! :smile:

We can close this issue as invalid then