[PR #5743] [MERGED] Scale box drawing glyphs to fit cells for visual bliss #26448

Open
opened 2026-01-31 09:16:11 +00:00 by claunia · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/microsoft/terminal/pull/5743
Author: @miniksa
Created: 5/4/2020
Status: Merged
Merged: 5/8/2020
Merged by: @miniksa

Base: masterHead: dev/miniksa/box


📝 Commits (10+)

  • eb03caa Add line gap calculations to total line height, spread across ascent and descent to center glyphs vertically (as much as possible).
  • e0c4d28 Create custom BoxDrawingEffect to specify when to do box drawing. Update CustomTextLayout to add a new analyzer pass to detect box drawing layouts and store a BoxDrawingEffect in the run. Add capability for Runs to hold a custom drawing effect. Pipe drawing effects all the way down. Make the glyph run drawing engine detect when a custom effect is applied and call a special method to perform the effect. Implement special effect for box drawing run based on the ol' manual glyph run drawing method, adjusting it to draw the outline as well as fill the geometries and also apply both a scale factor as well as the existing translation to the geometries.
  • 3467dc0 code format pass.
  • 3d92b73 This works for most of the fonts.
  • fe4e1c7 Hey put that gap back.
  • f6e4542 Fix translation factor when scaling.
  • 5d66def Make it work for font fallback by carrying the scale factor through and performing the analysis after the fallback font has been adjusted to make its advances align with the primary font (by applying a font size scalar).
  • 9310a5e Oops, dropped a noexcept and forgot a gsl::narrow
  • 33fcdff Write down as many of my notes as I can manage into comments.
  • e97b3dd Add xlsx used for math calculations to docs folder. Run code format. Update spell checker. Fix spelling mistake. Add comments and rearrange code in a bunch of places.

📊 Changes

14 files changed (+673 additions, -43 deletions)

View changed files

📝 .github/actions/spell-check/dictionary/apis.txt (+3 -0)
📝 .gitignore (+1 -0)
doc/reference/customtextlayout.xlsx (+0 -0)
src/renderer/dx/BoxDrawingEffect.cpp (+30 -0)
src/renderer/dx/BoxDrawingEffect.h (+30 -0)
📝 src/renderer/dx/CustomTextLayout.cpp (+393 -2)
📝 src/renderer/dx/CustomTextLayout.h (+15 -2)
📝 src/renderer/dx/CustomTextRenderer.cpp (+107 -19)
📝 src/renderer/dx/CustomTextRenderer.h (+10 -7)
📝 src/renderer/dx/DxRenderer.cpp (+47 -12)
📝 src/renderer/dx/DxRenderer.hpp (+1 -0)
src/renderer/dx/IBoxDrawingEffect.idl (+20 -0)
📝 src/renderer/dx/lib/dx.vcxproj (+11 -1)
📝 src/renderer/dx/lib/dx.vcxproj.filters (+5 -0)

📄 Description

Summary of the Pull Request

Identifies and scales glyphs in the box and line drawing ranges U+2500-U+259F to fit their cells.

PR Checklist

  • Closes A question about emulating openpty with the new CreatePseudoConsole API (#455)
  • I work here.
  • Manual tests. This is all graphical.
  • Metric ton of comments
  • Math spreadsheet included in PR.
  • Double check RTL glyphs.
  • Why is there the extra pixel?
  • Scrolling the mouse wheel check is done.
  • Not drawing outline?
  • Am core contributor. Roar.
  • Try suppressing negative scale factors and see if that gets rid of weird shading.

Detailed Description of the Pull Request / Additional comments

Background

  • We want the Terminal to be fast at drawing. To be fast at drawing, we perform differential drawing, or only drawing what is different from the previous frame. We use DXGI's Present1 method to help us with this as it helps us compose only the deltas onto the previous frame at drawing time and assists us in scrolling regions from the previous frame without intervention. However, it only works on strictly integer pixel row heights.
  • Most of the hit testing and size-calculation logic in both the conhost and the Terminal products are based on the size of an individual cell. Historically, a cell was always dictated in a COORD structure, or two SHORT values... which are integers. As such, when we specify the space for any individual glyph to be displayed inside our terminal drawing region, we want it to fall perfectly inside of an integer box to ensure all these other algorithms work correctly and continue to do so.
  • Finally, we want the Terminal to have font fallback and locate glyphs that aren't in the primary selected font from any other font it can find on the system that contains the glyph, per DirectWrite's font fallback mechanisms. These glyphs won't necessarily have the same font or glyph metrics as the base font, but we need them to fit inside the same cell dimensions as if they did because the hit testing and other algorithms aren't aware of which particular font is sourcing each glyph, just the dimensions of the bounding box per cell.

How does Terminal deal with this?

  • When we select a font, we perform some calculations using the design metrics of the font and glyphs to determine how we could fit them inside a cell with integer dimensions. Our process here is that we take the requested font size (which is generally a proxy for height), find the matching glyph width for that height then round it to an integer. We back convert from that now integer width to a height value which is almost certainly now a floating point number. But because we need an integer box value, we add line padding above and below the glyphs to ensure that the height is an integer as well as the width. Finally, we don't add the padding strictly equally. We attempt to align the English baseline of the glyph box directly onto an integer pixel multiple so most characters sit crisply on a line when displayed.
  • Note that fonts and their glyphs have a prescribed baseline, line gap, and advance values. We use those as guidelines to get us started, but then to meet our requirements, we pad out from those. This results in fonts that should be properly authored showing gaps. It also results in fonts that are improperly authored looking even worse than they normally would.

Now how does block and line drawing come in?

  • Block and Line drawing glyphs are generally authored so they will look fine when the font and glyph metrics are followed exactly as prescribed by the font. (For some fonts, this still isn't true and we want them to look fine anyway.)
  • When we add additional padding or rounding to make glyphs fit inside of a cell, we can be adding more space than was prescribed around these glyphs. This can cause a gap to be visible.
  • Additionally, when we move things like baselines to land on a perfect integer pixel, we may be drawing a glyph lower in the bounding box than was prescribed originally.

And how do we solve it?

  • We identify all glyphs in the line and block drawing ranges.
  • We find the bounding boxes of both the cell and the glyph.
  • We compare the height of the glyph to the height of the cell to see if we need to scale. We prescribe a scale transform if the glyph wouldn't be tall enough to fit the box. (We leave it alone otherwise as some glyphs intentionally overscan the box and scaling them can cause banding effects.)
  • We inspect the overhang/underhang above and below the boxes and translate transform them (slide them) so they cover the entire cell area.
  • We repeat the previous two steps but in the horizontal direction.

Validation Steps Performed

Also see the below one with more screenshots:


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/microsoft/terminal/pull/5743 **Author:** [@miniksa](https://github.com/miniksa) **Created:** 5/4/2020 **Status:** ✅ Merged **Merged:** 5/8/2020 **Merged by:** [@miniksa](https://github.com/miniksa) **Base:** `master` ← **Head:** `dev/miniksa/box` --- ### 📝 Commits (10+) - [`eb03caa`](https://github.com/microsoft/terminal/commit/eb03caa33055c9bdd6a6d623351d4334da964bea) Add line gap calculations to total line height, spread across ascent and descent to center glyphs vertically (as much as possible). - [`e0c4d28`](https://github.com/microsoft/terminal/commit/e0c4d28e29030aa65960cf39ceb98cf3da4b8922) Create custom BoxDrawingEffect to specify when to do box drawing. Update CustomTextLayout to add a new analyzer pass to detect box drawing layouts and store a BoxDrawingEffect in the run. Add capability for Runs to hold a custom drawing effect. Pipe drawing effects all the way down. Make the glyph run drawing engine detect when a custom effect is applied and call a special method to perform the effect. Implement special effect for box drawing run based on the ol' manual glyph run drawing method, adjusting it to draw the outline as well as fill the geometries and also apply both a scale factor as well as the existing translation to the geometries. - [`3467dc0`](https://github.com/microsoft/terminal/commit/3467dc0d99e2528df484085bb022fe733c58365a) code format pass. - [`3d92b73`](https://github.com/microsoft/terminal/commit/3d92b73222f25a90b77a2f79b60ff09a9e7c5bff) This works for most of the fonts. - [`fe4e1c7`](https://github.com/microsoft/terminal/commit/fe4e1c7fa02a38b158ca331f8785b7bc1c1217e1) Hey put that gap back. - [`f6e4542`](https://github.com/microsoft/terminal/commit/f6e4542340f100762573f8ce8f95887d7724dd08) Fix translation factor when scaling. - [`5d66def`](https://github.com/microsoft/terminal/commit/5d66defbad9b8a4f91a28fb242671cde65adbe84) Make it work for font fallback by carrying the scale factor through and performing the analysis after the fallback font has been adjusted to make its advances align with the primary font (by applying a font size scalar). - [`9310a5e`](https://github.com/microsoft/terminal/commit/9310a5e237349179ec5c3d21e16e304d36fb61d8) Oops, dropped a noexcept and forgot a gsl::narrow - [`33fcdff`](https://github.com/microsoft/terminal/commit/33fcdff9f6a3b77f6f994c9eab3b3d6f6f7e73ac) Write down as many of my notes as I can manage into comments. - [`e97b3dd`](https://github.com/microsoft/terminal/commit/e97b3ddbfabf73fe82da40f5616418b2e7c43311) Add xlsx used for math calculations to docs folder. Run code format. Update spell checker. Fix spelling mistake. Add comments and rearrange code in a bunch of places. ### 📊 Changes **14 files changed** (+673 additions, -43 deletions) <details> <summary>View changed files</summary> 📝 `.github/actions/spell-check/dictionary/apis.txt` (+3 -0) 📝 `.gitignore` (+1 -0) ➕ `doc/reference/customtextlayout.xlsx` (+0 -0) ➕ `src/renderer/dx/BoxDrawingEffect.cpp` (+30 -0) ➕ `src/renderer/dx/BoxDrawingEffect.h` (+30 -0) 📝 `src/renderer/dx/CustomTextLayout.cpp` (+393 -2) 📝 `src/renderer/dx/CustomTextLayout.h` (+15 -2) 📝 `src/renderer/dx/CustomTextRenderer.cpp` (+107 -19) 📝 `src/renderer/dx/CustomTextRenderer.h` (+10 -7) 📝 `src/renderer/dx/DxRenderer.cpp` (+47 -12) 📝 `src/renderer/dx/DxRenderer.hpp` (+1 -0) ➕ `src/renderer/dx/IBoxDrawingEffect.idl` (+20 -0) 📝 `src/renderer/dx/lib/dx.vcxproj` (+11 -1) 📝 `src/renderer/dx/lib/dx.vcxproj.filters` (+5 -0) </details> ### 📄 Description ## Summary of the Pull Request Identifies and scales glyphs in the box and line drawing ranges U+2500-U+259F to fit their cells. ## PR Checklist * [x] Closes #455 * [x] I work here. * [x] Manual tests. This is all graphical. * [x] Metric ton of comments * [x] Math spreadsheet included in PR. * [x] Double check RTL glyphs. * [x] Why is there the extra pixel? * [x] Scrolling the mouse wheel check is done. * [x] Not drawing outline? * [x] Am core contributor. Roar. * [x] Try suppressing negative scale factors and see if that gets rid of weird shading. ## Detailed Description of the Pull Request / Additional comments ### Background - We want the Terminal to be fast at drawing. To be fast at drawing, we perform differential drawing, or only drawing what is different from the previous frame. We use DXGI's `Present1` method to help us with this as it helps us compose only the deltas onto the previous frame at drawing time and assists us in scrolling regions from the previous frame without intervention. However, it only works on strictly integer pixel row heights. - Most of the hit testing and size-calculation logic in both the `conhost` and the Terminal products are based on the size of an individual cell. Historically, a cell was always dictated in a `COORD` structure, or two `SHORT` values... which are integers. As such, when we specify the space for any individual glyph to be displayed inside our terminal drawing region, we want it to fall perfectly inside of an integer box to ensure all these other algorithms work correctly and continue to do so. - Finally, we want the Terminal to have font fallback and locate glyphs that aren't in the primary selected font from any other font it can find on the system that contains the glyph, per DirectWrite's font fallback mechanisms. These glyphs won't necessarily have the same font or glyph metrics as the base font, but we need them to fit inside the same cell dimensions as if they did because the hit testing and other algorithms aren't aware of which particular font is sourcing each glyph, just the dimensions of the bounding box per cell. ### How does Terminal deal with this? - When we select a font, we perform some calculations using the design metrics of the font and glyphs to determine how we could fit them inside a cell with integer dimensions. Our process here is that we take the requested font size (which is generally a proxy for height), find the matching glyph width for that height then round it to an integer. We back convert from that now integer width to a height value which is almost certainly now a floating point number. But because we need an integer box value, we add line padding above and below the glyphs to ensure that the height is an integer as well as the width. Finally, we don't add the padding strictly equally. We attempt to align the English baseline of the glyph box directly onto an integer pixel multiple so most characters sit crisply on a line when displayed. - Note that fonts and their glyphs have a prescribed baseline, line gap, and advance values. We use those as guidelines to get us started, but then to meet our requirements, we pad out from those. This results in fonts that should be properly authored showing gaps. It also results in fonts that are improperly authored looking even worse than they normally would. ### Now how does block and line drawing come in? - Block and Line drawing glyphs are generally authored so they will look fine when the font and glyph metrics are followed exactly as prescribed by the font. (For some fonts, this still isn't true and we want them to look fine anyway.) - When we add additional padding or rounding to make glyphs fit inside of a cell, we can be adding more space than was prescribed around these glyphs. This can cause a gap to be visible. - Additionally, when we move things like baselines to land on a perfect integer pixel, we may be drawing a glyph lower in the bounding box than was prescribed originally. ### And how do we solve it? - We identify all glyphs in the line and block drawing ranges. - We find the bounding boxes of both the cell and the glyph. - We compare the height of the glyph to the height of the cell to see if we need to scale. We prescribe a scale transform if the glyph wouldn't be tall enough to fit the box. (We leave it alone otherwise as some glyphs intentionally overscan the box and scaling them can cause banding effects.) - We inspect the overhang/underhang above and below the boxes and translate transform them (slide them) so they cover the entire cell area. - We repeat the previous two steps but in the horizontal direction. ## Validation Steps Performed - See these commments: - https://github.com/microsoft/terminal/issues/455#issuecomment-620248375 - https://github.com/microsoft/terminal/issues/455#issuecomment-621533916 - https://github.com/microsoft/terminal/issues/455#issuecomment-622585453 Also see the below one with more screenshots: - https://github.com/microsoft/terminal/pull/5743#issuecomment-624940567 --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
claunia added the pull-request label 2026-01-31 09:16:11 +00:00
Sign in to join this conversation.
No Label pull-request
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#26448