mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-10 00:48:23 -06:00
Add initial bold font support to the GDI renderer (#19441)
Render SGR1 as bold in 256 and true colors, where "bold is intense" is not applicable. Implemented by creating 2 extra fonts: bold for 1 and bold italic for 1 + 3. No non-trivial changes, just extensions. LOGFONT also supports Underline and StrikeOut, but they seem to be already covered by other means, so no combinatorial explosion of fonts expected. Refs #18919 Co-authored-by: Leonard Hecker <lhecker@microsoft.com>
This commit is contained in:
parent
819987c90e
commit
84cc3e3e52
@ -102,8 +102,25 @@ namespace Microsoft::Console::Render
|
||||
HDC _hdcMemoryContext;
|
||||
bool _isTrueTypeFont;
|
||||
UINT _fontCodepage;
|
||||
HFONT _hfont;
|
||||
HFONT _hfontItalic;
|
||||
|
||||
enum class FontType : uint8_t
|
||||
{
|
||||
// Indices for _hfonts array below
|
||||
Default,
|
||||
Bold,
|
||||
Italic,
|
||||
BoldItalic,
|
||||
|
||||
// The number of fonts in _hfonts array below
|
||||
FontCount,
|
||||
|
||||
// Other
|
||||
Undefined = FontCount,
|
||||
Soft
|
||||
};
|
||||
|
||||
std::array<HFONT, static_cast<size_t>(FontType::FontCount)> _hfonts{};
|
||||
|
||||
TEXTMETRICW _tmFontMetrics;
|
||||
FontResource _softFont;
|
||||
|
||||
@ -147,13 +164,6 @@ namespace Microsoft::Console::Render
|
||||
COLORREF _lastFg;
|
||||
COLORREF _lastBg;
|
||||
|
||||
enum class FontType : uint8_t
|
||||
{
|
||||
Undefined,
|
||||
Default,
|
||||
Italic,
|
||||
Soft
|
||||
};
|
||||
FontType _lastFontType;
|
||||
bool _fontHasWesternScript = false;
|
||||
|
||||
@ -194,8 +204,7 @@ namespace Microsoft::Console::Render
|
||||
[[nodiscard]] HRESULT _GetProposedFont(const FontInfoDesired& FontDesired,
|
||||
_Out_ FontInfo& Font,
|
||||
const int iDpi,
|
||||
_Inout_ wil::unique_hfont& hFont,
|
||||
_Inout_ wil::unique_hfont& hFontItalic) noexcept;
|
||||
_Inout_ std::array<wil::unique_hfont, static_cast<size_t>(FontType::FontCount)>& hFonts) noexcept;
|
||||
|
||||
til::size _GetFontSize() const;
|
||||
bool _IsMinimized() const;
|
||||
|
||||
@ -34,8 +34,6 @@ GdiEngine::GdiEngine() :
|
||||
_currentLineRendition(LineRendition::SingleWidth),
|
||||
_fPaintStarted(false),
|
||||
_invalidCharacters{},
|
||||
_hfont(nullptr),
|
||||
_hfontItalic(nullptr),
|
||||
_pool{ til::pmr::get_default_resource() }, // It's important the pool is first so it can be given to the others on construction.
|
||||
_polyStrings{ &_pool },
|
||||
_polyWidths{ &_pool }
|
||||
@ -90,16 +88,13 @@ GdiEngine::~GdiEngine()
|
||||
_hbitmapMemorySurface = nullptr;
|
||||
}
|
||||
|
||||
if (_hfont != nullptr)
|
||||
for (auto& hfont : _hfonts)
|
||||
{
|
||||
LOG_HR_IF(E_FAIL, !(DeleteObject(_hfont)));
|
||||
_hfont = nullptr;
|
||||
}
|
||||
|
||||
if (_hfontItalic != nullptr)
|
||||
{
|
||||
LOG_HR_IF(E_FAIL, !(DeleteObject(_hfontItalic)));
|
||||
_hfontItalic = nullptr;
|
||||
if (hfont != nullptr)
|
||||
{
|
||||
LOG_HR_IF(E_FAIL, !(DeleteObject(hfont)));
|
||||
hfont = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (_hdcMemoryContext != nullptr)
|
||||
@ -300,24 +295,48 @@ GdiEngine::~GdiEngine()
|
||||
}
|
||||
|
||||
// If the font type has changed, select an appropriate font variant or soft font.
|
||||
const auto usingItalicFont = textAttributes.IsItalic();
|
||||
const auto fontType = usingSoftFont ? FontType::Soft :
|
||||
usingItalicFont ? FontType::Italic :
|
||||
FontType::Default;
|
||||
const auto fontType = [&] {
|
||||
if (usingSoftFont)
|
||||
{
|
||||
return FontType::Soft;
|
||||
}
|
||||
|
||||
// TODO: GH-18919 "Intense text as bold" option
|
||||
const auto usingBoldFont = textAttributes.IsBold(false);
|
||||
const auto usingItalicFont = textAttributes.IsItalic();
|
||||
|
||||
if (usingBoldFont && !usingItalicFont)
|
||||
{
|
||||
return FontType::Bold;
|
||||
}
|
||||
if (!usingBoldFont && usingItalicFont)
|
||||
{
|
||||
return FontType::Italic;
|
||||
}
|
||||
if (usingBoldFont && usingItalicFont)
|
||||
{
|
||||
return FontType::BoldItalic;
|
||||
}
|
||||
return FontType::Default;
|
||||
}();
|
||||
|
||||
if (fontType != _lastFontType)
|
||||
{
|
||||
switch (fontType)
|
||||
{
|
||||
case FontType::Default:
|
||||
case FontType::Bold:
|
||||
case FontType::Italic:
|
||||
case FontType::BoldItalic:
|
||||
SelectFont(_hdcMemoryContext, _hfonts[static_cast<size_t>(fontType)]);
|
||||
break;
|
||||
|
||||
case FontType::Soft:
|
||||
SelectFont(_hdcMemoryContext, _softFont);
|
||||
break;
|
||||
case FontType::Italic:
|
||||
SelectFont(_hdcMemoryContext, _hfontItalic);
|
||||
break;
|
||||
case FontType::Default:
|
||||
|
||||
default:
|
||||
SelectFont(_hdcMemoryContext, _hfont);
|
||||
break;
|
||||
__assume(false);
|
||||
}
|
||||
_lastFontType = fontType;
|
||||
_fontHasWesternScript = FontHasWesternScript(_hdcMemoryContext);
|
||||
@ -336,11 +355,11 @@ GdiEngine::~GdiEngine()
|
||||
// - S_OK if set successfully or relevant GDI error via HRESULT.
|
||||
[[nodiscard]] HRESULT GdiEngine::UpdateFont(const FontInfoDesired& FontDesired, _Out_ FontInfo& Font) noexcept
|
||||
{
|
||||
wil::unique_hfont hFont, hFontItalic;
|
||||
RETURN_IF_FAILED(_GetProposedFont(FontDesired, Font, _iCurrentDpi, hFont, hFontItalic));
|
||||
std::array<wil::unique_hfont, static_cast<size_t>(FontType::FontCount)> hFonts;
|
||||
RETURN_IF_FAILED(_GetProposedFont(FontDesired, Font, _iCurrentDpi, hFonts));
|
||||
|
||||
// Select into DC
|
||||
RETURN_HR_IF_NULL(E_FAIL, SelectFont(_hdcMemoryContext, hFont.get()));
|
||||
RETURN_HR_IF_NULL(E_FAIL, SelectFont(_hdcMemoryContext, hFonts[static_cast<size_t>(FontType::Default)].get()));
|
||||
|
||||
// Save off the font metrics for various other calculations
|
||||
RETURN_HR_IF(E_FAIL, !(GetTextMetricsW(_hdcMemoryContext, &_tmFontMetrics)));
|
||||
@ -454,26 +473,19 @@ GdiEngine::~GdiEngine()
|
||||
// Now find the size of a 0 in this current font and save it for conversions done later.
|
||||
_coordFontLast = Font.GetSize();
|
||||
|
||||
// Persist font for cleanup (and free existing if necessary)
|
||||
if (_hfont != nullptr)
|
||||
for (auto& hfont : _hfonts)
|
||||
{
|
||||
LOG_HR_IF(E_FAIL, !(DeleteObject(_hfont)));
|
||||
_hfont = nullptr;
|
||||
// Persist font for cleanup (and free existing if necessary)
|
||||
if (hfont != nullptr)
|
||||
{
|
||||
LOG_HR_IF(E_FAIL, !(DeleteObject(hfont)));
|
||||
hfont = nullptr;
|
||||
}
|
||||
|
||||
// Save the font.
|
||||
hfont = hFonts[&hfont - _hfonts.data()].release();
|
||||
}
|
||||
|
||||
// Save the font.
|
||||
_hfont = hFont.release();
|
||||
|
||||
// Persist italic font for cleanup (and free existing if necessary)
|
||||
if (_hfontItalic != nullptr)
|
||||
{
|
||||
LOG_HR_IF(E_FAIL, !(DeleteObject(_hfontItalic)));
|
||||
_hfontItalic = nullptr;
|
||||
}
|
||||
|
||||
// Save the italic font.
|
||||
_hfontItalic = hFontItalic.release();
|
||||
|
||||
// Save raster vs. TrueType and codepage data in case we need to convert.
|
||||
_isTrueTypeFont = Font.IsTrueTypeFont();
|
||||
_fontCodepage = Font.GetCodePage();
|
||||
@ -500,10 +512,10 @@ GdiEngine::~GdiEngine()
|
||||
{
|
||||
// If we previously called SelectFont(_hdcMemoryContext, _softFont), it will
|
||||
// still hold a reference to the _softFont object we're planning to overwrite.
|
||||
// --> First revert back to the standard _hfont, lest we have dangling pointers.
|
||||
// --> First revert back to the standard font, lest we have dangling pointers.
|
||||
if (_lastFontType == FontType::Soft)
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_FAIL, SelectFont(_hdcMemoryContext, _hfont));
|
||||
RETURN_HR_IF_NULL(E_FAIL, SelectFont(_hdcMemoryContext, _hfonts[static_cast<size_t>(FontType::Default)]));
|
||||
_lastFontType = FontType::Default;
|
||||
}
|
||||
|
||||
@ -550,8 +562,8 @@ GdiEngine::~GdiEngine()
|
||||
// - S_OK if set successfully or relevant GDI error via HRESULT.
|
||||
[[nodiscard]] HRESULT GdiEngine::GetProposedFont(const FontInfoDesired& FontDesired, _Out_ FontInfo& Font, const int iDpi) noexcept
|
||||
{
|
||||
wil::unique_hfont hFont, hFontItalic;
|
||||
return _GetProposedFont(FontDesired, Font, iDpi, hFont, hFontItalic);
|
||||
std::array<wil::unique_hfont, static_cast<size_t>(FontType::FontCount)> hFonts;
|
||||
return _GetProposedFont(FontDesired, Font, iDpi, hFonts);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@ -576,15 +588,13 @@ GdiEngine::~GdiEngine()
|
||||
// - FontDesired - reference to font information we should use while instantiating a font.
|
||||
// - Font - the actual font
|
||||
// - iDpi - The DPI we will have when rendering
|
||||
// - hFont - A smart pointer to receive a handle to a ready-to-use GDI font.
|
||||
// - hFontItalic - A smart pointer to receive a handle to an italic variant of the font.
|
||||
// - hFonts - An array of smart pointers to receive handles to ready-to-use GDI fonts, corresponding to FontType enum.
|
||||
// Return Value:
|
||||
// - S_OK if set successfully or relevant GDI error via HRESULT.
|
||||
[[nodiscard]] HRESULT GdiEngine::_GetProposedFont(const FontInfoDesired& FontDesired,
|
||||
_Out_ FontInfo& Font,
|
||||
const int iDpi,
|
||||
_Inout_ wil::unique_hfont& hFont,
|
||||
_Inout_ wil::unique_hfont& hFontItalic) noexcept
|
||||
_Inout_ std::array<wil::unique_hfont, static_cast<size_t>(FontType::FontCount)>& hFonts) noexcept
|
||||
{
|
||||
wil::unique_hdc hdcTemp(CreateCompatibleDC(_hdcMemoryContext));
|
||||
RETURN_HR_IF_NULL(E_FAIL, hdcTemp.get());
|
||||
@ -600,8 +610,10 @@ GdiEngine::~GdiEngine()
|
||||
// We do this because, for instance, if we ask GDI for an 8x12 OEM_FIXED_FONT,
|
||||
// it may very well decide to choose Courier New instead of the Terminal raster.
|
||||
#pragma prefast(suppress : 38037, "raster fonts get special handling, we need to get it this way")
|
||||
hFont.reset((HFONT)GetStockObject(OEM_FIXED_FONT));
|
||||
hFontItalic.reset((HFONT)GetStockObject(OEM_FIXED_FONT));
|
||||
for (auto& hFont : hFonts)
|
||||
{
|
||||
hFont.reset((HFONT)GetStockObject(OEM_FIXED_FONT));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -658,18 +670,35 @@ GdiEngine::~GdiEngine()
|
||||
|
||||
FontDesired.FillLegacyNameBuffer(lf.lfFaceName);
|
||||
|
||||
// Create font.
|
||||
hFont.reset(CreateFontIndirectW(&lf));
|
||||
RETURN_HR_IF_NULL(E_FAIL, hFont.get());
|
||||
struct FontStyle
|
||||
{
|
||||
LONG weight;
|
||||
BYTE italic;
|
||||
};
|
||||
|
||||
// Create italic variant of the font.
|
||||
lf.lfItalic = TRUE;
|
||||
hFontItalic.reset(CreateFontIndirectW(&lf));
|
||||
RETURN_HR_IF_NULL(E_FAIL, hFontItalic.get());
|
||||
static constexpr FontStyle s_fontStyles[] = {
|
||||
{ FW_NORMAL, FALSE }, // Default
|
||||
{ FW_BOLD, FALSE }, // Bold
|
||||
{ FW_NORMAL, TRUE }, // Italic
|
||||
{ FW_BOLD, TRUE }, // BoldItalic
|
||||
};
|
||||
|
||||
static_assert(std::size(s_fontStyles) == static_cast<size_t>(FontType::FontCount));
|
||||
|
||||
for (const auto& style : s_fontStyles)
|
||||
{
|
||||
lf.lfWeight = style.weight == FW_NORMAL ? FontDesired.GetWeight() : style.weight;
|
||||
lf.lfItalic = style.italic;
|
||||
|
||||
// Create font.
|
||||
auto& hFont = hFonts[&style - s_fontStyles];
|
||||
hFont.reset(CreateFontIndirectW(&lf));
|
||||
RETURN_HR_IF_NULL(E_FAIL, hFont.get());
|
||||
}
|
||||
}
|
||||
|
||||
// Select into DC
|
||||
wil::unique_hfont hFontOld(SelectFont(hdcTemp.get(), hFont.get()));
|
||||
wil::unique_hfont hFontOld(SelectFont(hdcTemp.get(), hFonts[static_cast<size_t>(FontType::Default)].get()));
|
||||
RETURN_HR_IF_NULL(E_FAIL, hFontOld.get());
|
||||
|
||||
// Save off the font metrics for various other calculations
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user