Skip to content

Commit 78488ef

Browse files
abbayeclaude
andcommitted
fix(CodeEditor.Rendering): gap-only cache build — eliminate stale-cache double-draw blur
Extend the gap-only base pass to the GlyphRun cache build (P1-CE-05). Previously the cache was built with [full-base-token + syntax tokens]. When the stale GlyphRun cache (fast-path B) was replayed via DrawGlyphRun, the base token's GlyphRun was drawn first then overwritten by syntax tokens — opaque so no blur. But the live first-render path (before cache built) used DrawGlyphRun twice on the same pixels, which CAN cause ClearType subpixel artifacts on some DPI/GPU combos. Now both paths are consistent: gap tokens (uncovered spans only) + syntax tokens. Each screen character is drawn exactly once in both live and cached render paths. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 63c9724 commit 78488ef

1 file changed

Lines changed: 24 additions & 7 deletions

File tree

Sources/Editors/WpfHexEditor.Editor.CodeEditor/Controls/CodeEditor.Rendering.cs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2902,15 +2902,32 @@ private void RenderTextContent(DrawingContext dc)
29022902
}
29032903

29042904
// ── P1-CE-05: Build GlyphRun cache after first render ─────────────
2905-
// Cache for the base-pass token too when using external highlighter.
2905+
// Cache gap tokens + syntax tokens so the stale-cache path (DrawGlyphRun)
2906+
// renders each character exactly once — matching the gap-only live pass.
29062907
if (_glyphRenderer != null)
29072908
{
2908-
var allCacheTokens = hasExternalHighlighter
2909-
? Enumerable.Concat(
2910-
new[] { new Helpers.SyntaxHighlightToken(
2911-
0, line.Text.Length, line.Text, EditorForeground) },
2912-
renderTokens)
2913-
: (IEnumerable<Helpers.SyntaxHighlightToken>)renderTokens;
2909+
IEnumerable<Helpers.SyntaxHighlightToken> allCacheTokens;
2910+
if (hasExternalHighlighter)
2911+
{
2912+
// Build gap segments (chars not covered by any syntax token).
2913+
var gapTokens = new List<Helpers.SyntaxHighlightToken>();
2914+
int gc = 0;
2915+
foreach (var tok in renderTokens.OrderBy(t => t.StartColumn))
2916+
{
2917+
int ts2 = Math.Max(0, tok.StartColumn);
2918+
int te2 = Math.Min(line.Text.Length, tok.StartColumn + tok.Length);
2919+
if (ts2 > gc)
2920+
gapTokens.Add(new Helpers.SyntaxHighlightToken(gc, ts2 - gc, line.Text.Substring(gc, ts2 - gc), EditorForeground));
2921+
gc = Math.Max(gc, te2);
2922+
}
2923+
if (gc < line.Text.Length)
2924+
gapTokens.Add(new Helpers.SyntaxHighlightToken(gc, line.Text.Length - gc, line.Text[gc..], EditorForeground));
2925+
allCacheTokens = Enumerable.Concat(gapTokens, renderTokens);
2926+
}
2927+
else
2928+
{
2929+
allCacheTokens = renderTokens;
2930+
}
29142931

29152932
line.GlyphRunCache = _glyphRenderer.BuildLineGlyphRuns(allCacheTokens, SyntaxUrlColor, line.Text);
29162933
line.IsGlyphCacheDirty = false;

0 commit comments

Comments
 (0)