Skip to content

Commit e5d7fd0

Browse files
lheckerDHowett
authored andcommitted
AtlasEngine: Improve dotted, dashed and curly underlines (#16719)
This changeset makes 3 improvements: * Dotted lines now use a 2:1 ratio between gaps and dots (from 1:1). This makes the dots a lot easier to spot at small font sizes. * Dashed lines use a 1:2 ratio and a cells-size independent stride. By being cell-size independent it works more consistently with a wider variety of fonts with weird cell aspect ratios. * Curly lines are now cell-size independent as well and have a height that equals the double-underline size. This ensures that the curve isn't cut off anymore and just like with dashed lines, that it works under weird aspect ratios. Closes #16712 ## Validation Steps Performed This was tested using RenderingTests using Cascadia Mono, Consolas, Courier New, Lucida Console and MS Gothic. (cherry picked from commit 9c8058c) Service-Card-Id: 91922825 Service-Version: 1.19
1 parent 7c1edbd commit e5d7fd0

File tree

3 files changed

+25
-45
lines changed

3 files changed

+25
-45
lines changed

src/renderer/atlas/BackendD3D.cpp

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -310,29 +310,18 @@ void BackendD3D::_updateFontDependents(const RenderingPayload& p)
310310
// baseline of curlyline is at the middle of singly underline. When there's
311311
// limited space to draw a curlyline, we apply a limit on the peak height.
312312
{
313-
// initialize curlyline peak height to a desired value. Clamp it to at
314-
// least 1.
315-
constexpr auto curlyLinePeakHeightEm = 0.075f;
316-
_curlyLinePeakHeight = std::max(1.0f, std::roundf(curlyLinePeakHeightEm * font.fontSize));
317-
318-
// calc the limit we need to apply
319-
const auto strokeHalfWidth = std::floor(font.underline.height / 2.0f);
320-
const auto underlineMidY = font.underline.position + strokeHalfWidth;
321-
const auto maxDrawableCurlyLinePeakHeight = font.cellSize.y - underlineMidY - font.underline.height;
322-
323-
// if the limit is <= 0 (no height at all), stick with the desired height.
324-
// This is how we force a curlyline even when there's no space, though it
325-
// might be clipped at the bottom.
326-
if (maxDrawableCurlyLinePeakHeight > 0.0f)
327-
{
328-
_curlyLinePeakHeight = std::min(_curlyLinePeakHeight, maxDrawableCurlyLinePeakHeight);
329-
}
313+
const auto cellHeight = static_cast<f32>(font.cellSize.y);
314+
const auto strokeWidth = static_cast<f32>(font.thinLineWidth);
315+
316+
// This gives it the same position and height as our double-underline. There's no particular reason for that, apart from
317+
// it being simple to implement and robust against more peculiar fonts with unusually large/small descenders, etc.
318+
// We still need to ensure though that it doesn't clip out of the cellHeight at the bottom.
319+
const auto height = std::max(3.0f, static_cast<f32>(font.doubleUnderline[1].position + font.doubleUnderline[1].height - font.doubleUnderline[0].position));
320+
const auto top = std::min(static_cast<f32>(font.doubleUnderline[0].position), floorf(cellHeight - height - strokeWidth));
330321

331-
const auto curlyUnderlinePos = underlineMidY - _curlyLinePeakHeight - font.underline.height;
332-
const auto curlyUnderlineWidth = 2.0f * (_curlyLinePeakHeight + font.underline.height);
333-
const auto curlyUnderlinePosU16 = gsl::narrow_cast<u16>(lrintf(curlyUnderlinePos));
334-
const auto curlyUnderlineWidthU16 = gsl::narrow_cast<u16>(lrintf(curlyUnderlineWidth));
335-
_curlyUnderline = { curlyUnderlinePosU16, curlyUnderlineWidthU16 };
322+
_curlyLineHalfHeight = height * 0.5f;
323+
_curlyUnderline.position = gsl::narrow_cast<u16>(lrintf(top));
324+
_curlyUnderline.height = gsl::narrow_cast<u16>(lrintf(height));
336325
}
337326

338327
DWrite_GetRenderParams(p.dwriteFactory.get(), &_gamma, &_cleartypeEnhancedContrast, &_grayscaleEnhancedContrast, _textRenderingParams.put());
@@ -573,9 +562,8 @@ void BackendD3D::_recreateConstBuffer(const RenderingPayload& p) const
573562
DWrite_GetGammaRatios(_gamma, data.gammaRatios);
574563
data.enhancedContrast = p.s->font->antialiasingMode == AntialiasingMode::ClearType ? _cleartypeEnhancedContrast : _grayscaleEnhancedContrast;
575564
data.underlineWidth = p.s->font->underline.height;
576-
data.curlyLineWaveFreq = 2.0f * 3.14f / p.s->font->cellSize.x;
577-
data.curlyLinePeakHeight = _curlyLinePeakHeight;
578-
data.curlyLineCellOffset = p.s->font->underline.position + p.s->font->underline.height / 2.0f;
565+
data.thinLineWidth = p.s->font->thinLineWidth;
566+
data.curlyLineHalfHeight = _curlyLineHalfHeight;
579567
p.deviceContext->UpdateSubresource(_psConstantBuffer.get(), 0, nullptr, &data, 0, 0);
580568
}
581569
}

