Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve appearance of curly underlines #18092

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions src/renderer/atlas/BackendD3D.cpp
Original file line number Diff line number Diff line change
@@ -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<int>(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);
1 change: 0 additions & 1 deletion src/renderer/atlas/BackendD3D.h
Original file line number Diff line number Diff line change
@@ -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
19 changes: 7 additions & 12 deletions src/renderer/atlas/shader_ps.hlsl
Original file line number Diff line number Diff line change
@@ -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: