Repository: microsoft/terminal (language: cpp)

Issue:
Powerline glyph does not fill whole 'cell' / 'box' height
### Windows Terminal version

1.12.10982.0

### Windows build number

10.0.19044.1645

### Other Software

Cascadia Code 2111.01

### Steps to reproduce

* Install static `Cascadia Code PL Regular` [1]:
  * `ttf/static/CascadiaCodePL-Regular.ttf` or
  * `otf/static/CascadiaCodePL-Regular.otf`
* Use it as font with 12 pt
* Utilize the powerline glyphs like so (copy and paste into command prompt terminal window):
  `set prompt=$E[0;33;40m$E[7;33;40m Manuel $E[0;33;40m$E[7;32;40m  ~ $E[0;32;40m$E[7;34;40m  $E[0;34;40m$E[0;37;40m $g`

[1] https://github.com/microsoft/cascadia-code/releases/tag/v2111.01

### Expected Behavior

The upper and lower 'border' of the powerline glyphs shall line up with the 'cell' top and bottom.
Cell top and bottom can be seen if insert a blank with reverse colors (like done in the prompt command above).

This is how it should look like, roughly (magnified 400%):

![image](https://user-images.githubusercontent.com/16012374/166445737-77ba0a2d-1d9a-4c89-b511-1afcdf598234.png)


### Actual Behavior

The glyphs are about 1 pixel to small vertically; here with the `otf` one pixel at the bottom seems to be missing:

![image](https://user-images.githubusercontent.com/16012374/166445966-9c06e4aa-56a2-43df-aa43-320bcce82eb8.png)

This is most notable with the rounded D shape powerline symbols, but in fact are present in all.

![image](https://user-images.githubusercontent.com/16012374/166446192-519be1ba-74de-4d61-8ea0-d6c92805f921.png)

The static `ttf` seem to have the missing pixel in the top.
I did not try the VF.

Origin of issue: Bottom (after closing) https://github.com/ryanoasis/nerd-fonts/issues/828

I believe this is not a Cascadia Code issue, because the original issue came up with a Nerd Font patched version of that font (Caskaydia Cove); and in the process of that issue I tried a lot like ascent and descent changing (all of them OS/2, typo, Win etc ;), PPEM modification, etc pp, all to no avail.
I do not really know how `Windows Terminal` finds out its cell size. If one understands that maybe the font can be beat into function. But I fear that it is a rendering issue, as this is always 1 px big, regardless of font size in point.

_Edit: Fix typo_


Discussion / hints:
Admittedly, you're probably using the default DX renderer, but I wonder if we fixed this for the Atlas engine in #13549, /cc @lhecker 

Also hints of:
* #8993
@zadjii-msft It looks terrible!

![image](https://user-images.githubusercontent.com/2256941/182234716-65f97c15-3dcc-4cd9-8c54-b7d74d88ed0e.png)

The lines occur because I subtract an extra 1px when downsizing glyphs and I did that in response to https://github.com/microsoft/terminal/pull/13549#issuecomment-1191466896
This situation is definitely a bit annoying! 😅

Fix patch:
diff --git a/src/renderer/atlas/AtlasEngine.r.cpp b/src/renderer/atlas/AtlasEngine.r.cpp
index 62fad5538da..cbff3d22185 100644
--- a/src/renderer/atlas/AtlasEngine.r.cpp
+++ b/src/renderer/atlas/AtlasEngine.r.cpp
@@ -22,6 +22,28 @@
 #pragma warning(disable : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
 #pragma warning(disable : 26482) // Only index into arrays using constant expressions (bounds.2).
 
+// https://en.wikipedia.org/wiki/Inversion_list
+template<size_t N>
+constexpr bool isInInversionList(const std::array<wchar_t, N>& ranges, wchar_t needle)
+{
+    const auto beg = ranges.begin();
+    const auto end = ranges.end();
+    decltype(ranges.begin()) it;
+
+    // Linear search is faster than binary search for short inputs.
+    if constexpr (N < 16)
+    {
+        it = std::find_if(beg, end, [=](wchar_t v) { return needle < v; });
+    }
+    else
+    {
+        it = std::upper_bound(beg, end, needle);
+    }
+
+    const auto idx = it - beg;
+    return (idx & 1) != 0;
+}
+
 using namespace Microsoft::Console::Render;
 
 #pragma region IRenderEngine
@@ -276,12 +298,28 @@ void AtlasEngine::_drawGlyph(const AtlasQueueItem& item) const
     f32x2 offset{ 0, 0 };
     f32x2 scale{ 1, 1 };
 
-    // Block Element and Box Drawing characters need to be handled separately from
-    // others, because unlike them they're supposed to fill the entire layout box.
-    // Or in other words: If they overflow the layout box, that's actually great!
-    // To be on the safe side measure this code however will always adjust
-    // their scale to result in an exact size match of the layoutBox.
-    if (charsLength == 1 && key->chars[0] >= 0x2500 && key->chars[0] <= 0x259F)
+    // Block Element and Box Drawing characters need to be handled separately,
+    // because unlike regular ones they're supposed to fill the entire layout box.
+    //
+    // Ranges:
+    // * 0x2500-0x257F: Box Drawing
+    // * 0x2580-0x259F: Block Elements
+    // * 0xE0A0-0xE0A3,0xE0B0-0xE0C8,0xE0CA-0xE0CA,0xE0CC-0xE0D4: PowerLine
+    //   (https://github.com/ryanoasis/nerd-fonts/wiki/Glyph-Sets-and-Code-Points#powerline-symbols)
+    //
+    // The following `blockCharacters` forms a so called "inversion list".
+    static constexpr std::array blockCharacters{
+        // clang-format off
+        L'\u2500', L'\u2580',
+        L'\u2580', L'\u25A0',
+        L'\uE0A0', L'\uE0A4',
+        L'\uE0B0', L'\uE0C9',
+        L'\uE0CA', L'\uE0CB',
+        L'\uE0CC', L'\uE0D5',
+        // clang-format on
+    };
+
+    if (charsLength == 1 && isInInversionList(blockCharacters, key->chars[0]))
     {
         wil::com_ptr<IDWriteFontCollection> fontCollection;
         THROW_IF_FAILED(textFormat->GetFontCollection(fontCollection.addressof()));
@@ -329,8 +367,8 @@ void AtlasEngine::_drawGlyph(const AtlasQueueItem& item) const
             // So for safe measure we'll always scale them to the exact size.
             // But add 1px to the destination size, so that we don't end up with fractional pixels.
             scalingRequired = true;
-            scale.x = (layoutBox.x + _r.dipPerPixel) / boxSize.x;
-            scale.y = (layoutBox.y + _r.dipPerPixel) / boxSize.y;
+            scale.x = layoutBox.x / boxSize.x;
+            scale.y = layoutBox.y / boxSize.y;
         }
     }
     else
@@ -409,14 +447,14 @@ void AtlasEngine::_drawGlyph(const AtlasQueueItem& item) const
         {
             scalingRequired = true;
             offset.x = (overhang.left - overhang.right) * 0.5f;
-            scale.x = (layoutBox.x - _r.dipPerPixel) / actualSize.x;
+            scale.x = layoutBox.x / actualSize.x;
             scale.y = scale.x;
         }
         if (actualSize.y > layoutBox.y)
         {
             scalingRequired = true;
             offset.y = (overhang.top - overhang.bottom) * 0.5f;
-            scale.x = std::min(scale.x, (layoutBox.y - _r.dipPerPixel) / actualSize.y);
+            scale.x = std::min(scale.x, layoutBox.y / actualSize.y);
             scale.y = scale.x;
         }
 


Test patch: