From e048410491521dfe48b74298348237cfc73d4ce6 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Mon, 17 Jul 2023 10:55:53 -0700 Subject: [PATCH] imgcat: add resize and resample functionality This is primarily to improve the chances of displaying an arbitrary image without resorting to additional external tools, that may be difficult or impossible to install. refs: #3716 refs: #3264 --- assets/shell-completion/bash | 28 ++- assets/shell-completion/fish | 10 +- assets/shell-completion/zsh | 12 +- ci/update-derived-files.sh | 15 +- docs/changelog.md | 5 + .../cmd-synopsis-wezterm-imgcat--help.txt | 67 +++++- ...is-wezterm-set-working-directory--help.txt | 8 +- wezterm/src/main.rs | 204 ++++++++++++++++-- 8 files changed, 323 insertions(+), 26 deletions(-) diff --git a/assets/shell-completion/bash b/assets/shell-completion/bash index fc90c86c0..5f4d8d1dd 100644 --- a/assets/shell-completion/bash +++ b/assets/shell-completion/bash @@ -1451,7 +1451,7 @@ _wezterm() { return 0 ;; wezterm__imgcat) - opts="-h --width --height --no-preserve-aspect-ratio --position --no-move-cursor --hold --help [FILE_NAME]" + opts="-h --width --height --no-preserve-aspect-ratio --position --no-move-cursor --hold --tmux-passthru --max-pixels --no-resample --resample-format --resample-filter --resize --show-resample-timing --help [FILE_NAME]" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1469,6 +1469,26 @@ _wezterm() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --tmux-passthru) + COMPREPLY=($(compgen -W "disable enable detect" -- "${cur}")) + return 0 + ;; + --max-pixels) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --resample-format) + COMPREPLY=($(compgen -W "png jpeg input" -- "${cur}")) + return 0 + ;; + --resample-filter) + COMPREPLY=($(compgen -W "nearest triangle catmull-rom gaussian lanczos3" -- "${cur}")) + return 0 + ;; + --resize) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; *) COMPREPLY=() ;; @@ -1553,12 +1573,16 @@ _wezterm() { return 0 ;; wezterm__set__working__directory) - opts="-h --help [CWD] [HOST]" + opts="-h --tmux-passthru --help [CWD] [HOST]" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 fi case "${prev}" in + --tmux-passthru) + COMPREPLY=($(compgen -W "disable enable detect" -- "${cur}")) + return 0 + ;; *) COMPREPLY=() ;; diff --git a/assets/shell-completion/fish b/assets/shell-completion/fish index 6da8859ad..9d7f6d78c 100644 --- a/assets/shell-completion/fish +++ b/assets/shell-completion/fish @@ -161,10 +161,18 @@ complete -c wezterm -n "__fish_seen_subcommand_from cli; and __fish_seen_subcomm complete -c wezterm -n "__fish_seen_subcommand_from imgcat" -l width -d 'Specify the display width; defaults to "auto" which automatically selects an appropriate size. You may also use an integer value `N` to specify the number of cells, or `Npx` to specify the number of pixels, or `N%` to size relative to the terminal width' -r complete -c wezterm -n "__fish_seen_subcommand_from imgcat" -l height -d 'Specify the display height; defaults to "auto" which automatically selects an appropriate size. You may also use an integer value `N` to specify the number of cells, or `Npx` to specify the number of pixels, or `N%` to size relative to the terminal height' -r complete -c wezterm -n "__fish_seen_subcommand_from imgcat" -l position -d 'Set the cursor position prior to displaying the image. The default is to use the current cursor position. Coordinates are expressed in cells with 0,0 being the top left cell position' -r +complete -c wezterm -n "__fish_seen_subcommand_from imgcat" -l tmux-passthru -d 'How to manage passing the escape through to tmux' -r -f -a "{disable ,enable ,detect }" +complete -c wezterm -n "__fish_seen_subcommand_from imgcat" -l max-pixels -d 'Set the maximum number of pixels per image frame. Images will be scaled down so that they do not exceed this size, unless `--no-resample` is also used. The default value matches the limit set by wezterm. Note that resampling the image here will reduce any animated images to a single frame' -r +complete -c wezterm -n "__fish_seen_subcommand_from imgcat" -l resample-format -d 'Specify the image format to use to encode resampled/resized images. The default is to match the input format, but you can choose an alternative format' -r -f -a "{png ,jpeg ,input }" +complete -c wezterm -n "__fish_seen_subcommand_from imgcat" -l resample-filter -d 'Specify the filtering technique used when resizing/resampling images. The default is a reasonable middle ground of speed and quality' -r -f -a "{nearest ,triangle ,catmull-rom ,gaussian ,lanczos3 }" +complete -c wezterm -n "__fish_seen_subcommand_from imgcat" -l resize -d 'Pre-process the image to resize it to the specified dimensions, expressed as eg: 800x600 (width x height). The resize is independent of other parameters that control the image placement and dimensions in the terminal; this is provided as a convenience preprocessing step' -r complete -c wezterm -n "__fish_seen_subcommand_from imgcat" -l no-preserve-aspect-ratio -d 'Do not respect the aspect ratio. The default is to respect the aspect ratio' complete -c wezterm -n "__fish_seen_subcommand_from imgcat" -l no-move-cursor -d 'Do not move the cursor after displaying the image. Note that when used like this from the shell, there is a very high chance that shell prompt will overwrite the image; you may wish to also use `--hold` in that case' complete -c wezterm -n "__fish_seen_subcommand_from imgcat" -l hold -d 'Wait for enter to be pressed after displaying the image' -complete -c wezterm -n "__fish_seen_subcommand_from imgcat" -s h -l help -d 'Print help' +complete -c wezterm -n "__fish_seen_subcommand_from imgcat" -l no-resample -d 'Do not resample images whose frames are larger than the max-pixels value. Note that this will typically result in the image refusing to display in wezterm' +complete -c wezterm -n "__fish_seen_subcommand_from imgcat" -l show-resample-timing -d 'When resampling or resizing, display some diagnostics around the timing/performance of that operation' +complete -c wezterm -n "__fish_seen_subcommand_from imgcat" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c wezterm -n "__fish_seen_subcommand_from set-working-directory" -l tmux-passthru -d 'How to manage passing the escape through to tmux' -r -f -a "{disable ,enable ,detect }" complete -c wezterm -n "__fish_seen_subcommand_from set-working-directory" -s h -l help -d 'Print help' complete -c wezterm -n "__fish_seen_subcommand_from record" -s h -l help -d 'Print help' complete -c wezterm -n "__fish_seen_subcommand_from replay" -l explain -d 'Explain what is being sent/received' diff --git a/assets/shell-completion/zsh b/assets/shell-completion/zsh index dba29e12b..8e4bb381a 100644 --- a/assets/shell-completion/zsh +++ b/assets/shell-completion/zsh @@ -382,16 +382,24 @@ _arguments "${_arguments_options[@]}" \ '--width=[Specify the display width; defaults to "auto" which automatically selects an appropriate size. You may also use an integer value \`N\` to specify the number of cells, or \`Npx\` to specify the number of pixels, or \`N%\` to size relative to the terminal width]:WIDTH: ' \ '--height=[Specify the display height; defaults to "auto" which automatically selects an appropriate size. You may also use an integer value \`N\` to specify the number of cells, or \`Npx\` to specify the number of pixels, or \`N%\` to size relative to the terminal height]:HEIGHT: ' \ '--position=[Set the cursor position prior to displaying the image. The default is to use the current cursor position. Coordinates are expressed in cells with 0,0 being the top left cell position]:POSITION: ' \ +'--tmux-passthru=[How to manage passing the escape through to tmux]:TMUX_PASSTHRU:(disable enable detect)' \ +'--max-pixels=[Set the maximum number of pixels per image frame. Images will be scaled down so that they do not exceed this size, unless \`--no-resample\` is also used. The default value matches the limit set by wezterm. Note that resampling the image here will reduce any animated images to a single frame]:MAX_PIXELS: ' \ +'--resample-format=[Specify the image format to use to encode resampled/resized images. The default is to match the input format, but you can choose an alternative format]:RESAMPLE_FORMAT:(png jpeg input)' \ +'--resample-filter=[Specify the filtering technique used when resizing/resampling images. The default is a reasonable middle ground of speed and quality]:RESAMPLE_FILTER:(nearest triangle catmull-rom gaussian lanczos3)' \ +'--resize=[Pre-process the image to resize it to the specified dimensions, expressed as eg\: 800x600 (width x height). The resize is independent of other parameters that control the image placement and dimensions in the terminal; this is provided as a convenience preprocessing step]:WIDTHxHEIGHT: ' \ '--no-preserve-aspect-ratio[Do not respect the aspect ratio. The default is to respect the aspect ratio]' \ '--no-move-cursor[Do not move the cursor after displaying the image. Note that when used like this from the shell, there is a very high chance that shell prompt will overwrite the image; you may wish to also use \`--hold\` in that case]' \ '--hold[Wait for enter to be pressed after displaying the image]' \ -'-h[Print help]' \ -'--help[Print help]' \ +'--no-resample[Do not resample images whose frames are larger than the max-pixels value. Note that this will typically result in the image refusing to display in wezterm]' \ +'--show-resample-timing[When resampling or resizing, display some diagnostics around the timing/performance of that operation]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ '::file_name -- The name of the image file to be displayed. If omitted, will attempt to read it from stdin:_files' \ && ret=0 ;; (set-working-directory) _arguments "${_arguments_options[@]}" \ +'--tmux-passthru=[How to manage passing the escape through to tmux]:TMUX_PASSTHRU:(disable enable detect)' \ '-h[Print help]' \ '--help[Print help]' \ '::cwd -- The directory to specify. If omitted, will use the current directory of the process itself:_files -/' \ diff --git a/ci/update-derived-files.sh b/ci/update-derived-files.sh index 4d5323711..f12385e5f 100755 --- a/ci/update-derived-files.sh +++ b/ci/update-derived-files.sh @@ -16,11 +16,20 @@ for mode in copy_mode search_mode ; do echo "\`\`\`" >> $fname done -cargo run --example narrow $PWD/target/debug/wezterm --help | ./target/debug/strip-ansi-escapes > docs/examples/cmd-synopsis-wezterm--help.txt +# For whatever reason, running --help on macOS vs. Linux results in different +# opinions on leading/trailing whitespace. In order to minimize diffs and +# be more consistent, explicitly trim leading/trailing space from the +# output stream. +# +trim_file() { + perl -0777 -pe 's/^\n+|\n\K\n+$//g' +} + +cargo run --example narrow $PWD/target/debug/wezterm --help | ./target/debug/strip-ansi-escapes | trim_file > docs/examples/cmd-synopsis-wezterm--help.txt for cmd in start ssh serial connect ls-fonts show-keys imgcat set-working-directory record replay ; do fname="docs/examples/cmd-synopsis-wezterm-${cmd}--help.txt" - cargo run --example narrow $PWD/target/debug/wezterm $cmd --help | ./target/debug/strip-ansi-escapes > $fname + cargo run --example narrow $PWD/target/debug/wezterm $cmd --help | ./target/debug/strip-ansi-escapes | trim_file > $fname done for cmd in \ @@ -42,5 +51,5 @@ for cmd in \ split-pane \ ; do fname="docs/examples/cmd-synopsis-wezterm-cli-${cmd}--help.txt" - cargo run --example narrow $PWD/target/debug/wezterm cli $cmd --help | ./target/debug/strip-ansi-escapes > $fname + cargo run --example narrow $PWD/target/debug/wezterm cli $cmd --help | ./target/debug/strip-ansi-escapes | trim_file > $fname done diff --git a/docs/changelog.md b/docs/changelog.md index 6a879fd3b..c7798c19d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -41,6 +41,11 @@ As features stabilize some brief notes about them will accumulate here. in order to avoid the shell/prompt from mangling the image after it is printing. Support for this has limitations and will not take effect when the new `--position` argument is used. #3624 +* [wezterm imgcat](cli/imgcat.md) will now resample very large images in + order to increase the chances of successfully displaying an arbitrary image. + In addition, there are now a number of options for explicitly resizing + as a preprocessing step, and controlling the filtering and format used + by the resizing, along with showing diagnostics around the resize operation. #3264 * Color schemes: [Ef-Cyprus](colorschemes/e/index.md#ef-cyprus), [Ef-Day](colorschemes/e/index.md#ef-day), [Ef-Deuteranopia-Dark](colorschemes/e/index.md#ef-deuteranopia-dark), diff --git a/docs/examples/cmd-synopsis-wezterm-imgcat--help.txt b/docs/examples/cmd-synopsis-wezterm-imgcat--help.txt index 594508484..ef7deba7f 100644 --- a/docs/examples/cmd-synopsis-wezterm-imgcat--help.txt +++ b/docs/examples/cmd-synopsis-wezterm-imgcat--help.txt @@ -3,8 +3,9 @@ Output an image to the terminal Usage: wezterm imgcat [OPTIONS] [FILE_NAME] Arguments: - [FILE_NAME] The name of the image file to be displayed. If omitted, will - attempt to read it from stdin + [FILE_NAME] + The name of the image file to be displayed. If omitted, will attempt + to read it from stdin Options: --width @@ -12,24 +13,84 @@ Options: selects an appropriate size. You may also use an integer value `N` to specify the number of cells, or `Npx` to specify the number of pixels, or `N%` to size relative to the terminal width + --height Specify the display height; defaults to "auto" which automatically selects an appropriate size. You may also use an integer value `N` to specify the number of cells, or `Npx` to specify the number of pixels, or `N%` to size relative to the terminal height + --no-preserve-aspect-ratio Do not respect the aspect ratio. The default is to respect the aspect ratio + --position Set the cursor position prior to displaying the image. The default is to use the current cursor position. Coordinates are expressed in cells with 0,0 being the top left cell position + --no-move-cursor Do not move the cursor after displaying the image. Note that when used like this from the shell, there is a very high chance that shell prompt will overwrite the image; you may wish to also use `--hold` in that case + --hold Wait for enter to be pressed after displaying the image + + --tmux-passthru + How to manage passing the escape through to tmux + + [possible values: disable, enable, detect] + + --max-pixels + Set the maximum number of pixels per image frame. Images will be + scaled down so that they do not exceed this size, unless + `--no-resample` is also used. The default value matches the limit set + by wezterm. Note that resampling the image here will reduce any + animated images to a single frame + + [default: 25000000] + + --no-resample + Do not resample images whose frames are larger than the max-pixels + value. Note that this will typically result in the image refusing to + display in wezterm + + --resample-format + Specify the image format to use to encode resampled/resized images. + The default is to match the input format, but you can choose an + alternative format + + [default: input] + [possible values: png, jpeg, input] + + --resample-filter + Specify the filtering technique used when resizing/resampling images. + The default is a reasonable middle ground of speed and quality. + + See + + for examples of the different techniques and their tradeoffs. + + [default: catmull-rom] + [possible values: nearest, triangle, catmull-rom, gaussian, lanczos3] + + --resize + Pre-process the image to resize it to the specified dimensions, + expressed as eg: 800x600 (width x height). The resize is independent + of other parameters that control the image placement and dimensions in + the terminal; this is provided as a convenience preprocessing step. + + Resizing animated images will reduce the image to a single frame. + + The `--resample-filter` and `--resample-format` options give some + control over the quality of the resizing operation and the image + format used. + + --show-resample-timing + When resampling or resizing, display some diagnostics around the + timing/performance of that operation + -h, --help - Print help + Print help (see a summary with '-h') diff --git a/docs/examples/cmd-synopsis-wezterm-set-working-directory--help.txt b/docs/examples/cmd-synopsis-wezterm-set-working-directory--help.txt index bd79e7166..91f619a51 100644 --- a/docs/examples/cmd-synopsis-wezterm-set-working-directory--help.txt +++ b/docs/examples/cmd-synopsis-wezterm-set-working-directory--help.txt @@ -1,7 +1,7 @@ Advise the terminal of the current working directory by emitting an OSC 7 escape sequence -Usage: wezterm set-working-directory [CWD] [HOST] +Usage: wezterm set-working-directory [OPTIONS] [CWD] [HOST] Arguments: [CWD] The directory to specify. If omitted, will use the current directory @@ -10,4 +10,8 @@ Arguments: system hostname will be used Options: - -h, --help Print help + --tmux-passthru + How to manage passing the escape through to tmux [possible values: + disable, enable, detect] + -h, --help + Print help diff --git a/wezterm/src/main.rs b/wezterm/src/main.rs index 1aa917735..d5aabe459 100644 --- a/wezterm/src/main.rs +++ b/wezterm/src/main.rs @@ -183,6 +183,56 @@ struct ImgCatCommand { #[arg(long, value_parser)] tmux_passthru: Option, + /// Set the maximum number of pixels per image frame. + /// Images will be scaled down so that they do not exceed this size, + /// unless `--no-resample` is also used. + /// The default value matches the limit set by wezterm. + /// Note that resampling the image here will reduce any animated + /// images to a single frame. + #[arg(long, default_value = "25000000")] + max_pixels: usize, + + /// Do not resample images whose frames are larger than the + /// max-pixels value. + /// Note that this will typically result in the image refusing + /// to display in wezterm. + #[arg(long)] + no_resample: bool, + + /// Specify the image format to use to encode resampled/resized + /// images. The default is to match the input format, but you + /// can choose an alternative format. + #[arg(long, default_value = "input")] + resample_format: ResampleImageFormat, + + /// Specify the filtering technique used when resizing/resampling + /// images. The default is a reasonable middle ground of speed + /// and quality. + /// + /// See + /// for examples of the different techniques and their tradeoffs. + #[arg(long, default_value = "catmull-rom")] + resample_filter: ResampleFilter, + + /// Pre-process the image to resize it to the specified dimensions, + /// expressed as eg: 800x600 (width x height). + /// The resize is independent of other parameters that control + /// the image placement and dimensions in the terminal; this is provided + /// as a convenience preprocessing step. + /// + /// Resizing animated images will reduce the image to a single frame. + /// + /// The `--resample-filter` and `--resample-format` options give + /// some control over the quality of the resizing operation and + /// the image format used. + #[arg(long, name="WIDTHxHEIGHT", value_parser=ValueParser::new(width_x_height))] + resize: Option, + + /// When resampling or resizing, display some diagnostics + /// around the timing/performance of that operation. + #[arg(long)] + show_resample_timing: bool, + /// The name of the image file to be displayed. /// If omitted, will attempt to read it from stdin. #[arg(value_parser, value_hint=ValueHint::FilePath)] @@ -206,15 +256,54 @@ fn x_comma_y(arg: &str) -> Result { })?; Ok(ImagePosition { x, y }) } else { - Err(format!("Expected name=value, but got {}", arg)) + Err(format!("Expected x,y, but got {}", arg)) } } -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] +struct ImageDimension { + width: u32, + height: u32, +} + +fn width_x_height(arg: &str) -> Result { + if let Some(eq) = arg.find('x') { + let (left, right) = arg.split_at(eq); + let width = left.parse().map_err(|err| { + format!("Expected WxH to be integer values, got {arg}. '{left}': {err:#}") + })?; + let height = right[1..].parse().map_err(|err| { + format!("Expected WxH to be integer values, got {arg}. '{right}': {err:#}") + })?; + Ok(ImageDimension { width, height }) + } else { + Err(format!("Expected WxH, but got {}", arg)) + } +} + +#[derive(Copy, Clone, Debug, ValueEnum, Default)] +enum ResampleFilter { + Nearest, + Triangle, + #[default] + CatmullRom, + Gaussian, + Lanczos3, +} + +#[derive(Copy, Clone, Debug, ValueEnum, Default)] +enum ResampleImageFormat { + Png, + Jpeg, + #[default] + Input, +} + +#[derive(Debug, Clone, Copy)] pub(crate) struct ImageInfo { pub width: u32, pub height: u32, - pub _format: image::ImageFormat, + pub format: image::ImageFormat, } impl ImgCatCommand { @@ -292,16 +381,78 @@ impl ImgCatCommand { let reader = image::io::Reader::new(std::io::Cursor::new(data)).with_guessed_format()?; let format = reader .format() - .ok_or_else(|| anyhow::anyhow!("unknown format!?"))?; + .ok_or_else(|| anyhow::anyhow!("unknown image format!?"))?; let (width, height) = reader.into_dimensions()?; Ok(ImageInfo { width, height, - _format: format, + format, }) } - fn run(&self) -> anyhow::Result<()> { + fn resize_image( + &self, + data: &[u8], + target_width: u32, + target_height: u32, + image_info: ImageInfo, + ) -> anyhow::Result<(Vec, ImageInfo)> { + let start = std::time::Instant::now(); + let im = image::load_from_memory(data).with_context(|| match self.file_name.as_ref() { + Some(file_name) => format!("loading image from file {file_name:?}"), + None => format!("loading image from stdin"), + })?; + if self.show_resample_timing { + eprintln!( + "loading image took {:?} for {} stored bytes -> {image_info:?}", + start.elapsed(), + data.len() + ); + } + + let start = std::time::Instant::now(); + use image::imageops::FilterType; + let filter = match self.resample_filter { + ResampleFilter::Nearest => FilterType::Nearest, + ResampleFilter::Triangle => FilterType::Triangle, + ResampleFilter::CatmullRom => FilterType::CatmullRom, + ResampleFilter::Gaussian => FilterType::Gaussian, + ResampleFilter::Lanczos3 => FilterType::Lanczos3, + }; + let im = im.resize_to_fill(target_width, target_height, filter); + if self.show_resample_timing { + eprintln!("resizing took {:?}", start.elapsed()); + } + + let mut data = vec![]; + let start = std::time::Instant::now(); + + let output_format = match self.resample_format { + ResampleImageFormat::Png => image::ImageFormat::Png, + ResampleImageFormat::Jpeg => image::ImageFormat::Jpeg, + ResampleImageFormat::Input => image_info.format, + }; + im.write_to(&mut std::io::Cursor::new(&mut data), output_format) + .with_context(|| format!("encoding resampled image as {output_format:?}"))?; + + let new_info = ImageInfo { + width: target_width, + height: target_height, + format: output_format, + }; + + if self.show_resample_timing { + eprintln!( + "encoding took {:?} to produce {} stored bytes -> {new_info:?}", + start.elapsed(), + data.len() + ); + } + + Ok((data, new_info)) + } + + fn get_image_data(&self) -> anyhow::Result<(Vec, ImageInfo)> { let mut data = Vec::new(); if let Some(file_name) = self.file_name.as_ref() { let mut f = std::fs::File::open(file_name) @@ -312,6 +463,34 @@ impl ImgCatCommand { stdin.read_to_end(&mut data)?; } + let image_info = Self::image_dimensions(&data)?; + + let (data, image_info) = if let Some(dimension) = self.resize { + self.resize_image(&data, dimension.width, dimension.height, image_info)? + } else { + (data, image_info) + }; + + let total_pixels = image_info.width.saturating_mul(image_info.height) as usize; + + if !self.no_resample && total_pixels > self.max_pixels { + let max_area = self.max_pixels as f32; + let area = total_pixels as f32; + + let scale = area / max_area; + + let target_width = (image_info.width as f32 / scale).floor() as u32; + let target_height = (image_info.height as f32 / scale).floor() as u32; + + self.resize_image(&data, target_width, target_height, image_info) + } else { + Ok((data, image_info)) + } + } + + fn run(&self) -> anyhow::Result<()> { + let (data, image_info) = self.get_image_data()?; + let caps = Capabilities::new_from_env()?; let mut term = termwiz::terminal::new_terminal(caps)?; term.set_raw_mode()?; @@ -357,13 +536,12 @@ impl ImgCatCommand { let (begin, end) = self.tmux_passthru.unwrap_or_default().get(); - let image_dims = Self::image_dimensions(&data) - .map(|info| self.compute_image_cell_dimensions(info, term_size)); + let image_dims = self.compute_image_cell_dimensions(image_info, term_size); - if let (Ok((_cursor_x, cursor_y)), true) = (&image_dims, needs_force_cursor_move) { + if let ((_cursor_x, cursor_y), true) = (image_dims, needs_force_cursor_move) { // Before we emit the image, we need to emit some new lines so that // if the image would scroll the display, things end up in the right place - let new_lines = "\n".repeat(*cursor_y); + let new_lines = "\n".repeat(cursor_y); print!("{new_lines}"); // and move back up again. @@ -371,7 +549,7 @@ impl ImgCatCommand { // column as a result of doing this. term.render(&[Change::CursorPosition { x: Position::Absolute(0), - y: Position::Relative(-1 * (*cursor_y as isize)), + y: Position::Relative(-1 * (cursor_y as isize)), }])?; } @@ -389,12 +567,12 @@ impl ImgCatCommand { ))); println!("{begin}{osc}{end}"); - if let (Ok((_cursor_x, cursor_y)), true) = (&image_dims, needs_force_cursor_move) { + if let ((_cursor_x, cursor_y), true) = (image_dims, needs_force_cursor_move) { // tell the terminal that doesn't fully understand the image sequence // to move the cursor to where it should end up term.render(&[Change::CursorPosition { x: Position::Absolute(0), - y: Position::Relative(*cursor_y as isize), + y: Position::Relative(cursor_y as isize), }])?; } else if self.position.is_some() { print!("{restore_cursor}");