From d655ff7bb7ea1064d21fad47efb7f2e01bfdaa90 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 29 Sep 2022 03:20:53 +0200 Subject: [PATCH 1/2] AtlasEngine: Block chars done right-er --- src/renderer/atlas/AtlasEngine.r.cpp | 59 +++++++++++++++++----------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/src/renderer/atlas/AtlasEngine.r.cpp b/src/renderer/atlas/AtlasEngine.r.cpp index 73d6a709296..6958f43a05c 100644 --- a/src/renderer/atlas/AtlasEngine.r.cpp +++ b/src/renderer/atlas/AtlasEngine.r.cpp @@ -538,32 +538,45 @@ AtlasEngine::CachedGlyphLayout AtlasEngine::_getCachedGlyphLayout(const wchar_t* DWRITE_FONT_METRICS metrics; fontFace->GetMetrics(&metrics); - const u32 codePoint = chars[0]; + static constexpr u32 codePoint = L'\u2588'; // Full Block character u16 glyphIndex; THROW_IF_FAILED(fontFace->GetGlyphIndicesW(&codePoint, 1, &glyphIndex)); - DWRITE_GLYPH_METRICS glyphMetrics; - THROW_IF_FAILED(fontFace->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetrics)); - - const f32x2 boxSize{ - static_cast(glyphMetrics.advanceWidth) / static_cast(metrics.designUnitsPerEm) * _r.fontMetrics.fontSizeInDIP, - static_cast(glyphMetrics.advanceHeight) / static_cast(metrics.designUnitsPerEm) * _r.fontMetrics.fontSizeInDIP, - }; - - // NOTE: Don't adjust the offset. - // Despite these being block characters, some fonts position them really weird with glyphs hanging out - // on all sides by up to 0.2em. They still expect the glyphs to be drawn on to their regular baseline. - // At the time of writing this can be tested with MesloLGM Nerd Font and U+E0B0 for instance. - - scalingRequired = true; - // We always want box drawing glyphs to exactly match the size of a terminal cell. - // But add 1px to the destination size, so that we don't end up with fractional pixels. - scale.x = (layoutBox.x + _r.dipPerPixel) / boxSize.x; - scale.y = (layoutBox.y + _r.dipPerPixel) / boxSize.y; - // Now that the glyph is in the center of the cell thanks - // to the offset, the scaleCenter is center of the cell. - scaleCenter.x = layoutBox.x * 0.5f; - scaleCenter.y = layoutBox.y * 0.5f; + if (glyphIndex) + { + DWRITE_GLYPH_METRICS glyphMetrics; + THROW_IF_FAILED(fontFace->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetrics)); + + const auto fontScale = _r.fontMetrics.fontSizeInDIP / metrics.designUnitsPerEm; + + // How-to-overhang given a single glyph: + DWRITE_OVERHANG_METRICS overhang; + overhang.left = static_cast(glyphMetrics.leftSideBearing) * fontScale; + overhang.top = static_cast(glyphMetrics.verticalOriginY - glyphMetrics.topSideBearing) * fontScale - _r.fontMetrics.baselineInDIP; + overhang.right = static_cast(gsl::narrow_cast(glyphMetrics.advanceWidth) - glyphMetrics.rightSideBearing) * fontScale - layoutBox.x; + overhang.bottom = static_cast(gsl::narrow_cast(glyphMetrics.advanceHeight) - glyphMetrics.verticalOriginY - glyphMetrics.bottomSideBearing) * fontScale + _r.fontMetrics.baselineInDIP - layoutBox.y; + + // Marvel at this beautiful code. Don't copy this, use DirectXMath instead. + if ( + overhang.left < _r.pixelPerDIP || overhang.left > _r.pixelPerDIP || + overhang.top < _r.pixelPerDIP || overhang.top > _r.pixelPerDIP || + overhang.right < _r.pixelPerDIP || overhang.right > _r.pixelPerDIP || + overhang.bottom < _r.pixelPerDIP || overhang.bottom > _r.pixelPerDIP) + { + scalingRequired = true; + // Center glyphs. + offset.x = (overhang.left - overhang.right) * 0.5f; + offset.y = (overhang.top - overhang.bottom) * 0.5f; + // We always want box drawing glyphs to exactly match the size of a terminal cell. + // But add 1px to the destination size, so that we don't end up with fractional pixels. + scale.x = (layoutBox.x + _r.pixelPerDIP) / (layoutBox.x + overhang.left + overhang.right); + scale.y = (layoutBox.y + _r.pixelPerDIP) / (layoutBox.y + overhang.top + overhang.bottom); + // Now that the glyph is in the center of the cell thanks + // to the offset, the scaleCenter is center of the cell. + scaleCenter.x = layoutBox.x * 0.5f; + scaleCenter.y = layoutBox.y * 0.5f; + } + } } } else From e2989ba7fe15aca81258ac84b840213cabef8ced Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 29 Sep 2022 04:30:49 +0200 Subject: [PATCH 2/2] Don't adjust the size of the user's chosen font --- src/renderer/atlas/AtlasEngine.api.cpp | 1 + src/renderer/atlas/AtlasEngine.cpp | 4 +++ src/renderer/atlas/AtlasEngine.h | 2 ++ src/renderer/atlas/AtlasEngine.r.cpp | 46 ++++++++++++-------------- 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp index aca166f8524..1e1776559bc 100644 --- a/src/renderer/atlas/AtlasEngine.api.cpp +++ b/src/renderer/atlas/AtlasEngine.api.cpp @@ -699,6 +699,7 @@ void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const Fo // as we might cause _api to be in an inconsistent state otherwise. fontMetrics->fontCollection = std::move(fontCollection); + fontMetrics->fontFamily = std::move(fontFamily); fontMetrics->fontName = std::move(fontName); fontMetrics->fontSizeInDIP = fontSizeInDIP; fontMetrics->baselineInDIP = baseline / static_cast(_api.dpi) * 96.0f; diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp index d6d3dd45645..c99c9303b21 100644 --- a/src/renderer/atlas/AtlasEngine.cpp +++ b/src/renderer/atlas/AtlasEngine.cpp @@ -1101,6 +1101,10 @@ void AtlasEngine::_recreateFontDependentResources() const auto fontStyle = italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL; auto& textFormat = _r.textFormats[italic][bold]; + wil::com_ptr font; + THROW_IF_FAILED(_r.fontMetrics.fontFamily->GetFirstMatchingFont(fontWeight, DWRITE_FONT_STRETCH_NORMAL, fontStyle, font.addressof())); + THROW_IF_FAILED(font->CreateFontFace(_r.fontFaces[italic << 1 | bold].put())); + THROW_IF_FAILED(_sr.dwriteFactory->CreateTextFormat(_api.fontMetrics.fontName.c_str(), _api.fontMetrics.fontCollection.get(), fontWeight, fontStyle, DWRITE_FONT_STRETCH_NORMAL, _api.fontMetrics.fontSizeInDIP, L"", textFormat.put())); THROW_IF_FAILED(textFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP)); diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index fe3b9712bb0..6a1533b4f6e 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -410,6 +410,7 @@ namespace Microsoft::Console::Render struct FontMetrics { wil::com_ptr fontCollection; + wil::com_ptr fontFamily; std::wstring fontName; float baselineInDIP = 0.0f; float fontSizeInDIP = 0.0f; @@ -1010,6 +1011,7 @@ namespace Microsoft::Console::Render wil::com_ptr atlasView; wil::com_ptr d2dRenderTarget; wil::com_ptr brush; + wil::com_ptr fontFaces[4]; wil::com_ptr textFormats[2][2]; Buffer textFormatAxes[2][2]; wil::com_ptr typography; diff --git a/src/renderer/atlas/AtlasEngine.r.cpp b/src/renderer/atlas/AtlasEngine.r.cpp index 6958f43a05c..a8bc092c0b8 100644 --- a/src/renderer/atlas/AtlasEngine.r.cpp +++ b/src/renderer/atlas/AtlasEngine.r.cpp @@ -535,34 +535,30 @@ AtlasEngine::CachedGlyphLayout AtlasEngine::_getCachedGlyphLayout(const wchar_t* wil::com_ptr fontFace; THROW_IF_FAILED(mappedFont->CreateFontFace(fontFace.addressof())); - DWRITE_FONT_METRICS metrics; - fontFace->GetMetrics(&metrics); + // Don't adjust the size of block glyphs that are part of the user's chosen font. + if (std::ranges::find(_r.fontFaces, fontFace) == std::end(_r.fontFaces)) + { + DWRITE_FONT_METRICS metrics; + fontFace->GetMetrics(&metrics); - static constexpr u32 codePoint = L'\u2588'; // Full Block character - u16 glyphIndex; - THROW_IF_FAILED(fontFace->GetGlyphIndicesW(&codePoint, 1, &glyphIndex)); + static constexpr u32 codePoint = L'\u2588'; // Full Block character + u16 glyphIndex; + THROW_IF_FAILED(fontFace->GetGlyphIndicesW(&codePoint, 1, &glyphIndex)); - if (glyphIndex) - { - DWRITE_GLYPH_METRICS glyphMetrics; - THROW_IF_FAILED(fontFace->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetrics)); - - const auto fontScale = _r.fontMetrics.fontSizeInDIP / metrics.designUnitsPerEm; - - // How-to-overhang given a single glyph: - DWRITE_OVERHANG_METRICS overhang; - overhang.left = static_cast(glyphMetrics.leftSideBearing) * fontScale; - overhang.top = static_cast(glyphMetrics.verticalOriginY - glyphMetrics.topSideBearing) * fontScale - _r.fontMetrics.baselineInDIP; - overhang.right = static_cast(gsl::narrow_cast(glyphMetrics.advanceWidth) - glyphMetrics.rightSideBearing) * fontScale - layoutBox.x; - overhang.bottom = static_cast(gsl::narrow_cast(glyphMetrics.advanceHeight) - glyphMetrics.verticalOriginY - glyphMetrics.bottomSideBearing) * fontScale + _r.fontMetrics.baselineInDIP - layoutBox.y; - - // Marvel at this beautiful code. Don't copy this, use DirectXMath instead. - if ( - overhang.left < _r.pixelPerDIP || overhang.left > _r.pixelPerDIP || - overhang.top < _r.pixelPerDIP || overhang.top > _r.pixelPerDIP || - overhang.right < _r.pixelPerDIP || overhang.right > _r.pixelPerDIP || - overhang.bottom < _r.pixelPerDIP || overhang.bottom > _r.pixelPerDIP) + if (glyphIndex) { + DWRITE_GLYPH_METRICS glyphMetrics; + THROW_IF_FAILED(fontFace->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetrics)); + + const auto fontScale = _r.fontMetrics.fontSizeInDIP / metrics.designUnitsPerEm; + + // How-to-DWRITE_OVERHANG_METRICS given a single glyph: + DWRITE_OVERHANG_METRICS overhang; + overhang.left = static_cast(glyphMetrics.leftSideBearing) * fontScale; + overhang.top = static_cast(glyphMetrics.verticalOriginY - glyphMetrics.topSideBearing) * fontScale - _r.fontMetrics.baselineInDIP; + overhang.right = static_cast(gsl::narrow_cast(glyphMetrics.advanceWidth) - glyphMetrics.rightSideBearing) * fontScale - layoutBox.x; + overhang.bottom = static_cast(gsl::narrow_cast(glyphMetrics.advanceHeight) - glyphMetrics.verticalOriginY - glyphMetrics.bottomSideBearing) * fontScale + _r.fontMetrics.baselineInDIP - layoutBox.y; + scalingRequired = true; // Center glyphs. offset.x = (overhang.left - overhang.right) * 0.5f;