graphics: a placement with only c= or r= does not keep the image's aspect ratio #42

Closed
opened 2026-10-07 09:34:27 +09:00 by natsukium · 0 comments
Owner

When a placement gives only one of c=/r=, felis resolves the missing axis to that axis's natural size, ceil(image_px / cell_px), independently of the given one. The renderer then scales the image into a box whose aspect ratio is not the image's, so the image is stretched or squashed.

kitty: update_dest_rect (kitty/graphics.c) derives the missing axis from the given one. With only r= given, it computes width_px = (cell.height * rows + cell_y_offset) * src_width / src_height and takes cols = ceil(width_px / cell.width), and symmetrically for c= only. It draws the image at that aspect-preserving pixel size, not at the rounded-up cell box.

Repro: a 101×100 px image at 10×20 cells with r=2. kitty draws it 40.4×40 px and records 5 columns. felis records 11 columns (ceil(101 / 10)) and draws it 110×40 px.

Decide: where the aspect-preserving pixel size lives. The cell extent alone cannot carry it (5 columns is 50 px, not 40.4), so either the renderer needs the request (which axis was given) or the wire needs the destination pixel size. The daemon's cell extent must follow the same rule, because the cursor move and erase use it, and it must still re-resolve on a cell-size change.

When a placement gives only one of `c=`/`r=`, felis resolves the missing axis to that axis's natural size, `ceil(image_px / cell_px)`, independently of the given one. The renderer then scales the image into a box whose aspect ratio is not the image's, so the image is stretched or squashed. **kitty:** `update_dest_rect` (`kitty/graphics.c`) derives the missing axis from the given one. With only `r=` given, it computes `width_px = (cell.height * rows + cell_y_offset) * src_width / src_height` and takes `cols = ceil(width_px / cell.width)`, and symmetrically for `c=` only. It draws the image at that aspect-preserving pixel size, not at the rounded-up cell box. **Repro:** a 101×100 px image at 10×20 cells with `r=2`. kitty draws it 40.4×40 px and records 5 columns. felis records 11 columns (`ceil(101 / 10)`) and draws it 110×40 px. **Decide:** where the aspect-preserving pixel size lives. The cell extent alone cannot carry it (5 columns is 50 px, not 40.4), so either the renderer needs the request (which axis was given) or the wire needs the destination pixel size. The daemon's cell extent must follow the same rule, because the cursor move and erase use it, and it must still re-resolve on a cell-size change.
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
felis-terminal/felis#42
No description provided.