Skip to content

Commit 94e74d2

Browse files
authored
Make shaded block glyphs look even betterer (#16760)
Shaded glyphs (U+2591..3, etc.) all have one problem in common: The cell size may not be evenly divisible by the pixel/dot size in the glyph. This either results in blurring, or in moiré-like patterns at the edges of the cells with its neighbors, because they happen to start with a pattern that overlaps with the end of the previous cell. This PR solves the issue by moving the pixel/dot pattern generation into the shader. That way the pixel/dot location can be made dependent on the viewport-position of the actual underlying pixels, which avoids repeating patterns between cells. The PR contains some additional modifications, all of which either extend or improve the existing debug facilities in AtlasEngine. Suppressing whitespaces changes makes the diff way smaller.
1 parent badc00e commit 94e74d2

11 files changed

+270
-187
lines changed

src/renderer/atlas/AtlasEngine.api.cpp

+11-9
Original file line numberDiff line numberDiff line change
@@ -688,29 +688,30 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo
688688
const auto underlineWidth = std::max(1.0f, std::roundf(underlineThickness));
689689
const auto strikethroughPos = std::roundf(baseline + strikethroughPosition);
690690
const auto strikethroughWidth = std::max(1.0f, std::roundf(strikethroughThickness));
691-
const auto thinLineWidth = std::max(1.0f, std::roundf(underlineThickness / 2.0f));
691+
const auto doubleUnderlineWidth = std::max(1.0f, std::roundf(underlineThickness / 2.0f));
692+
const auto thinLineWidth = std::max(1.0f, std::roundf(std::max(adjustedWidth / 16.0f, adjustedHeight / 32.0f)));
692693

693694
// For double underlines we loosely follow what Word does:
694-
// 1. The lines are half the width of an underline (= thinLineWidth)
695+
// 1. The lines are half the width of an underline (= doubleUnderlineWidth)
695696
// 2. Ideally the bottom line is aligned with the bottom of the underline
696697
// 3. The top underline is vertically in the middle between baseline and ideal bottom underline
697698
// 4. If the top line gets too close to the baseline the underlines are shifted downwards
698699
// 5. The minimum gap between the two lines appears to be similar to Tex (1.2pt)
699700
// (Additional notes below.)
700701

701702
// 2.
702-
auto doubleUnderlinePosBottom = underlinePos + underlineWidth - thinLineWidth;
703+
auto doubleUnderlinePosBottom = underlinePos + underlineWidth - doubleUnderlineWidth;
703704
// 3. Since we don't align the center of our two lines, but rather the top borders
704705
// we need to subtract half a line width from our center point.
705-
auto doubleUnderlinePosTop = std::roundf((baseline + doubleUnderlinePosBottom - thinLineWidth) / 2.0f);
706+
auto doubleUnderlinePosTop = std::roundf((baseline + doubleUnderlinePosBottom - doubleUnderlineWidth) / 2.0f);
706707
// 4.
707-
doubleUnderlinePosTop = std::max(doubleUnderlinePosTop, baseline + thinLineWidth);
708+
doubleUnderlinePosTop = std::max(doubleUnderlinePosTop, baseline + doubleUnderlineWidth);
708709
// 5. The gap is only the distance _between_ the lines, but we need the distance from the
709710
// top border of the top and bottom lines, which includes an additional line width.
710711
const auto doubleUnderlineGap = std::max(1.0f, std::roundf(1.2f / 72.0f * dpi));
711-
doubleUnderlinePosBottom = std::max(doubleUnderlinePosBottom, doubleUnderlinePosTop + doubleUnderlineGap + thinLineWidth);
712+
doubleUnderlinePosBottom = std::max(doubleUnderlinePosBottom, doubleUnderlinePosTop + doubleUnderlineGap + doubleUnderlineWidth);
712713
// Our cells can't overlap each other so we additionally clamp the bottom line to be inside the cell boundaries.
713-
doubleUnderlinePosBottom = std::min(doubleUnderlinePosBottom, adjustedHeight - thinLineWidth);
714+
doubleUnderlinePosBottom = std::min(doubleUnderlinePosBottom, adjustedHeight - doubleUnderlineWidth);
714715

715716
const auto cellWidth = gsl::narrow<u16>(lrintf(adjustedWidth));
716717
const auto cellHeight = gsl::narrow<u16>(lrintf(adjustedHeight));
@@ -749,6 +750,7 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo
749750
const auto strikethroughWidthU16 = gsl::narrow_cast<u16>(lrintf(strikethroughWidth));
750751
const auto doubleUnderlinePosTopU16 = gsl::narrow_cast<u16>(lrintf(doubleUnderlinePosTop));
751752
const auto doubleUnderlinePosBottomU16 = gsl::narrow_cast<u16>(lrintf(doubleUnderlinePosBottom));
753+
const auto doubleUnderlineWidthU16 = gsl::narrow_cast<u16>(lrintf(doubleUnderlineWidth));
752754

753755
// NOTE: From this point onward no early returns or throwing code should exist,
754756
// as we might cause _api to be in an inconsistent state otherwise.
@@ -771,8 +773,8 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo
771773

772774
fontMetrics->underline = { underlinePosU16, underlineWidthU16 };
773775
fontMetrics->strikethrough = { strikethroughPosU16, strikethroughWidthU16 };
774-
fontMetrics->doubleUnderline[0] = { doubleUnderlinePosTopU16, thinLineWidthU16 };
775-
fontMetrics->doubleUnderline[1] = { doubleUnderlinePosBottomU16, thinLineWidthU16 };
776+
fontMetrics->doubleUnderline[0] = { doubleUnderlinePosTopU16, doubleUnderlineWidthU16 };
777+
fontMetrics->doubleUnderline[1] = { doubleUnderlinePosBottomU16, doubleUnderlineWidthU16 };
776778
fontMetrics->overline = { 0, underlineWidthU16 };
777779

778780
fontMetrics->builtinGlyphs = fontInfoDesired.GetEnableBuiltinGlyphs();

src/renderer/atlas/Backend.h

+12
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,25 @@
77

88
namespace Microsoft::Console::Render::Atlas
99
{
10+
// Don't use this definition in the code elsewhere.
11+
// It only exists to make the definitions below possible.
12+
#ifdef NDEBUG
13+
#define ATLAS_DEBUG__IS_DEBUG 0
14+
#else
15+
#define ATLAS_DEBUG__IS_DEBUG 1
16+
#endif
17+
1018
// If set to 1, this will cause the entire viewport to be invalidated at all times.
1119
// Helpful for benchmarking our text shaping code based on DirectWrite.
1220
#define ATLAS_DEBUG_DISABLE_PARTIAL_INVALIDATION 0
1321

1422
// Redraw at display refresh rate at all times. This helps with shader debugging.
1523
#define ATLAS_DEBUG_CONTINUOUS_REDRAW 0
1624

25+
// Hot reload the builtin .hlsl files whenever they change on disk.
26+
// Enabled by default in debug builds.
27+
#define ATLAS_DEBUG_SHADER_HOT_RELOAD ATLAS_DEBUG__IS_DEBUG
28+
1729
// Disables the use of DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT.
1830
// This helps with benchmarking the application as it'll run beyond display refresh rate.
1931
#define ATLAS_DEBUG_DISABLE_FRAME_LATENCY_WAITABLE_OBJECT 0

0 commit comments

Comments
 (0)