yaaaay zooming out once you're already zoomed in and respecting kitty's limits for how big of an image to display

This commit is contained in:
itsjunetime
2025-06-18 13:19:17 -06:00
parent 484d248e26
commit b368f8d41d
3 changed files with 89 additions and 41 deletions
+17 -11
View File
@@ -28,14 +28,25 @@ pub enum MaybeTransferred {
pub enum ConvertedImage {
Generic(Protocol),
Kitty { img: MaybeTransferred, area: Rect }
Kitty {
img: MaybeTransferred,
cell_w: u16,
cell_h: u16
}
}
impl ConvertedImage {
pub fn area(&self) -> Rect {
pub fn w_h(&self) -> (u16, u16) {
match self {
Self::Generic(prot) => prot.area(),
Self::Kitty { img: _, area } => *area
Self::Generic(prot) => {
let a = prot.area();
(a.width, a.height)
}
Self::Kitty {
img: _,
cell_w,
cell_h
} => (*cell_w, *cell_h)
}
}
}
@@ -125,12 +136,6 @@ pub async fn run_conversion_loop(
let txt_img = match picker.protocol_type() {
ProtocolType::Kitty => {
let area = ratatui_image::protocol::ImageSource::round_pixel_size_to_cells(
dyn_img.width(),
dyn_img.height(),
picker.font_size()
);
let rn = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
@@ -149,7 +154,8 @@ pub async fn run_conversion_loop(
img.num_or_id = NumberOrId::Id(NonZeroU32::new(page_num as u32 + 1).unwrap());
ConvertedImage::Kitty {
img: MaybeTransferred::NotYet(img),
area
cell_w: page_info.img_data.cell_w,
cell_h: page_info.img_data.cell_h
}
}
_ => ConvertedImage::Generic(
+14 -3
View File
@@ -10,6 +10,8 @@ use crate::{
FitOrFill, PrerenderLimit, ScaledResult, scale_img_for_area, skip::InterleavedAroundWithMax
};
const KITTY_MAX_W_OR_H: f32 = 10_000.0;
#[derive(Debug)]
pub enum RenderNotif {
Area(Rect),
@@ -313,6 +315,8 @@ pub fn start_rendering(
continue;
};
log::debug!("got pixmap for page {page_num} with WxH {w}x{h}");
rendered.num_search_found = Some(ctx.result_rects.len());
rendered.successful = true;
@@ -465,11 +469,18 @@ fn render_single_page_to_ctx(
let scaled = scale_img_for_area(page_dim, (area_w, area_h), fit_or_fill);
let ScaledResult {
width: surface_w,
height: surface_h,
scale_factor
width: mut surface_w,
height: mut surface_h,
mut scale_factor
} = scaled;
if surface_w > KITTY_MAX_W_OR_H || surface_h > KITTY_MAX_W_OR_H {
let descale = (surface_w / KITTY_MAX_W_OR_H).max(surface_h / KITTY_MAX_W_OR_H);
surface_w /= descale;
surface_h /= descale;
scale_factor /= descale;
}
let colorspace = Colorspace::device_rgb();
let matrix = Matrix::new_scale(scale_factor, scale_factor);
+51 -20
View File
@@ -79,7 +79,7 @@ struct PageConstraints {
struct Zoom {
// just how much 'zoom' you have. Doesn't relate to anything specific yet, except that 0 means
// it fills the screen (instead of fits)
level: u16,
level: i16,
// how many terminal-cells worth of content overflow the left side of the screen (and are thus
// not displayed)
cell_pan_from_left: u16,
@@ -194,33 +194,46 @@ impl Tui {
.as_ref()
.is_some_and(|c| matches!(c, ConvertedImage::Kitty { .. }))
{
let Some(ConvertedImage::Kitty { ref mut img, area }) =
self.rendered[self.page].img
let Some(ConvertedImage::Kitty {
ref mut img,
cell_w,
cell_h
}) = self.rendered[self.page].img
else {
unreachable!()
};
if zoom.level < 0 {
img_area = Rect {
width: img_area.width.saturating_sub((zoom.level * 2).unsigned_abs()).max(1),
x: img_area.x + (zoom.level.unsigned_abs().min(img_area.width / 2)),
..img_area
}
}
// Ugh I don't like this logic. I wish we could simplify it.
let (cell_width, cell_height) = if area.width >= img_area.width
&& area.height >= img_area.height
{
let (new_cell_width, new_cell_height) =
if cell_w >= img_area.width && cell_h >= img_area.height {
(f32::from(img_area.width), f32::from(img_area.height))
} else {
let img_width = f32::from(area.width);
let img_height = f32::from(area.height);
let available_to_real_width_ratio = f32::from(img_area.width) / img_width;
let available_to_real_height_ratio =
f32::from(img_area.height) / img_height;
let img_width = f32::from(cell_w);
let img_height = f32::from(cell_h);
let img_area_width = f32::from(img_area.width);
let img_area_height = f32::from(img_area.height);
let available_to_real_width_ratio = img_area_width / img_width;
let available_to_real_height_ratio = img_area_height / img_height;
if available_to_real_width_ratio > available_to_real_height_ratio {
(img_width, img_height / available_to_real_width_ratio)
(img_width, img_area_height / available_to_real_width_ratio)
} else {
(img_width / available_to_real_height_ratio, img_height)
(img_area_width / available_to_real_height_ratio, img_height)
}
};
let width = (cell_width * f32::from(font_size.0)) as u32;
let height = (cell_height * f32::from(font_size.1)) as u32;
log::debug!("new_cell stuff is {new_cell_width}x{new_cell_height}");
let width = (new_cell_width * f32::from(font_size.0)) as u32;
let height = (new_cell_height * f32::from(font_size.1)) as u32;
self.last_render = LastRender {
rect: size,
@@ -230,10 +243,10 @@ impl Tui {
zoom.cell_pan_from_left = zoom
.cell_pan_from_left
.min(area.width.saturating_sub(cell_width as u16));
.min(cell_w.saturating_sub(new_cell_width as u16));
zoom.cell_pan_from_top = zoom
.cell_pan_from_top
.min(area.height.saturating_sub(cell_height as u16));
.min(cell_h.saturating_sub(new_cell_height as u16));
return KittyDisplay::DisplayImages(vec![KittyReadyToDisplay {
img,
@@ -271,7 +284,7 @@ impl Tui {
take
})
// and map it to their width (in cells on the terminal, not pixels)
.filter_map(|(_, page)| page.img.as_mut().map(|img| (img.area().width, img)))
.filter_map(|(_, page)| page.img.as_mut().map(|img| (img.w_h().0, img)))
// and then take them as long as they won't overflow the available area.
.take_while(|(width, _)| match test_area_w.checked_sub(*width) {
Some(new_val) => {
@@ -336,7 +349,11 @@ impl Tui {
frame.render_widget(Image::new(page_img), img_area);
None
}
ConvertedImage::Kitty { img, area: _ } => Some((img, Position {
ConvertedImage::Kitty {
img,
cell_h: _,
cell_w: _
} => Some((img, Position {
x: img_area.x,
y: img_area.y
}))
@@ -396,7 +413,7 @@ impl Tui {
if page_num >= self.page && page_num <= self.page + self.last_render.pages_shown {
self.last_render.rect = Rect::default();
} else {
let img_w = img.area().width;
let img_w = img.w_h().0;
if img_w <= self.last_render.unused_width {
let num_fit = self.last_render.unused_width / img_w;
if page_num >= self.page && (self.page + num_fit as usize) >= page_num {
@@ -632,6 +649,20 @@ impl Tui {
self.last_render.rect = Rect::default();
Some(InputAction::SwitchRenderZoom(f_or_f))
}
'o' if self.is_kitty => {
if let Some(z) = &mut self.zoom {
z.level = z.level.saturating_add(1);
}
self.last_render.rect = Rect::default();
Some(InputAction::Redraw)
}
'O' if self.is_kitty => {
if let Some(z) = &mut self.zoom {
z.level = z.level.saturating_sub(1);
}
self.last_render.rect = Rect::default();
Some(InputAction::Redraw)
}
'L' if self.is_kitty => {
if let Some(z) = &mut self.zoom {
z.cell_pan_from_left = z.cell_pan_from_left.saturating_add(1);