mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-10 00:48:23 -06:00
AtlasEngine: Improve appearance of curly underlines (#17501)
We'd previously subtract one underline-height from the curly line offset, even though we already had subtracted its complete height. Additionally, the pixel shader received some fine tuning: * Shrink the stroke width so that the anti-aliasing can be seen all the way up to the horizontal edges of the bounding box. * Add a phase shift to break apart the symmetry of the curve. Closes #17482 Co-authored-by: Carlos Zamora <carlos.zamora@microsoft.com>
This commit is contained in:
parent
c9e200734e
commit
ad3797ace9
@ -306,7 +306,7 @@ void BackendD3D::_updateFontDependents(const RenderingPayload& p)
|
||||
// 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);
|
||||
const auto position = std::min(duTop, cellHeight - height - duHeight);
|
||||
const auto position = std::min(duTop, cellHeight - height);
|
||||
|
||||
_curlyLineHalfHeight = height * 0.5f;
|
||||
_curlyUnderline.position = gsl::narrow_cast<u16>(position);
|
||||
|
||||
@ -37,7 +37,7 @@ Output main(PSData data) : SV_Target
|
||||
{
|
||||
case SHADING_TYPE_TEXT_BACKGROUND:
|
||||
{
|
||||
const float2 cell = data.position.xy / backgroundCellSize;
|
||||
float2 cell = data.position.xy / backgroundCellSize;
|
||||
color = all(cell < backgroundCellCount) ? background[cell] : backgroundColor;
|
||||
weights = float4(1, 1, 1, 1);
|
||||
break;
|
||||
@ -45,13 +45,13 @@ Output main(PSData data) : SV_Target
|
||||
case SHADING_TYPE_TEXT_GRAYSCALE:
|
||||
{
|
||||
// These are independent of the glyph texture and could be moved to the vertex shader or CPU side of things.
|
||||
const float4 foreground = premultiplyColor(data.color);
|
||||
const float blendEnhancedContrast = DWrite_ApplyLightOnDarkContrastAdjustment(enhancedContrast, data.color.rgb);
|
||||
const float intensity = DWrite_CalcColorIntensity(data.color.rgb);
|
||||
float4 foreground = premultiplyColor(data.color);
|
||||
float blendEnhancedContrast = DWrite_ApplyLightOnDarkContrastAdjustment(enhancedContrast, data.color.rgb);
|
||||
float intensity = DWrite_CalcColorIntensity(data.color.rgb);
|
||||
// These aren't.
|
||||
const float4 glyph = glyphAtlas[data.texcoord];
|
||||
const float contrasted = DWrite_EnhanceContrast(glyph.a, blendEnhancedContrast);
|
||||
const float alphaCorrected = DWrite_ApplyAlphaCorrection(contrasted, intensity, gammaRatios);
|
||||
float4 glyph = glyphAtlas[data.texcoord];
|
||||
float contrasted = DWrite_EnhanceContrast(glyph.a, blendEnhancedContrast);
|
||||
float alphaCorrected = DWrite_ApplyAlphaCorrection(contrasted, intensity, gammaRatios);
|
||||
color = alphaCorrected * foreground;
|
||||
weights = color.aaaa;
|
||||
break;
|
||||
@ -59,11 +59,11 @@ Output main(PSData data) : SV_Target
|
||||
case SHADING_TYPE_TEXT_CLEARTYPE:
|
||||
{
|
||||
// These are independent of the glyph texture and could be moved to the vertex shader or CPU side of things.
|
||||
const float blendEnhancedContrast = DWrite_ApplyLightOnDarkContrastAdjustment(enhancedContrast, data.color.rgb);
|
||||
float blendEnhancedContrast = DWrite_ApplyLightOnDarkContrastAdjustment(enhancedContrast, data.color.rgb);
|
||||
// These aren't.
|
||||
const float4 glyph = glyphAtlas[data.texcoord];
|
||||
const float3 contrasted = DWrite_EnhanceContrast3(glyph.rgb, blendEnhancedContrast);
|
||||
const float3 alphaCorrected = DWrite_ApplyAlphaCorrection3(contrasted, data.color.rgb, gammaRatios);
|
||||
float4 glyph = glyphAtlas[data.texcoord];
|
||||
float3 contrasted = DWrite_EnhanceContrast3(glyph.rgb, blendEnhancedContrast);
|
||||
float3 alphaCorrected = DWrite_ApplyAlphaCorrection3(contrasted, data.color.rgb, gammaRatios);
|
||||
weights = float4(alphaCorrected * data.color.a, 1);
|
||||
color = weights * data.color;
|
||||
break;
|
||||
@ -157,26 +157,53 @@ Output main(PSData data) : SV_Target
|
||||
}
|
||||
case SHADING_TYPE_DOTTED_LINE:
|
||||
{
|
||||
const bool on = frac(data.position.x / (3.0f * underlineWidth * data.renditionScale.x)) < (1.0f / 3.0f);
|
||||
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 / (6.0f * underlineWidth * data.renditionScale.x)) < (4.0f / 6.0f);
|
||||
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:
|
||||
{
|
||||
const float strokeWidthHalf = doubleUnderlineWidth * 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);
|
||||
// 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;
|
||||
float amplitude = (curlyLineHalfHeight - strokeWidthHalf) * data.renditionScale.y;
|
||||
// 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 = data.renditionScale.x / curlyLineHalfHeight * 1.57079632679489661923f;
|
||||
// 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;
|
||||
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:
|
||||
// abs(pixel.y - sin(pixel.x))
|
||||
//
|
||||
// ...because while a pixel may be vertically far away it may be horizontally close to the sine curve.
|
||||
// We need a proper distance calculation. This makes a large difference at especially small font sizes.
|
||||
//
|
||||
// While calculating the distance to a sine curve is complex, calculating the distance to its tangent is easy,
|
||||
// because tangents are straight lines and line-point distance are trivial. The tangent of sin(x) is cos(x).
|
||||
// The line-point distance is the vertical distance multiplied by the cos(angle) of the line.
|
||||
// To turn out tangent cos(x) into an angle we need to calculate atan(cos(x)). This nets us:
|
||||
// abs(pixel.y - sin(pixel.x)) * cos(atan(cos(pixel.x))
|
||||
//
|
||||
// The expanded sine form of cos(atan(cos(x))) is 1 / sqrt(2 - sin(x)^2), which results in:
|
||||
// abs(pixel.y - sin(pixel.x)) * rsqrt(2 - sin(pixel.x)^2)
|
||||
float distance = abs(curlyLineHalfHeight - data.texcoord.y - sine * amplitude) * rsqrt(2 - sine * sine);
|
||||
// Since pixel coordinates are always offset by half a pixel (i.e. data.texcoord is 1.5f, 2.5f, 3.5f, ...)
|
||||
// the distance is also off by half a pixel. We undo that by adding half a pixel to the distance.
|
||||
// This gives the line its proper thickness appearance.
|
||||
float a = 1 - saturate(distance - strokeWidthHalf + 0.5f);
|
||||
color = a * premultiplyColor(data.color);
|
||||
weights = color.aaaa;
|
||||
break;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user