Repository: wezterm/wezterm (language: rust)

Issue:
Background image stretched when height/width is 'Contain'
### What Operating System(s) are you seeing this problem on?

Linux X11

### Which Wayland compositor or X11 Window manager(s) are you using?

GNOME Shell

### WezTerm version

20230712-072601-f4abf8fd

### Did you try the latest nightly build to see if the issue is better (or worse!) than your current version?

Yes, and I updated the version box above to show the version of the nightly that I tried

### Describe the bug

According to the [document](https://wezfurlong.org/wezterm/config/lua/config/background.html#layer-definition):

> "Contain" - Scales the image as large as possible without cropping or stretching. If the viewport is larger than the image, tiles the image unless repeat_y is set to "NoRepeat".

Set the height/width to 'Contain' would not change original image's ratio. But after I use the following configuration, the image is stretched horizontally:

```lua
{
  source = {
    File = '/home/ihnfsa/Pictures/Collection/ghost-in-the-shell.jpg',
  },
  attachment = 'Fixed',
  repeat_x = 'NoRepeat',
  repeat_y = 'NoRepeat',
  vertical_align = 'Middle',
  horizontal_align = 'Right',
  height = 'Contain',
  width = 'Contain',
},
```

And the changed ratio maintains. The effect of the configuration is what I want except the image ratio is changed.


### To Reproduce

- Find an image.
- Use my configuration.

### Configuration

-- Pull in the wezterm API
local wezterm = require 'wezterm'
local act = wezterm.action

-- This table will hold the configuration.
local config = {}

-- In newer versions of wezterm, use the config_builder which will
-- help provide clearer error messages
if wezterm.config_builder then
  config = wezterm.config_builder()
end

-- This is where you actually apply your config choices
config.font =
  wezterm.font('FiraCode Nerd Font Mono', { weight = 400 })
 
config.font_size = 11.2

config.color_scheme = 'Dracula'

config.background = {
  {
    source = {
      File = '/home/ihnfsa/Pictures/Collection/ghost-in-the-shell-color.jpg',
    },
    attachment = 'Fixed',
    repeat_x = 'NoRepeat',
    repeat_y = 'NoRepeat',
    height = '100%',
    width = '100%',
  },
  {
    source = {
      File = '/home/ihnfsa/Pictures/Collection/ghost-in-the-shell.jpg',
    },
    attachment = 'Fixed',
    repeat_x = 'NoRepeat',
    repeat_y = 'NoRepeat',
    vertical_align = 'Middle',
    horizontal_align = 'Right',
    height = 'Contain',
    width = 'Contain',
  },
}

-- right click to copy-paste
config.mouse_bindings = {
  {
    event = { Down = { streak = 1, button = "Right" } },
    mods = "NONE",
    action = wezterm.action_callback(function(window, pane)
      local has_selection = window:get_selection_text_for_pane(pane) ~= ""
      if has_selection then
        window:perform_action(act.CopyTo("ClipboardAndPrimarySelection"), pane)
        window:perform_action(act.ClearSelection, pane)
      else
        window:perform_action(act({ PasteFrom = "Clipboard" }), pane)
      end
    end),
  },
}

-- and finally, return the configuration to wezterm
return config


### Expected Behavior

The image maintains the original ratio.

### Logs

_No response_

### Anything else?

_No response_


Discussion / hints:

Fix patch:
diff --git a/wezterm-gui/src/termwindow/background.rs b/wezterm-gui/src/termwindow/background.rs
index 606f6c5c877..8d38ca7c9a1 100644
--- a/wezterm-gui/src/termwindow/background.rs
+++ b/wezterm-gui/src/termwindow/background.rs
@@ -438,11 +438,11 @@ impl crate::TermWindow {
 
         let pixel_width = self.dimensions.pixel_width as f32;
         let pixel_height = self.dimensions.pixel_height as f32;
-        let pixel_aspect = pixel_width / pixel_height;
-
         let tex_width = sprite.coords.width() as f32;
         let tex_height = sprite.coords.height() as f32;
-        let aspect = tex_width as f32 / tex_height as f32;
+
+        let scale_width = pixel_width / tex_width as f32;
+        let scale_height = pixel_height / tex_height as f32;
 
         let h_context = DimensionContext {
             dpi: self.dimensions.dpi as f32,
@@ -457,47 +457,26 @@ impl crate::TermWindow {
 
         // log::info!("tex {tex_width}x{tex_height} aspect={aspect}");
 
-        // Compute the largest aspect-preserved size that will fill the space
-        let (max_aspect_width, max_aspect_height) = if aspect >= 1.0 {
-            // Width is the longest side
-            let target_height = pixel_width / aspect;
-            if target_height > pixel_height {
-                (
-                    (pixel_width * pixel_height / target_height).floor(),
-                    pixel_height,
-                )
-            } else {
-                (pixel_width, target_height)
-            }
-        } else {
-            // Height is the longest side
-            let target_width = pixel_height / aspect;
-            if target_width > pixel_width {
-                (
-                    pixel_width,
-                    (pixel_height * pixel_width / target_width).floor(),
-                )
-            } else {
-                (target_width, pixel_height)
-            }
-        };
-
         // Compute the smallest aspect-preserved size that will fit the space
-        let (min_aspect_width, min_aspect_height) = if pixel_aspect > aspect {
-            (pixel_width, (pixel_width / aspect).floor())
-        } else {
-            ((pixel_height * aspect).floor(), pixel_height)
+        let (min_aspect_width, min_aspect_height) = {
+            let scale = scale_width.min(scale_height);
+            (tex_width * scale, tex_height * scale)
+        };
+        // Compute the largest aspect-preserved size that will fill the space
+        let (max_aspect_width, max_aspect_height) = {
+            let scale = scale_width.max(scale_height);
+            (tex_width * scale, tex_height * scale)
         };
 
         let width = match layer.def.width {
-            BackgroundSize::Contain => max_aspect_width as f32,
-            BackgroundSize::Cover => min_aspect_width as f32,
+            BackgroundSize::Contain => min_aspect_width as f32,
+            BackgroundSize::Cover => max_aspect_width as f32,
             BackgroundSize::Dimension(n) => n.evaluate_as_pixels(h_context),
         };
 
         let height = match layer.def.height {
-            BackgroundSize::Contain => max_aspect_height as f32,
-            BackgroundSize::Cover => min_aspect_height as f32,
+            BackgroundSize::Contain => min_aspect_height as f32,
+            BackgroundSize::Cover => max_aspect_height as f32,
             BackgroundSize::Dimension(n) => n.evaluate_as_pixels(v_context),
         };
 


Test patch: