From c2b5cd275b96ecbc704b30cb9a9b68e4547069c1 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Sat, 20 Dec 2025 16:17:40 -0600 Subject: [PATCH] Another underline fix, but this time use the bitmap; consider moving underline production into PaintBufferLine itself --- src/renderer/atlas/AtlasEngine.cpp | 24 +++++++------ src/renderer/atlas/AtlasEngine.h | 2 +- src/renderer/atlas/BackendD3D.cpp | 58 ++++++++++++++++++------------ src/renderer/atlas/common.h | 7 ++-- 4 files changed, 56 insertions(+), 35 deletions(-) diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp index af9a92f89a..2a6458b9d1 100644 --- a/src/renderer/atlas/AtlasEngine.cpp +++ b/src/renderer/atlas/AtlasEngine.cpp @@ -203,7 +203,7 @@ try auto dst = _p.colorBitmap.data() + dstOffset; const auto bytes = count * sizeof(u32); - for (size_t i = 0; i < 2; ++i) + for (size_t i = 0; i < 3; ++i) { // Avoid bumping the colorBitmapGeneration unless necessary. This approx. further halves // the (already small) GPU load. This could easily be replaced with some custom SIMD @@ -353,7 +353,7 @@ CATCH_RETURN() return S_OK; } -void AtlasEngine::_fillColorBitmap(const size_t y, const size_t x1, const size_t x2, const u32 fgColor, const u32 bgColor) noexcept +void AtlasEngine::_fillColorBitmap(const size_t y, const size_t x1, const size_t x2, const u32 fgColor, const u32 bgColor, const u32 ulColor) noexcept { const auto bitmap = _p.colorBitmap.begin() + _p.colorBitmapRowStride * y; const auto shift = gsl::narrow_cast(_p.rows[y]->lineRendition != LineRendition::SingleWidth); @@ -363,10 +363,11 @@ void AtlasEngine::_fillColorBitmap(const size_t y, const size_t x1, const size_t const u32 colors[] = { u32ColorPremultiply(bgColor), fgColor, + ulColor, }; // This fills the color in the background bitmap, and then in the foreground bitmap. - for (size_t i = 0; i < 2; ++i) + for (size_t i = 0; i < 3; ++i) { const auto color = colors[i]; @@ -428,7 +429,7 @@ try { const auto isFinalRow = y == hiEnd.y; const auto end = isFinalRow ? std::min(hiEnd.x, x2) : x2; - _fillColorBitmap(row, x1, end, fgColor, bgColor); + _fillColorBitmap(row, x1, end, fgColor, bgColor, fgColor); // Return early if we couldn't paint the whole region (either this was not the last row, or // it was the last row but the highlight ends outside of our x range.) @@ -451,7 +452,7 @@ try const auto isEndInside = y == hiEnd.y && hiEnd.x <= x2; if (isStartInside && isEndInside) { - _fillColorBitmap(row, hiStart.x, static_cast(hiEnd.x), fgColor, bgColor); + _fillColorBitmap(row, hiStart.x, static_cast(hiEnd.x), fgColor, bgColor, fgColor); ++it; } else @@ -460,7 +461,7 @@ try if (isStartInside) { const auto start = std::max(x1, hiStart.x); - _fillColorBitmap(y, start, x2, fgColor, bgColor); + _fillColorBitmap(y, start, x2, fgColor, bgColor, fgColor); } break; @@ -517,7 +518,7 @@ try } // Apply the current foreground and background colors to the cells - _fillColorBitmap(y, x, columnEnd, _api.currentForeground, _api.currentBackground); + _fillColorBitmap(y, x, columnEnd, _api.currentForeground, _api.currentBackground, 0xfffa7720u); // Apply the highlighting colors to the highlighted cells RETURN_IF_FAILED(_drawHighlighted(_api.searchHighlights, y, x, columnEnd, highlightFg, highlightBg)); @@ -532,13 +533,15 @@ CATCH_RETURN() [[nodiscard]] HRESULT AtlasEngine::PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept try { + UNREFERENCED_PARAMETER(gridlineColor); + UNREFERENCED_PARAMETER(underlineColor); const auto shift = gsl::narrow_cast(_api.lineRendition != LineRendition::SingleWidth); const auto x = std::max(0, coordTarget.x - (_api.viewportOffset.x >> shift)); const auto y = gsl::narrow_cast(clamp(coordTarget.y, 0, _p.s->viewportCellCount.y - 1)); const auto from = gsl::narrow_cast(clamp(x << shift, 0, _p.s->viewportCellCount.x - 1)); const auto to = gsl::narrow_cast(clamp((x + cchLine) << shift, from, _p.s->viewportCellCount.x)); - const auto glColor = gsl::narrow_cast(gridlineColor) | 0xff000000; - const auto ulColor = gsl::narrow_cast(underlineColor) | 0xff000000; + const auto glColor = 0xffff0000U; //gsl::narrow_cast(gridlineColor) | 0xff000000; + const auto ulColor = 0xff00ff00U; //gsl::narrow_cast(underlineColor) | 0xff000000; _p.rows[y]->gridLineRanges.emplace_back(lines, glColor, ulColor, from, to); return S_OK; } @@ -791,9 +794,10 @@ void AtlasEngine::_recreateCellCountDependentResources() // so we round up to multiple of 8 because 8 * sizeof(u32) == 32. _p.colorBitmapRowStride = alignForward(_p.s->viewportCellCount.x, 8); _p.colorBitmapDepthStride = _p.colorBitmapRowStride * _p.s->viewportCellCount.y; - _p.colorBitmap = Buffer(_p.colorBitmapDepthStride * 2); + _p.colorBitmap = Buffer(_p.colorBitmapDepthStride * 3); _p.backgroundBitmap = { _p.colorBitmap.data(), _p.colorBitmapDepthStride }; _p.foregroundBitmap = { _p.colorBitmap.data() + _p.colorBitmapDepthStride, _p.colorBitmapDepthStride }; + _p.underlineBitmap = { _p.colorBitmap.data() + (_p.colorBitmapDepthStride << 1), _p.colorBitmapDepthStride }; memset(_p.colorBitmap.data(), 0, _p.colorBitmap.size() * sizeof(u32)); diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index af77dcfe69..51173b1b3c 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -89,7 +89,7 @@ namespace Microsoft::Console::Render::Atlas void _mapCharacters(const wchar_t* text, u32 textLength, u32* mappedLength, IDWriteFontFace2** mappedFontFace) const; void _mapComplex(IDWriteFontFace2* mappedFontFace, u32 idx, u32 length, ShapedRow& row); ATLAS_ATTR_COLD void _mapReplacementCharacter(u32 from, u32 to, ShapedRow& row); - void _fillColorBitmap(const size_t y, const size_t x1, const size_t x2, const u32 fgColor, const u32 bgColor) noexcept; + void _fillColorBitmap(const size_t y, const size_t x1, const size_t x2, const u32 fgColor, const u32 bgColor, const u32 ulColor) noexcept; [[nodiscard]] HRESULT _drawHighlighted(std::span& highlights, const u16 row, const u16 begX, const u16 endX, const u32 fgColor, const u32 bgColor) noexcept; // AtlasEngine.api.cpp diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp index 60ee6aaf1d..98d50bf500 100644 --- a/src/renderer/atlas/BackendD3D.cpp +++ b/src/renderer/atlas/BackendD3D.cpp @@ -678,12 +678,12 @@ void BackendD3D::_debugUpdateShaders(const RenderingPayload& p) noexcept struct FileVS { std::wstring_view filename; - wil::com_ptr BackendD3D::*target; + wil::com_ptr BackendD3D::* target; }; struct FilePS { std::wstring_view filename; - wil::com_ptr BackendD3D::*target; + wil::com_ptr BackendD3D::* target; }; static constexpr std::array filesVS{ @@ -1784,37 +1784,51 @@ void BackendD3D::_drawGridlines(const RenderingPayload& p, u16 y) auto posX = r.from * cellSize.x + offset; const auto end = r.to * cellSize.x; + const auto colors = &p.foregroundBitmap[p.colorBitmapRowStride * y]; + for (; posX < end; posX += textCellWidth) { _appendQuad() = { .shadingType = static_cast(ShadingType::SolidLine), .position = { static_cast(posX), rowTop }, .size = { width, p.s->font->cellSize.y }, - .color = r.gridlineColor, + .color = colors[r.from], }; } }; - const auto appendHorizontalLine = [&](const GridLineRange& r, FontDecorationPosition pos, ShadingType shadingType, const u32 color) { + const auto appendHorizontalLine = [&](const GridLineRange& r, FontDecorationPosition pos, ShadingType shadingType, const std::span& colorBitmap) { const auto offset = pos.position << verticalShift; const auto height = static_cast(pos.height << verticalShift); - const auto left = static_cast(r.from * cellSize.x); - const auto width = static_cast((r.to - r.from) * cellSize.x); - i32 rt = textCellTop + offset; i32 rb = rt + height; rt = clamp(rt, clipTop, clipBottom); rb = clamp(rb, clipTop, clipBottom); + const auto colors = &colorBitmap[p.colorBitmapRowStride * y]; + if (rt < rb) { - _appendQuad() = { - .shadingType = static_cast(shadingType), - .renditionScale = { static_cast(1 << horizontalShift), static_cast(1 << verticalShift) }, - .position = { left, static_cast(rt) }, - .size = { width, static_cast(rb - rt) }, - .color = color, - }; + for (auto from = r.from; from < r.to;) + { + const auto start = colors[from]; + u16 w = 1u; + for (; colors[from + w] == start && w < (r.to - r.from); ++w) + ; + + const auto left = static_cast(from * cellSize.x); + const auto width = static_cast(w * cellSize.x); + + _appendQuad() = { + .shadingType = static_cast(shadingType), + .renditionScale = { static_cast(1 << horizontalShift), static_cast(1 << verticalShift) }, + .position = { left, static_cast(rt) }, + .size = { width, static_cast(rb - rt) }, + .color = start, + }; + + from += w; + } } }; @@ -1833,38 +1847,38 @@ void BackendD3D::_drawGridlines(const RenderingPayload& p, u16 y) } if (r.lines.test(GridLines::Top)) { - appendHorizontalLine(r, p.s->font->gridTop, ShadingType::SolidLine, r.gridlineColor); + appendHorizontalLine(r, p.s->font->gridTop, ShadingType::SolidLine, p.foregroundBitmap); } if (r.lines.test(GridLines::Bottom)) { - appendHorizontalLine(r, p.s->font->gridBottom, ShadingType::SolidLine, r.gridlineColor); + appendHorizontalLine(r, p.s->font->gridBottom, ShadingType::SolidLine, p.foregroundBitmap); } if (r.lines.test(GridLines::Strikethrough)) { - appendHorizontalLine(r, p.s->font->strikethrough, ShadingType::SolidLine, r.gridlineColor); + appendHorizontalLine(r, p.s->font->strikethrough, ShadingType::SolidLine, p.foregroundBitmap); } if (r.lines.test(GridLines::Underline)) { - appendHorizontalLine(r, p.s->font->underline, ShadingType::SolidLine, r.underlineColor); + appendHorizontalLine(r, p.s->font->underline, ShadingType::SolidLine, p.underlineBitmap); } else if (r.lines.any(GridLines::DottedUnderline, GridLines::HyperlinkUnderline)) { - appendHorizontalLine(r, p.s->font->underline, ShadingType::DottedLine, r.underlineColor); + appendHorizontalLine(r, p.s->font->underline, ShadingType::DottedLine, p.underlineBitmap); } else if (r.lines.test(GridLines::DashedUnderline)) { - appendHorizontalLine(r, p.s->font->underline, ShadingType::DashedLine, r.underlineColor); + appendHorizontalLine(r, p.s->font->underline, ShadingType::DashedLine, p.underlineBitmap); } else if (r.lines.test(GridLines::CurlyUnderline)) { - appendHorizontalLine(r, _curlyUnderline, ShadingType::CurlyLine, r.underlineColor); + appendHorizontalLine(r, _curlyUnderline, ShadingType::CurlyLine, p.underlineBitmap); } else if (r.lines.test(GridLines::DoubleUnderline)) { for (const auto pos : p.s->font->doubleUnderline) { - appendHorizontalLine(r, pos, ShadingType::SolidLine, r.underlineColor); + appendHorizontalLine(r, pos, ShadingType::SolidLine, p.underlineBitmap); } } } diff --git a/src/renderer/atlas/common.h b/src/renderer/atlas/common.h index 6668e2ce3c..68c544e7eb 100644 --- a/src/renderer/atlas/common.h +++ b/src/renderer/atlas/common.h @@ -562,14 +562,17 @@ namespace Microsoft::Console::Render::Atlas // This exists as a convenience access to colorBitmap and // contains a view into the foreground color bitmap. std::span foregroundBitmap; + // This exists as a convenience access to colorBitmap and + // contains a view into the underline color bitmap. + std::span underlineBitmap; // This stride of the colorBitmap is a "count" of u32 and not in bytes. size_t colorBitmapRowStride = 0; // FYI depth refers to the `colorBitmapRowStride * height` size of each bitmap contained - // in colorBitmap. colorBitmap contains 2 bitmaps (background and foreground colors). + // in colorBitmap. colorBitmap contains 3 bitmaps (background, foreground and underline colors). size_t colorBitmapDepthStride = 0; // A generation of 1 ensures that the backends redraw the background on the first Present(). // The 1st entry in this array corresponds to the background and the 2nd to the foreground bitmap. - std::array colorBitmapGenerations{ 1, 1 }; + std::array colorBitmapGenerations{ 1, 1, 1 }; // In columns/rows. til::rect cursorRect; // The viewport/SwapChain area to be presented. In pixel.