Skip to content

Commit 8d4f242

Browse files
committed
Improve appearance of curly underlines
1 parent 18098ec commit 8d4f242

File tree

3 files changed

+11
-20
lines changed

3 files changed

+11
-20
lines changed

src/renderer/atlas/BackendD3D.cpp

+4-7
Original file line numberDiff line numberDiff line change
@@ -298,13 +298,11 @@ void BackendD3D::_updateFontDependents(const RenderingPayload& p)
298298
{
299299
const int cellHeight = font.cellSize.y;
300300
const int duTop = font.doubleUnderline[0].position;
301-
const int duBottom = font.doubleUnderline[1].position;
302-
const int duHeight = font.doubleUnderline[0].height;
303301

304-
// This gives it the same position and height as our double-underline. There's no particular reason for that, apart from
305-
// it being simple to implement and robust against more peculiar fonts with unusually large/small descenders, etc.
306-
// We still need to ensure though that it doesn't clip out of the cellHeight at the bottom, which is why `position` has a min().
307-
const auto height = std::max(3, duBottom + duHeight - duTop);
302+
// We want 1 period per cell. Calculating the corresponding height is simple.
303+
// The height must be rounded to give the extrema a crisp look. We still need to ensure though
304+
// that it doesn't clip out of the cellHeight at the bottom, which is why `position` has a min().
305+
const auto height = std::max(3, static_cast<int>(lroundf(font.cellSize.x / 3.14159265359f)));
308306
const auto position = std::min(duTop, cellHeight - height);
309307

310308
_curlyLineHalfHeight = height * 0.5f;
@@ -586,7 +584,6 @@ void BackendD3D::_recreateConstBuffer(const RenderingPayload& p) const
586584
DWrite_GetGammaRatios(_gamma, data.gammaRatios);
587585
data.enhancedContrast = p.s->font->antialiasingMode == AntialiasingMode::ClearType ? _cleartypeEnhancedContrast : _grayscaleEnhancedContrast;
588586
data.underlineWidth = p.s->font->underline.height;
589-
data.doubleUnderlineWidth = p.s->font->doubleUnderline[0].height;
590587
data.curlyLineHalfHeight = _curlyLineHalfHeight;
591588
data.shadedGlyphDotSize = std::max(1.0f, std::roundf(std::max(p.s->font->cellSize.x / 16.0f, p.s->font->cellSize.y / 32.0f)));
592589
p.deviceContext->UpdateSubresource(_psConstantBuffer.get(), 0, nullptr, &data, 0, 0);

src/renderer/atlas/BackendD3D.h

-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ 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 doubleUnderlineWidth = 0;
4645
alignas(sizeof(f32)) f32 curlyLineHalfHeight = 0;
4746
alignas(sizeof(f32)) f32 shadedGlyphDotSize = 0;
4847
#pragma warning(suppress : 4324) // 'PSConstBuffer': structure was padded due to alignment specifier

src/renderer/atlas/shader_ps.hlsl

+7-12
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ cbuffer ConstBuffer : register(b0)
1212
float4 gammaRatios;
1313
float enhancedContrast;
1414
float underlineWidth;
15-
float doubleUnderlineWidth;
1615
float curlyLineHalfHeight;
1716
float shadedGlyphDotSize;
1817
}
@@ -171,19 +170,15 @@ Output main(PSData data) : SV_Target
171170
}
172171
case SHADING_TYPE_CURLY_LINE:
173172
{
174-
// The curly line has the same thickness as a double underline.
175-
// We halve it to make the math a bit easier.
176-
float strokeWidthHalf = doubleUnderlineWidth * data.renditionScale.y * 0.5f;
173+
// The curly line has the same thickness as an underline.
174+
// We halve it to get the stroke width and make the math a bit easier.
175+
float strokeWidthHalf = underlineWidth * data.renditionScale.y * 0.5f;
177176
float center = curlyLineHalfHeight * data.renditionScale.y;
178177
float amplitude = center - strokeWidthHalf;
179-
// We multiply the frequency by pi/2 to get a sine wave which has an integer period.
180-
// This makes every period of the wave look exactly the same.
181-
float frequency = 1.57079632679489661923f / (curlyLineHalfHeight * data.renditionScale.x);
182-
// At very small sizes, like when the wave is just 3px tall and 1px wide, it'll look too fat and/or blurry.
183-
// Because we multiplied our frequency with pi, the extrema of the curve and its intersections with the
184-
// centerline always occur right between two pixels. This causes both to be lit with the same color.
185-
// By adding a small phase shift, we can break this symmetry up. It'll make the wave look a lot more crispy.
186-
float phase = 1.57079632679489661923f;
178+
// This calculates a frequency that results in one 1 period per cell.
179+
float frequency = (4.0f * 1.57079632679489661923f) / (backgroundCellSize.x * data.renditionScale.x);
180+
// This shifts the wave so that the peak is in the middle of the cell.
181+
float phase = 3.0f * 1.57079632679489661923f;
187182
float sine = sin(data.position.x * frequency + phase);
188183
// We use the distance to the sine curve as its alpha value - the closer the more opaque.
189184
// To give it a smooth appearance we don't want to simply calculate the vertical distance to the curve:

0 commit comments

Comments
 (0)