@@ -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
{
@@ -1207,22 +1249,20 @@ bool BackendD3D::_drawGlyph(const RenderingPayload& p, const BackendD3D::AtlasFo
1207
1249
#endif
1208
1250
1209
1251
const auto lineRendition = static_cast <LineRendition>(fontFaceEntry.lineRendition );
1210
- std::optional<D2D1_MATRIX_3X2_F> transform ;
1252
+ const auto needsTransform = lineRendition != LineRendition::SingleWidth ;
1211
1253
1212
- if (lineRendition != LineRendition::SingleWidth)
1254
+ static constexpr D2D1_MATRIX_3X2_F identityTransform{ .m11 = 1 , .m22 = 1 };
1255
+ D2D1_MATRIX_3X2_F transform = identityTransform;
1256
+
1257
+ if (needsTransform)
1213
1258
{
1214
- auto & t = transform.emplace ();
1215
- t.m11 = 2 .0f ;
1216
- t.m22 = lineRendition >= LineRendition::DoubleHeightTop ? 2 .0f : 1 .0f ;
1217
- _d2dRenderTarget->SetTransform (&t);
1259
+ transform.m11 = 2 .0f ;
1260
+ transform.m22 = lineRendition >= LineRendition::DoubleHeightTop ? 2 .0f : 1 .0f ;
1261
+ _d2dRenderTarget->SetTransform (&transform);
1218
1262
}
1219
1263
1220
1264
const auto restoreTransform = wil::scope_exit ([&]() noexcept {
1221
- if (transform)
1222
- {
1223
- static constexpr D2D1_MATRIX_3X2_F identity{ .m11 = 1 , .m22 = 1 };
1224
- _d2dRenderTarget->SetTransform (&identity);
1225
- }
1265
+ _d2dRenderTarget->SetTransform (&identityTransform);
1226
1266
});
1227
1267
1228
1268
// This calculates the black box of the glyph, or in other words,
@@ -1246,7 +1286,7 @@ bool BackendD3D::_drawGlyph(const RenderingPayload& p, const BackendD3D::AtlasFo
1246
1286
bool isColorGlyph = false ;
1247
1287
D2D1_RECT_F bounds = GlyphRunEmptyBounds;
1248
1288
1249
- const auto cleanup = wil::scope_exit ([&]() {
1289
+ const auto antialiasingCleanup = wil::scope_exit ([&]() {
1250
1290
if (isColorGlyph)
1251
1291
{
1252
1292
_d2dRenderTarget4->SetTextAntialiasMode (static_cast <D2D1_TEXT_ANTIALIAS_MODE>(p.s ->font ->antialiasingMode ));
@@ -1273,7 +1313,23 @@ bool BackendD3D::_drawGlyph(const RenderingPayload& p, const BackendD3D::AtlasFo
1273
1313
}
1274
1314
}
1275
1315
1276
- // box may be empty if the glyph is whitespace.
1316
+ // Overhangs for box glyphs can produce unsightly effects, where the antialiased edges of horizontal
1317
+ // and vertical lines overlap between neighboring glyphs and produce "boldened" intersections.
1318
+ // It looks a little something like this:
1319
+ // ---+---+---
1320
+ // This avoids the issue in most cases by simply clipping the glyph to the size of a single cell.
1321
+ // The downside is that it fails to work well for custom line heights, etc.
1322
+ const auto isBoxGlyph = fontFaceEntry.boxGlyphs .lookup (glyphEntry.glyphIndex ) != nullptr ;
1323
+ if (isBoxGlyph)
1324
+ {
1325
+ // NOTE: As mentioned above, the "origin" of a glyph's coordinate system is its baseline.
1326
+ bounds.left = std::max (bounds.left , 0 .0f );
1327
+ bounds.top = std::max (bounds.top , static_cast <f32>(-p.s ->font ->baseline ) * transform.m22 );
1328
+ bounds.right = std::min (bounds.right , static_cast <f32>(p.s ->font ->cellSize .x ) * transform.m11 );
1329
+ bounds.bottom = std::min (bounds.bottom , static_cast <f32>(p.s ->font ->descender ) * transform.m22 );
1330
+ }
1331
+
1332
+ // The bounds may be empty if the glyph is whitespace.
1277
1333
if (bounds.left >= bounds.right || bounds.top >= bounds.bottom )
1278
1334
{
1279
1335
return true ;
@@ -1299,15 +1355,31 @@ bool BackendD3D::_drawGlyph(const RenderingPayload& p, const BackendD3D::AtlasFo
1299
1355
static_cast <f32>(rect.y - bt),
1300
1356
};
1301
1357
1302
- if (transform)
1358
+ _d2dBeginDrawing ();
1359
+
1360
+ if (isBoxGlyph)
1303
1361
{
1304
- auto & t = *transform;
1305
- t.dx = (1 .0f - t.m11 ) * baselineOrigin.x ;
1306
- t.dy = (1 .0f - t.m22 ) * baselineOrigin.y ;
1307
- _d2dRenderTarget->SetTransform (&t);
1362
+ const D2D1_RECT_F clipRect{
1363
+ static_cast <f32>(rect.x ) / transform.m11 ,
1364
+ static_cast <f32>(rect.y ) / transform.m22 ,
1365
+ static_cast <f32>(rect.x + rect.w ) / transform.m11 ,
1366
+ static_cast <f32>(rect.y + rect.h ) / transform.m22 ,
1367
+ };
1368
+ _d2dRenderTarget4->PushAxisAlignedClip (&clipRect, D2D1_ANTIALIAS_MODE_ALIASED);
1308
1369
}
1370
+ const auto boxGlyphCleanup = wil::scope_exit ([&]() {
1371
+ if (isBoxGlyph)
1372
+ {
1373
+ _d2dRenderTarget4->PopAxisAlignedClip ();
1374
+ }
1375
+ });
1309
1376
1310
- _d2dBeginDrawing ();
1377
+ if (needsTransform)
1378
+ {
1379
+ transform.dx = (1 .0f - transform.m11 ) * baselineOrigin.x ;
1380
+ transform.dy = (1 .0f - transform.m22 ) * baselineOrigin.y ;
1381
+ _d2dRenderTarget->SetTransform (&transform);
1382
+ }
1311
1383
1312
1384
if (!isColorGlyph)
1313
1385
{
0 commit comments