Repository: microsoft/terminal (language: cpp)

Issue:
Hyperlink uses different underscore dots when not hover over it
### Windows Terminal version

1.19.10302.0

### Windows build number

10.0.19045.0

### Other Software

powershell version 7.4.0

### Steps to reproduce

Try hyperlink in version 1.18.3181.0 vs 1.19.10302.0

### Expected Behavior

It would display it the same

### Actual Behavior

Shows with dots that are more closely spaced, and now it looks as if it was underscored
![image](https://github.com/microsoft/terminal/assets/97449446/c2984b7f-2c3f-4046-a5cc-d74a0182ef46)



Discussion / hints:
Just to note, in the picture, I have path output as hyperlink through modified prompt function. 
Thanks for filing. This is a side effect of switching the rendering engine from the DxEngine to the AtlasEngine.

Fix patch:
diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp
index 69d597ed0bd..221cf3b5ba3 100644
--- a/src/renderer/atlas/BackendD3D.cpp
+++ b/src/renderer/atlas/BackendD3D.cpp
@@ -310,29 +310,18 @@ void BackendD3D::_updateFontDependents(const RenderingPayload& p)
     // baseline of curlyline is at the middle of singly underline. When there's
     // limited space to draw a curlyline, we apply a limit on the peak height.
     {
-        // initialize curlyline peak height to a desired value. Clamp it to at
-        // least 1.
-        constexpr auto curlyLinePeakHeightEm = 0.075f;
-        _curlyLinePeakHeight = std::max(1.0f, std::roundf(curlyLinePeakHeightEm * font.fontSize));
-
-        // calc the limit we need to apply
-        const auto strokeHalfWidth = std::floor(font.underline.height / 2.0f);
-        const auto underlineMidY = font.underline.position + strokeHalfWidth;
-        const auto maxDrawableCurlyLinePeakHeight = font.cellSize.y - underlineMidY - font.underline.height;
-
-        // if the limit is <= 0 (no height at all), stick with the desired height.
-        // This is how we force a curlyline even when there's no space, though it
-        // might be clipped at the bottom.
-        if (maxDrawableCurlyLinePeakHeight > 0.0f)
-        {
-            _curlyLinePeakHeight = std::min(_curlyLinePeakHeight, maxDrawableCurlyLinePeakHeight);
-        }
+        const auto cellHeight = static_cast<f32>(font.cellSize.y);
+        const auto strokeWidth = static_cast<f32>(font.thinLineWidth);
+
+        // This gives it the same position and height as our double-underline. There's no particular reason for that, apart from
+        // it being simple to implement and robust against more peculiar fonts with unusually large/small descenders, etc.
+        // We still need to ensure though that it doesn't clip out of the cellHeight at the bottom.
+        const auto height = std::max(3.0f, static_cast<f32>(font.doubleUnderline[1].position + font.doubleUnderline[1].height - font.doubleUnderline[0].position));
+        const auto top = std::min(static_cast<f32>(font.doubleUnderline[0].position), floorf(cellHeight - height - strokeWidth));
 
-        const auto curlyUnderlinePos = underlineMidY - _curlyLinePeakHeight - font.underline.height;
-        const auto curlyUnderlineWidth = 2.0f * (_curlyLinePeakHeight + font.underline.height);
-        const auto curlyUnderlinePosU16 = gsl::narrow_cast<u16>(lrintf(curlyUnderlinePos));
-        const auto curlyUnderlineWidthU16 = gsl::narrow_cast<u16>(lrintf(curlyUnderlineWidth));
-        _curlyUnderline = { curlyUnderlinePosU16, curlyUnderlineWidthU16 };
+        _curlyLineHalfHeight = height * 0.5f;
+        _curlyUnderline.position = gsl::narrow_cast<u16>(lrintf(top));
+        _curlyUnderline.height = gsl::narrow_cast<u16>(lrintf(height));
     }
 
     DWrite_GetRenderParams(p.dwriteFactory.get(), &_gamma, &_cleartypeEnhancedContrast, &_grayscaleEnhancedContrast, _textRenderingParams.put());
@@ -573,9 +562,8 @@ void BackendD3D::_recreateConstBuffer(const RenderingPayload& p) const
         DWrite_GetGammaRatios(_gamma, data.gammaRatios);
         data.enhancedContrast = p.s->font->antialiasingMode == AntialiasingMode::ClearType ? _cleartypeEnhancedContrast : _grayscaleEnhancedContrast;
         data.underlineWidth = p.s->font->underline.height;
-        data.curlyLineWaveFreq = 2.0f * 3.14f / p.s->font->cellSize.x;
-        data.curlyLinePeakHeight = _curlyLinePeakHeight;
-        data.curlyLineCellOffset = p.s->font->underline.position + p.s->font->underline.height / 2.0f;
+        data.thinLineWidth = p.s->font->thinLineWidth;
+        data.curlyLineHalfHeight = _curlyLineHalfHeight;
         p.deviceContext->UpdateSubresource(_psConstantBuffer.get(), 0, nullptr, &data, 0, 0);
     }
 }
