fix(console): correct CRT scanline alternation (was skipping rows)

The per-line scanline shade alternated on rowIndex = floor((cumulativeY +
yOffset) / lineHeight + 0.5), but cumulativeY includes the 2px inter-line gap
while the divisor is lineHeight alone. That drifts ~0.14 row per line and, with
the rounding, jumps the index by 2 every ~7 rows — so two adjacent rows get the
same shade and the alternation visibly breaks in patches.

Replace it with a true per-visual-row counter (one increment per drawn sub-row),
seeded with the first on-screen line's sub-row ordinal so the shading stays
stable while scrolling. Parity now alternates exactly every row.

Full-node + lite build clean; source hygiene clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-01 23:25:09 -05:00
parent f0243307f1
commit c5ef9e42ed

View File

@@ -836,6 +836,14 @@ void ConsoleTab::drawVisibleLines(float padX, float lineHeight, bool hasTextFilt
// mid-draw. -1 = none.
int pendingFoldToggle = -1;
// CRT scanline parity is a true per-visual-row counter (each drawn sub-row = one row).
// Deriving it from cumulativeY/lineHeight drifted (cumulativeY includes the inter-line
// gap) and skipped rows, breaking the alternation. Seed it with the sub-row ordinal of
// the first on-screen line so the shading stays stable across scrolling.
int scanRowOrdinal = 0;
for (int k = 0; k < first_visible && k < static_cast<int>(layout_.segments.size()); k++)
scanRowOrdinal += static_cast<int>(layout_.segments[k].size());
int last_rendered_vi = first_visible - 1;
for (int vi = first_visible; vi < visible_count; vi++) {
if (vi < static_cast<int>(layout_.cumulativeY.size()) &&
@@ -917,9 +925,9 @@ void ConsoleTab::drawVisibleLines(float padX, float lineHeight, bool hasTextFilt
const char* segEnd = line.text.c_str() + seg.byteEnd;
if (s_scanline_enabled && lineHeight > 0.0f) {
int rowIndex = static_cast<int>(std::floor((layout_.cumulativeY[vi] + seg.yOffset) / lineHeight + 0.5f));
scanline_rows_.push_back({rowY, rowY + seg.height, rowIndex});
scanline_rows_.push_back({rowY, rowY + seg.height, scanRowOrdinal});
}
++scanRowOrdinal; // one per visual sub-row, so parity always alternates
// Selection highlight for this sub-row
if (lineSelected && selByteStart < seg.byteEnd && selByteEnd > seg.byteStart) {