Skip to content

Commit

Permalink
Allow users to set font features and font axes (#10525)
Browse files Browse the repository at this point in the history
Adds support for users to be able to set font features and axes (see the spec for more details!)

## Detailed Description

**CustomTextLayout**
- Asks the `DxFontRenderData` for the font features when getting glyphs
- _If any features have been set/updated, we always skip the "isTextSimple" shortcut_
- Asks the `_formatInUse` for any font axes when mapping characters in `_AnalyzeFontFallback`

**DxFontRenderData**
- Stores a map of font features (initialized to the [standard feature list])
- Stores a map of font axes
- Has methods to add font features/axes to the map or update existing ones
- Has methods to retrieve the font features/axes
- Sets the font axes in the `IDWriteTextFormat` when creating it

## Validation Steps Performed
It works!

[standard feature list]: https://github.com/fdwr/TextLayoutSampler/blob/ac5aef67d1cc0cb67c5e3be29b30bda5a90c3e2b/DrawableObject.ixx#L802

Specified in #10457
Related to #1790 
Closes #759
Closes #5828
  • Loading branch information
PankajBhojwani authored Jul 22, 2021
1 parent 335f69e commit 4c16cb2
Show file tree
Hide file tree
Showing 17 changed files with 531 additions and 60 deletions.
8 changes: 8 additions & 0 deletions .github/actions/spelling/allow/allow.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
apc
calt
ccmp
Apc
clickable
clig
copyable
dalet
dcs
Expand All @@ -14,6 +17,7 @@ dzhe
Enum'd
formattings
ftp
fvar
geeksforgeeks
ghe
gje
Expand All @@ -27,7 +31,9 @@ It'd
kje
liga
lje
locl
maxed
mkmk
mru
nje
ogonek
Expand All @@ -37,10 +43,12 @@ postmodern
ptys
qof
qps
rclt
reimplementation
reserialization
reserialize
reserializes
rlig
runtimes
shcha
slnt
Expand Down
57 changes: 41 additions & 16 deletions src/cascadia/TerminalControl/ControlCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Set up the DX Engine
auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
_renderer->AddRenderEngine(dxEngine.get());
_renderEngine = std::move(dxEngine);

// Initialize our font with the renderer
// We don't have to care about DPI. We'll get a change message immediately if it's not 96
Expand All @@ -168,12 +169,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Then, using the font, get the number of characters that can fit.
// Resize our terminal connection to match that size, and initialize the terminal with that size.
const auto viewInPixels = Viewport::FromDimensions({ 0, 0 }, windowSize);
LOG_IF_FAILED(dxEngine->SetWindowSize({ viewInPixels.Width(), viewInPixels.Height() }));
LOG_IF_FAILED(_renderEngine->SetWindowSize({ viewInPixels.Width(), viewInPixels.Height() }));

// Update DxEngine's SelectionBackground
dxEngine->SetSelectionBackground(til::color{ _settings.SelectionBackground() });
_renderEngine->SetSelectionBackground(til::color{ _settings.SelectionBackground() });

const auto vp = dxEngine->GetViewportInCharacters(viewInPixels);
const auto vp = _renderEngine->GetViewportInCharacters(viewInPixels);
const auto width = vp.Width();
const auto height = vp.Height();
_connection.Resize(height, width);
Expand All @@ -188,27 +189,26 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// after Enable, then it'll be possible to paint the frame once
// _before_ the warning handler is set up, and then warnings from
// the first paint will be ignored!
dxEngine->SetWarningCallback(std::bind(&ControlCore::_rendererWarning, this, std::placeholders::_1));
_renderEngine->SetWarningCallback(std::bind(&ControlCore::_rendererWarning, this, std::placeholders::_1));

// Tell the DX Engine to notify us when the swap chain changes.
// We do this after we initially set the swapchain so as to avoid unnecessary callbacks (and locking problems)
dxEngine->SetCallback(std::bind(&ControlCore::_renderEngineSwapChainChanged, this));
_renderEngine->SetCallback(std::bind(&ControlCore::_renderEngineSwapChainChanged, this));

dxEngine->SetRetroTerminalEffect(_settings.RetroTerminalEffect());
dxEngine->SetPixelShaderPath(_settings.PixelShaderPath());
dxEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering());
dxEngine->SetSoftwareRendering(_settings.SoftwareRendering());
_renderEngine->SetRetroTerminalEffect(_settings.RetroTerminalEffect());
_renderEngine->SetPixelShaderPath(_settings.PixelShaderPath());
_renderEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering());
_renderEngine->SetSoftwareRendering(_settings.SoftwareRendering());

_updateAntiAliasingMode(dxEngine.get());
_updateAntiAliasingMode(_renderEngine.get());

// GH#5098: Inform the engine of the opacity of the default text background.
if (_settings.UseAcrylic())
{
dxEngine->SetDefaultTextBackgroundOpacity(::base::saturated_cast<float>(_settings.TintOpacity()));
_renderEngine->SetDefaultTextBackgroundOpacity(::base::saturated_cast<float>(_settings.TintOpacity()));
}

THROW_IF_FAILED(dxEngine->Enable());
_renderEngine = std::move(dxEngine);
THROW_IF_FAILED(_renderEngine->Enable());

_initializedTerminal = true;
} // scope for TerminalLock
Expand Down Expand Up @@ -603,9 +603,34 @@ namespace winrt::Microsoft::Terminal::Control::implementation

_terminal->SetFontInfo(_actualFont);

// TODO: MSFT:20895307 If the font doesn't exist, this doesn't
// actually fail. We need a way to gracefully fallback.
_renderer->TriggerFontChange(newDpi, _desiredFont, _actualFont);
if (_renderEngine)
{
std::unordered_map<std::wstring_view, uint32_t> featureMap;
if (const auto fontFeatures = _settings.FontFeatures())
{
featureMap.reserve(fontFeatures.Size());

for (const auto& [tag, param] : fontFeatures)
{
featureMap.emplace(tag, param);
}
}
std::unordered_map<std::wstring_view, float> axesMap;
if (const auto fontAxes = _settings.FontAxes())
{
axesMap.reserve(fontAxes.Size());

for (const auto& [axis, value] : fontAxes)
{
axesMap.emplace(axis, value);
}
}

// TODO: MSFT:20895307 If the font doesn't exist, this doesn't
// actually fail. We need a way to gracefully fallback.
LOG_IF_FAILED(_renderEngine->UpdateDpi(newDpi));
LOG_IF_FAILED(_renderEngine->UpdateFont(_desiredFont, _actualFont, featureMap, axesMap));
}

// If the actual font isn't what was requested...
if (_actualFont.GetFaceName() != _desiredFont.GetFaceName())
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalControl/IControlSettings.idl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ namespace Microsoft.Terminal.Control
Int32 FontSize;
Windows.UI.Text.FontWeight FontWeight;
String Padding;
Windows.Foundation.Collections.IMap<String, UInt32> FontFeatures;
Windows.Foundation.Collections.IMap<String, Single> FontAxes;

Microsoft.Terminal.Control.IKeyBindings KeyBindings;

Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,8 @@ winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::Duplicate
DUPLICATE_SETTING_MACRO_SUB(font, target, FontFace);
DUPLICATE_SETTING_MACRO_SUB(font, target, FontSize);
DUPLICATE_SETTING_MACRO_SUB(font, target, FontWeight);
DUPLICATE_SETTING_MACRO_SUB(font, target, FontFeatures);
DUPLICATE_SETTING_MACRO_SUB(font, target, FontAxes);
}

