From 9cb6852c2182009944f30d4cf76900d744b952f8 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 15 Sep 2023 09:30:47 +0200 Subject: [PATCH 1/4] Improve the Image API a bit --- crates/egui/src/widgets/button.rs | 7 +++--- crates/egui/src/widgets/image.rs | 40 ++++++++++++++++--------------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/crates/egui/src/widgets/button.rs b/crates/egui/src/widgets/button.rs index b67f8d491a0..55511c338ae 100644 --- a/crates/egui/src/widgets/button.rs +++ b/crates/egui/src/widgets/button.rs @@ -276,7 +276,7 @@ impl Widget for Button<'_> { image_size, ); cursor_x += image_size.x; - let tlr = image.load(ui); + let tlr = image.load_for_size(ui.ctx(), image_size); widgets::image::paint_texture_load_result( ui, &tlr, @@ -605,11 +605,12 @@ impl<'a> Widget for ImageButton<'a> { Vec2::ZERO }; - let tlr = self.image.load(ui); + let available_size_for_image = ui.available_size() - 2.0 * padding; + let tlr = self.image.load_for_size(ui.ctx(), available_size_for_image); let texture_size = tlr.as_ref().ok().and_then(|t| t.size()); let image_size = self .image - .calculate_size(ui.available_size() - 2.0 * padding, texture_size); + .calculate_size(available_size_for_image, texture_size); let padded_size = image_size + 2.0 * padding; let (rect, response) = ui.allocate_exact_size(padded_size, self.sense); diff --git a/crates/egui/src/widgets/image.rs b/crates/egui/src/widgets/image.rs index 71c77decb74..e7d80fa7d99 100644 --- a/crates/egui/src/widgets/image.rs +++ b/crates/egui/src/widgets/image.rs @@ -241,12 +241,12 @@ impl<'a> Image<'a> { #[inline] pub fn calculate_size(&self, available_size: Vec2, image_size: Option) -> Vec2 { let image_size = image_size.unwrap_or(Vec2::splat(24.0)); // Fallback for still-loading textures, or failure to load. - self.size.get(available_size, image_size) + self.size.calc_size(available_size, image_size) } pub fn load_and_calculate_size(&self, ui: &mut Ui, available_size: Vec2) -> Option { - let image_size = self.load(ui).ok()?.size()?; - Some(self.size.get(available_size, image_size)) + let image_size = self.load_for_size(ui.ctx(), available_size).ok()?.size()?; + Some(self.size.calc_size(available_size, image_size)) } #[inline] @@ -269,13 +269,15 @@ impl<'a> Image<'a> { /// Load the image from its [`Image::source`], returning the resulting [`SizedTexture`]. /// + /// The `available_size` is used as a hint when e.g. rendering an svg. + /// /// # Errors /// May fail if they underlying [`Context::try_load_texture`] call fails. - pub fn load(&self, ui: &Ui) -> TextureLoadResult { - let size_hint = self.size.hint(ui.available_size()); + pub fn load_for_size(&self, ctx: &Context, available_size: Vec2) -> TextureLoadResult { + let size_hint = self.size.hint(available_size); self.source .clone() - .load(ui.ctx(), self.texture_options, size_hint) + .load(ctx, self.texture_options, size_hint) } /// Paint the image in the given rectangle. @@ -283,7 +285,7 @@ impl<'a> Image<'a> { pub fn paint_at(&self, ui: &mut Ui, rect: Rect) { paint_texture_load_result( ui, - &self.load(ui), + &self.load_for_size(ui.ctx(), rect.size()), rect, self.show_loading_spinner, &self.image_options, @@ -293,7 +295,7 @@ impl<'a> Image<'a> { impl<'a> Widget for Image<'a> { fn ui(self, ui: &mut Ui) -> Response { - let tlr = self.load(ui); + let tlr = self.load_for_size(ui.ctx(), ui.available_size()); let texture_size = tlr.as_ref().ok().and_then(|t| t.size()); let ui_size = self.calculate_size(ui.available_size(), texture_size); @@ -364,29 +366,29 @@ impl ImageFit { } impl ImageSize { + /// Size hint for e.g. rasterizing an svg. pub fn hint(&self, available_size: Vec2) -> SizeHint { - if self.maintain_aspect_ratio { - return SizeHint::Scale(1.0.ord()); - }; - - let fit = match self.fit { + let size = match self.fit { ImageFit::Original { scale } => return SizeHint::Scale(scale.ord()), ImageFit::Fraction(fract) => available_size * fract, ImageFit::Exact(size) => size, }; - let fit = fit.min(self.max_size); + let size = size.min(self.max_size); + + // TODO(emilk): take pixels_per_point into account here! // `inf` on an axis means "any value" - match (fit.x.is_finite(), fit.y.is_finite()) { - (true, true) => SizeHint::Size(fit.x.round() as u32, fit.y.round() as u32), - (true, false) => SizeHint::Width(fit.x.round() as u32), - (false, true) => SizeHint::Height(fit.y.round() as u32), + match (size.x.is_finite(), size.y.is_finite()) { + (true, true) => SizeHint::Size(size.x.round() as u32, size.y.round() as u32), + (true, false) => SizeHint::Width(size.x.round() as u32), + (false, true) => SizeHint::Height(size.y.round() as u32), (false, false) => SizeHint::Scale(1.0.ord()), } } - pub fn get(&self, available_size: Vec2, image_size: Vec2) -> Vec2 { + /// Calculate the final on-screen size in points. + pub fn calc_size(&self, available_size: Vec2, image_size: Vec2) -> Vec2 { let Self { maintain_aspect_ratio, max_size, From ea30debc378c630ac988b457252749fbaf8b6d95 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 15 Sep 2023 09:32:45 +0200 Subject: [PATCH 2/4] Improve image view demo --- crates/egui_demo_app/src/apps/image_viewer.rs | 12 ++++++++---- crates/egui_demo_app/src/wrap_app.rs | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/egui_demo_app/src/apps/image_viewer.rs b/crates/egui_demo_app/src/apps/image_viewer.rs index 98f80f727c0..7015e83b0f1 100644 --- a/crates/egui_demo_app/src/apps/image_viewer.rs +++ b/crates/egui_demo_app/src/apps/image_viewer.rs @@ -105,13 +105,17 @@ impl eframe::App for ImageViewer { // bg_fill ui.add_space(2.0); - ui.label("Background color"); - ui.color_edit_button_srgba(&mut self.image_options.bg_fill); + ui.horizontal(|ui| { + ui.color_edit_button_srgba(&mut self.image_options.bg_fill); + ui.label("Background color"); + }); // tint ui.add_space(2.0); - ui.label("Tint"); - ui.color_edit_button_srgba(&mut self.image_options.tint); + ui.horizontal(|ui| { + ui.color_edit_button_srgba(&mut self.image_options.tint); + ui.label("Tint"); + }); // fit ui.add_space(10.0); diff --git a/crates/egui_demo_app/src/wrap_app.rs b/crates/egui_demo_app/src/wrap_app.rs index 71a4611b670..d2a0e9102a3 100644 --- a/crates/egui_demo_app/src/wrap_app.rs +++ b/crates/egui_demo_app/src/wrap_app.rs @@ -212,7 +212,7 @@ impl WrapApp { ), #[cfg(feature = "image_viewer")] ( - "🖼️ Image Viewer", + "🖼 Image Viewer", Anchor::ImageViewer, &mut self.state.image_viewer as &mut dyn eframe::App, ), From 6e4db12fbf7c121e391f5e2b63136abc678ae178 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 15 Sep 2023 09:47:02 +0200 Subject: [PATCH 3/4] Better names --- crates/egui/src/widgets/button.rs | 4 ++-- crates/egui/src/widgets/image.rs | 30 ++++++++++++++++++++---------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/crates/egui/src/widgets/button.rs b/crates/egui/src/widgets/button.rs index 55511c338ae..30d658313a3 100644 --- a/crates/egui/src/widgets/button.rs +++ b/crates/egui/src/widgets/button.rs @@ -607,10 +607,10 @@ impl<'a> Widget for ImageButton<'a> { let available_size_for_image = ui.available_size() - 2.0 * padding; let tlr = self.image.load_for_size(ui.ctx(), available_size_for_image); - let texture_size = tlr.as_ref().ok().and_then(|t| t.size()); + let original_image_size = tlr.as_ref().ok().and_then(|t| t.size()); let image_size = self .image - .calculate_size(available_size_for_image, texture_size); + .calculate_size(available_size_for_image, original_image_size); let padded_size = image_size + 2.0 * padding; let (rect, response) = ui.allocate_exact_size(padded_size, self.sense); diff --git a/crates/egui/src/widgets/image.rs b/crates/egui/src/widgets/image.rs index e7d80fa7d99..f5c36d1cc7a 100644 --- a/crates/egui/src/widgets/image.rs +++ b/crates/egui/src/widgets/image.rs @@ -239,14 +239,15 @@ impl<'a, T: Into>> From for Image<'a> { impl<'a> Image<'a> { /// Returns the size the image will occupy in the final UI. #[inline] - pub fn calculate_size(&self, available_size: Vec2, image_size: Option) -> Vec2 { - let image_size = image_size.unwrap_or(Vec2::splat(24.0)); // Fallback for still-loading textures, or failure to load. - self.size.calc_size(available_size, image_size) + pub fn calculate_size(&self, available_size: Vec2, original_image_size: Option) -> Vec2 { + let original_image_size = original_image_size.unwrap_or(Vec2::splat(24.0)); // Fallback for still-loading textures, or failure to load. + self.size + .calculate_size(available_size, original_image_size) } pub fn load_and_calculate_size(&self, ui: &mut Ui, available_size: Vec2) -> Option { let image_size = self.load_for_size(ui.ctx(), available_size).ok()?.size()?; - Some(self.size.calc_size(available_size, image_size)) + Some(self.size.calculate_size(available_size, image_size)) } #[inline] @@ -296,8 +297,8 @@ impl<'a> Image<'a> { impl<'a> Widget for Image<'a> { fn ui(self, ui: &mut Ui) -> Response { let tlr = self.load_for_size(ui.ctx(), ui.available_size()); - let texture_size = tlr.as_ref().ok().and_then(|t| t.size()); - let ui_size = self.calculate_size(ui.available_size(), texture_size); + let original_image_size = tlr.as_ref().ok().and_then(|t| t.size()); + let ui_size = self.calculate_size(ui.available_size(), original_image_size); let (rect, response) = ui.allocate_exact_size(ui_size, self.sense); paint_texture_load_result( @@ -388,7 +389,7 @@ impl ImageSize { } /// Calculate the final on-screen size in points. - pub fn calc_size(&self, available_size: Vec2, image_size: Vec2) -> Vec2 { + pub fn calculate_size(&self, available_size: Vec2, original_image_size: Vec2) -> Vec2 { let Self { maintain_aspect_ratio, max_size, @@ -396,7 +397,7 @@ impl ImageSize { } = *self; match fit { ImageFit::Original { scale } => { - let image_size = image_size * scale; + let image_size = original_image_size * scale; if image_size.x <= max_size.x && image_size.y <= max_size.y { image_size } else { @@ -405,11 +406,11 @@ impl ImageSize { } ImageFit::Fraction(fract) => { let scale_to_size = (available_size * fract).min(max_size); - scale_to_fit(image_size, scale_to_size, maintain_aspect_ratio) + scale_to_fit(original_image_size, scale_to_size, maintain_aspect_ratio) } ImageFit::Exact(size) => { let scale_to_size = size.min(max_size); - scale_to_fit(image_size, scale_to_size, maintain_aspect_ratio) + scale_to_fit(original_image_size, scale_to_size, maintain_aspect_ratio) } } } @@ -485,6 +486,15 @@ impl<'a> std::fmt::Debug for ImageSource<'a> { } impl<'a> ImageSource<'a> { + /// Size of the texture, if known. + #[inline] + pub fn texture_size(&self) -> Option { + match self { + ImageSource::Texture(texture) => Some(texture.size), + ImageSource::Uri(_) | ImageSource::Bytes(_, _) => None, + } + } + /// # Errors /// Failure to load the texture. pub fn load( From de0a03f20eefc571f7554a8fc3e659083c4d9437 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 15 Sep 2023 10:04:12 +0200 Subject: [PATCH 4/4] calculate -> calc --- crates/egui/src/widgets/button.rs | 4 ++-- crates/egui/src/widgets/image.rs | 13 ++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/egui/src/widgets/button.rs b/crates/egui/src/widgets/button.rs index 30d658313a3..4c94cc9d4ac 100644 --- a/crates/egui/src/widgets/button.rs +++ b/crates/egui/src/widgets/button.rs @@ -187,7 +187,7 @@ impl Widget for Button<'_> { let image_size = if let Some(image) = &image { image - .load_and_calculate_size(ui, space_available_for_image) + .load_and_calc_size(ui, space_available_for_image) .unwrap_or(space_available_for_image) } else { Vec2::ZERO @@ -610,7 +610,7 @@ impl<'a> Widget for ImageButton<'a> { let original_image_size = tlr.as_ref().ok().and_then(|t| t.size()); let image_size = self .image - .calculate_size(available_size_for_image, original_image_size); + .calc_size(available_size_for_image, original_image_size); let padded_size = image_size + 2.0 * padding; let (rect, response) = ui.allocate_exact_size(padded_size, self.sense); diff --git a/crates/egui/src/widgets/image.rs b/crates/egui/src/widgets/image.rs index f5c36d1cc7a..6951f8c8961 100644 --- a/crates/egui/src/widgets/image.rs +++ b/crates/egui/src/widgets/image.rs @@ -239,15 +239,14 @@ impl<'a, T: Into>> From for Image<'a> { impl<'a> Image<'a> { /// Returns the size the image will occupy in the final UI. #[inline] - pub fn calculate_size(&self, available_size: Vec2, original_image_size: Option) -> Vec2 { + pub fn calc_size(&self, available_size: Vec2, original_image_size: Option) -> Vec2 { let original_image_size = original_image_size.unwrap_or(Vec2::splat(24.0)); // Fallback for still-loading textures, or failure to load. - self.size - .calculate_size(available_size, original_image_size) + self.size.calc_size(available_size, original_image_size) } - pub fn load_and_calculate_size(&self, ui: &mut Ui, available_size: Vec2) -> Option { + pub fn load_and_calc_size(&self, ui: &mut Ui, available_size: Vec2) -> Option { let image_size = self.load_for_size(ui.ctx(), available_size).ok()?.size()?; - Some(self.size.calculate_size(available_size, image_size)) + Some(self.size.calc_size(available_size, image_size)) } #[inline] @@ -298,7 +297,7 @@ impl<'a> Widget for Image<'a> { fn ui(self, ui: &mut Ui) -> Response { let tlr = self.load_for_size(ui.ctx(), ui.available_size()); let original_image_size = tlr.as_ref().ok().and_then(|t| t.size()); - let ui_size = self.calculate_size(ui.available_size(), original_image_size); + let ui_size = self.calc_size(ui.available_size(), original_image_size); let (rect, response) = ui.allocate_exact_size(ui_size, self.sense); paint_texture_load_result( @@ -389,7 +388,7 @@ impl ImageSize { } /// Calculate the final on-screen size in points. - pub fn calculate_size(&self, available_size: Vec2, original_image_size: Vec2) -> Vec2 { + pub fn calc_size(&self, available_size: Vec2, original_image_size: Vec2) -> Vec2 { let Self { maintain_aspect_ratio, max_size,