mirror of
https://github.com/wez/wezterm.git
synced 2024-08-16 17:50:28 +03:00
fix kitty image protocol display parameters
This commit is contained in:
parent
e7111a2bfa
commit
500f84617e
@ -22,17 +22,19 @@ pub struct ImageAttachParams {
|
|||||||
pub image_height: u32,
|
pub image_height: u32,
|
||||||
|
|
||||||
/// Dimensions of the area of the image to be displayed, in pixels
|
/// Dimensions of the area of the image to be displayed, in pixels
|
||||||
pub source_width: u32,
|
pub source_width: Option<u32>,
|
||||||
pub source_height: u32,
|
pub source_height: Option<u32>,
|
||||||
|
|
||||||
/// Origin of the source data region, top left corner in pixels
|
/// Origin of the source data region, top left corner in pixels
|
||||||
pub source_origin_x: u32,
|
pub source_origin_x: u32,
|
||||||
pub source_origin_y: u32,
|
pub source_origin_y: u32,
|
||||||
|
|
||||||
/// When rendering in the cell, use this offset from the top left
|
/// When rendering in the cell, use this offset from the top left
|
||||||
/// of the cell
|
/// of the cell. This is only used in the Kitty image protocol.
|
||||||
pub padding_left: u16,
|
/// This should be smaller than the size of the cell. Larger values will
|
||||||
pub padding_top: u16,
|
/// be truncated.
|
||||||
|
pub cell_padding_left: u16,
|
||||||
|
pub cell_padding_top: u16,
|
||||||
|
|
||||||
/// Plane on which to display the image
|
/// Plane on which to display the image
|
||||||
pub z_index: i32,
|
pub z_index: i32,
|
||||||
@ -68,45 +70,59 @@ impl TerminalState {
|
|||||||
let physical_rows = self.screen().physical_rows;
|
let physical_rows = self.screen().physical_rows;
|
||||||
let cell_pixel_width = self.pixel_width / physical_cols;
|
let cell_pixel_width = self.pixel_width / physical_cols;
|
||||||
let cell_pixel_height = self.pixel_height / physical_rows;
|
let cell_pixel_height = self.pixel_height / physical_rows;
|
||||||
|
let cell_padding_left = params
|
||||||
let avail_width = params.image_width.saturating_sub(params.source_origin_x);
|
.cell_padding_left
|
||||||
let avail_height = params.image_height.saturating_sub(params.source_origin_y);
|
.min(cell_pixel_width.saturating_sub(1) as u16);
|
||||||
let source_width = params.source_width.min(params.image_width).min(avail_width);
|
let cell_padding_top = params
|
||||||
let source_height = params
|
.cell_padding_top
|
||||||
|
.min(cell_pixel_height.saturating_sub(1) as u16);
|
||||||
|
//NOTE: review conflicting origin vs drawing going over image
|
||||||
|
let image_max_width = params.image_width.saturating_sub(params.source_origin_x);
|
||||||
|
let image_max_height = params.image_height.saturating_sub(params.source_origin_y);
|
||||||
|
let draw_width = params
|
||||||
|
.source_width
|
||||||
|
.unwrap_or(image_max_width)
|
||||||
|
.min(image_max_width);
|
||||||
|
let draw_height = params
|
||||||
.source_height
|
.source_height
|
||||||
.min(params.image_height)
|
.unwrap_or(image_max_height)
|
||||||
.min(avail_height);
|
.min(image_max_height);
|
||||||
|
|
||||||
let aspect = source_width as f32 / source_height as f32;
|
let (fullcells_width, remainder_width_cell, x_delta_divisor) = params
|
||||||
|
|
||||||
let width_in_cells = params
|
|
||||||
.columns
|
.columns
|
||||||
.unwrap_or_else(|| (source_width as f32 / cell_pixel_width as f32).ceil() as usize);
|
.map(|cols| {
|
||||||
let height_in_cells = params
|
(
|
||||||
|
cols,
|
||||||
|
0,
|
||||||
|
(cols * cell_pixel_width) as u32 * params.image_width / draw_width,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
(
|
||||||
|
draw_width as usize / cell_pixel_width,
|
||||||
|
draw_width as usize % cell_pixel_width,
|
||||||
|
params.image_width,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
let (fullcells_height, remainder_height_cell, y_delta_divisor) = params
|
||||||
.rows
|
.rows
|
||||||
.unwrap_or_else(|| (source_height as f32 / cell_pixel_height as f32).ceil() as usize);
|
.map(|rows| {
|
||||||
|
(
|
||||||
// Figure out the desired pixel dimensions, respecting the original
|
rows,
|
||||||
// aspect of the picture if they specific rows/columns as the max size.
|
0,
|
||||||
let target_pixel_width = if params.columns.is_some() {
|
(rows * cell_pixel_height) as u32 * params.image_height / draw_height,
|
||||||
if source_width > source_height {
|
)
|
||||||
width_in_cells * cell_pixel_width
|
})
|
||||||
} else {
|
.unwrap_or_else(|| {
|
||||||
((height_in_cells * cell_pixel_height) as f32 * aspect).ceil() as usize
|
(
|
||||||
}
|
draw_height as usize / cell_pixel_height,
|
||||||
} else {
|
draw_height as usize % cell_pixel_height,
|
||||||
source_width as usize
|
params.image_height,
|
||||||
};
|
)
|
||||||
let target_pixel_height = if params.rows.is_some() {
|
});
|
||||||
if source_height > source_width {
|
|
||||||
height_in_cells * cell_pixel_height
|
|
||||||
} else {
|
|
||||||
((width_in_cells * cell_pixel_width) as f32 / aspect).ceil() as usize
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
source_height as usize
|
|
||||||
};
|
|
||||||
|
|
||||||
|
let target_pixel_width = fullcells_width * cell_pixel_width + remainder_width_cell;
|
||||||
|
let target_pixel_height = fullcells_height * cell_pixel_height + remainder_height_cell;
|
||||||
let first_row = self.screen().visible_row_to_stable_row(self.cursor.y);
|
let first_row = self.screen().visible_row_to_stable_row(self.cursor.y);
|
||||||
|
|
||||||
let mut ypos = NotNan::new(params.source_origin_y as f32 / params.image_height as f32)
|
let mut ypos = NotNan::new(params.source_origin_y as f32 / params.image_height as f32)
|
||||||
@ -115,6 +131,15 @@ impl TerminalState {
|
|||||||
.context("computing xpos")?;
|
.context("computing xpos")?;
|
||||||
|
|
||||||
let cursor_x = self.cursor.x;
|
let cursor_x = self.cursor.x;
|
||||||
|
|
||||||
|
let width_in_cells = fullcells_width + (remainder_width_cell > 0) as usize;
|
||||||
|
let height_in_cells = fullcells_height + (remainder_height_cell > 0) as usize;
|
||||||
|
let height_in_cells = if params.do_not_move_cursor {
|
||||||
|
height_in_cells.min(self.screen().physical_rows - self.cursor.y as usize)
|
||||||
|
} else {
|
||||||
|
height_in_cells
|
||||||
|
};
|
||||||
|
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"image is {}x{} cells (cell is {}x{}), target pixel dims {}x{}, {:?}, (term is {}x{}@{}x{})",
|
"image is {}x{} cells (cell is {}x{}), target pixel dims {}x{}, {:?}, (term is {}x{}@{}x{})",
|
||||||
width_in_cells,
|
width_in_cells,
|
||||||
@ -130,16 +155,10 @@ impl TerminalState {
|
|||||||
self.pixel_height
|
self.pixel_height
|
||||||
);
|
);
|
||||||
|
|
||||||
let height_in_cells = if params.do_not_move_cursor {
|
let mut remain_y = target_pixel_height;
|
||||||
height_in_cells.min(self.screen().physical_rows - self.cursor.y as usize)
|
|
||||||
} else {
|
|
||||||
height_in_cells
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut remain_y = target_pixel_height as usize;
|
|
||||||
for y in 0..height_in_cells {
|
for y in 0..height_in_cells {
|
||||||
let padding_bottom = cell_pixel_height.saturating_sub(remain_y) as u16;
|
let padding_bottom = cell_pixel_height.saturating_sub(remain_y) as u16;
|
||||||
let y_delta = (remain_y.min(cell_pixel_height) as f32) / (target_pixel_height as f32);
|
let y_delta = (remain_y.min(cell_pixel_height) as f32) / y_delta_divisor as f32;
|
||||||
remain_y = remain_y.saturating_sub(cell_pixel_height);
|
remain_y = remain_y.saturating_sub(cell_pixel_height);
|
||||||
|
|
||||||
let mut xpos = start_xpos;
|
let mut xpos = start_xpos;
|
||||||
@ -152,22 +171,22 @@ impl TerminalState {
|
|||||||
"setting cells for y={} x=[{}..{}]",
|
"setting cells for y={} x=[{}..{}]",
|
||||||
cursor_y,
|
cursor_y,
|
||||||
cursor_x,
|
cursor_x,
|
||||||
cursor_x + width_in_cells
|
cursor_x + fullcells_width
|
||||||
);
|
);
|
||||||
let mut remain_x = target_pixel_width as usize;
|
let mut remain_x = target_pixel_width;
|
||||||
for x in 0..width_in_cells {
|
for x in 0..width_in_cells {
|
||||||
let padding_right = cell_pixel_width.saturating_sub(remain_x) as u16;
|
let padding_right = cell_pixel_width.saturating_sub(remain_x) as u16;
|
||||||
let x_delta = (remain_x.min(cell_pixel_width) as f32) / (target_pixel_width as f32);
|
let x_delta = (remain_x.min(cell_pixel_width) as f32) / x_delta_divisor as f32;
|
||||||
|
remain_x = remain_x.saturating_sub(cell_pixel_width);
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"x_delta {} ({} px), y_delta {} ({} px), padding_right={}, padding_bottom={}",
|
"x_delta {} ({} px), y_delta {} ({} px), padding_right={}, padding_bottom={}",
|
||||||
x_delta,
|
x_delta,
|
||||||
x_delta * source_width as f32,
|
x_delta * x_delta_divisor as f32,
|
||||||
y_delta,
|
y_delta,
|
||||||
y_delta * source_width as f32,
|
y_delta * y_delta_divisor as f32,
|
||||||
padding_right,
|
padding_right,
|
||||||
padding_bottom
|
padding_bottom
|
||||||
);
|
);
|
||||||
remain_x = remain_x.saturating_sub(cell_pixel_width);
|
|
||||||
let mut cell = self
|
let mut cell = self
|
||||||
.screen_mut()
|
.screen_mut()
|
||||||
.get_cell(cursor_x + x, cursor_y)
|
.get_cell(cursor_x + x, cursor_y)
|
||||||
@ -178,8 +197,8 @@ impl TerminalState {
|
|||||||
TextureCoordinate::new(xpos + x_delta, ypos + y_delta),
|
TextureCoordinate::new(xpos + x_delta, ypos + y_delta),
|
||||||
params.data.clone(),
|
params.data.clone(),
|
||||||
params.z_index,
|
params.z_index,
|
||||||
params.padding_left,
|
cell_padding_left,
|
||||||
params.padding_top,
|
cell_padding_top,
|
||||||
padding_right,
|
padding_right,
|
||||||
padding_bottom,
|
padding_bottom,
|
||||||
params.image_id,
|
params.image_id,
|
||||||
@ -202,6 +221,11 @@ impl TerminalState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adjust cursor position if the drawn cells move beyond current cell
|
||||||
|
let x_padding_shift = (draw_width as usize + cell_padding_left as usize
|
||||||
|
> cell_pixel_width * width_in_cells) as i64;
|
||||||
|
let y_padding_shift = (draw_height as usize + cell_padding_top as usize
|
||||||
|
> cell_pixel_height * height_in_cells) as i64;
|
||||||
if !params.do_not_move_cursor {
|
if !params.do_not_move_cursor {
|
||||||
// Sixel places the cursor under the left corner of the image,
|
// Sixel places the cursor under the left corner of the image,
|
||||||
// unless sixel_scrolls_right is enabled.
|
// unless sixel_scrolls_right is enabled.
|
||||||
@ -213,8 +237,8 @@ impl TerminalState {
|
|||||||
|
|
||||||
if bottom_right {
|
if bottom_right {
|
||||||
self.set_cursor_pos(
|
self.set_cursor_pos(
|
||||||
&Position::Relative(width_in_cells as i64),
|
&Position::Relative(width_in_cells as i64 + x_padding_shift),
|
||||||
&Position::Relative(0),
|
&Position::Relative(y_padding_shift),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,12 +132,12 @@ impl TerminalState {
|
|||||||
if let Err(err) = self.assign_image_to_cells(ImageAttachParams {
|
if let Err(err) = self.assign_image_to_cells(ImageAttachParams {
|
||||||
image_width: width as u32,
|
image_width: width as u32,
|
||||||
image_height: height as u32,
|
image_height: height as u32,
|
||||||
source_width: width as u32,
|
source_width: None,
|
||||||
source_height: height as u32,
|
source_height: None,
|
||||||
source_origin_x: 0,
|
source_origin_x: 0,
|
||||||
source_origin_y: 0,
|
source_origin_y: 0,
|
||||||
padding_left: 0,
|
cell_padding_left: 0,
|
||||||
padding_top: 0,
|
cell_padding_top: 0,
|
||||||
z_index: 0,
|
z_index: 0,
|
||||||
columns: None,
|
columns: None,
|
||||||
rows: None,
|
rows: None,
|
||||||
|
@ -35,7 +35,9 @@ impl KittyImageState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn record_id_to_data(&mut self, image_id: u32, data: Arc<ImageData>) {
|
fn record_id_to_data(&mut self, image_id: u32, data: Arc<ImageData>) {
|
||||||
self.remove_data_for_id(image_id);
|
if image_id != 0 {
|
||||||
|
self.remove_data_for_id(image_id);
|
||||||
|
}
|
||||||
self.prune_unreferenced();
|
self.prune_unreferenced();
|
||||||
self.used_memory += data.len();
|
self.used_memory += data.len();
|
||||||
self.id_to_data.insert(image_id, data);
|
self.id_to_data.insert(image_id, data);
|
||||||
@ -98,7 +100,9 @@ impl TerminalState {
|
|||||||
placement,
|
placement,
|
||||||
verbosity
|
verbosity
|
||||||
);
|
);
|
||||||
self.kitty_remove_placement(image_id, placement.placement_id);
|
if image_id != 0 {
|
||||||
|
self.kitty_remove_placement(image_id, placement.placement_id);
|
||||||
|
}
|
||||||
let img = Arc::clone(self.kitty_img.id_to_data.get(&image_id).ok_or_else(|| {
|
let img = Arc::clone(self.kitty_img.id_to_data.get(&image_id).ok_or_else(|| {
|
||||||
anyhow::anyhow!(
|
anyhow::anyhow!(
|
||||||
"no matching image id {} in id_to_data for image_number {:?}",
|
"no matching image id {} in id_to_data for image_number {:?}",
|
||||||
@ -112,12 +116,12 @@ impl TerminalState {
|
|||||||
let info = self.assign_image_to_cells(ImageAttachParams {
|
let info = self.assign_image_to_cells(ImageAttachParams {
|
||||||
image_width,
|
image_width,
|
||||||
image_height,
|
image_height,
|
||||||
source_width: placement.w.unwrap_or(image_width),
|
source_width: placement.w,
|
||||||
source_height: placement.h.unwrap_or(image_height),
|
source_height: placement.h,
|
||||||
source_origin_x: placement.x.unwrap_or(0),
|
source_origin_x: placement.x.unwrap_or(0),
|
||||||
source_origin_y: placement.y.unwrap_or(0),
|
source_origin_y: placement.y.unwrap_or(0),
|
||||||
padding_left: placement.x_offset.unwrap_or(0) as u16,
|
cell_padding_left: placement.x_offset.unwrap_or(0) as u16,
|
||||||
padding_top: placement.y_offset.unwrap_or(0) as u16,
|
cell_padding_top: placement.y_offset.unwrap_or(0) as u16,
|
||||||
data: img,
|
data: img,
|
||||||
style: ImageAttachStyle::Kitty,
|
style: ImageAttachStyle::Kitty,
|
||||||
z_index: placement.z_index.unwrap_or(0),
|
z_index: placement.z_index.unwrap_or(0),
|
||||||
|
@ -132,14 +132,14 @@ impl TerminalState {
|
|||||||
if let Err(err) = self.assign_image_to_cells(ImageAttachParams {
|
if let Err(err) = self.assign_image_to_cells(ImageAttachParams {
|
||||||
image_width: width,
|
image_width: width,
|
||||||
image_height: height,
|
image_height: height,
|
||||||
source_width: width,
|
source_width: None,
|
||||||
source_height: height,
|
source_height: None,
|
||||||
rows: None,
|
rows: None,
|
||||||
columns: None,
|
columns: None,
|
||||||
source_origin_x: 0,
|
source_origin_x: 0,
|
||||||
source_origin_y: 0,
|
source_origin_y: 0,
|
||||||
padding_left: 0,
|
cell_padding_left: 0,
|
||||||
padding_top: 0,
|
cell_padding_top: 0,
|
||||||
data: image_data,
|
data: image_data,
|
||||||
style: ImageAttachStyle::Sixel,
|
style: ImageAttachStyle::Sixel,
|
||||||
z_index: 0,
|
z_index: 0,
|
||||||
|
@ -24,5 +24,29 @@ def write_chunked(**cmd):
|
|||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
cmd.clear()
|
cmd.clear()
|
||||||
|
|
||||||
with open(sys.argv[-1], 'rb') as f:
|
def just_print(img):
|
||||||
write_chunked(a='T', f=100, data=f.read())
|
write_chunked(a='T', f=100, data=img)
|
||||||
|
|
||||||
|
def test_x_y_w_h_c_r(img):
|
||||||
|
write_chunked(a='T', f=100, y=150, h=105, C=1, data=img)
|
||||||
|
write_chunked(a='T', f=100, y=200, w=1, data=img)
|
||||||
|
write_chunked(a='T', f=100, y=200, h=1, data=img)
|
||||||
|
write_chunked(a='T', f=100, x=300, y=100, h=10, w=10, data=img)
|
||||||
|
write_chunked(a='T', f=100, x=300, y=100, h=10, w=10, r=15, data=img)
|
||||||
|
write_chunked(a='T', f=100, x=300, y=100, h=10, w=10, c=1, data=img)
|
||||||
|
write_chunked(a='T', f=100, x=300, y=100, h=10, w=10, r=1, data=img)
|
||||||
|
write_chunked(a='T', f=100, x=300, y=100, h=10, w=10, r=15, c=20, data=img)
|
||||||
|
|
||||||
|
|
||||||
|
def test_cell_offsets(img):
|
||||||
|
write_chunked(a='T', f=100, h=10, w=10, X=2, Y=2, data=img)
|
||||||
|
write_chunked(a='T', f=100, h=20, w=10, X=2, Y=2, data=img)
|
||||||
|
write_chunked(a='T', f=100, h=2, Y=20, data=img)
|
||||||
|
write_chunked(a='T', f=100, h=38, w=2, X=19, data=img)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
with open(sys.argv[-1], 'rb') as f:
|
||||||
|
img = f.read()
|
||||||
|
just_print(img)
|
||||||
|
# test_x_y_w_h_c_r(img)
|
||||||
|
# test_cell_offsets(img)
|
||||||
|
Loading…
Reference in New Issue
Block a user