{
Expand Down
8 changes: 8 additions & 0 deletions src/cascadia/TerminalSettingsModel/FontConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ static constexpr std::string_view FontInfoKey{ "font" };
static constexpr std::string_view FontFaceKey{ "face" };
static constexpr std::string_view FontSizeKey{ "size" };
static constexpr std::string_view FontWeightKey{ "weight" };
static constexpr std::string_view FontFeaturesKey{ "features" };
static constexpr std::string_view FontAxesKey{ "axes" };
static constexpr std::string_view LegacyFontFaceKey{ "fontFace" };
static constexpr std::string_view LegacyFontSizeKey{ "fontSize" };
static constexpr std::string_view LegacyFontWeightKey{ "fontWeight" };
Expand All @@ -29,6 +31,8 @@ winrt::com_ptr<FontConfig> FontConfig::CopyFontInfo(const winrt::com_ptr<FontCon
fontInfo->_FontFace = source->_FontFace;
fontInfo->_FontSize = source->_FontSize;
fontInfo->_FontWeight = source->_FontWeight;
fontInfo->_FontAxes = source->_FontAxes;
fontInfo->_FontFeatures = source->_FontFeatures;
return fontInfo;
}

Expand All @@ -39,6 +43,8 @@ Json::Value FontConfig::ToJson() const
JsonUtils::SetValueForKey(json, FontFaceKey, _FontFace);
JsonUtils::SetValueForKey(json, FontSizeKey, _FontSize);
JsonUtils::SetValueForKey(json, FontWeightKey, _FontWeight);
JsonUtils::SetValueForKey(json, FontAxesKey, _FontAxes);
JsonUtils::SetValueForKey(json, FontFeaturesKey, _FontFeatures);

return json;
}
Expand All @@ -65,6 +71,8 @@ void FontConfig::LayerJson(const Json::Value& json)
JsonUtils::GetValueForKey(fontInfoJson, FontFaceKey, _FontFace);
JsonUtils::GetValueForKey(fontInfoJson, FontSizeKey, _FontSize);
JsonUtils::GetValueForKey(fontInfoJson, FontWeightKey, _FontWeight);
JsonUtils::GetValueForKey(fontInfoJson, FontFeaturesKey, _FontFeatures);
JsonUtils::GetValueForKey(fontInfoJson, FontAxesKey, _FontAxes);
}
else
{
Expand Down
5 changes: 5 additions & 0 deletions src/cascadia/TerminalSettingsModel/FontConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ Author(s):
#include "IInheritable.h"
#include <DefaultSettings.h>

using IFontAxesMap = winrt::Windows::Foundation::Collections::IMap<winrt::hstring, float>;
using IFontFeatureMap = winrt::Windows::Foundation::Collections::IMap<winrt::hstring, uint32_t>;

namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
struct FontConfig : FontConfigT<FontConfig>, IInheritable<FontConfig>
Expand All @@ -39,6 +42,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::FontConfig, hstring, FontFace, DEFAULT_FONT_FACE);
INHERITABLE_SETTING(Model::FontConfig, int32_t, FontSize, DEFAULT_FONT_SIZE);
INHERITABLE_SETTING(Model::FontConfig, Windows::UI::Text::FontWeight, FontWeight, DEFAULT_FONT_WEIGHT);
INHERITABLE_SETTING(Model::FontConfig, IFontAxesMap, FontAxes);
INHERITABLE_SETTING(Model::FontConfig, IFontFeatureMap, FontFeatures);

private:
winrt::weak_ref<Profile> _sourceProfile;
Expand Down
5 changes: 5 additions & 0 deletions src/cascadia/TerminalSettingsModel/FontConfig.idl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import "Profile.idl";
_BASE_INHERITABLE_SETTING(Type, Name); \
Microsoft.Terminal.Settings.Model.FontConfig Name##OverrideSource { get; }

#define COMMA ,

namespace Microsoft.Terminal.Settings.Model
{
[default_interface] runtimeclass FontConfig {
Expand All @@ -16,5 +18,8 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_FONT_SETTING(String, FontFace);
INHERITABLE_FONT_SETTING(Int32, FontSize);
INHERITABLE_FONT_SETTING(Windows.UI.Text.FontWeight, FontWeight);

INHERITABLE_FONT_SETTING(Windows.Foundation.Collections.IMap<String COMMA UInt32>, FontFeatures);
INHERITABLE_FONT_SETTING(Windows.Foundation.Collections.IMap<String COMMA Single>, FontAxes);
}
}
104 changes: 104 additions & 0 deletions src/cascadia/TerminalSettingsModel/JsonUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,58 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
}
};

template<typename T>
struct ConversionTrait<std::unordered_map<std::string, T>>
{
std::unordered_map<std::string, T> FromJson(const Json::Value& json) const
{
std::unordered_map<std::string, T> val;
val.reserve(json.size());

ConversionTrait<T> trait;
for (auto it = json.begin(), end = json.end(); it != end; ++it)
{
GetValue(*it, val[it.name()], trait);
}

return val;
}

bool CanConvert(const Json::Value& json) const
{
if (!json.isObject())
{
return false;
}
ConversionTrait<T> trait;
for (const auto& v : json)
{
if (!trait.CanConvert(v))
{
return false;
}
}
return true;
}

Json::Value ToJson(const std::unordered_map<std::string, T>& val)
{
Json::Value json{ Json::objectValue };

for (const auto& [k, v] : val)
{
SetValueForKey(json, k, v);
}

return json;
}

std::string TypeDescription() const
{
return fmt::format("map (string, {})", ConversionTrait<T>{}.TypeDescription());
}
};

