Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions src/XTerm.NET.Tests/Graphics/ImageCellLifetimeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,62 @@ private static int ImageCellCount(Terminal terminal)
return count;
}

[Fact]
public void Sixel_over_a_sixel_replaces_the_runs_it_covers()
{
var terminal = Fresh();
WriteSixel(terminal);
var first = ImageAssertions.ImageAt(terminal, 0, 0);

// The same picture drawn again at the same place: one run per row, not two
terminal.Write($"{Esc}[1;1H");
WriteSixel(terminal);

var line = terminal.Buffer.Lines[terminal.Buffer.YBase]!;
Assert.Single(line.Placements);
Assert.Single(line.Images);
Assert.NotSame(first, ImageAssertions.ImageAt(terminal, 0, 0));
Assert.Equal(8, ImageCellCount(terminal));
}

[Fact]
public void Sixel_over_part_of_a_sixel_keeps_the_rest_of_it()
{
var terminal = Fresh();
WriteSixel(terminal);
var first = ImageAssertions.ImageAt(terminal, 0, 0);

// A one-cell-wide picture on the second column, over the right half of the first one
terminal.Write($"{Esc}[1;2H");
WriteSixel(terminal, "#0;2;0;100;0!2~-!2~");

var line = terminal.Buffer.Lines[terminal.Buffer.YBase]!;
Assert.Equal(2, line.Placements.Count);
Assert.Same(first, ImageAssertions.ImageAt(terminal, 0, 0));
Assert.NotSame(first, ImageAssertions.ImageAt(terminal, 1, 0));
Assert.Equal(8, ImageCellCount(terminal));
}

[Fact]
public void Repainting_a_cell_many_times_does_not_accumulate_runs()
{
var terminal = Fresh();
WriteSixel(terminal);

for (int i = 0; i < 50; i++)
{
terminal.Write($"{Esc}[1;2H");
WriteSixel(terminal, "#0;2;0;0;100!2~-!2~");
}

for (int row = 0; row < 4; row++)
{
var line = terminal.Buffer.Lines[terminal.Buffer.YBase + row]!;
Assert.Equal(2, line.Placements.Count);
Assert.Equal(2, line.Images.Count);
}
}

[Fact]
public void Printing_over_a_tile_replaces_it_with_the_character()
{
Expand Down
9 changes: 9 additions & 0 deletions src/XTerm.NET/InputHandler.KittyGraphics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1040,6 +1040,15 @@ private void PlaceImage(Graphics.ImagePlacement placement,
if (line is null)
break;

// Sixel is content, so a picture landing on cells that already show one replaces that
// part of it, exactly as printing a character there does. Without this every Sixel a
// program draws over an earlier one is kept beneath it: a line in the alternate screen
// never dies, so an application repainting small regions (a cursor, a hover highlight)
// grew an unbounded stack of runs per line, each drawn every frame and each pinning its
// picture in memory. Kitty placements are overlays ordered by z-index and are left alone.
if (kind == Graphics.PlacementKind.Sixel)
line.SplitPlacementsOver(startCol, placement.Cols);

// One run per line. Cols is the picture's NATURAL width — deliberately NOT clipped to
// the terminal, so a window widened later reveals more of the picture rather than
// having lost it.
Expand Down
Loading