rust - How to place image at a specific location? - Stack Overflow

admin2025-04-21  3

Currently I want to create a little application, where I want to place certain geometric shapes and images within a limited area of the application. I use eframe/egui its Painter struct. I looked around how to add images, but only found stuff like ui.image which just places the image at the next free location.

What I want:

Draw geometric shapes and images at specific location (given something like Pos2) within the painter widget.

How far I got:

I managed to draw circles and and I know that the enum Shape also contains Mesh which can somehow contain a picture, but I could not find out how to use it in the way I desire.

The question:

Let's say I have .png file with a 60 x 60 pixel image. How do I have to alter the code, so that this image is drawn three times at given locations within my app?

use eframe::egui;
use egui::{Sense, Shape, Vec2};
use epaint::{CircleShape, Color32, Pos2, Stroke};

pub struct MyApp {
    // vector full of `CircleShape`s
    circles: Vec<Shape>,
    // should somehow contain the images to draw
    images: Vec<Shape>,
}

impl MyApp {
    fn new(cc: &eframe::CreationContext<'_>, coordinates: Vec<(f32, f32)>) -> Self {
        let circles = coordinates
            .iter()
            .map(|(x, y)| {
                Shape::Circle(CircleShape {
                    center: Pos2 { x: *x, y: *y },
                    radius: 40.0,
                    fill: Color32::from_rgb(255, 0, 0),
                    stroke: Stroke::new(10.0, Color32::from_rgb(130, 0, 0)),
                })
            })
            .collect();
        Self {
            circles,
            // how to add images with specific (center) position?
            images: Vec::new(),
        }
    }
   // some functions to intialize and remove and add shapes
}

impl eframe::App for MyApp {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            ui.heading("Show some circles and images");
            let (_reponse, painter) = ui.allocate_painter(Vec2::new(1000.0, 300.0), Sense::hover());
            painter.extend(self.circles.clone());

            // I want to the painter to place images here
        });
    }
}

Currently I want to create a little application, where I want to place certain geometric shapes and images within a limited area of the application. I use eframe/egui its Painter struct. I looked around how to add images, but only found stuff like ui.image which just places the image at the next free location.

What I want:

Draw geometric shapes and images at specific location (given something like Pos2) within the painter widget.

How far I got:

I managed to draw circles and and I know that the enum Shape also contains Mesh which can somehow contain a picture, but I could not find out how to use it in the way I desire.

The question:

Let's say I have .png file with a 60 x 60 pixel image. How do I have to alter the code, so that this image is drawn three times at given locations within my app?

use eframe::egui;
use egui::{Sense, Shape, Vec2};
use epaint::{CircleShape, Color32, Pos2, Stroke};

pub struct MyApp {
    // vector full of `CircleShape`s
    circles: Vec<Shape>,
    // should somehow contain the images to draw
    images: Vec<Shape>,
}

impl MyApp {
    fn new(cc: &eframe::CreationContext<'_>, coordinates: Vec<(f32, f32)>) -> Self {
        let circles = coordinates
            .iter()
            .map(|(x, y)| {
                Shape::Circle(CircleShape {
                    center: Pos2 { x: *x, y: *y },
                    radius: 40.0,
                    fill: Color32::from_rgb(255, 0, 0),
                    stroke: Stroke::new(10.0, Color32::from_rgb(130, 0, 0)),
                })
            })
            .collect();
        Self {
            circles,
            // how to add images with specific (center) position?
            images: Vec::new(),
        }
    }
   // some functions to intialize and remove and add shapes
}

impl eframe::App for MyApp {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            ui.heading("Show some circles and images");
            let (_reponse, painter) = ui.allocate_painter(Vec2::new(1000.0, 300.0), Sense::hover());
            painter.extend(self.circles.clone());

