Preserve aspect ratio when scaling images

Nathan Sobo and Max Brunsfeld created

Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>

Change summary

gpui/src/elements.rs       | 15 ++++++++++++++-
gpui/src/elements/image.rs |  6 +++++-
gpui/src/elements/svg.rs   | 21 ++++++---------------
3 files changed, 25 insertions(+), 17 deletions(-)

Detailed changes

gpui/src/elements.rs 🔗

@@ -24,7 +24,10 @@ pub use self::{
 };
 pub use crate::presenter::ChildView;
 use crate::{
-    geometry::{rect::RectF, vector::Vector2F},
+    geometry::{
+        rect::RectF,
+        vector::{vec2f, Vector2F},
+    },
     json, DebugContext, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
 };
 use core::panic;
@@ -359,3 +362,13 @@ pub trait ParentElement<'a>: Extend<ElementBox> + Sized {
 }
 
 impl<'a, T> ParentElement<'a> for T where T: Extend<ElementBox> {}
+
+fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
+    if max_size.x().is_infinite() && max_size.y().is_infinite() {
+        size
+    } else if max_size.x().is_infinite() || max_size.x() / max_size.y() > size.x() / size.y() {
+        vec2f(size.x() * max_size.y() / size.y(), max_size.y())
+    } else {
+        vec2f(max_size.x(), size.y() * max_size.x() / size.x())
+    }
+}

gpui/src/elements/image.rs 🔗

@@ -6,6 +6,8 @@ use crate::{
 };
 use std::sync::Arc;
 
+use super::constrain_size_preserving_aspect_ratio;
+
 pub struct Image {
     data: Arc<ImageData>,
     border: Border,
@@ -41,7 +43,9 @@ impl Element for Image {
         constraint: SizeConstraint,
         _: &mut LayoutContext,
     ) -> (Vector2F, Self::LayoutState) {
-        (constraint.max, ())
+        let size =
+            constrain_size_preserving_aspect_ratio(constraint.max, self.data.size().to_f32());
+        (size, ())
     }
 
     fn paint(

gpui/src/elements/svg.rs 🔗

@@ -41,21 +41,10 @@ impl Element for Svg {
     ) -> (Vector2F, Self::LayoutState) {
         match cx.asset_cache.svg(&self.path) {
             Ok(tree) => {
-                let size = if constraint.max.x().is_infinite() && constraint.max.y().is_infinite() {
-                    let rect = from_usvg_rect(tree.svg_node().view_box.rect);
-                    rect.size()
-                } else {
-                    let max_size = constraint.max;
-                    let svg_size = from_usvg_rect(tree.svg_node().view_box.rect).size();
-
-                    if max_size.x().is_infinite()
-                        || max_size.x() / max_size.y() > svg_size.x() / svg_size.y()
-                    {
-                        vec2f(svg_size.x() * max_size.y() / svg_size.y(), max_size.y())
-                    } else {
-                        vec2f(max_size.x(), svg_size.y() * max_size.x() / svg_size.x())
-                    }
-                };
+                let size = constrain_size_preserving_aspect_ratio(
+                    constraint.max,
+                    from_usvg_rect(tree.svg_node().view_box.rect).size(),
+                );
                 (size, Some(tree))
             }
             Err(error) => {
@@ -111,6 +100,8 @@ impl Element for Svg {
 
 use crate::json::ToJson;
 
+use super::constrain_size_preserving_aspect_ratio;
+
 fn from_usvg_rect(rect: usvg::Rect) -> RectF {
     RectF::new(
         vec2f(rect.x() as f32, rect.y() as f32),