diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp index 74b64c53571..923747ef94a 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(font.cellSize.y); + const auto strokeWidth = static_cast(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(font.doubleUnderline[1].position + font.doubleUnderline[1].height - font.doubleUnderline[0].position)); + const auto top = std::min(static_cast(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(lrintf(curlyUnderlinePos)); - const auto curlyUnderlineWidthU16 = gsl::narrow_cast(lrintf(curlyUnderlineWidth)); - _curlyUnderline = { curlyUnderlinePosU16, curlyUnderlineWidthU16 }; + _curlyLineHalfHeight = height * 0.5f; + _curlyUnderline.position = gsl::narrow_cast(lrintf(top)); + _curlyUnderline.height = gsl::narrow_cast(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 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;