@@ -36,10 +36,23 @@ TIL_FAST_MATH_BEGIN
36
36
#pragma warning(disable : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
37
37
#pragma warning(disable : 26482) // Only index into arrays using constant expressions (bounds.2).
38
38
39
+ // Initializing large arrays can be very costly compared to how cheap some of these functions are.
40
+ #define ALLOW_UNINITIALIZED_BEGIN _Pragma (" warning(push)" ) _Pragma(" warning(disable : 26494)" )
41
+ #define ALLOW_UNINITIALIZED_END _Pragma (" warning(pop)" )
42
+
39
43
using namespace Microsoft::Console::Render::Atlas;
40
44
41
45
template <>
42
- struct ::std::hash<BackendD3D::AtlasGlyphEntry>
46
+ struct std ::hash<u16>
47
+ {
48
+ constexpr size_t operator ()(u16 key) const noexcept
49
+ {
50
+ return til::flat_set_hash_integer (key);
51
+ }
52
+ };
53
+
54
+ template <>
55
+ struct std ::hash<BackendD3D::AtlasGlyphEntry>
43
56
{
44
57
constexpr size_t operator ()(u16 key) const noexcept
45
58
{
@@ -53,7 +66,7 @@ struct ::std::hash<BackendD3D::AtlasGlyphEntry>
53
66
};
54
67
55
68
template <>
56
- struct :: std::hash<BackendD3D::AtlasFontFaceEntry>
69
+ struct std ::hash<BackendD3D::AtlasFontFaceEntry>
57
70
{
58
71
using T = BackendD3D::AtlasFontFaceEntry;
59
72
@@ -982,7 +995,13 @@ void BackendD3D::_drawText(RenderingPayload& p)
982
995
// We need to goto here, because a retry will cause the atlas texture as well as the
983
996
// _glyphCache hashmap to be cleared, and so we'll have to call insert() again.
984
997
drawGlyphRetry:
985
- auto & fontFaceEntry = *_glyphAtlasMap.insert (fontFaceKey).first .inner ;
998
+ const auto [fontFaceEntryOuter, fontFaceInserted] = _glyphAtlasMap.insert (fontFaceKey);
999
+ auto & fontFaceEntry = *fontFaceEntryOuter.inner ;
1000
+
1001
+ if (fontFaceInserted)
1002
+ {
1003
+ _initializeFontFaceEntry (fontFaceEntry);
1004
+ }
986
1005
987
1006
while (x < m.glyphsTo )
988
1007
{
@@ -1124,7 +1143,30 @@ void BackendD3D::_drawTextOverlapSplit(const RenderingPayload& p, u16 y)
1124
1143
}
1125
1144
}
1126
1145
1127
- bool BackendD3D::_drawGlyph (const RenderingPayload& p, const BackendD3D::AtlasFontFaceEntryInner& fontFaceEntry, BackendD3D::AtlasGlyphEntry& glyphEntry)
1146
+ void BackendD3D::_initializeFontFaceEntry (AtlasFontFaceEntryInner& fontFaceEntry)
1147
+ {
1148
+ ALLOW_UNINITIALIZED_BEGIN
1149
+ std::array<u32, 0x100 > codepoints;
1150
+ std::array<u16, 0x100 > indices;
1151
+ ALLOW_UNINITIALIZED_END
1152
+
1153
+ for (u32 i = 0 ; i < codepoints.size (); ++i)
1154
+ {
1155
+ codepoints[i] = 0x2500 + i;
1156
+ }
1157
+
1158
+ THROW_IF_FAILED (fontFaceEntry.fontFace ->GetGlyphIndicesW (codepoints.data (), codepoints.size (), indices.data ()));
1159
+
1160
+ for (u32 i = 0 ; i < indices.size (); ++i)
1161
+ {
1162
+ if (const auto idx = indices[i])
1163
+ {
1164
+ fontFaceEntry.boxGlyphs .insert (idx);
1165
+ }
1166
+ }
1167
+ }
1168
+
1169
+ bool BackendD3D::_drawGlyph (const RenderingPayload& p, const AtlasFontFaceEntryInner& fontFaceEntry, AtlasGlyphEntry& glyphEntry)
1128
1170
{
1129
1171
if (!fontFaceEntry.fontFace )
1130
1172
{
@@ -1246,7 +1288,7 @@ bool BackendD3D::_drawGlyph(const RenderingPayload& p, const BackendD3D::AtlasFo
1246
1288
bool isColorGlyph = false ;
1247
1289
D2D1_RECT_F bounds = GlyphRunEmptyBounds;
1248
1290
1249
- const auto cleanup = wil::scope_exit ([&]() {
1291
+ const auto antialiasingCleanup = wil::scope_exit ([&]() {
1250
1292
if (isColorGlyph)
1251
1293
{
1252
1294
_d2dRenderTarget4->SetTextAntialiasMode (static_cast <D2D1_TEXT_ANTIALIAS_MODE>(p.s ->font ->antialiasingMode ));
@@ -1273,7 +1315,23 @@ bool BackendD3D::_drawGlyph(const RenderingPayload& p, const BackendD3D::AtlasFo
1273
1315
}
1274
1316
}
1275
1317
1276
- // box may be empty if the glyph is whitespace.
1318
+ // Overhangs for box glyphs can produce unsightly effects, where the antialiased edges of horizontal
1319
+ // and vertical lines overlap between neighboring glyphs and produce "boldened" intersections.
1320
+ // It looks a little something like this:
1321
+ // ---+---+---
1322
+ // This avoids the issue in most cases by simply clipping the glyph to the size of a single cell.
1323
+ // The downside is that it fails to work well for custom line heights, etc.
1324
+ const auto isBoxGlyph = fontFaceEntry.boxGlyphs .lookup (glyphEntry.glyphIndex ) != nullptr ;
1325
+ if (isBoxGlyph)
1326
+ {
1327
+ // NOTE: As mentioned above, the "origin" of a glyph's coordinate system is its baseline.
1328
+ bounds.left = std::max (bounds.left , 0 .0f );
1329
+ bounds.top = std::max (bounds.top , static_cast <f32>(-p.s ->font ->baseline ));
1330
+ bounds.right = std::min (bounds.right , static_cast <f32>(p.s ->font ->cellSize .x ));
1331
+ bounds.bottom = std::min (bounds.bottom , static_cast <f32>(p.s ->font ->descender ));
1332
+ }
1333
+
1334
+ // The bounds may be empty if the glyph is whitespace.
1277
1335
if (bounds.left >= bounds.right || bounds.top >= bounds.bottom )
1278
1336
{
1279
1337
return true ;
@@ -1309,6 +1367,23 @@ bool BackendD3D::_drawGlyph(const RenderingPayload& p, const BackendD3D::AtlasFo
1309
1367
1310
1368
_d2dBeginDrawing ();
1311
1369
1370
+ if (isBoxGlyph)
1371
+ {
1372
+ const D2D1_RECT_F clipRect{
1373
+ static_cast <f32>(rect.x ),
1374
+ static_cast <f32>(rect.y ),
1375
+ static_cast <f32>(rect.x + rect.w ),
1376
+ static_cast <f32>(rect.y + rect.h ),
1377
+ };
1378
+ _d2dRenderTarget4->PushAxisAlignedClip (&clipRect, D2D1_ANTIALIAS_MODE_ALIASED);
1379
+ }
1380
+ const auto boxGlyphCleanup = wil::scope_exit ([&]() {
1381
+ if (isBoxGlyph)
1382
+ {
1383
+ _d2dRenderTarget4->PopAxisAlignedClip ();
1384
+ }
1385
+ });
1386
+
1312
1387
if (!isColorGlyph)
1313
1388
{
1314
1389
_d2dRenderTarget->DrawGlyphRun (baselineOrigin, &glyphRun, _brush.get (), DWRITE_MEASURING_MODE_NATURAL);
0 commit comments