Better define the meaning of `SizeHint` (#7079)

This commit is contained in:
Emil Ernerfeldt 2025-05-23 13:52:36 +02:00 committed by GitHub
parent ec8b41f7ec
commit 87de733da3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 59 additions and 39 deletions

View File

@ -151,14 +151,21 @@ pub enum SizeHint {
/// Scale original size by some factor. /// Scale original size by some factor.
Scale(OrderedFloat<f32>), Scale(OrderedFloat<f32>),
/// Scale to width. /// Scale to exactly this pixel width, keeping the original aspect ratio.
Width(u32), Width(u32),
/// Scale to height. /// Scale to exactly this pixel height, keeping the original aspect ratio.
Height(u32), Height(u32),
/// Scale to size. /// Scale to this pixel size.
Size(u32, u32), 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 { impl Default for SizeHint {
@ -168,13 +175,6 @@ impl Default for SizeHint {
} }
} }
impl From<Vec2> for SizeHint {
#[inline]
fn from(value: Vec2) -> Self {
Self::Size(value.x.round() as u32, value.y.round() as u32)
}
}
/// Represents a byte buffer. /// Represents a byte buffer.
/// ///
/// This is essentially `Cow<'static, [u8]>` but with the `Owned` variant being an `Arc`. /// This is essentially `Cow<'static, [u8]>` but with the `Owned` variant being an `Arc`.

View File

@ -384,7 +384,11 @@ impl<'a> Image<'a> {
let texture = self.source(ui.ctx()).clone().load( let texture = self.source(ui.ctx()).clone().load(
ui.ctx(), ui.ctx(),
self.texture_options, 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( paint_texture_load_result(
@ -481,22 +485,30 @@ impl ImageFit {
impl ImageSize { impl ImageSize {
/// Size hint for e.g. rasterizing an svg. /// Size hint for e.g. rasterizing an svg.
pub fn hint(&self, available_size: Vec2, pixels_per_point: f32) -> SizeHint { 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 } => { ImageFit::Original { scale } => {
return SizeHint::Scale((pixels_per_point * scale).ord()) return SizeHint::Scale((pixels_per_point * scale).ord())
} }
ImageFit::Fraction(fract) => available_size * fract, ImageFit::Fraction(fract) => available_size * fract,
ImageFit::Exact(size) => size, 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; let pixel_size = pixels_per_point * point_size;
// `inf` on an axis means "any value" // `inf` on an axis means "any value"
match (pixel_size.x.is_finite(), pixel_size.y.is_finite()) { match (pixel_size.x.is_finite(), pixel_size.y.is_finite()) {
(true, true) => { (true, true) => SizeHint::Size {
SizeHint::Size(pixel_size.x.round() as u32, pixel_size.y.round() as u32) 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), (true, false) => SizeHint::Width(pixel_size.x.round() as u32),
(false, true) => SizeHint::Height(pixel_size.y.round() as u32), (false, true) => SizeHint::Height(pixel_size.y.round() as u32),
(false, false) => SizeHint::Scale(pixels_per_point.ord()), (false, false) => SizeHint::Scale(pixels_per_point.ord()),

View File

@ -255,43 +255,51 @@ pub fn load_svg_bytes_with_size(
size_hint: Option<SizeHint>, size_hint: Option<SizeHint>,
options: &resvg::usvg::Options<'_>, options: &resvg::usvg::Options<'_>,
) -> Result<egui::ColorImage, String> { ) -> Result<egui::ColorImage, String> {
use resvg::tiny_skia::{IntSize, Pixmap}; use egui::Vec2;
use resvg::usvg::{Transform, Tree}; use resvg::{
tiny_skia::Pixmap,
usvg::{Transform, Tree},
};
profiling::function_scope!(); profiling::function_scope!();
let rtree = Tree::from_data(svg_bytes, options).map_err(|err| err.to_string())?; 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 { let scaled_size = match size_hint {
None => size, None => original_size,
Some(SizeHint::Size(w, h)) => size.scale_to( Some(SizeHint::Size {
IntSize::from_wh(w, h).ok_or_else(|| format!("Failed to scale SVG to {w}x{h}"))?, width,
), height,
Some(SizeHint::Height(h)) => size maintain_aspect_ratio,
.scale_to_height(h) }) => {
.ok_or_else(|| format!("Failed to scale SVG to height {h}"))?, if maintain_aspect_ratio {
Some(SizeHint::Width(w)) => size // As large as possible, without exceeding the given size:
.scale_to_width(w) let mut size = original_size;
.ok_or_else(|| format!("Failed to scale SVG to width {w}"))?, size *= width as f32 / original_size.x;
Some(SizeHint::Scale(z)) => { if size.y > height as f32 {
let z_inner = z.into_inner(); size *= height as f32 / size.y;
size.scale_by(z_inner) }
.ok_or_else(|| format!("Failed to scale SVG by {z_inner}"))? 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 = let mut pixmap =
Pixmap::new(w, h).ok_or_else(|| format!("Failed to create SVG Pixmap of size {w}x{h}"))?; Pixmap::new(w, h).ok_or_else(|| format!("Failed to create SVG Pixmap of size {w}x{h}"))?;
resvg::render( resvg::render(
&rtree, &rtree,
Transform::from_scale( Transform::from_scale(w as f32 / original_size.x, h as f32 / original_size.y),
w as f32 / size.width() as f32,
h as f32 / size.height() as f32,
),
&mut pixmap.as_mut(), &mut pixmap.as_mut(),
); );