diff --git a/crates/egui/src/widgets/button.rs b/crates/egui/src/widgets/button.rs index b67f8d491a0..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 @@ -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 texture_size = tlr.as_ref().ok().and_then(|t| t.size()); + 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 original_image_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); + .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 71c77decb74..6951f8c8961 100644 --- a/crates/egui/src/widgets/image.rs +++ b/crates/egui/src/widgets/image.rs @@ -239,14 +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, 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) + 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.calc_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(ui).ok()?.size()?; - Some(self.size.get(available_size, image_size)) + 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.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,9 +295,9 @@ impl<'a> Image<'a> { impl<'a> Widget for Image<'a> { fn ui(self, ui: &mut Ui) -> Response { - let tlr = self.load(ui); - let texture_size = tlr.as_ref().ok().and_then(|t| t.size()); - let ui_size = self.calculate_size(ui.available_size(), texture_size); + 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.calc_size(ui.available_size(), original_image_size); let (rect, response) = ui.allocate_exact_size(ui_size, self.sense); paint_texture_load_result( @@ -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, original_image_size: Vec2) -> Vec2 { let Self { maintain_aspect_ratio, max_size, @@ -394,7 +396,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 { @@ -403,11 +405,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) } } } @@ -483,6 +485,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( 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, ),