src/renderer/atlas/BackendD3D.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,8 @@ namespace Microsoft::Console::Render::Atlas
4242
alignas(sizeof(f32x4)) f32 gammaRatios[4]{};
4343
alignas(sizeof(f32)) f32 enhancedContrast = 0;
4444
alignas(sizeof(f32)) f32 underlineWidth = 0;
45-
alignas(sizeof(f32)) f32 curlyLinePeakHeight = 0;
46-
alignas(sizeof(f32)) f32 curlyLineWaveFreq = 0;
47-
alignas(sizeof(f32)) f32 curlyLineCellOffset = 0;
45+
alignas(sizeof(f32)) f32 thinLineWidth = 0;
46+
alignas(sizeof(f32)) f32 curlyLineHalfHeight = 0;
4847
#pragma warning(suppress : 4324) // 'PSConstBuffer': structure was padded due to alignment specifier
4948
};
5049

@@ -291,7 +290,7 @@ namespace Microsoft::Console::Render::Atlas
291290
// The bounding rect of _cursorRects in pixels.
292291
til::rect _cursorPosition;
293292

294-
f32 _curlyLinePeakHeight = 0.0f;
293+
f32 _curlyLineHalfHeight = 0.0f;
295294
FontDecorationPosition _curlyUnderline;
296295

297296
bool _requiresContinuousRedraw = false;

src/renderer/atlas/shader_ps.hlsl

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@ cbuffer ConstBuffer : register(b0)
1212
float4 gammaRatios;
1313
float enhancedContrast;
1414
float underlineWidth;
15-
float curlyLinePeakHeight;
16-
float curlyLineWaveFreq;
17-
float curlyLineCellOffset;
15+
float thinLineWidth;
16+
float curlyLineHalfHeight;
1817
}
1918

2019
Texture2D<float4> background : register(t0);
@@ -76,31 +75,25 @@ Output main(PSData data) : SV_Target
7675
}
7776
case SHADING_TYPE_DOTTED_LINE:
7877
{
79-
const bool on = frac(data.position.x / (2.0f * underlineWidth * data.renditionScale.x)) < 0.5f;
78+
const bool on = frac(data.position.x / (3.0f * underlineWidth * data.renditionScale.x)) < (1.0f / 3.0f);
8079
color = on * premultiplyColor(data.color);
8180
weights = color.aaaa;
8281
break;
8382
}
8483
case SHADING_TYPE_DASHED_LINE:
8584
{
86-
const bool on = frac(data.position.x / (backgroundCellSize.x * data.renditionScale.x)) < 0.5f;
85+
const bool on = frac(data.position.x / (6.0f * underlineWidth * data.renditionScale.x)) < (4.0f / 6.0f);
8786
color = on * premultiplyColor(data.color);
8887
weights = color.aaaa;
8988
break;
9089
}
9190
case SHADING_TYPE_CURLY_LINE:
9291
{
93-
uint cellRow = floor(data.position.y / backgroundCellSize.y);
94-
// Use the previous cell when drawing 'Double Height' curly line.
95-
cellRow -= data.renditionScale.y - 1;
96-
const float cellTop = cellRow * backgroundCellSize.y;
97-
const float centerY = cellTop + curlyLineCellOffset * data.renditionScale.y;
98-
const float strokeWidthHalf = underlineWidth * data.renditionScale.y / 2.0f;
99-
const float amp = curlyLinePeakHeight * data.renditionScale.y;
100-
const float freq = curlyLineWaveFreq / data.renditionScale.x;
101-
102-
const float s = sin(data.position.x * freq);
103-
const float d = abs(centerY - (s * amp) - data.position.y);
92+
const float strokeWidthHalf = thinLineWidth * data.renditionScale.y * 0.5f;
93+
const float amp = (curlyLineHalfHeight - strokeWidthHalf) * data.renditionScale.y;
94+
const float freq = data.renditionScale.x / curlyLineHalfHeight * 1.57079632679489661923f;
95+
const float s = sin(data.position.x * freq) * amp;
96+
const float d = abs(curlyLineHalfHeight - data.texcoord.y - s);
10497
const float a = 1 - saturate(d - strokeWidthHalf);
10598
color = a * premultiplyColor(data.color);
10699
weights = color.aaaa;

0 commit comments

Comments
 (0)