Skip to content

Fix 50 bugs and add 50 performance optimizations in Sixel and Kitty implementations - #67

Merged
Marc-André Moreau (mamoreau-devolutions) merged 6 commits into
masterfrom
copilot/sixel-and-kitty-fixes-and-optimizations
Nov 12, 2026
Merged

Marc-André Moreau (mamoreau-devolutions) merged 6 commits into
masterfrom
copilot/sixel-and-kitty-fixes-and-optimizations

Conversation

@mamoreau-devolutions

Copy link
Copy Markdown
Contributor

Summary

Completes 5 iterative rounds of Sixel and Kitty graphics/keyboard protocol bug fixes and performance optimizations across Devolutions.Terminal.Core, Devolutions.Terminal.Render, Devolutions.Terminal.Control, Devolutions.Terminal.Automation, and docs/advanced-vt-protocols.md (50 bug fixes + 50 performance improvements).

Highlights by Round

Round 1 — Parser & Sixel Decoder

  • Bug fixes (1–10): Ignore C0 cursor/scroll controls during DCS passthrough; ignore DEL (0x7F) in APC; prevent BEL from terminating StringIgnore on oversized APC strings and handle non-\ escapes in StringEscape; ignore whitespace/C0 inside Sixel !Pn<sixel> and parameter lists; preserve parameter indices on leading ;; default omitted DECGRA Pad to 1 and ignore mid-stream " mutations; charge _remainingPixelWrites on '?' repeats to prevent CPU DoS; wrap color register indices modulo 256 and HLS hues modulo 360°; resolve WrapPending and report post-scroll AnchorRow in DispatchSixel.
  • Performance (1–10): Vectorized SearchValues<byte> bulk scanning for DCS and APC payloads; zero-allocation TerminateDcs span dispatch and pooled FinishApc UTF-8 decoding; static DefaultPalette, stackalloc parameter parsing, BitOperations.PopCount, fast 1:1 pixel writes, ArrayPool<ushort> Sixel working buffer with single-pass background fill, DECGRA pre-sizing, and PercentToByteTable LUT.

Round 2 — Kitty Graphics Decoder, Chunking & Store

  • Bug fixes (11–20): Carry-buffered non-4-char-aligned m=1/m=0 chunked base64 reassembly; pre-scan i=, I=, and q= on parse failures; inherit q= and I= across continuation chunk errors; abort incomplete chunked transfers when non-continuation actions (a=d, a=p) arrive; validate and resolve I=<uint32> image numbers; replace existing non-zero (imageId, placementId) overlays on active buffer; reference-count shared KittyImageData bytes across placements; validate o=, C=, m=, and q=; support d=i/d=I with p= and free unreferenced data on d=P; scope d=a/d=A to the active buffer.
  • Performance (11–20): Zero-string Convert.TryFromBase64Chars; direct streaming base64 quantum decode into _kittyChunkPayload; ArrayPool<byte> inflate scratch buffer; exact-size raw f=24/f=32 zlib inflate without MemoryStream; zero-copy f=32 ownership transfer; Vector128.Shuffle SIMD RGB24 -> RGBA32 expansion; zero-alloc comma key scanning and inline integer parsers; struct KittyContinuationChunk fast path; Utf8.TryWrite stackalloc APC responses; O(1) LinkedList<uint> store eviction.

Round 3 — Skia Renderer, Planner & Frame Differ

  • Bug fixes (21–30): Compare CellGeometry, image data identity, and all KittyImage placement fields in TerminalFrameDiffer; support partial Kitty crops (X/Y/w/h individually); render nothing on out-of-bounds CropX/CropY; clip viewport-overflowing Sixel/Kitty overlays instead of squishing aspect ratio; cull off-screen scrollback images before DecodeImage; scale natural Kitty dimensions and pixel offsets by PhysicalPixel on HiDPI displays; clamp PixelOffsetX/PixelOffsetY within a single cell; sort negative-z placements by (ZIndex, Id); fix Dictionary.First() cache eviction with true LinkedList LRU; clear cached images and _invalidImages in Invalidate().
  • Performance (21–30): Eliminate per-frame LINQ/HashSet/closure allocations in DrawImages; direct GetPixelSpan() Sixel and Kitty RGBA blits without SKColor[] LOH allocations; span-based DecodeCodecImage; shared SKBitmap cache keyed by underlying pixel-data identity (ReferenceEqualityComparer); early exit before Save/ClipRect/Restore when empty; skip sort when monotonic; guard stale-cache sweep; zero-alloc FilterActiveBufferImages in TerminalRenderPlanner; fast TerminalFrameDiffer checks.

