Repository navigation
A Sixel replaces the Sixel runs it lands on - #175
Conversation
Placing a Sixel added a run to each line it covered but never removed the Sixel runs already under it. Printing a character over a picture correctly splits and replaces that part; a picture over a picture did not. Every small repaint an application sent (a cursor move, a hover highlight) left the earlier pieces stacked on the line, and in the alternate screen lines never die, so the per-line run list, the retained images and the per-frame draw work all grew without bound. Avalonia.Terminal got slower by the minute under Avalonia.SixelBuffer; Windows Terminal, which replaces covered cells, did not. A Sixel run now splits the Sixel runs over its columns before it is added, exactly as a printed character does: fully covered runs go, partially covered ones keep their uncovered fragments. Kitty placements are overlays ordered by z-index with their own delete commands and are left alone. A Sixel with background select 1 (transparent unset pixels) therefore replaces what it covers rather than showing the older picture through its holes, which is consistent with treating Sixel as content. Tests: the same picture drawn twice leaves one run and one image per row; a narrow picture over part of a wider one keeps the uncovered fragment; fifty repaints of one cell leave two runs per row. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Perf comparison — this change, against its base3 run(s) of each side, alternating on one machine. Allocation is a count and is gated exactly. Time is a measurement, so its gate is derived from the spread this job just observed in itself rather than fixed in advance.
Each corpus is gated at assemblies measured
Perf comparison — cumulative, everything since 2.0.73 run(s) of each side, alternating on one machine. Allocation is a count and is gated exactly. Time is a measurement, so its gate is derived from the spread this job just observed in itself rather than fixed in advance.
Each corpus is gated at assemblies measured
|
Problem
PlaceImageadded a run to each line a Sixel covered but never removed the Sixel runs already under it. Printing a character over a picture correctly splits and replaces that part of it; a picture over a picture did not.An application that repaints small regions with Sixels (Avalonia.SixelBuffer sends a tiny sixel for every cursor move and hover change) therefore left every earlier picture piece stacked on the line. In the alternate screen, lines never die, so the per-line run list, the retained
TerminalImages, the view's per-image bitmaps, and the per-frame draw work all grew without bound. Avalonia.Terminal got slower by the minute; Windows Terminal, which replaces covered cells, did not.Fix
Before a Sixel run is added to a line, the Sixel runs over its columns are split with
SplitPlacementsOver, exactly as a printed character does. Fully covered runs disappear; partially covered ones keep their uncovered fragments. Kitty placements are overlays ordered by z-index with their own delete commands and are left alone.Semantic note
A Sixel sent with background select 1 (transparent unset pixels) now replaces what it covers rather than showing the older picture through its holes. The DEC behaviour would composite; this is consistent with XTerm.NET already treating Sixel as content that printing replaces.
Tests
Three new cases in
ImageCellLifetimeTests: the same picture drawn twice leaves one run and one image per row; a one-cell picture over part of a wider one keeps the uncovered fragment; fifty repaints of one cell leave two runs per row rather than fifty-two. Full suite: 2301 tests pass.🤖 Generated with Claude Code