gpui: Render SVGs at 2x size when rendered in an `img` (#24332)

Marshall Bowers created

This PR adjusts the rendering of SVGs when used with the `img` element
such that they are rendered at 2x their displayed size.

This results in much crisper icons for icons loaded by icon themes:

<img width="1136" alt="Screenshot 2025-02-05 at 7 39 48 PM"
src="https://github.com/user-attachments/assets/47d1fcee-c54d-4717-8fca-9b9d2bc8da9a"
/>

<img width="1136" alt="Screenshot 2025-02-05 at 7 40 01 PM"
src="https://github.com/user-attachments/assets/3061157c-8c88-41c1-a5dc-83ef9cd341cb"
/>

Release Notes:

- Improved the resolution of icons rendered by icon themes.

Change summary

crates/gpui/src/elements/img.rs | 3 ++-
crates/gpui/src/svg_renderer.rs | 3 +++
crates/gpui/src/window.rs       | 9 ++++-----
3 files changed, 9 insertions(+), 6 deletions(-)

Detailed changes

crates/gpui/src/elements/img.rs 🔗

@@ -3,6 +3,7 @@ use crate::{
     DefiniteLength, Element, ElementId, GlobalElementId, Hitbox, Image, InteractiveElement,
     Interactivity, IntoElement, LayoutId, Length, ObjectFit, Pixels, RenderImage, Resource,
     SharedString, SharedUri, StyleRefinement, Styled, SvgSize, Task, Window,
+    SMOOTH_SVG_SCALE_FACTOR,
 };
 use anyhow::{anyhow, Result};
 
@@ -610,7 +611,7 @@ impl Asset for ImageAssetLoader {
             } else {
                 let pixmap =
                     // TODO: Can we make svgs always rescale?
-                    svg_renderer.render_pixmap(&bytes, SvgSize::ScaleFactor(1.0))?;
+                    svg_renderer.render_pixmap(&bytes, SvgSize::ScaleFactor(SMOOTH_SVG_SCALE_FACTOR))?;
 
                 let mut buffer =
                     ImageBuffer::from_raw(pixmap.width(), pixmap.height(), pixmap.take()).unwrap();

crates/gpui/src/svg_renderer.rs 🔗

@@ -3,6 +3,9 @@ use anyhow::anyhow;
 use resvg::tiny_skia::Pixmap;
 use std::{hash::Hash, sync::Arc};
 
+/// When rendering SVGs, we render them at twice the size to get a higher-quality result.
+pub const SMOOTH_SVG_SCALE_FACTOR: f32 = 2.;
+
 #[derive(Clone, PartialEq, Hash, Eq)]
 pub(crate) struct RenderSvgParams {
     pub(crate) path: SharedString,

crates/gpui/src/window.rs 🔗

@@ -13,7 +13,7 @@ use crate::{
     Subscription, TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement, TransformationMatrix,
     Underline, UnderlineStyle, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
     WindowControls, WindowDecorations, WindowOptions, WindowParams, WindowTextSystem,
-    SUBPIXEL_VARIANTS,
+    SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS,
 };
 use anyhow::{anyhow, Context as _, Result};
 use collections::{FxHashMap, FxHashSet};
@@ -2553,12 +2553,11 @@ impl Window {
         let element_opacity = self.element_opacity();
         let scale_factor = self.scale_factor();
         let bounds = bounds.scale(scale_factor);
-        // Render the SVG at twice the size to get a higher quality result.
         let params = RenderSvgParams {
             path,
-            size: bounds
-                .size
-                .map(|pixels| DevicePixels::from((pixels.0 * 2.).ceil() as i32)),
+            size: bounds.size.map(|pixels| {
+                DevicePixels::from((pixels.0 * SMOOTH_SVG_SCALE_FACTOR).ceil() as i32)
+            }),
         };
 
         let Some(tile) =