#ifdef WINRT_BASE_H
template<>
struct ConversionTrait<winrt::hstring> : public ConversionTrait<std::wstring>
Expand Down Expand Up @@ -206,6 +258,58 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
return ConversionTrait<std::wstring>::CanConvert(json) || json.isNull();
}
};

template<typename T>
struct ConversionTrait<winrt::Windows::Foundation::Collections::IMap<winrt::hstring, T>>
{
winrt::Windows::Foundation::Collections::IMap<winrt::hstring, T> FromJson(const Json::Value& json) const
{
std::unordered_map<winrt::hstring, T> val;
val.reserve(json.size());

ConversionTrait<T> trait;
for (auto it = json.begin(), end = json.end(); it != end; ++it)
{
GetValue(*it, val[winrt::to_hstring(it.name())], trait);
}

return winrt::single_threaded_map<winrt::hstring, T>(std::move(val));
}

bool CanConvert(const Json::Value& json) const
{
if (!json.isObject())
{
return false;
}
ConversionTrait<T> trait;
for (const auto& v : json)
{
if (!trait.CanConvert(v))
{
return false;
}
}
return true;
}

Json::Value ToJson(const winrt::Windows::Foundation::Collections::IMap<winrt::hstring, T>& val)
{
Json::Value json{ Json::objectValue };

for (const auto& [k, v] : val)
{
SetValueForKey(json, til::u16u8(k), v);
}

return json;
}

std::string TypeDescription() const
{
return fmt::format("map (string, {})", ConversionTrait<T>{}.TypeDescription());
}
};
#endif

template<>
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalSettingsModel/TerminalSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
_FontFace = profile.FontInfo().FontFace();
_FontSize = profile.FontInfo().FontSize();
_FontWeight = profile.FontInfo().FontWeight();
_FontFeatures = profile.FontInfo().FontFeatures();
_FontAxes = profile.FontInfo().FontAxes();
_Padding = profile.Padding();

_Commandline = profile.Commandline();
Expand Down
5 changes: 5 additions & 0 deletions src/cascadia/TerminalSettingsModel/TerminalSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ Author(s):
#include <DefaultSettings.h>
#include <conattrs.hpp>

using IFontAxesMap = winrt::Windows::Foundation::Collections::IMap<winrt::hstring, float>;
using IFontFeatureMap = winrt::Windows::Foundation::Collections::IMap<winrt::hstring, uint32_t>;

// fwdecl unittest classes
namespace SettingsModelLocalTests
{
Expand Down Expand Up @@ -123,6 +126,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::TerminalSettings, int32_t, FontSize, DEFAULT_FONT_SIZE);

INHERITABLE_SETTING(Model::TerminalSettings, winrt::Windows::UI::Text::FontWeight, FontWeight);
INHERITABLE_SETTING(Model::TerminalSettings, IFontAxesMap, FontAxes);
INHERITABLE_SETTING(Model::TerminalSettings, IFontFeatureMap, FontFeatures);

INHERITABLE_SETTING(Model::TerminalSettings, hstring, BackgroundImage);
INHERITABLE_SETTING(Model::TerminalSettings, double, BackgroundImageOpacity, 1.0);
Expand Down
6 changes: 6 additions & 0 deletions src/cascadia/UnitTests_Control/MockControlSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Licensed under the MIT license.
#include <DefaultSettings.h>
#include <conattrs.hpp>

using IFontFeatureMap = winrt::Windows::Foundation::Collections::IMap<winrt::hstring, uint32_t>;
using IFontAxesMap = winrt::Windows::Foundation::Collections::IMap<winrt::hstring, float>;

namespace ControlUnitTests
{
class MockControlSettings : public winrt::implements<MockControlSettings, winrt::Microsoft::Terminal::Core::ICoreSettings, winrt::Microsoft::Terminal::Control::IControlSettings, winrt::Microsoft::Terminal::Core::ICoreAppearance, winrt::Microsoft::Terminal::Control::IControlAppearance>
Expand Down Expand Up @@ -80,6 +83,9 @@ namespace ControlUnitTests

WINRT_PROPERTY(winrt::hstring, PixelShaderPath);

WINRT_PROPERTY(IFontFeatureMap, FontFeatures);
WINRT_PROPERTY(IFontAxesMap, FontAxes);

private:
std::array<winrt::Microsoft::Terminal::Core::Color, COLOR_TABLE_SIZE> _ColorTable;

Expand Down
Loading

0 comments on commit 4c16cb2

Please sign in to comment.