Round 4 — Kitty Keyboard Protocol & Buffer Image Anchors

  • Bug fixes (31–40): Separate primary and alternate screen Kitty keyboard flag stacks; reset flags to None when popping past bottom and evict oldest entry on full 16-entry stack; validate CSI = mode (1/2/3); add Kitty functional key codepoints (F13–F24, keypad, lock/system, and modifier keys); colon-join multi-rune associated text; lowercase non-ASCII Unicode base codepoints; emit SS3 P/Q/S for unmodified F1/F2/F4; gate EncodeKittyTextInput on ReportAllKeysAsEscapeCodes and suppress duplicate OnTextInput on modified Kitty keys; track (Key, PhysicalKey) in ProcessKeyUpInput to suppress orphan releases; validate raw s/v dimensions before zlib decompression.
  • Performance (31–40): ImageAnchorLayoutVersion O(1) gate on PruneEvictedImages; viewport-first scan + monotonic O(log N) binary search in TryResolveImageAnchor; zero-LINQ ExtractBufferAnchors on resize; min/max LogicalLineId fast skip in Reflow/LastTextLine; reuse pre-resolved anchors in CreateSnapshotImages; stackalloc ReapUnplacedKittyImages; closure-free RemoveKittyOverlays; span-hoisted SixelImage.ToRgba32; layout-version sync in AddImage; zero-LINQ/zero-Enum.ToString() CreateObservedImages in Automation.

Round 5 — Sixel/Kitty Lifecycle, Screen Erase & Blitter Optimizations

  • Bug fixes (41–50): Implement XTSMGRAPHICS (CSI ? Pi;Pa;Pv S); implement DECSET/DECRST/DECRQM 1070 (private Sixel palette) and 8452 (Sixel cursor-right mode); avoid advancing auto Kitty image ID on a=q queries or rejected payloads; preserve primary a=t images across \e[?1049h alternate-screen entry; clear erased viewport overlays on CSI 0/1/2 J and prune scrollback on CSI 3 J; resolve WrapPending and aspect-scaled cursor rows (c > 0, r == 0) on Kitty a=T; validate unsigned Kitty control values and allow tall/wide r/c placements beyond current viewport size; use SKAlphaType.Opaque for opaque codec images (e.g. JPEG); honor DECOM in DECSDM; clamp non-reflowed alternate-buffer anchors on narrowing.
  • Performance (41–50): Single-pass stackalloc formatting + cached literals in KeyMapper.EncodeKitty, EncodeLegacyFunctionalKey, and EncodeKittyTextInput; boxing-free IsTextInputCandidate; pre-built CSI ? u byte response table; consecutive identical sixel byte run coalescing; fast '?' skip and contiguous '~' fill in WriteSixel; Utf8.TryWrite stackalloc XTSMGRAPHICS responses; SKAlphaType.Opaque tagging for opaque Sixel and f=24 Kitty bitmaps; branchless 257-entry stackalloc Sixel palette LUT in Skia; bounded TextBuffer.AdvanceLines batching.

Validation

  • dotnet build Devolutions.Terminal.slnx -c Debug (0 warnings, 0 errors)
  • dotnet build Devolutions.Terminal.slnx -c Release (0 warnings, 0 errors)
  • dotnet test Devolutions.Terminal.slnx -c Release — 3,360 passed, 0 failed across all test suites (Core, Render, Control, Automation, Connection, App, UI, Settings, Settings.Editor, Cli, Package, Broker, Compatibility)

…tions

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…mizations

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…ng optimizations

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
… optimizations

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…oard/blitter optimizations

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…and TermControl

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@mamoreau-devolutions
Marc-André Moreau (mamoreau-devolutions) merged commit bf3425a into master Nov 12, 2026
77 of 78 checks passed
@mamoreau-devolutions
Marc-André Moreau (mamoreau-devolutions) deleted the copilot/sixel-and-kitty-fixes-and-optimizations branch November 12, 2026 15:57

This branch was successfully deployed

1 active deployment
publish-dry-run — 44edaaaa Deployed Nov 12, 2026 by mamoreau-devolutions via MSI packages #400
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant