diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp index baa44c2bb15..86eefe56868 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp @@ -7,55 +7,8 @@ #include "EnumEntry.h" #include -#include "..\WinRTUtils\inc\Utils.h" - -// This function is a copy of DxFontInfo::_NearbyCollection() with -// * the call to DxFontInfo::s_GetNearbyFonts() inlined -// * checkForUpdates for GetSystemFontCollection() set to true -static wil::com_ptr NearbyCollection(IDWriteFactory* dwriteFactory) -{ - // The convenience interfaces for loading fonts from files - // are only available on Windows 10+. - wil::com_ptr factory6; - // wil's query() facilities don't work inside WinRT land at the moment. - // They produce a compilation error due to IUnknown and winrt::Windows::Foundation::IUnknown being ambiguous. - if (!SUCCEEDED(dwriteFactory->QueryInterface(__uuidof(IDWriteFactory6), factory6.put_void()))) - { - return nullptr; - } - - wil::com_ptr systemFontCollection; - THROW_IF_FAILED(factory6->GetSystemFontCollection(false, systemFontCollection.addressof(), true)); - - wil::com_ptr systemFontSet; - THROW_IF_FAILED(systemFontCollection->GetFontSet(systemFontSet.addressof())); - - wil::com_ptr fontSetBuilder2; - THROW_IF_FAILED(factory6->CreateFontSetBuilder(fontSetBuilder2.addressof())); - - THROW_IF_FAILED(fontSetBuilder2->AddFontSet(systemFontSet.get())); - - { - const std::filesystem::path module{ wil::GetModuleFileNameW(nullptr) }; - const auto folder{ module.parent_path() }; - - for (const auto& p : std::filesystem::directory_iterator(folder)) - { - if (til::ends_with(p.path().native(), L".ttf")) - { - fontSetBuilder2->AddFontFile(p.path().c_str()); - } - } - } - - wil::com_ptr fontSet; - THROW_IF_FAILED(fontSetBuilder2->CreateFontSet(fontSet.addressof())); - - wil::com_ptr fontCollection; - THROW_IF_FAILED(factory6->CreateFontCollectionFromFontSet(fontSet.get(), &fontCollection)); - - return fontCollection; -} +#include "../WinRTUtils/inc/Utils.h" +#include "../../renderer/base/FontCache.h" using namespace winrt::Windows::UI::Text; using namespace winrt::Windows::UI::Xaml; @@ -166,15 +119,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation std::vector fontList; std::vector monospaceFontList; - // get a DWriteFactory - com_ptr factory; - THROW_IF_FAILED(DWriteCreateFactory( - DWRITE_FACTORY_TYPE_SHARED, - __uuidof(IDWriteFactory), - reinterpret_cast<::IUnknown**>(factory.put()))); - // get the font collection; subscribe to updates - const auto fontCollection = NearbyCollection(factory.get()); + const auto fontCollection = ::Microsoft::Console::Render::FontCache::GetFresh(); for (UINT32 i = 0; i < fontCollection->GetFontFamilyCount(); ++i) { diff --git a/src/features.xml b/src/features.xml index 4e586f1ebc9..bfb1e6374eb 100644 --- a/src/features.xml +++ b/src/features.xml @@ -67,6 +67,15 @@ + + Feature_NearbyFontLoading + Controls whether fonts in the same directory as the binary are used during rendering. Disabled for conhost so that it doesn't iterate the entire system32 directory. + AlwaysEnabled + + WindowsInbox + + + Feature_AdjustIndistinguishableText If enabled, the foreground color will, when necessary, be automatically adjusted to make it more visible. diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp index 2d0fa45e78a..2e0db997333 100644 --- a/src/renderer/atlas/AtlasEngine.api.cpp +++ b/src/renderer/atlas/AtlasEngine.api.cpp @@ -4,6 +4,8 @@ #include "pch.h" #include "AtlasEngine.h" +#include "../base/FontCache.h" + // #### NOTE #### // If you see any code in here that contains "_r." you might be seeing a race condition. // The AtlasEngine::Present() method is called on a background thread without any locks, @@ -227,7 +229,7 @@ try } #endif - _resolveFontMetrics(fontInfoDesired, fontInfo); + _resolveFontMetrics(nullptr, fontInfoDesired, fontInfo); return S_OK; } CATCH_RETURN() @@ -401,7 +403,50 @@ void AtlasEngine::ToggleShaderEffects() noexcept } [[nodiscard]] HRESULT AtlasEngine::UpdateFont(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map& features, const std::unordered_map& axes) noexcept -try +{ + static constexpr std::array fallbackFaceNames{ static_cast(nullptr), L"Consolas", L"Lucida Console", L"Courier New" }; + auto it = fallbackFaceNames.begin(); + const auto end = fallbackFaceNames.end(); + + for (;;) + { + try + { + _updateFont(*it, fontInfoDesired, fontInfo, features, axes); + return S_OK; + } + catch (...) + { + ++it; + if (it == end) + { + RETURN_CAUGHT_EXCEPTION(); + } + else + { + LOG_CAUGHT_EXCEPTION(); + } + } + } +} + +void AtlasEngine::UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept +{ + _api.hyperlinkHoveredId = hoveredId; +} + +#pragma endregion + +void AtlasEngine::_resolveAntialiasingMode() noexcept +{ + // If the user asks for ClearType, but also for a transparent background + // (which our ClearType shader doesn't simultaneously support) + // then we need to sneakily force the renderer to grayscale AA. + const auto forceGrayscaleAA = _api.antialiasingMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && !_api.backgroundOpaqueMixin; + _api.realizedAntialiasingMode = forceGrayscaleAA ? D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE : _api.antialiasingMode; +} + +void AtlasEngine::_updateFont(const wchar_t* faceName, const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map& features, const std::unordered_map& axes) { std::vector fontFeatures; if (!features.empty()) @@ -478,7 +523,7 @@ try } const auto previousCellSize = _api.fontMetrics.cellSize; - _resolveFontMetrics(fontInfoDesired, fontInfo, &_api.fontMetrics); + _resolveFontMetrics(faceName, fontInfoDesired, fontInfo, &_api.fontMetrics); _api.fontFeatures = std::move(fontFeatures); _api.fontAxisValues = std::move(fontAxisValues); @@ -489,37 +534,21 @@ try _api.cellCount = _api.sizeInPixel / _api.fontMetrics.cellSize; WI_SetFlag(_api.invalidations, ApiInvalidations::Size); } - - return S_OK; -} -CATCH_RETURN() - -void AtlasEngine::UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept -{ - _api.hyperlinkHoveredId = hoveredId; } -#pragma endregion - -void AtlasEngine::_resolveAntialiasingMode() noexcept +void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontMetrics* fontMetrics) const { - // If the user asks for ClearType, but also for a transparent background - // (which our ClearType shader doesn't simultaneously support) - // then we need to sneakily force the renderer to grayscale AA. - const auto forceGrayscaleAA = _api.antialiasingMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && !_api.backgroundOpaqueMixin; - _api.realizedAntialiasingMode = forceGrayscaleAA ? D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE : _api.antialiasingMode; -} - -void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontMetrics* fontMetrics) const -{ - auto requestedFaceName = fontInfoDesired.GetFaceName().c_str(); const auto requestedFamily = fontInfoDesired.GetFamily(); auto requestedWeight = fontInfoDesired.GetWeight(); auto requestedSize = fontInfoDesired.GetEngineSize(); if (!requestedFaceName) { - requestedFaceName = L"Consolas"; + requestedFaceName = fontInfoDesired.GetFaceName().c_str(); + if (!requestedFaceName) + { + requestedFaceName = L"Consolas"; + } } if (!requestedSize.Y) { @@ -530,16 +559,15 @@ void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, Fo requestedWeight = DWRITE_FONT_WEIGHT_NORMAL; } - wil::com_ptr systemFontCollection; - THROW_IF_FAILED(_sr.dwriteFactory->GetSystemFontCollection(systemFontCollection.addressof(), false)); + auto fontCollection = FontCache::GetCached(); u32 index = 0; BOOL exists = false; - THROW_IF_FAILED(systemFontCollection->FindFamilyName(requestedFaceName, &index, &exists)); + THROW_IF_FAILED(fontCollection->FindFamilyName(requestedFaceName, &index, &exists)); THROW_HR_IF(DWRITE_E_NOFONT, !exists); wil::com_ptr fontFamily; - THROW_IF_FAILED(systemFontCollection->GetFontFamily(index, fontFamily.addressof())); + THROW_IF_FAILED(fontCollection->GetFontFamily(index, fontFamily.addressof())); wil::com_ptr font; THROW_IF_FAILED(fontFamily->GetFirstMatchingFont(static_cast(requestedWeight), DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL, font.addressof())); @@ -601,6 +629,7 @@ void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, Fo // NOTE: From this point onward no early returns or throwing code should exist, // as we might cause _api to be in an inconsistent state otherwise. + fontMetrics->fontCollection = std::move(fontCollection); fontMetrics->fontName = std::move(fontName); fontMetrics->fontSizeInDIP = static_cast(fontSizeInPx / static_cast(_api.dpi) * 96.0); fontMetrics->baselineInDIP = static_cast(baseline / static_cast(_api.dpi) * 96.0); diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp index dd9267698b3..bf108602862 100644 --- a/src/renderer/atlas/AtlasEngine.cpp +++ b/src/renderer/atlas/AtlasEngine.cpp @@ -7,6 +7,7 @@ #include #include +#include "../base/FontCache.h" #include "../../interactivity/win32/CustomWindowMessages.h" // #### NOTE #### @@ -1023,7 +1024,7 @@ void AtlasEngine::_recreateFontDependentResources() const auto fontStyle = italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL; auto& textFormat = _r.textFormats[italic][bold]; - THROW_IF_FAILED(_sr.dwriteFactory->CreateTextFormat(_api.fontMetrics.fontName.get(), nullptr, fontWeight, fontStyle, DWRITE_FONT_STRETCH_NORMAL, _api.fontMetrics.fontSizeInDIP, L"", textFormat.put())); + THROW_IF_FAILED(_sr.dwriteFactory->CreateTextFormat(_api.fontMetrics.fontName.get(), _api.fontMetrics.fontCollection.get(), fontWeight, fontStyle, DWRITE_FONT_STRETCH_NORMAL, _api.fontMetrics.fontSizeInDIP, L"", textFormat.put())); textFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); textFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index df4f095e42c..5c417b7386b 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -376,6 +376,7 @@ namespace Microsoft::Console::Render struct FontMetrics { + wil::com_ptr fontCollection; wil::unique_process_heap_string fontName; float baselineInDIP = 0.0f; float fontSizeInDIP = 0.0f; @@ -615,7 +616,8 @@ namespace Microsoft::Console::Render // AtlasEngine.api.cpp void _resolveAntialiasingMode() noexcept; - void _resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontMetrics* fontMetrics = nullptr) const; + void _updateFont(const wchar_t* faceName, const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map& features, const std::unordered_map& axes); + void _resolveFontMetrics(const wchar_t* faceName, const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontMetrics* fontMetrics = nullptr) const; // AtlasEngine.r.cpp void _setShaderResources() const; diff --git a/src/renderer/base/FontCache.h b/src/renderer/base/FontCache.h new file mode 100644 index 00000000000..e7a5c4134ca --- /dev/null +++ b/src/renderer/base/FontCache.h @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +namespace Microsoft::Console::Render::FontCache +{ + namespace details + { + inline const std::vector>& getNearbyFontFiles(IDWriteFactory5* factory5) + { + static const auto fontFiles = [=]() { + std::vector> files; + + const std::filesystem::path module{ wil::GetModuleFileNameW(nullptr) }; + const auto folder{ module.parent_path() }; + + for (const auto& p : std::filesystem::directory_iterator(folder)) + { + if (til::ends_with(p.path().native(), L".ttf")) + { + wil::com_ptr fontFile; + if (SUCCEEDED_LOG(factory5->CreateFontFileReference(p.path().c_str(), nullptr, fontFile.addressof()))) + { + files.emplace_back(std::move(fontFile)); + } + } + } + + files.shrink_to_fit(); + return files; + }(); + return fontFiles; + } + + inline wil::com_ptr getFontCollection(bool forceUpdate) + { + wil::com_ptr factory; + THROW_IF_FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(factory), reinterpret_cast<::IUnknown**>(factory.addressof()))); + + wil::com_ptr systemFontCollection; + THROW_IF_FAILED(factory->GetSystemFontCollection(systemFontCollection.addressof(), forceUpdate)); + + if constexpr (Feature_NearbyFontLoading::IsEnabled()) + { + // IDWriteFactory5 is supported since Windows 10, build 15021. + const auto factory5 = factory.try_query(); + if (!factory5) + { + return systemFontCollection; + } + + const auto& nearbyFontFiles = getNearbyFontFiles(factory5.get()); + if (nearbyFontFiles.empty()) + { + return systemFontCollection; + } + + wil::com_ptr systemFontSet; + // IDWriteFontCollection1 is supported since Windows 7. + THROW_IF_FAILED(systemFontCollection.query()->GetFontSet(systemFontSet.addressof())); + + wil::com_ptr fontSetBuilder; + THROW_IF_FAILED(factory5->CreateFontSetBuilder(fontSetBuilder.addressof())); + THROW_IF_FAILED(fontSetBuilder->AddFontSet(systemFontSet.get())); + + for (const auto& file : nearbyFontFiles) + { + LOG_IF_FAILED(fontSetBuilder->AddFontFile(file.get())); + } + + wil::com_ptr fontSet; + THROW_IF_FAILED(fontSetBuilder->CreateFontSet(fontSet.addressof())); + + wil::com_ptr fontCollection; + THROW_IF_FAILED(factory5->CreateFontCollectionFromFontSet(fontSet.get(), fontCollection.addressof())); + + return std::move(fontCollection); + } + else + { + return systemFontCollection; + } + } + } + + inline wil::com_ptr GetCached() + { + return details::getFontCollection(false); + } + + inline wil::com_ptr GetFresh() + { + return details::getFontCollection(true); + } +} diff --git a/src/renderer/base/lib/base.vcxproj b/src/renderer/base/lib/base.vcxproj index 1bcfe8dddb0..5d7bc816c41 100644 --- a/src/renderer/base/lib/base.vcxproj +++ b/src/renderer/base/lib/base.vcxproj @@ -34,6 +34,7 @@ + diff --git a/src/renderer/base/lib/base.vcxproj.filters b/src/renderer/base/lib/base.vcxproj.filters index bda75e18fe8..4e8f12c1821 100644 --- a/src/renderer/base/lib/base.vcxproj.filters +++ b/src/renderer/base/lib/base.vcxproj.filters @@ -89,6 +89,9 @@ Header Files\inc + + Header Files + diff --git a/src/renderer/dx/DxFontInfo.cpp b/src/renderer/dx/DxFontInfo.cpp index e5c6749f4be..ba3eca1a864 100644 --- a/src/renderer/dx/DxFontInfo.cpp +++ b/src/renderer/dx/DxFontInfo.cpp @@ -2,11 +2,9 @@ // Licensed under the MIT license. #include "precomp.h" - #include "DxFontInfo.h" -#include "unicode.hpp" - +#include #include static constexpr std::wstring_view FALLBACK_FONT_FACES[] = { L"Consolas", L"Lucida Console", L"Courier New" }; @@ -14,7 +12,6 @@ static constexpr std::wstring_view FALLBACK_FONT_FACES[] = { L"Consolas", L"Luci using namespace Microsoft::Console::Render; DxFontInfo::DxFontInfo() noexcept : - _familyName(), _weight(DWRITE_FONT_WEIGHT_NORMAL), _style(DWRITE_FONT_STYLE_NORMAL), _stretch(DWRITE_FONT_STRETCH_NORMAL), @@ -96,11 +93,6 @@ bool DxFontInfo::GetFallback() const noexcept return _didFallback; } -IDWriteFontCollection* DxFontInfo::GetNearbyCollection() const noexcept -{ - return _nearbyCollection.Get(); -} - void DxFontInfo::SetFromEngine(const std::wstring_view familyName, const DWRITE_FONT_WEIGHT weight, const DWRITE_FONT_STYLE style, @@ -122,8 +114,7 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, // - localeName - Locale to search for appropriate fonts // Return Value: // - Smart pointer holding interface reference for queryable font data. -[[nodiscard]] Microsoft::WRL::ComPtr DxFontInfo::ResolveFontFaceWithFallback(gsl::not_null dwriteFactory, - std::wstring& localeName) +[[nodiscard]] Microsoft::WRL::ComPtr DxFontInfo::ResolveFontFaceWithFallback(IDWriteFontCollection* fontCollection, std::wstring& localeName) { // First attempt to find exactly what the user asked for. _didFallback = false; @@ -134,7 +125,7 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, // method. We still want to fall back to a font that's reasonable, below. try { - face = _FindFontFace(dwriteFactory, localeName, true); + face = _FindFontFace(fontCollection, localeName); if (!face) { @@ -161,7 +152,7 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, _familyName = _familyName.substr(0, lastSpace); // Try to find it with the shortened family name - face = _FindFontFace(dwriteFactory, localeName, true); + face = _FindFontFace(fontCollection, localeName); } } } @@ -175,25 +166,8 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, for (const auto fallbackFace : FALLBACK_FONT_FACES) { _familyName = fallbackFace; - // With these fonts, don't attempt the nearby lookup. We're looking - // for system fonts only. If one of the nearby fonts is causing us - // problems (like in GH#10211), then we don't want to go anywhere - - // near it in this part. - face = _FindFontFace(dwriteFactory, localeName, false); - - if (face) - { - _didFallback = true; - break; - } - - _familyName = fallbackFace; - _weight = DWRITE_FONT_WEIGHT_NORMAL; - _stretch = DWRITE_FONT_STRETCH_NORMAL; - _style = DWRITE_FONT_STYLE_NORMAL; - face = _FindFontFace(dwriteFactory, localeName, false); + face = _FindFontFace(fontCollection, localeName); if (face) { _didFallback = true; @@ -214,27 +188,15 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, // - localeName - Locale to search for appropriate fonts // Return Value: // - Smart pointer holding interface reference for queryable font data. -[[nodiscard]] Microsoft::WRL::ComPtr DxFontInfo::_FindFontFace(gsl::not_null dwriteFactory, std::wstring& localeName, const bool withNearbyLookup) +#pragma warning(suppress : 26429) // C26429: Symbol 'fontCollection' is never tested for nullness, it can be marked as not_null (f.23). +[[nodiscard]] Microsoft::WRL::ComPtr DxFontInfo::_FindFontFace(IDWriteFontCollection* fontCollection, std::wstring& localeName) { Microsoft::WRL::ComPtr fontFace; - Microsoft::WRL::ComPtr fontCollection; - THROW_IF_FAILED(dwriteFactory->GetSystemFontCollection(&fontCollection, false)); - UINT32 familyIndex; BOOL familyExists; - THROW_IF_FAILED(fontCollection->FindFamilyName(_familyName.data(), &familyIndex, &familyExists)); - // If the system collection missed, try the files sitting next to our binary. - if (withNearbyLookup && !familyExists) - { - // May be null on OS below Windows 10. If null, just skip the attempt. - if (const auto nearbyCollection = _NearbyCollection(dwriteFactory)) - { - THROW_IF_FAILED(nearbyCollection->FindFamilyName(_familyName.data(), &familyIndex, &familyExists)); - fontCollection = nearbyCollection; - } - } + THROW_IF_FAILED(fontCollection->FindFamilyName(_familyName.data(), &familyIndex, &familyExists)); if (familyExists) { @@ -327,81 +289,3 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, // and return it. return retVal; } - -// Routine Description: -// - Creates a DirectWrite font collection of font files that are sitting next to the running -// binary (in the same directory as the EXE). -// Arguments: -// - dwriteFactory - The DWrite factory to use -// Return Value: -// - DirectWrite font collection. May be null if one cannot be created. -[[nodiscard]] IDWriteFontCollection* DxFontInfo::_NearbyCollection(gsl::not_null dwriteFactory) -{ - if (_nearbyCollection) - { - return _nearbyCollection.Get(); - } - - // The convenience interfaces for loading fonts from files - // are only available on Windows 10+. - ::Microsoft::WRL::ComPtr factory6; - if (FAILED(dwriteFactory->QueryInterface(&factory6))) - { - return nullptr; - } - - ::Microsoft::WRL::ComPtr systemFontCollection; - THROW_IF_FAILED(factory6->GetSystemFontCollection(false, &systemFontCollection, 0)); - - ::Microsoft::WRL::ComPtr systemFontSet; - THROW_IF_FAILED(systemFontCollection->GetFontSet(&systemFontSet)); - - ::Microsoft::WRL::ComPtr fontSetBuilder2; - THROW_IF_FAILED(factory6->CreateFontSetBuilder(&fontSetBuilder2)); - - THROW_IF_FAILED(fontSetBuilder2->AddFontSet(systemFontSet.Get())); - - // Magic static so we only attempt to grovel the hard disk once no matter how many instances - // of the font collection itself we require. - static const auto knownPaths = s_GetNearbyFonts(); - for (auto& p : knownPaths) - { - fontSetBuilder2->AddFontFile(p.c_str()); - } - - ::Microsoft::WRL::ComPtr fontSet; - THROW_IF_FAILED(fontSetBuilder2->CreateFontSet(&fontSet)); - - ::Microsoft::WRL::ComPtr fontCollection; - THROW_IF_FAILED(factory6->CreateFontCollectionFromFontSet(fontSet.Get(), &fontCollection)); - - _nearbyCollection = fontCollection; - return _nearbyCollection.Get(); -} - -// Routine Description: -// - Digs through the directory that the current executable is running within to find -// any TTF files sitting next to it. -// Arguments: -// - -// Return Value: -// - Iterable collection of filesystem paths, one per font file that was found -[[nodiscard]] std::vector DxFontInfo::s_GetNearbyFonts() -{ - std::vector paths; - - // Find the directory we're running from then enumerate all the TTF files - // sitting next to us. - const std::filesystem::path module{ wil::GetModuleFileNameW(nullptr) }; - const auto folder{ module.parent_path() }; - - for (const auto& p : std::filesystem::directory_iterator(folder)) - { - if (til::ends_with(p.path().native(), L".ttf")) - { - paths.push_back(p.path()); - } - } - - return paths; -} diff --git a/src/renderer/dx/DxFontInfo.h b/src/renderer/dx/DxFontInfo.h index 9b336accd0c..0e8bbf837c2 100644 --- a/src/renderer/dx/DxFontInfo.h +++ b/src/renderer/dx/DxFontInfo.h @@ -41,30 +41,19 @@ namespace Microsoft::Console::Render bool GetFallback() const noexcept; - IDWriteFontCollection* GetNearbyCollection() const noexcept; - void SetFromEngine(const std::wstring_view familyName, const DWRITE_FONT_WEIGHT weight, const DWRITE_FONT_STYLE style, const DWRITE_FONT_STRETCH stretch); - [[nodiscard]] ::Microsoft::WRL::ComPtr ResolveFontFaceWithFallback(gsl::not_null dwriteFactory, - std::wstring& localeName); + [[nodiscard]] ::Microsoft::WRL::ComPtr ResolveFontFaceWithFallback(IDWriteFontCollection* fontCollection, std::wstring& localeName); private: - [[nodiscard]] ::Microsoft::WRL::ComPtr _FindFontFace(gsl::not_null dwriteFactory, - std::wstring& localeName, - const bool withNearbyLookup); + [[nodiscard]] ::Microsoft::WRL::ComPtr _FindFontFace(IDWriteFontCollection* fontCollection, std::wstring& localeName); [[nodiscard]] std::wstring _GetFontFamilyName(gsl::not_null const fontFamily, std::wstring& localeName); - [[nodiscard]] IDWriteFontCollection* _NearbyCollection(gsl::not_null dwriteFactory); - - [[nodiscard]] static std::vector s_GetNearbyFonts(); - - ::Microsoft::WRL::ComPtr _nearbyCollection; - // The font name we should be looking for std::wstring _familyName; diff --git a/src/renderer/dx/DxFontRenderData.cpp b/src/renderer/dx/DxFontRenderData.cpp index c251b7bf0c6..869a1d4fc45 100644 --- a/src/renderer/dx/DxFontRenderData.cpp +++ b/src/renderer/dx/DxFontRenderData.cpp @@ -2,22 +2,21 @@ // Licensed under the MIT license. #include "precomp.h" - #include "DxFontRenderData.h" -#include "unicode.hpp" - #include +#include "../base/FontCache.h" + static constexpr float POINTS_PER_INCH = 72.0f; -static constexpr std::wstring_view FALLBACK_FONT_FACES[] = { L"Consolas", L"Lucida Console", L"Courier New" }; static constexpr std::wstring_view FALLBACK_LOCALE = L"en-us"; static constexpr size_t TAG_LENGTH = 4; using namespace Microsoft::Console::Render; -DxFontRenderData::DxFontRenderData(::Microsoft::WRL::ComPtr dwriteFactory) noexcept : +DxFontRenderData::DxFontRenderData(::Microsoft::WRL::ComPtr dwriteFactory) : _dwriteFactory(dwriteFactory), + _nearbyCollection{ FontCache::GetCached() }, _fontSize{}, _glyphCell{}, _lineMetrics{}, @@ -165,7 +164,7 @@ DxFontRenderData::DxFontRenderData(::Microsoft::WRL::ComPtr dwr fontInfo.SetStretch(stretch); std::wstring fontLocaleName = UserLocaleName(); - Microsoft::WRL::ComPtr fontFace = fontInfo.ResolveFontFaceWithFallback(_dwriteFactory.Get(), fontLocaleName); + Microsoft::WRL::ComPtr fontFace = fontInfo.ResolveFontFaceWithFallback(_nearbyCollection.get(), fontLocaleName); _fontFaceMap.emplace(_ToMapKey(weight, style, stretch), fontFace); return fontFace; @@ -711,7 +710,7 @@ void DxFontRenderData::_BuildFontRenderData(const FontInfoDesired& desired, Font // This is the first attempt to resolve font face after `UpdateFont`. // Note that the following line may cause property changes _inside_ `_defaultFontInfo` because the desired font may not exist. // See the implementation of `ResolveFontFaceWithFallback` for details. - const Microsoft::WRL::ComPtr face = _defaultFontInfo.ResolveFontFaceWithFallback(_dwriteFactory.Get(), fontLocaleName); + const Microsoft::WRL::ComPtr face = _defaultFontInfo.ResolveFontFaceWithFallback(_nearbyCollection.get(), fontLocaleName); DWRITE_FONT_METRICS1 fontMetrics; face->GetMetrics(&fontMetrics); @@ -898,7 +897,7 @@ Microsoft::WRL::ComPtr DxFontRenderData::_BuildTextFormat(con { Microsoft::WRL::ComPtr format; THROW_IF_FAILED(_dwriteFactory->CreateTextFormat(fontInfo.GetFamilyName().data(), - fontInfo.GetNearbyCollection(), + _nearbyCollection.get(), fontInfo.GetWeight(), fontInfo.GetStyle(), fontInfo.GetStretch(), diff --git a/src/renderer/dx/DxFontRenderData.h b/src/renderer/dx/DxFontRenderData.h index ce69169c53a..b2a0e7053bd 100644 --- a/src/renderer/dx/DxFontRenderData.h +++ b/src/renderer/dx/DxFontRenderData.h @@ -39,7 +39,7 @@ namespace Microsoft::Console::Render float strikethroughWidth; }; - DxFontRenderData(::Microsoft::WRL::ComPtr dwriteFactory) noexcept; + DxFontRenderData(::Microsoft::WRL::ComPtr dwriteFactory); // DirectWrite text analyzer from the factory [[nodiscard]] Microsoft::WRL::ComPtr Analyzer(); @@ -132,6 +132,7 @@ namespace Microsoft::Console::Render ::Microsoft::WRL::ComPtr _dwriteFactory; ::Microsoft::WRL::ComPtr _dwriteTextAnalyzer; + wil::com_ptr _nearbyCollection; std::wstring _userLocaleName; DxFontInfo _defaultFontInfo; til::size _glyphCell; diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 42055233ce5..11769474eba 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -2083,10 +2083,12 @@ float DxEngine::GetScaling() const noexcept [[nodiscard]] HRESULT DxEngine::GetProposedFont(const FontInfoDesired& pfiFontInfoDesired, FontInfo& pfiFontInfo, int const iDpi) noexcept +try { DxFontRenderData fontRenderData(_dwriteFactory); return fontRenderData.UpdateFont(pfiFontInfoDesired, pfiFontInfo, iDpi); } +CATCH_RETURN(); // Routine Description: // - Gets the area that we currently believe is dirty within the character cell grid