From 87de733da3083bda31344d7dcbd85e17183db65a Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 23 May 2025 13:52:36 +0200 Subject: [PATCH] Better define the meaning of `SizeHint` (#7079) --- crates/egui/src/load.rs | 22 +++++++------- crates/egui/src/widgets/image.rs | 24 +++++++++++---- crates/egui_extras/src/image.rs | 52 ++++++++++++++++++-------------- 3 files changed, 59 insertions(+), 39 deletions(-) diff --git a/crates/egui/src/load.rs b/crates/egui/src/load.rs index 88dfdf43..27bc9ae3 100644 --- a/crates/egui/src/load.rs +++ b/crates/egui/src/load.rs @@ -151,14 +151,21 @@ pub enum SizeHint { /// Scale original size by some factor. Scale(OrderedFloat), - /// Scale to width. + /// Scale to exactly this pixel width, keeping the original aspect ratio. Width(u32), - /// Scale to height. + /// Scale to exactly this pixel height, keeping the original aspect ratio. Height(u32), - /// Scale to size. - Size(u32, u32), + /// Scale to this pixel size. + Size { + width: u32, + height: u32, + + /// If true, the image will be as large as possible + /// while still fitting within the given width/height. + maintain_aspect_ratio: bool, + }, } impl Default for SizeHint { @@ -168,13 +175,6 @@ impl Default for SizeHint { } } -impl From for SizeHint { - #[inline] - fn from(value: Vec2) -> Self { - Self::Size(value.x.round() as u32, value.y.round() as u32) - } -} - /// Represents a byte buffer. /// /// This is essentially `Cow<'static, [u8]>` but with the `Owned` variant being an `Arc`. diff --git a/crates/egui/src/widgets/image.rs b/crates/egui/src/widgets/image.rs index c3f9d1d9..79bd6d2c 100644 --- a/crates/egui/src/widgets/image.rs +++ b/crates/egui/src/widgets/image.rs @@ -384,7 +384,11 @@ impl<'a> Image<'a> { let texture = self.source(ui.ctx()).clone().load( ui.ctx(), self.texture_options, - SizeHint::Size(pixel_size.x as _, pixel_size.y as _), + SizeHint::Size { + width: pixel_size.x as _, + height: pixel_size.y as _, + maintain_aspect_ratio: false, // no - just get exactly what we asked for + }, ); paint_texture_load_result( @@ -481,22 +485,30 @@ impl ImageFit { impl ImageSize { /// Size hint for e.g. rasterizing an svg. pub fn hint(&self, available_size: Vec2, pixels_per_point: f32) -> SizeHint { - let point_size = match self.fit { + let Self { + maintain_aspect_ratio, + max_size, + fit, + } = *self; + + let point_size = match fit { ImageFit::Original { scale } => { return SizeHint::Scale((pixels_per_point * scale).ord()) } ImageFit::Fraction(fract) => available_size * fract, ImageFit::Exact(size) => size, }; - let point_size = point_size.at_most(self.max_size); + let point_size = point_size.at_most(max_size); let pixel_size = pixels_per_point * point_size; // `inf` on an axis means "any value" match (pixel_size.x.is_finite(), pixel_size.y.is_finite()) { - (true, true) => { - SizeHint::Size(pixel_size.x.round() as u32, pixel_size.y.round() as u32) - } + (true, true) => SizeHint::Size { + width: pixel_size.x.round() as u32, + height: pixel_size.y.round() as u32, + maintain_aspect_ratio, + }, (true, false) => SizeHint::Width(pixel_size.x.round() as u32), (false, true) => SizeHint::Height(pixel_size.y.round() as u32), (false, false) => SizeHint::Scale(pixels_per_point.ord()), diff --git a/crates/egui_extras/src/image.rs b/crates/egui_extras/src/image.rs index fd97fb87..40cbdc53 100644 --- a/crates/egui_extras/src/image.rs +++ b/crates/egui_extras/src/image.rs @@ -255,43 +255,51 @@ pub fn load_svg_bytes_with_size( size_hint: Option, options: &resvg::usvg::Options<'_>, ) -> Result { - use resvg::tiny_skia::{IntSize, Pixmap}; - use resvg::usvg::{Transform, Tree}; + use egui::Vec2; + use resvg::{ + tiny_skia::Pixmap, + usvg::{Transform, Tree}, + }; profiling::function_scope!(); let rtree = Tree::from_data(svg_bytes, options).map_err(|err| err.to_string())?; - let size = rtree.size().to_int_size(); + let original_size = Vec2::new(rtree.size().width(), rtree.size().height()); + let scaled_size = match size_hint { - None => size, - Some(SizeHint::Size(w, h)) => size.scale_to( - IntSize::from_wh(w, h).ok_or_else(|| format!("Failed to scale SVG to {w}x{h}"))?, - ), - Some(SizeHint::Height(h)) => size - .scale_to_height(h) - .ok_or_else(|| format!("Failed to scale SVG to height {h}"))?, - Some(SizeHint::Width(w)) => size - .scale_to_width(w) - .ok_or_else(|| format!("Failed to scale SVG to width {w}"))?, - Some(SizeHint::Scale(z)) => { - let z_inner = z.into_inner(); - size.scale_by(z_inner) - .ok_or_else(|| format!("Failed to scale SVG by {z_inner}"))? + None => original_size, + Some(SizeHint::Size { + width, + height, + maintain_aspect_ratio, + }) => { + if maintain_aspect_ratio { + // As large as possible, without exceeding the given size: + let mut size = original_size; + size *= width as f32 / original_size.x; + if size.y > height as f32 { + size *= height as f32 / size.y; + } + size + } else { + Vec2::new(width as _, height as _) + } } + Some(SizeHint::Height(h)) => original_size * (h as f32 / original_size.y), + Some(SizeHint::Width(w)) => original_size * (w as f32 / original_size.x), + Some(SizeHint::Scale(scale)) => scale.into_inner() * original_size, }; - let (w, h) = (scaled_size.width(), scaled_size.height()); + let scaled_size = scaled_size.round(); + let (w, h) = (scaled_size.x as u32, scaled_size.y as u32); let mut pixmap = Pixmap::new(w, h).ok_or_else(|| format!("Failed to create SVG Pixmap of size {w}x{h}"))?; resvg::render( &rtree, - Transform::from_scale( - w as f32 / size.width() as f32, - h as f32 / size.height() as f32, - ), + Transform::from_scale(w as f32 / original_size.x, h as f32 / original_size.y), &mut pixmap.as_mut(), );