            // I want to the painter to place images here
        });
    }
}
Share Improve this question edited Feb 10 at 7:29 DarkBee 15.5k8 gold badges72 silver badges118 bronze badges asked Jan 23 at 9:24 BlindschleicheBlindschleiche 3482 silver badges8 bronze badges 4
  • Ui::allocate_rect – cafce25 Commented Jan 23 at 10:47
  • what will that help? this will place the image relative to the UI but not in relation to the painter. And if I manage that these coordinates fit, this does not explain how to include any image. – Blindschleiche Commented Jan 23 at 10:58
  • You already know how to add an image to an ui at least that's what I got from your question.. I'll probably expand in an answer later if I got the time. – cafce25 Commented Jan 23 at 11:16
  • the Painter struct has an image method, see here I'm also struggling with this to work how i want but let me know if it works for you – Preston Bourne Commented Feb 7 at 23:35
Add a comment  | 

1 Answer 1

Reset to default 2

Unfortunately, I forgot about my question here, but I were able to get the images painted as I desired. I think all the answers are present in the documentation, but there is just no example for the current version that connects all the dots. I extended my example above, so far that the only things missing are the exact image positions, but everyone should be able to handle this stuff on their own, as the required function is given as well, I just left out the parameters. And as far as I understand, this method is quite efficient, as the textures are only loaded once and are just reused every frame.

use eframe::egui;
use egui::{Sense, Shape, Vec2};
use epaint::{CircleShape, Color32, Pos2, Stroke};
// new crate, it is important for the function below
use image;

// loads an image from a given path
fn load_image_from_path(path: &std::path::Path) -> Result<egui::ColorImage, image::ImageError> {
    let image = image::ImageReader::open(path)?.decode()?;
    let size = [image.width() as _, image.height() as _];
    let image_buffer = image.to_rgba8();
    let pixels = image_buffer.as_flat_samples();
    Ok(egui::ColorImage::from_rgba_unmultiplied(
        size,
        pixels.as_slice(),
    ))
}

pub struct MyApp {
    // vector full of `CircleShape`s
    circles: Vec<Shape>,
    // another change, the vector should store 'TextureHandle's
    images: Vec<egui::TextureHandle>,
}

impl MyApp {
    fn new(cc: &eframe::CreationContext<'_>,
           coordinates: Vec<(f32, f32)>,
           image_paths: Vec<std::path::Path>) -> Self {
        let circles = coordinates
            .iter()
            .map(|(x, y)| {
                Shape::Circle(CircleShape {
                    center: Pos2 { x: *x, y: *y },
                    radius: 40.0,
                    fill: Color32::from_rgb(255, 0, 0),
                    stroke: Stroke::new(10.0, Color32::from_rgb(130, 0, 0)),
                })
            })
            .collect();
        // store the images as texture handles
        let texture_handles: Vec<egui::TextureHandle> = image_paths
            .iter()
            .enumerate()
            .map(|(i, img_path)| {
                cc.egui_ctx.load_texture(
                    format!("image_{}", i),
                    load_image_from_path(img_path).unwrap(),
                    egui::TextureOptions::default())
            })
            .collect();
        Self {
            circles,
            // use loaded texture handles here
            images: texture_handles,
        }
    }
   // some functions to intialize and remove and add shapes
}

impl eframe::App for MyApp {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            ui.heading("Show some circles and images");
            let (_reponse, painter) = ui.allocate_painter(Vec2::new(1000.0, 300.0), Sense::hover());
            painter.extend(self.circles.clone());

            // next critical step, actually place the images
            for texture_handle in self.images.iter() {
                let texture_id = egui::TextureId::from(texture_handle);
                // choose a position where to place the image
                // as you can see this code part is not finished
                let center_position = egui::Rect::from_center_size(...)
                painter.image(
                    texture_id,
                    center_position,
                    egui::Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0)),
                    Color32::WHITE,
                );
            }
        });
    }
}
转载请注明原文地址:http://anycun.com/QandA/1745206271a90416.html