From 8d4f242218643863ab59439b9b5a66f6d4ccf2ab Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Mon, 21 Oct 2024 21:53:58 +0200 Subject: [PATCH] Improve appearance of curly underlines --- src/renderer/atlas/BackendD3D.cpp | 11 ++++------- src/renderer/atlas/BackendD3D.h | 1 - src/renderer/atlas/shader_ps.hlsl | 19 +++++++------------ 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp index 6d832ae0d75..f0ee8a48c93 100644 --- a/src/renderer/atlas/BackendD3D.cpp +++ b/src/renderer/atlas/BackendD3D.cpp @@ -298,13 +298,11 @@ void BackendD3D::_updateFontDependents(const RenderingPayload& p) { const int cellHeight = font.cellSize.y; const int duTop = font.doubleUnderline[0].position; - const int duBottom = font.doubleUnderline[1].position; - const int duHeight = font.doubleUnderline[0].height; - // 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, which is why `position` has a min(). - const auto height = std::max(3, duBottom + duHeight - duTop); + // We want 1 period per cell. Calculating the corresponding height is simple. + // The height must be rounded to give the extrema a crisp look. 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(). + const auto height = std::max(3, static_cast(lroundf(font.cellSize.x / 3.14159265359f))); const auto position = std::min(duTop, cellHeight - height); _curlyLineHalfHeight = height * 0.5f; @@ -586,7 +584,6 @@ 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.doubleUnderlineWidth = p.s->font->doubleUnderline[0].height; data.curlyLineHalfHeight = _curlyLineHalfHeight; data.shadedGlyphDotSize = std::max(1.0f, std::roundf(std::max(p.s->font->cellSize.x / 16.0f, p.s->font->cellSize.y / 32.0f))); 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 962d691b01d..0da13228ce8 100644 --- a/src/renderer/atlas/BackendD3D.h +++ b/src/renderer/atlas/BackendD3D.h @@ -42,7 +42,6 @@ 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 doubleUnderlineWidth = 0; alignas(sizeof(f32)) f32 curlyLineHalfHeight = 0; alignas(sizeof(f32)) f32 shadedGlyphDotSize = 0; #pragma warning(suppress : 4324) // 'PSConstBuffer': structure was padded due to alignment specifier diff --git a/src/renderer/atlas/shader_ps.hlsl b/src/renderer/atlas/shader_ps.hlsl index a39ec79dc7d..6a017faaf90 100644 --- a/src/renderer/atlas/shader_ps.hlsl +++ b/src/renderer/atlas/shader_ps.hlsl @@ -12,7 +12,6 @@ cbuffer ConstBuffer : register(b0) float4 gammaRatios; float enhancedContrast; float underlineWidth; - float doubleUnderlineWidth; float curlyLineHalfHeight; float shadedGlyphDotSize; } @@ -171,19 +170,15 @@ Output main(PSData data) : SV_Target } case SHADING_TYPE_CURLY_LINE: { - // The curly line has the same thickness as a double underline. - // We halve it to make the math a bit easier. - float strokeWidthHalf = doubleUnderlineWidth * data.renditionScale.y * 0.5f; + // The curly line has the same thickness as an underline. + // We halve it to get the stroke width and make the math a bit easier. + float strokeWidthHalf = underlineWidth * data.renditionScale.y * 0.5f; float center = curlyLineHalfHeight * data.renditionScale.y; float amplitude = center - strokeWidthHalf; - // We multiply the frequency by pi/2 to get a sine wave which has an integer period. - // This makes every period of the wave look exactly the same. - float frequency = 1.57079632679489661923f / (curlyLineHalfHeight * data.renditionScale.x); - // At very small sizes, like when the wave is just 3px tall and 1px wide, it'll look too fat and/or blurry. - // Because we multiplied our frequency with pi, the extrema of the curve and its intersections with the - // centerline always occur right between two pixels. This causes both to be lit with the same color. - // By adding a small phase shift, we can break this symmetry up. It'll make the wave look a lot more crispy. - float phase = 1.57079632679489661923f; + // This calculates a frequency that results in one 1 period per cell. + float frequency = (4.0f * 1.57079632679489661923f) / (backgroundCellSize.x * data.renditionScale.x); + // This shifts the wave so that the peak is in the middle of the cell. + float phase = 3.0f * 1.57079632679489661923f; float sine = sin(data.position.x * frequency + phase); // We use the distance to the sine curve as its alpha value - the closer the more opaque. // To give it a smooth appearance we don't want to simply calculate the vertical distance to the curve: