Skip to content

Commit

Permalink
AtlasEngine: Block chars done right-er (#14099)
Browse files Browse the repository at this point in the history
This commit makes the following improvements:
* Only adjust block characters that come from fallback fonts. This ensures
  that the glyphs of the chosen font all look exactly as they were designed.
* When adjusting the size, use the fallback font's full block glyph U+2588
  to determine the size that the given glyph should have.

Closes #14098

## Validation Steps Performed
* Print `UTF-8-demo.txt` in Consolas.
* All block glyphs look uniform. ✅

(cherry picked from commit 97abc3d)
Service-Card-Id: 86159056
Service-Version: 1.16
  • Loading branch information
lhecker authored and DHowett committed Dec 1, 2022
1 parent f29c3e3 commit cd367e6
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 27 deletions.
1 change: 1 addition & 0 deletions src/renderer/atlas/AtlasEngine.api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<float>(_api.dpi) * 96.0f;
Expand Down
4 changes: 4 additions & 0 deletions src/renderer/atlas/AtlasEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<IDWriteFont> 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));

Expand Down
2 changes: 2 additions & 0 deletions src/renderer/atlas/AtlasEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ namespace Microsoft::Console::Render
struct FontMetrics
{
wil::com_ptr<IDWriteFontCollection> fontCollection;
wil::com_ptr<IDWriteFontFamily> fontFamily;
std::wstring fontName;
float baselineInDIP = 0.0f;
float fontSizeInDIP = 0.0f;
Expand Down Expand Up @@ -1010,6 +1011,7 @@ namespace Microsoft::Console::Render
wil::com_ptr<ID3D11ShaderResourceView> atlasView;
wil::com_ptr<ID2D1DeviceContext> d2dRenderTarget;
wil::com_ptr<ID2D1SolidColorBrush> brush;
wil::com_ptr<IDWriteFontFace> fontFaces[4];
wil::com_ptr<IDWriteTextFormat> textFormats[2][2];
Buffer<DWRITE_FONT_AXIS_VALUE> textFormatAxes[2][2];
wil::com_ptr<IDWriteTypography> typography;
Expand Down
63 changes: 36 additions & 27 deletions src/renderer/atlas/AtlasEngine.r.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -535,35 +535,44 @@ AtlasEngine::CachedGlyphLayout AtlasEngine::_getCachedGlyphLayout(const wchar_t*
wil::com_ptr<IDWriteFontFace> fontFace;
THROW_IF_FAILED(mappedFont->CreateFontFace(fontFace.addressof()));

DWRITE_FONT_METRICS metrics;
fontFace->GetMetrics(&metrics);

const u32 codePoint = chars[0];
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<f32>(glyphMetrics.advanceWidth) / static_cast<f32>(metrics.designUnitsPerEm) * _r.fontMetrics.fontSizeInDIP,
static_cast<f32>(glyphMetrics.advanceHeight) / static_cast<f32>(metrics.designUnitsPerEm) * _r.fontMetrics.fontSizeInDIP,
};
// 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);

// 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.
static constexpr u32 codePoint = L'\u2588'; // Full Block character
u16 glyphIndex;
THROW_IF_FAILED(fontFace->GetGlyphIndicesW(&codePoint, 1, &glyphIndex));

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-DWRITE_OVERHANG_METRICS given a single glyph:
DWRITE_OVERHANG_METRICS overhang;
overhang.left = static_cast<f32>(glyphMetrics.leftSideBearing) * fontScale;
overhang.top = static_cast<f32>(glyphMetrics.verticalOriginY - glyphMetrics.topSideBearing) * fontScale - _r.fontMetrics.baselineInDIP;
overhang.right = static_cast<f32>(gsl::narrow_cast<INT32>(glyphMetrics.advanceWidth) - glyphMetrics.rightSideBearing) * fontScale - layoutBox.x;
overhang.bottom = static_cast<f32>(gsl::narrow_cast<INT32>(glyphMetrics.advanceHeight) - glyphMetrics.verticalOriginY - glyphMetrics.bottomSideBearing) * fontScale + _r.fontMetrics.baselineInDIP - layoutBox.y;

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
Expand Down

0 comments on commit cd367e6

Please sign in to comment.