diff --git a/src/renderer/atlas/BackendD3D.h b/src/renderer/atlas/BackendD3D.h
index 4c248ed3ff3..cdf9937cc61 100644
--- a/src/renderer/atlas/BackendD3D.h
+++ b/src/renderer/atlas/BackendD3D.h
@@ -42,9 +42,8 @@ namespace Microsoft::Console::Render::Atlas
             alignas(sizeof(f32x4)) f32 gammaRatios[4]{};
             alignas(sizeof(f32)) f32 enhancedContrast = 0;
             alignas(sizeof(f32)) f32 underlineWidth = 0;
-            alignas(sizeof(f32)) f32 curlyLinePeakHeight = 0;
-            alignas(sizeof(f32)) f32 curlyLineWaveFreq = 0;
-            alignas(sizeof(f32)) f32 curlyLineCellOffset = 0;
+            alignas(sizeof(f32)) f32 thinLineWidth = 0;
+            alignas(sizeof(f32)) f32 curlyLineHalfHeight = 0;
 #pragma warning(suppress : 4324) // 'PSConstBuffer': structure was padded due to alignment specifier
         };
 
@@ -291,7 +290,7 @@ namespace Microsoft::Console::Render::Atlas
         // The bounding rect of _cursorRects in pixels.
         til::rect _cursorPosition;
 
-        f32 _curlyLinePeakHeight = 0.0f;
+        f32 _curlyLineHalfHeight = 0.0f;
         FontDecorationPosition _curlyUnderline;
 
         bool _requiresContinuousRedraw = false;
diff --git a/src/renderer/atlas/shader_ps.hlsl b/src/renderer/atlas/shader_ps.hlsl
index e19ba955fe5..d3c0bfac6c3 100644
--- a/src/renderer/atlas/shader_ps.hlsl
+++ b/src/renderer/atlas/shader_ps.hlsl
@@ -12,9 +12,8 @@ cbuffer ConstBuffer : register(b0)
     float4 gammaRatios;
     float enhancedContrast;
     float underlineWidth;
-    float curlyLinePeakHeight;
-    float curlyLineWaveFreq;
-    float curlyLineCellOffset;
+    float thinLineWidth;
+    float curlyLineHalfHeight;
 }
 
 Texture2D<float4> background : register(t0);
@@ -76,31 +75,25 @@ Output main(PSData data) : SV_Target
     }
     case SHADING_TYPE_DOTTED_LINE:
     {
-        const bool on = frac(data.position.x / (2.0f * underlineWidth * data.renditionScale.x)) < 0.5f;
+        const bool on = frac(data.position.x / (3.0f * underlineWidth * data.renditionScale.x)) < (1.0f / 3.0f);
         color = on * premultiplyColor(data.color);
         weights = color.aaaa;
         break;
     }
     case SHADING_TYPE_DASHED_LINE:
     {
-        const bool on = frac(data.position.x / (backgroundCellSize.x * data.renditionScale.x)) < 0.5f;
+        const bool on = frac(data.position.x / (6.0f * underlineWidth * data.renditionScale.x)) < (4.0f / 6.0f);
         color = on * premultiplyColor(data.color);
         weights = color.aaaa;
         break;
     }
     case SHADING_TYPE_CURLY_LINE:
     {
-        uint cellRow = floor(data.position.y / backgroundCellSize.y);
-        // Use the previous cell when drawing 'Double Height' curly line.
-        cellRow -= data.renditionScale.y - 1;
-        const float cellTop = cellRow * backgroundCellSize.y;
-        const float centerY = cellTop + curlyLineCellOffset * data.renditionScale.y;
-        const float strokeWidthHalf = underlineWidth * data.renditionScale.y / 2.0f;
-        const float amp = curlyLinePeakHeight * data.renditionScale.y;
-        const float freq = curlyLineWaveFreq / data.renditionScale.x;
-
-        const float s = sin(data.position.x * freq);
-        const float d = abs(centerY - (s * amp) - data.position.y);
+        const float strokeWidthHalf = thinLineWidth * data.renditionScale.y * 0.5f;
+        const float amp = (curlyLineHalfHeight - strokeWidthHalf) * data.renditionScale.y;
+        const float freq = data.renditionScale.x / curlyLineHalfHeight * 1.57079632679489661923f;
+        const float s = sin(data.position.x * freq) * amp;
+        const float d = abs(curlyLineHalfHeight - data.texcoord.y - s);
         const float a = 1 - saturate(d - strokeWidthHalf);
         color = a * premultiplyColor(data.color);
         weights = color.aaaa;


Test patch: