mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-10 18:43:54 -06:00
Remove DxEngine (#16278)
With AtlasEngine being fairly stable at this point and being enabled by default in the 1.19 branch, this changeset removes DxEngine. ## Validation Steps Performed * WT builds and runs ✅ * WpfTestNetCore ✅ * Saving the config removes the `useAtlasEngine` key ✅
This commit is contained in:
parent
9654fc6afe
commit
bf25595961
@ -161,8 +161,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RendererVt.unittest", "src\
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BufferOut", "src\buffer\out\lib\bufferout.vcxproj", "{0CF235BD-2DA0-407E-90EE-C467E8BBC714}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RendererDx", "src\renderer\dx\lib\dx.vcxproj", "{48D21369-3D7B-4431-9967-24E81292CF62}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalConnection", "src\cascadia\TerminalConnection\TerminalConnection.vcxproj", "{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} = {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}
|
||||
@ -173,7 +171,6 @@ EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Control.Lib", "src\cascadia\TerminalControl\TerminalControlLib.vcxproj", "{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{1CF55140-EF6A-4736-A403-957E4F7430BB} = {1CF55140-EF6A-4736-A403-957E4F7430BB}
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62} = {48D21369-3D7B-4431-9967-24E81292CF62}
|
||||
{48D21369-3D7B-4431-9967-24E81292CF63} = {48D21369-3D7B-4431-9967-24E81292CF63}
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F} = {8222900C-8B6C-452A-91AC-BE95DB04B95F}
|
||||
{AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F} = {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}
|
||||
@ -319,8 +316,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{D3EF
|
||||
build\scripts\Test-WindowsTerminalPackage.ps1 = build\scripts\Test-WindowsTerminalPackage.ps1
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Dx.Unit.Tests", "src\renderer\dx\ut_dx\Dx.Unit.Tests.vcxproj", "{95B136F9-B238-490C-A7C5-5843C1FECAC4}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winconpty.Tests.Feature", "src\winconpty\ft_pty\winconpty.FeatureTests.vcxproj", "{024052DE-83FB-4653-AEA4-90790D29D5BD}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalAzBridge", "src\cascadia\TerminalAzBridge\TerminalAzBridge.vcxproj", "{067F0A06-FCB7-472C-96E9-B03B54E8E18D}"
|
||||
@ -1260,32 +1255,6 @@ Global
|
||||
{0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Release|x64.Build.0 = Release|x64
|
||||
{0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Release|x86.ActiveCfg = Release|Win32
|
||||
{0CF235BD-2DA0-407E-90EE-C467E8BBC714}.Release|x86.Build.0 = Release|Win32
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.AuditMode|x64.ActiveCfg = AuditMode|x64
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.AuditMode|x64.Build.0 = AuditMode|x64
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.AuditMode|x86.ActiveCfg = AuditMode|Win32
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.AuditMode|x86.Build.0 = AuditMode|Win32
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Debug|x64.Build.0 = Debug|x64
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Debug|x86.Build.0 = Debug|Win32
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Fuzzing|x64.ActiveCfg = Fuzzing|x64
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Fuzzing|x64.Build.0 = Fuzzing|x64
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Release|x64.ActiveCfg = Release|x64
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Release|x64.Build.0 = Release|x64
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Release|x86.ActiveCfg = Release|Win32
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62}.Release|x86.Build.0 = Release|Win32
|
||||
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.AuditMode|Any CPU.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.AuditMode|ARM64.ActiveCfg = Release|ARM64
|
||||
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.AuditMode|x64.ActiveCfg = AuditMode|x64
|
||||
@ -1779,30 +1748,6 @@ Global
|
||||
{A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Release|x64.Build.0 = Release|x64
|
||||
{A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Release|x86.ActiveCfg = Release|Win32
|
||||
{A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Release|x86.Build.0 = Release|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.AuditMode|x64.ActiveCfg = Release|x64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.AuditMode|x86.ActiveCfg = AuditMode|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.AuditMode|x86.Build.0 = AuditMode|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|x64.Build.0 = Debug|x64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|x86.Build.0 = Debug|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Fuzzing|x64.ActiveCfg = Fuzzing|x64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|x64.ActiveCfg = Release|x64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|x64.Build.0 = Release|x64
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|x86.ActiveCfg = Release|Win32
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|x86.Build.0 = Release|Win32
|
||||
{024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
|
||||
{024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
|
||||
{024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
|
||||
@ -2485,7 +2430,6 @@ Global
|
||||
{18D09A24-8240-42D6-8CB6-236EEE820263} = {89CDCC5C-9F53-4054-97A4-639D99F169CD}
|
||||
{990F2657-8580-4828-943F-5DD657D11843} = {05500DEF-2294-41E3-AF9A-24E580B82836}
|
||||
{0CF235BD-2DA0-407E-90EE-C467E8BBC714} = {1E4A062E-293B-4817-B20D-BF16B979E350}
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62} = {05500DEF-2294-41E3-AF9A-24E580B82836}
|
||||
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{CA5CAD1A-ABCD-429C-B551-8562EC954746} = {9921CA0A-320C-4460-8623-3A3196E7F4CB}
|
||||
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {9921CA0A-320C-4460-8623-3A3196E7F4CB}
|
||||
@ -2515,7 +2459,6 @@ Global
|
||||
{53DD5520-E64C-4C06-B472-7CE62CA539C9} = {04170EEF-983A-4195-BFEF-2321E5E38A1E}
|
||||
{6B5A44ED-918D-4747-BFB1-2472A1FCA173} = {04170EEF-983A-4195-BFEF-2321E5E38A1E}
|
||||
{D3EF7B96-CD5E-47C9-B9A9-136259563033} = {04170EEF-983A-4195-BFEF-2321E5E38A1E}
|
||||
{95B136F9-B238-490C-A7C5-5843C1FECAC4} = {05500DEF-2294-41E3-AF9A-24E580B82836}
|
||||
{024052DE-83FB-4653-AEA4-90790D29D5BD} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
|
||||
{067F0A06-FCB7-472C-96E9-B03B54E8E18D} = {61901E80-E97D-4D61-A9BB-E8F2FDA8B40C}
|
||||
{6BAE5851-50D5-4934-8D5E-30361A8A40F3} = {81C352DB-1818-45B7-A284-18E259F1CC87}
|
||||
|
||||
@ -2791,11 +2791,6 @@
|
||||
"description": "Use to set a path to a pixel shader to use with the Terminal. Overrides `experimental.retroTerminalEffect`. This is an experimental feature, and its continued existence is not guaranteed.",
|
||||
"type": "string"
|
||||
},
|
||||
"useAtlasEngine": {
|
||||
"description": "Windows Terminal 1.16 and later ship with a new, performant text renderer. Set this to false to revert back to the old text renderer.",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"fontFace": {
|
||||
"default": "Cascadia Mono",
|
||||
"description": "[deprecated] Define 'face' within the 'font' object instead.",
|
||||
|
||||
@ -17,7 +17,6 @@
|
||||
#include "EventArgs.h"
|
||||
#include "../../buffer/out/search.h"
|
||||
#include "../../renderer/atlas/AtlasEngine.h"
|
||||
#include "../../renderer/dx/DxRenderer.hpp"
|
||||
|
||||
#include "ControlCore.g.cpp"
|
||||
#include "SelectionColor.g.cpp"
|
||||
@ -335,15 +334,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_settings->UseAtlasEngine())
|
||||
{
|
||||
_renderEngine = std::make_unique<::Microsoft::Console::Render::AtlasEngine>();
|
||||
}
|
||||
else
|
||||
{
|
||||
_renderEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
|
||||
}
|
||||
|
||||
_renderEngine = std::make_unique<::Microsoft::Console::Render::AtlasEngine>();
|
||||
_renderer->AddRenderEngine(_renderEngine.get());
|
||||
|
||||
// Initialize our font with the renderer
|
||||
@ -359,7 +350,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
const auto viewInPixels = Viewport::FromDimensions({ 0, 0 }, windowSize);
|
||||
LOG_IF_FAILED(_renderEngine->SetWindowSize({ viewInPixels.Width(), viewInPixels.Height() }));
|
||||
|
||||
// Update DxEngine's SelectionBackground
|
||||
// Update AtlasEngine's SelectionBackground
|
||||
_renderEngine->SetSelectionBackground(til::color{ _settings->SelectionBackground() });
|
||||
|
||||
const auto vp = _renderEngine->GetViewportInCharacters(viewInPixels);
|
||||
@ -915,10 +906,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// Update the terminal core with its new Core settings
|
||||
_terminal->UpdateAppearance(*newAppearance);
|
||||
|
||||
// Update DxEngine settings under the lock
|
||||
// Update AtlasEngine settings under the lock
|
||||
if (_renderEngine)
|
||||
{
|
||||
// Update DxEngine settings under the lock
|
||||
// Update AtlasEngine settings under the lock
|
||||
_renderEngine->SetSelectionBackground(til::color{ newAppearance->SelectionBackground() });
|
||||
_renderEngine->SetRetroTerminalEffect(newAppearance->RetroTerminalEffect());
|
||||
_renderEngine->SetPixelShaderPath(newAppearance->PixelShaderPath());
|
||||
@ -954,7 +945,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void ControlCore::_updateAntiAliasingMode()
|
||||
{
|
||||
D2D1_TEXT_ANTIALIAS_MODE mode;
|
||||
// Update DxEngine's AntialiasingMode
|
||||
// Update AtlasEngine's AntialiasingMode
|
||||
switch (_settings->AntialiasingMode())
|
||||
{
|
||||
case TextAntialiasingMode::Cleartype:
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
// - ControlCore.h
|
||||
//
|
||||
// Abstract:
|
||||
// - This encapsulates a `Terminal` instance, a `DxEngine` and `Renderer`, and
|
||||
// - This encapsulates a `Terminal` instance, a `AtlasEngine` and `Renderer`, and
|
||||
// an `ITerminalConnection`. This is intended to be everything that someone
|
||||
// might need to stand up a terminal instance in a control, but without any
|
||||
// regard for how the UX works.
|
||||
|
||||
@ -3,11 +3,18 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "HwndTerminal.hpp"
|
||||
#include <windowsx.h>
|
||||
#include <DefaultSettings.h>
|
||||
#include "../../types/viewport.cpp"
|
||||
#include "../../types/inc/GlyphWidth.hpp"
|
||||
|
||||
#include <DefaultSettings.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
#include "HwndTerminalAutomationPeer.hpp"
|
||||
#include "../../cascadia/TerminalCore/Terminal.hpp"
|
||||
#include "../../renderer/atlas/AtlasEngine.h"
|
||||
#include "../../renderer/base/renderer.hpp"
|
||||
#include "../../renderer/uia/UiaRenderer.hpp"
|
||||
#include "../../types/viewport.cpp"
|
||||
|
||||
using namespace ::Microsoft::Console::VirtualTerminal;
|
||||
using namespace ::Microsoft::Terminal::Core;
|
||||
|
||||
static LPCWSTR term_window_class = L"HwndTerminalClass";
|
||||
@ -102,25 +109,23 @@ try
|
||||
}
|
||||
break;
|
||||
case WM_RBUTTONDOWN:
|
||||
if (publicTerminal->_terminal && publicTerminal->_terminal->IsSelectionActive())
|
||||
try
|
||||
{
|
||||
try
|
||||
if (publicTerminal->_terminal)
|
||||
{
|
||||
Terminal::TextCopyData bufferData;
|
||||
const auto lock = publicTerminal->_terminal->LockForWriting();
|
||||
if (publicTerminal->_terminal->IsSelectionActive())
|
||||
{
|
||||
const auto lock = publicTerminal->_terminal->LockForWriting();
|
||||
bufferData = publicTerminal->_terminal->RetrieveSelectedTextFromBuffer(false, true, true);
|
||||
const auto bufferData = publicTerminal->_terminal->RetrieveSelectedTextFromBuffer(false, true, true);
|
||||
LOG_IF_FAILED(publicTerminal->_CopyTextToSystemClipboard(bufferData.plainText, bufferData.html, bufferData.rtf));
|
||||
publicTerminal->_ClearSelection();
|
||||
return 0;
|
||||
}
|
||||
LOG_IF_FAILED(publicTerminal->_CopyTextToSystemClipboard(bufferData.plainText, bufferData.html, bufferData.rtf));
|
||||
publicTerminal->_ClearSelection();
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
else
|
||||
{
|
||||
publicTerminal->_PasteTextFromClipboard();
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
CATCH_LOG();
|
||||
case WM_DESTROY:
|
||||
// Release Terminal's hwnd so Teardown doesn't try to destroy it again
|
||||
publicTerminal->_hwnd.release();
|
||||
@ -210,10 +215,10 @@ HRESULT HwndTerminal::Initialize()
|
||||
RETURN_HR_IF_NULL(E_POINTER, localPointerToThread);
|
||||
RETURN_IF_FAILED(localPointerToThread->Initialize(_renderer.get()));
|
||||
|
||||
auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
|
||||
RETURN_IF_FAILED(dxEngine->SetHwnd(_hwnd.get()));
|
||||
RETURN_IF_FAILED(dxEngine->Enable());
|
||||
_renderer->AddRenderEngine(dxEngine.get());
|
||||
auto engine = std::make_unique<::Microsoft::Console::Render::AtlasEngine>();
|
||||
RETURN_IF_FAILED(engine->SetHwnd(_hwnd.get()));
|
||||
RETURN_IF_FAILED(engine->Enable());
|
||||
_renderer->AddRenderEngine(engine.get());
|
||||
|
||||
_UpdateFont(USER_DEFAULT_SCREEN_DPI);
|
||||
RECT windowRect;
|
||||
@ -224,9 +229,9 @@ HRESULT HwndTerminal::Initialize()
|
||||
// Fist set up the dx engine with the window size in pixels.
|
||||
// Then, using the font, get the number of characters that can fit.
|
||||
const auto viewInPixels = Viewport::FromDimensions({ 0, 0 }, windowSize);
|
||||
RETURN_IF_FAILED(dxEngine->SetWindowSize({ viewInPixels.Width(), viewInPixels.Height() }));
|
||||
RETURN_IF_FAILED(engine->SetWindowSize({ viewInPixels.Width(), viewInPixels.Height() }));
|
||||
|
||||
_renderEngine = std::move(dxEngine);
|
||||
_renderEngine = std::move(engine);
|
||||
|
||||
_terminal->Create({ 80, 25 }, 9001, *_renderer);
|
||||
_terminal->SetWriteInputCallback([=](std::wstring_view input) noexcept { _WriteTextToConnection(input); });
|
||||
@ -749,7 +754,7 @@ try
|
||||
ScreenToClient(_hwnd.get(), cursorPosition.as_win32_point());
|
||||
}
|
||||
|
||||
const TerminalInput::MouseButtonState state{
|
||||
const Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state{
|
||||
WI_IsFlagSet(GetKeyState(VK_LBUTTON), KeyPressed),
|
||||
WI_IsFlagSet(GetKeyState(VK_MBUTTON), KeyPressed),
|
||||
WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed)
|
||||
@ -894,7 +899,7 @@ void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR font
|
||||
[[gsl::suppress(bounds .3)]] renderSettings.SetColorTableEntry(tableIndex, gsl::at(theme.ColorTable, tableIndex));
|
||||
}
|
||||
|
||||
publicTerminal->_terminal->SetCursorStyle(static_cast<DispatchTypes::CursorStyle>(theme.CursorStyle));
|
||||
publicTerminal->_terminal->SetCursorStyle(static_cast<Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle>(theme.CursorStyle));
|
||||
|
||||
publicTerminal->_desiredFont = { fontFamily, 0, DEFAULT_FONT_WEIGHT, static_cast<float>(fontSize), CP_UTF8 };
|
||||
publicTerminal->_UpdateFont(newDpi);
|
||||
|
||||
@ -3,14 +3,31 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../renderer/base/Renderer.hpp"
|
||||
#include "../../renderer/dx/DxRenderer.hpp"
|
||||
#include "../../renderer/uia/UiaRenderer.hpp"
|
||||
#include "../../cascadia/TerminalCore/Terminal.hpp"
|
||||
#include "../../buffer/out/textBuffer.hpp"
|
||||
#include "../../renderer/inc/FontInfoDesired.hpp"
|
||||
#include "../../types/IControlAccessibilityInfo.h"
|
||||
#include "HwndTerminalAutomationPeer.hpp"
|
||||
|
||||
using namespace Microsoft::Console::VirtualTerminal;
|
||||
namespace Microsoft::Console::Render::Atlas
|
||||
{
|
||||
class AtlasEngine;
|
||||
}
|
||||
|
||||
namespace Microsoft::Console::Render
|
||||
{
|
||||
using AtlasEngine = Atlas::AtlasEngine;
|
||||
class IRenderData;
|
||||
class Renderer;
|
||||
class UiaEngine;
|
||||
}
|
||||
|
||||
namespace Microsoft::Terminal::Core
|
||||
{
|
||||
class Terminal;
|
||||
}
|
||||
|
||||
class FontInfo;
|
||||
class FontInfoDesired;
|
||||
class HwndTerminalAutomationPeer;
|
||||
|
||||
// Keep in sync with TerminalTheme.cs
|
||||
typedef struct _TerminalTheme
|
||||
@ -79,7 +96,7 @@ private:
|
||||
std::unique_ptr<::Microsoft::Terminal::Core::Terminal> _terminal;
|
||||
|
||||
std::unique_ptr<::Microsoft::Console::Render::Renderer> _renderer;
|
||||
std::unique_ptr<::Microsoft::Console::Render::DxEngine> _renderEngine;
|
||||
std::unique_ptr<::Microsoft::Console::Render::AtlasEngine> _renderEngine;
|
||||
std::unique_ptr<::Microsoft::Console::Render::UiaEngine> _uiaEngine;
|
||||
|
||||
bool _focused{ false };
|
||||
|
||||
@ -35,8 +35,6 @@ namespace Microsoft.Terminal.Control
|
||||
Boolean EnableUnfocusedAcrylic;
|
||||
ScrollbarState ScrollState { get; };
|
||||
|
||||
Boolean UseAtlasEngine { get; };
|
||||
|
||||
String FontFace { get; };
|
||||
Single FontSize { get; };
|
||||
Windows.UI.Text.FontWeight FontWeight { get; };
|
||||
|
||||
@ -2404,25 +2404,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// then use it to measure how much space the requested rows and columns
|
||||
// will take up.
|
||||
// TODO: MSFT:21254947 - use a static function to do this instead of
|
||||
// instantiating a DxEngine/AtlasEngine.
|
||||
// instantiating a AtlasEngine.
|
||||
// GH#10211 - UNDER NO CIRCUMSTANCE should this fail. If it does, the
|
||||
// whole app will crash instantaneously on launch, which is no good.
|
||||
float scale;
|
||||
if (settings.UseAtlasEngine())
|
||||
{
|
||||
auto engine = std::make_unique<::Microsoft::Console::Render::AtlasEngine>();
|
||||
LOG_IF_FAILED(engine->UpdateDpi(dpi));
|
||||
LOG_IF_FAILED(engine->UpdateFont(desiredFont, actualFont));
|
||||
scale = engine->GetScaling();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto engine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
|
||||
LOG_IF_FAILED(engine->UpdateDpi(dpi));
|
||||
LOG_IF_FAILED(engine->UpdateFont(desiredFont, actualFont));
|
||||
scale = engine->GetScaling();
|
||||
}
|
||||
const auto engine = std::make_unique<::Microsoft::Console::Render::AtlasEngine>();
|
||||
LOG_IF_FAILED(engine->UpdateDpi(dpi));
|
||||
LOG_IF_FAILED(engine->UpdateFont(desiredFont, actualFont));
|
||||
|
||||
const auto scale = engine->GetScaling();
|
||||
const auto actualFontSize = actualFont.GetSize();
|
||||
|
||||
// UWP XAML scrollbars aren't guaranteed to be the same size as the
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
#include "XamlLights.h"
|
||||
#include "EventArgs.h"
|
||||
#include "../../renderer/base/Renderer.hpp"
|
||||
#include "../../renderer/dx/DxRenderer.hpp"
|
||||
#include "../../renderer/uia/UiaRenderer.hpp"
|
||||
#include "../../cascadia/TerminalCore/Terminal.hpp"
|
||||
#include "../buffer/out/search.h"
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
|
||||
|
||||
|
||||
<!-- ========================= Headers ======================== -->
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
@ -166,7 +166,6 @@
|
||||
<ProjectReference Include="..\..\buffer\out\lib\bufferout.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\renderer\base\lib\base.vcxproj" />
|
||||
<ProjectReference Include="..\..\renderer\atlas\atlas.vcxproj" />
|
||||
<ProjectReference Include="..\..\renderer\dx\lib\dx.vcxproj" />
|
||||
<ProjectReference Include="..\..\renderer\uia\lib\uia.vcxproj" />
|
||||
<ProjectReference Include="..\..\terminal\parser\lib\parser.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\terminal\input\lib\terminalinput.vcxproj" />
|
||||
|
||||
@ -43,9 +43,6 @@
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\renderer\atlas\atlas.vcxproj">
|
||||
<Project>{8222900C-8B6C-452A-91AC-BE95DB04B95F}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\renderer\dx\lib\dx.vcxproj">
|
||||
<Project>{48d21369-3d7b-4431-9967-24e81292cf62}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\audio\midi\lib\midi.vcxproj">
|
||||
<Project>{3c67784e-1453-49c2-9660-483e2cc7f7ad}</Project>
|
||||
</ProjectReference>
|
||||
|
||||
@ -102,7 +102,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, SnapOnInput);
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, AltGrAliasing);
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, BellStyle);
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, UseAtlasEngine);
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, Elevate);
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, VtPassthrough);
|
||||
OBSERVABLE_PROJECTED_SETTING(_profile, ReloadEnvironmentVariables);
|
||||
|
||||
@ -103,7 +103,6 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, SnapOnInput);
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AltGrAliasing);
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Settings.Model.BellStyle, BellStyle);
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, UseAtlasEngine);
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, Elevate);
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, VtPassthrough);
|
||||
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, ReloadEnvironmentVariables);
|
||||
|
||||
@ -117,15 +117,6 @@
|
||||
</StackPanel>
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- AtlasEngine -->
|
||||
<local:SettingContainer x:Uid="Profile_UseAtlasEngine"
|
||||
ClearSettingValue="{x:Bind Profile.ClearUseAtlasEngine}"
|
||||
HasSettingValue="{x:Bind Profile.HasUseAtlasEngine, Mode=OneWay}"
|
||||
SettingOverrideSource="{x:Bind Profile.UseAtlasEngineOverrideSource, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind Profile.UseAtlasEngine, Mode=TwoWay}"
|
||||
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- VtPassthrough -->
|
||||
<local:SettingContainer x:Uid="Profile_VtPassthrough"
|
||||
ClearSettingValue="{x:Bind Profile.ClearVtPassthrough}"
|
||||
|
||||
@ -22,12 +22,6 @@
|
||||
<TextBlock x:Uid="Globals_RenderingDisclaimer"
|
||||
Style="{StaticResource DisclaimerStyle}" />
|
||||
|
||||
<!-- AtlasEngine -->
|
||||
<local:SettingContainer x:Uid="Profile_UseAtlasEngine">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.UseAtlasEngine, Mode=TwoWay}"
|
||||
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Force Full Repaint -->
|
||||
<local:SettingContainer x:Uid="Globals_ForceFullRepaint">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.ForceFullRepaintRendering, Mode=TwoWay}"
|
||||
|
||||
@ -12,7 +12,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
explicit RenderingViewModel(Model::CascadiaSettings settings) noexcept;
|
||||
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_settings.ProfileDefaults(), UseAtlasEngine);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_settings.GlobalSettings(), ForceFullRepaintRendering);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_settings.GlobalSettings(), SoftwareRendering);
|
||||
|
||||
|
||||
@ -11,7 +11,6 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
RenderingViewModel(Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
|
||||
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, UseAtlasEngine);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, ForceFullRepaintRendering);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, SoftwareRendering);
|
||||
}
|
||||
|
||||
@ -1138,10 +1138,6 @@
|
||||
<value>Controls what happens when the application emits a BEL character.</value>
|
||||
<comment>A description for what the "bell style" setting does. Presented near "Profile_BellStyle".{Locked="BEL"}</comment>
|
||||
</data>
|
||||
<data name="Profile_UseAtlasEngine.Header" xml:space="preserve">
|
||||
<value>Use the new text renderer ("AtlasEngine")</value>
|
||||
<comment>{Locked="AtlasEngine"}</comment>
|
||||
</data>
|
||||
<data name="Profile_ReloadEnvVars.Header" xml:space="preserve">
|
||||
<value>Launch this application with a new environment block</value>
|
||||
<comment>"environment variables" are user-definable values that can affect the way running processes will behave on a computer</comment>
|
||||
|
||||
@ -90,7 +90,6 @@ Author(s):
|
||||
X(hstring, TabTitle, "tabTitle") \
|
||||
X(Model::BellStyle, BellStyle, "bellStyle", BellStyle::Audible) \
|
||||
X(IEnvironmentVariableMap, EnvironmentVariables, "environment", nullptr) \
|
||||
X(bool, UseAtlasEngine, "useAtlasEngine", true) \
|
||||
X(bool, RightClickContextMenu, "experimental.rightClickContextMenu", false) \
|
||||
X(Windows::Foundation::Collections::IVector<winrt::hstring>, BellSound, "bellSound", nullptr) \
|
||||
X(bool, Elevate, "elevate", false) \
|
||||
|
||||
@ -85,7 +85,6 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
|
||||
INHERITABLE_PROFILE_SETTING(Windows.Foundation.Collections.IMap<String COMMA String>, EnvironmentVariables);
|
||||
|
||||
INHERITABLE_PROFILE_SETTING(Boolean, UseAtlasEngine);
|
||||
INHERITABLE_PROFILE_SETTING(Windows.Foundation.Collections.IVector<String>, BellSound);
|
||||
|
||||
INHERITABLE_PROFILE_SETTING(Boolean, Elevate);
|
||||
|
||||
@ -307,7 +307,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
_SuppressApplicationTitle = profile.SuppressApplicationTitle();
|
||||
}
|
||||
|
||||
_UseAtlasEngine = profile.UseAtlasEngine();
|
||||
_ScrollState = profile.ScrollState();
|
||||
|
||||
_AntialiasingMode = profile.AntialiasingMode();
|
||||
|
||||
@ -149,7 +149,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
INHERITABLE_SETTING(Model::TerminalSettings, IEnvironmentVariableMap, EnvironmentVariables);
|
||||
|
||||
INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::ScrollbarState, ScrollState, Microsoft::Terminal::Control::ScrollbarState::Visible);
|
||||
INHERITABLE_SETTING(Model::TerminalSettings, bool, UseAtlasEngine, true);
|
||||
|
||||
INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale);
|
||||
|
||||
|
||||
@ -40,7 +40,6 @@
|
||||
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\buffer\out\lib\bufferout.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\renderer\base\lib\base.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\renderer\dx\lib\dx.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\renderer\uia\lib\uia.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\terminal\parser\lib\parser.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\terminal\input\lib\terminalinput.vcxproj" />
|
||||
|
||||
@ -2,18 +2,10 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include <WexTestClass.h>
|
||||
|
||||
#include <DefaultSettings.h>
|
||||
|
||||
#include "../renderer/inc/DummyRenderer.hpp"
|
||||
#include "../renderer/base/Renderer.hpp"
|
||||
#include "../renderer/dx/DxRenderer.hpp"
|
||||
|
||||
#include "../cascadia/TerminalCore/Terminal.hpp"
|
||||
#include "MockTermSettings.h"
|
||||
#include "consoletaeftemplates.hpp"
|
||||
#include "../../inc/TestUtils.h"
|
||||
#include "../renderer/inc/DummyRenderer.hpp"
|
||||
#include "../renderer/inc/RenderEngineBase.hpp"
|
||||
|
||||
using namespace winrt::Microsoft::Terminal::Core;
|
||||
using namespace Microsoft::Terminal::Core;
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
|
||||
#include "../renderer/inc/DummyRenderer.hpp"
|
||||
#include "../renderer/base/Renderer.hpp"
|
||||
#include "../renderer/dx/DxRenderer.hpp"
|
||||
|
||||
#include "../cascadia/TerminalCore/Terminal.hpp"
|
||||
#include "MockTermSettings.h"
|
||||
|
||||
@ -72,7 +72,6 @@
|
||||
X(winrt::Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, winrt::Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale) \
|
||||
X(bool, ForceFullRepaintRendering, false) \
|
||||
X(bool, SoftwareRendering, false) \
|
||||
X(bool, UseAtlasEngine, true) \
|
||||
X(bool, UseBackgroundImageForWindow, false) \
|
||||
X(bool, ShowMarks, false) \
|
||||
X(winrt::Microsoft::Terminal::Control::CopyFormat, CopyFormatting, 0) \
|
||||
|
||||
@ -26,15 +26,6 @@
|
||||
</alwaysEnabledBrandingTokens>
|
||||
</feature>
|
||||
|
||||
<feature>
|
||||
<name>Feature_ConhostDxEngine</name>
|
||||
<description>Controls whether conhost supports the DX engine and the UseDx registry key</description>
|
||||
<stage>AlwaysEnabled</stage>
|
||||
<alwaysDisabledBrandingTokens>
|
||||
<brandingToken>WindowsInbox</brandingToken>
|
||||
</alwaysDisabledBrandingTokens>
|
||||
</feature>
|
||||
|
||||
<feature>
|
||||
<name>Feature_ConhostAtlasEngine</name>
|
||||
<description>Controls whether conhost supports the Atlas engine</description>
|
||||
@ -44,15 +35,6 @@
|
||||
</alwaysDisabledBrandingTokens>
|
||||
</feature>
|
||||
|
||||
<feature>
|
||||
<name>Feature_DxEngineShaderSupport</name>
|
||||
<description>Controls whether the DX engine is built with shader support.</description>
|
||||
<stage>AlwaysEnabled</stage>
|
||||
<alwaysDisabledBrandingTokens>
|
||||
<brandingToken>WindowsInbox</brandingToken>
|
||||
</alwaysDisabledBrandingTokens>
|
||||
</feature>
|
||||
|
||||
<feature>
|
||||
<name>Feature_UseNumpadEventsForClipboardInput</name>
|
||||
<description>Controls whether the clipboard converter (and ConPTY InputStateMachine) uses Numpad events instead of UChar</description>
|
||||
|
||||
@ -47,9 +47,6 @@
|
||||
<ProjectReference Include="..\..\renderer\atlas\atlas.vcxproj">
|
||||
<Project>{8222900C-8B6C-452A-91AC-BE95DB04B95F}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\renderer\dx\lib\dx.vcxproj">
|
||||
<Project>{48d21369-3d7b-4431-9967-24e81292cf62}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\renderer\gdi\lib\gdi.vcxproj">
|
||||
<Project>{1c959542-bac2-4e55-9a6d-13251914cbb9}</Project>
|
||||
</ProjectReference>
|
||||
|
||||
@ -42,9 +42,6 @@
|
||||
<ProjectReference Include="..\..\renderer\atlas\atlas.vcxproj">
|
||||
<Project>{8222900C-8B6C-452A-91AC-BE95DB04B95F}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\renderer\dx\lib\dx.vcxproj">
|
||||
<Project>{48d21369-3d7b-4431-9967-24e81292cf62}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\renderer\gdi\lib\gdi.vcxproj">
|
||||
<Project>{1c959542-bac2-4e55-9a6d-13251914cbb9}</Project>
|
||||
</ProjectReference>
|
||||
|
||||
@ -53,7 +53,7 @@ Settings::Settings() :
|
||||
_fUseWindowSizePixels(false),
|
||||
// window size pixels initialized below
|
||||
_fInterceptCopyPaste(0),
|
||||
_fUseDx(UseDx::Disabled),
|
||||
_fUseDx(false),
|
||||
_fCopyColor(false)
|
||||
{
|
||||
_dwScreenBufferSize.X = 80;
|
||||
@ -767,7 +767,7 @@ void Settings::SetTerminalScrolling(const bool terminalScrollingEnabled) noexcep
|
||||
|
||||
// Determines whether our primary renderer should be DirectX or GDI.
|
||||
// This is based on user preference and velocity hold back state.
|
||||
UseDx Settings::GetUseDx() const noexcept
|
||||
bool Settings::GetUseDx() const noexcept
|
||||
{
|
||||
return _fUseDx;
|
||||
}
|
||||
|
||||
@ -24,13 +24,6 @@ constexpr unsigned short MIN_WINDOW_OPACITY = 0x4D; // 0x4D is approximately 30%
|
||||
#include "ConsoleArguments.hpp"
|
||||
#include "../renderer/inc/RenderSettings.hpp"
|
||||
|
||||
enum class UseDx : DWORD
|
||||
{
|
||||
Disabled = 0,
|
||||
DxEngine,
|
||||
AtlasEngine,
|
||||
};
|
||||
|
||||
class Settings
|
||||
{
|
||||
using RenderSettings = Microsoft::Console::Render::RenderSettings;
|
||||
@ -176,7 +169,7 @@ public:
|
||||
bool IsTerminalScrolling() const noexcept;
|
||||
void SetTerminalScrolling(const bool terminalScrollingEnabled) noexcept;
|
||||
|
||||
UseDx GetUseDx() const noexcept;
|
||||
bool GetUseDx() const noexcept;
|
||||
bool GetCopyColor() const noexcept;
|
||||
|
||||
private:
|
||||
@ -219,7 +212,7 @@ private:
|
||||
std::wstring _LaunchFaceName;
|
||||
bool _fAllowAltF4Close;
|
||||
DWORD _dwVirtTermLevel;
|
||||
UseDx _fUseDx;
|
||||
bool _fUseDx;
|
||||
bool _fCopyColor;
|
||||
|
||||
// this is used for the special STARTF_USESIZE mode.
|
||||
|
||||
@ -47,9 +47,6 @@
|
||||
<ProjectReference Include="..\..\renderer\atlas\atlas.vcxproj">
|
||||
<Project>{8222900C-8B6C-452A-91AC-BE95DB04B95F}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\renderer\dx\lib\dx.vcxproj">
|
||||
<Project>{48d21369-3d7b-4431-9967-24e81292cf62}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\renderer\vt\ut_lib\vt.unittest.vcxproj">
|
||||
<Project>{990F2657-8580-4828-943F-5DD657D11843}</Project>
|
||||
</ProjectReference>
|
||||
|
||||
@ -12,10 +12,6 @@
|
||||
#include "../../renderer/vt/Xterm256Engine.hpp"
|
||||
#include "../../renderer/vt/XtermEngine.hpp"
|
||||
|
||||
#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
|
||||
#include "../../renderer/dx/DxRenderer.hpp"
|
||||
#endif
|
||||
|
||||
using namespace WEX::Common;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
@ -38,10 +34,6 @@ class Microsoft::Console::VirtualTerminal::VtIoTests
|
||||
|
||||
TEST_METHOD(RendererDtorAndThread);
|
||||
|
||||
#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
|
||||
TEST_METHOD(RendererDtorAndThreadAndDx);
|
||||
#endif
|
||||
|
||||
TEST_METHOD(BasicAnonymousPipeOpeningWithSignalChannelTest);
|
||||
};
|
||||
|
||||
@ -428,37 +420,6 @@ void VtIoTests::RendererDtorAndThread()
|
||||
}
|
||||
}
|
||||
|
||||
#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
|
||||
void VtIoTests::RendererDtorAndThreadAndDx()
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Test deleting a Renderer a bunch of times"));
|
||||
|
||||
for (auto i = 0; i < 16; ++i)
|
||||
{
|
||||
auto data = std::make_unique<MockRenderData>();
|
||||
auto thread = std::make_unique<Microsoft::Console::Render::RenderThread>();
|
||||
auto* pThread = thread.get();
|
||||
auto pRenderer = std::make_unique<Microsoft::Console::Render::Renderer>(RenderSettings{}, data.get(), nullptr, 0, std::move(thread));
|
||||
VERIFY_SUCCEEDED(pThread->Initialize(pRenderer.get()));
|
||||
|
||||
auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
|
||||
pRenderer->AddRenderEngine(dxEngine.get());
|
||||
// Sleep for a hot sec to make sure the thread starts before we enable painting
|
||||
// If you don't, the thread might wait on the paint enabled event AFTER
|
||||
// EnablePainting gets called, and if that happens, then the thread will
|
||||
// never get destructed. This will only ever happen in the vstest test runner,
|
||||
// which is what CI uses.
|
||||
/*Sleep(500);*/
|
||||
|
||||
(void)dxEngine->Enable();
|
||||
pThread->EnablePainting();
|
||||
pRenderer->TriggerTeardown();
|
||||
pRenderer.reset();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void VtIoTests::BasicAnonymousPipeOpeningWithSignalChannelTest()
|
||||
{
|
||||
Log::Comment(L"Test using anonymous pipes for the input and adding a signal channel.");
|
||||
|
||||
@ -63,9 +63,6 @@
|
||||
<ProjectReference Include="..\..\..\renderer\atlas\atlas.vcxproj">
|
||||
<Project>{8222900C-8B6C-452A-91AC-BE95DB04B95F}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\renderer\dx\lib\dx.vcxproj">
|
||||
<Project>{48d21369-3d7b-4431-9967-24e81292cf62}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
|
||||
@ -26,9 +26,6 @@
|
||||
<ProjectReference Include="..\..\..\renderer\atlas\atlas.vcxproj">
|
||||
<Project>{8222900C-8B6C-452A-91AC-BE95DB04B95F}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\renderer\dx\lib\dx.vcxproj">
|
||||
<Project>{48d21369-3d7b-4431-9967-24e81292cf62}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\renderer\vt\lib\vt.vcxproj">
|
||||
<Project>{990f2657-8580-4828-943f-5dd657d11842}</Project>
|
||||
</ProjectReference>
|
||||
|
||||
@ -28,9 +28,6 @@
|
||||
#if TIL_FEATURE_CONHOSTATLASENGINE_ENABLED
|
||||
#include "../../renderer/atlas/AtlasEngine.h"
|
||||
#endif
|
||||
#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
|
||||
#include "../../renderer/dx/DxRenderer.hpp"
|
||||
#endif
|
||||
|
||||
#include "../inc/ServiceLocator.hpp"
|
||||
#include "../../types/inc/Viewport.hpp"
|
||||
@ -70,9 +67,6 @@ Window::~Window()
|
||||
// reducing the change for existing race conditions to turn into deadlocks.
|
||||
#ifndef NDEBUG
|
||||
delete pGdiEngine;
|
||||
#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
|
||||
delete pDxEngine;
|
||||
#endif
|
||||
#if TIL_FEATURE_CONHOSTATLASENGINE_ENABLED
|
||||
delete pAtlasEngine;
|
||||
#endif
|
||||
@ -217,31 +211,17 @@ void Window::_UpdateSystemMetrics() const
|
||||
const auto useDx = pSettings->GetUseDx();
|
||||
try
|
||||
{
|
||||
switch (useDx)
|
||||
{
|
||||
#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
|
||||
case UseDx::DxEngine:
|
||||
pDxEngine = new DxEngine();
|
||||
// TODO: MSFT:21255595 make this less gross
|
||||
// Manually set the Dx Engine to Hwnd mode. When we're trying to
|
||||
// determine the initial window size, which happens BEFORE the
|
||||
// window is created, we'll want to make sure the DX engine does
|
||||
// math in the hwnd mode, not the Composition mode.
|
||||
THROW_IF_FAILED(pDxEngine->SetHwnd(nullptr));
|
||||
g.pRender->AddRenderEngine(pDxEngine);
|
||||
break;
|
||||
#endif
|
||||
#if TIL_FEATURE_CONHOSTATLASENGINE_ENABLED
|
||||
case UseDx::AtlasEngine:
|
||||
if (useDx)
|
||||
{
|
||||
pAtlasEngine = new AtlasEngine();
|
||||
g.pRender->AddRenderEngine(pAtlasEngine);
|
||||
break;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
default:
|
||||
{
|
||||
pGdiEngine = new GdiEngine();
|
||||
g.pRender->AddRenderEngine(pGdiEngine);
|
||||
break;
|
||||
#pragma warning(suppress : 4065)
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
@ -332,20 +312,8 @@ void Window::_UpdateSystemMetrics() const
|
||||
{
|
||||
_hWnd = hWnd;
|
||||
|
||||
#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
|
||||
if (pDxEngine)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
if (SUCCEEDED(hr = pDxEngine->SetHwnd(hWnd)))
|
||||
{
|
||||
hr = pDxEngine->Enable();
|
||||
}
|
||||
status = NTSTATUS_FROM_HRESULT(hr);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#if TIL_FEATURE_CONHOSTATLASENGINE_ENABLED
|
||||
if (pAtlasEngine)
|
||||
if (pAtlasEngine)
|
||||
{
|
||||
const auto hr = pAtlasEngine->SetHwnd(hWnd);
|
||||
status = NTSTATUS_FROM_HRESULT(hr);
|
||||
|
||||
@ -24,7 +24,6 @@ namespace Microsoft::Console::Render::Atlas
|
||||
namespace Microsoft::Console::Render
|
||||
{
|
||||
using AtlasEngine = Atlas::AtlasEngine;
|
||||
class DxEngine;
|
||||
class GdiEngine;
|
||||
}
|
||||
|
||||
@ -113,9 +112,6 @@ namespace Microsoft::Console::Interactivity::Win32
|
||||
HWND _hWnd;
|
||||
|
||||
Render::GdiEngine* pGdiEngine = nullptr;
|
||||
#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED
|
||||
Render::DxEngine* pDxEngine = nullptr;
|
||||
#endif
|
||||
#if TIL_FEATURE_CONHOSTATLASENGINE_ENABLED
|
||||
Render::AtlasEngine* pAtlasEngine = nullptr;
|
||||
#endif
|
||||
|
||||
@ -60,7 +60,7 @@ const RegistrySerialization::_RegPropertyMap RegistrySerialization::s_PropertyMa
|
||||
{ _RegPropertyType::Dword, CONSOLE_REGISTRY_CURSORTYPE, SET_FIELD_AND_SIZE(_CursorType) },
|
||||
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_INTERCEPTCOPYPASTE, SET_FIELD_AND_SIZE(_fInterceptCopyPaste) },
|
||||
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_TERMINALSCROLLING, SET_FIELD_AND_SIZE(_TerminalScrolling) },
|
||||
{ _RegPropertyType::Dword, CONSOLE_REGISTRY_USEDX, SET_FIELD_AND_SIZE(_fUseDx) },
|
||||
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_USEDX, SET_FIELD_AND_SIZE(_fUseDx) },
|
||||
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_COPYCOLOR, SET_FIELD_AND_SIZE(_fCopyColor) }
|
||||
|
||||
// Special cases that are handled manually in Registry::LoadFromRegistry:
|
||||
|
||||
@ -8,7 +8,6 @@ graph TD
|
||||
Renderer["Renderer (base/renderer.cpp)\n<small>breaks the text buffer down into GDI-oriented graphics\nprimitives (#quot;change brush to color X#quot;, #quot;draw string Y#quot;, ...)</small>"]
|
||||
RenderEngineBase[/"RenderEngineBase\n(base/RenderEngineBase.cpp)\n<small>abstracts 24 LOC 👻</small>"\]
|
||||
GdiEngine["GdiEngine (gdi/...)"]
|
||||
DxEngine["DxEngine (dx/...)"]
|
||||
|
||||
subgraph AtlasEngine["AtlasEngine (atlas/...)"]
|
||||
AtlasEngine.cpp["AtlasEngine.cpp\n<small>Implements IRenderEngine text rendering API\nbreaks GDI graphics primitives down into DWRITE_GLYPH_RUNs</small>"]
|
||||
@ -24,7 +23,6 @@ graph TD
|
||||
Renderer -.-> RenderEngineBase
|
||||
%% Mermaid.js has no support for backwards arrow at the moment
|
||||
RenderEngineBase <-.->|extends| GdiEngine
|
||||
RenderEngineBase <-.->|extends| DxEngine
|
||||
Renderer ----> AtlasEngine
|
||||
AtlasEngine.cpp <--> AtlasEngine.api.cpp
|
||||
AtlasEngine.cpp <--> AtlasEngine.r.cpp
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#include "BoxDrawingEffect.h"
|
||||
|
||||
using namespace Microsoft::Console::Render;
|
||||
|
||||
BoxDrawingEffect::BoxDrawingEffect() noexcept :
|
||||
_scale{ 1.0f, 0.0f, 1.0f, 0.0f }
|
||||
{
|
||||
}
|
||||
|
||||
#pragma warning(suppress : 26434) // WRL RuntimeClassInitialize base is a no-op and we need this for MakeAndInitialize
|
||||
HRESULT BoxDrawingEffect::RuntimeClassInitialize(float verticalScale, float verticalTranslate, float horizontalScale, float horizontalTranslate) noexcept
|
||||
{
|
||||
_scale.VerticalScale = verticalScale;
|
||||
_scale.VerticalTranslation = verticalTranslate;
|
||||
_scale.HorizontalScale = horizontalScale;
|
||||
_scale.HorizontalTranslation = horizontalTranslate;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE BoxDrawingEffect::GetScale(BoxScale* scale) noexcept
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, scale);
|
||||
*scale = _scale;
|
||||
return S_OK;
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <wrl.h>
|
||||
#include <wrl/client.h>
|
||||
#include <wrl/implements.h>
|
||||
|
||||
#include "IBoxDrawingEffect_h.h"
|
||||
|
||||
namespace Microsoft::Console::Render
|
||||
{
|
||||
class BoxDrawingEffect : public ::Microsoft::WRL::RuntimeClass<::Microsoft::WRL::RuntimeClassFlags<::Microsoft::WRL::ClassicCom | ::Microsoft::WRL::InhibitFtmBase>, IBoxDrawingEffect>
|
||||
{
|
||||
public:
|
||||
BoxDrawingEffect() noexcept;
|
||||
HRESULT RuntimeClassInitialize(float verticalScale, float verticalTranslate, float horizontalScale, float horizontalTranslate) noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE GetScale(BoxScale* scale) noexcept override;
|
||||
|
||||
protected:
|
||||
private:
|
||||
BoxScale _scale;
|
||||
#ifdef UNIT_TESTING
|
||||
public:
|
||||
friend class BoxDrawingEffectTests;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,216 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dwrite.h>
|
||||
#include <d2d1.h>
|
||||
|
||||
#include <wrl.h>
|
||||
#include <wrl/client.h>
|
||||
#include <wrl/implements.h>
|
||||
|
||||
#include "BoxDrawingEffect.h"
|
||||
#include "DxFontRenderData.h"
|
||||
#include "../inc/Cluster.hpp"
|
||||
|
||||
namespace Microsoft::Console::Render
|
||||
{
|
||||
class CustomTextLayout : public ::Microsoft::WRL::RuntimeClass<::Microsoft::WRL::RuntimeClassFlags<::Microsoft::WRL::ClassicCom | ::Microsoft::WRL::InhibitFtmBase>, IDWriteTextAnalysisSource, IDWriteTextAnalysisSink>
|
||||
{
|
||||
public:
|
||||
// Based on the Windows 7 SDK sample at https://github.com/pauldotknopf/WindowsSDK7-Samples/tree/master/multimedia/DirectWrite/CustomLayout
|
||||
|
||||
CustomTextLayout(const gsl::not_null<DxFontRenderData*> fontRenderData);
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE AppendClusters(const std::span<const ::Microsoft::Console::Render::Cluster> clusters);
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE Reset() noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE GetColumns(_Out_ UINT32* columns);
|
||||
|
||||
// IDWriteTextLayout methods (but we don't actually want to implement them all, so just this one matching the existing interface)
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE Draw(_In_opt_ void* clientDrawingContext,
|
||||
_In_ IDWriteTextRenderer* renderer,
|
||||
FLOAT originX,
|
||||
FLOAT originY) noexcept;
|
||||
|
||||
// IDWriteTextAnalysisSource methods
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE GetTextAtPosition(UINT32 textPosition,
|
||||
_Outptr_result_buffer_(*textLength) WCHAR const** textString,
|
||||
_Out_ UINT32* textLength) override;
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE GetTextBeforePosition(UINT32 textPosition,
|
||||
_Outptr_result_buffer_(*textLength) WCHAR const** textString,
|
||||
_Out_ UINT32* textLength) noexcept override;
|
||||
[[nodiscard]] DWRITE_READING_DIRECTION STDMETHODCALLTYPE GetParagraphReadingDirection() noexcept override;
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE GetLocaleName(UINT32 textPosition,
|
||||
_Out_ UINT32* textLength,
|
||||
_Outptr_result_z_ const WCHAR** localeName) noexcept override;
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE GetNumberSubstitution(UINT32 textPosition,
|
||||
_Out_ UINT32* textLength,
|
||||
_COM_Outptr_ IDWriteNumberSubstitution** numberSubstitution) noexcept override;
|
||||
|
||||
// IDWriteTextAnalysisSink methods
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE SetScriptAnalysis(UINT32 textPosition,
|
||||
UINT32 textLength,
|
||||
_In_ const DWRITE_SCRIPT_ANALYSIS* scriptAnalysis) override;
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE SetLineBreakpoints(UINT32 textPosition,
|
||||
UINT32 textLength,
|
||||
_In_reads_(textLength) DWRITE_LINE_BREAKPOINT const* lineBreakpoints) override;
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE SetBidiLevel(UINT32 textPosition,
|
||||
UINT32 textLength,
|
||||
UINT8 explicitLevel,
|
||||
UINT8 resolvedLevel) override;
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE SetNumberSubstitution(UINT32 textPosition,
|
||||
UINT32 textLength,
|
||||
_In_ IDWriteNumberSubstitution* numberSubstitution) override;
|
||||
|
||||
protected:
|
||||
// A single contiguous run of characters containing the same analysis results.
|
||||
struct Run
|
||||
{
|
||||
Run() noexcept :
|
||||
textStart(),
|
||||
textLength(),
|
||||
glyphStart(),
|
||||
glyphCount(),
|
||||
bidiLevel(),
|
||||
script(),
|
||||
isNumberSubstituted(),
|
||||
isSideways(),
|
||||
fontFace{ nullptr },
|
||||
fontScale{ 1.0 },
|
||||
drawingEffect{ nullptr }
|
||||
{
|
||||
}
|
||||
|
||||
UINT32 textStart; // starting text position of this run
|
||||
UINT32 textLength; // number of contiguous code units covered
|
||||
UINT32 glyphStart; // starting glyph in the glyphs array
|
||||
UINT32 glyphCount; // number of glyphs associated with this run of text
|
||||
DWRITE_SCRIPT_ANALYSIS script;
|
||||
UINT8 bidiLevel;
|
||||
bool isNumberSubstituted;
|
||||
bool isSideways;
|
||||
::Microsoft::WRL::ComPtr<IDWriteFontFace1> fontFace;
|
||||
FLOAT fontScale;
|
||||
::Microsoft::WRL::ComPtr<IUnknown> drawingEffect;
|
||||
|
||||
inline bool ContainsTextPosition(UINT32 desiredTextPosition) const noexcept
|
||||
{
|
||||
return desiredTextPosition >= textStart && desiredTextPosition < textStart + textLength;
|
||||
}
|
||||
|
||||
inline bool operator==(UINT32 desiredTextPosition) const noexcept
|
||||
{
|
||||
// Search by text position using std::find
|
||||
return ContainsTextPosition(desiredTextPosition);
|
||||
}
|
||||
};
|
||||
|
||||
// Single text analysis run, which points to the next run.
|
||||
struct LinkedRun : Run
|
||||
{
|
||||
LinkedRun() noexcept :
|
||||
nextRunIndex(0)
|
||||
{
|
||||
}
|
||||
|
||||
UINT32 nextRunIndex; // index of next run
|
||||
};
|
||||
|
||||
[[nodiscard]] LinkedRun& _FetchNextRun(UINT32& textLength);
|
||||
[[nodiscard]] LinkedRun& _GetCurrentRun();
|
||||
void _SetCurrentRun(const UINT32 textPosition);
|
||||
void _SplitCurrentRun(const UINT32 splitPosition);
|
||||
void _OrderRuns();
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE _AnalyzeFontFallback(IDWriteTextAnalysisSource* const source, UINT32 textPosition, UINT32 textLength);
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE _SetMappedFontFace(UINT32 textPosition, UINT32 textLength, const ::Microsoft::WRL::ComPtr<IDWriteFontFace>& fontFace, FLOAT const scale);
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE _AnalyzeBoxDrawing(const gsl::not_null<IDWriteTextAnalysisSource*> source, UINT32 textPosition, UINT32 textLength);
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE _SetBoxEffect(UINT32 textPosition, UINT32 textLength);
|
||||
|
||||
[[nodiscard]] HRESULT _AnalyzeTextComplexity() noexcept;
|
||||
[[nodiscard]] HRESULT _AnalyzeRuns() noexcept;
|
||||
[[nodiscard]] HRESULT _ShapeGlyphRuns() noexcept;
|
||||
[[nodiscard]] HRESULT _ShapeGlyphRun(const UINT32 runIndex, UINT32& glyphStart) noexcept;
|
||||
[[nodiscard]] HRESULT _CorrectGlyphRuns() noexcept;
|
||||
[[nodiscard]] HRESULT _CorrectGlyphRun(const UINT32 runIndex) noexcept;
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE _CorrectBoxDrawing() noexcept;
|
||||
[[nodiscard]] HRESULT _DrawGlyphRuns(_In_opt_ void* clientDrawingContext,
|
||||
IDWriteTextRenderer* renderer,
|
||||
const D2D_POINT_2F origin) noexcept;
|
||||
[[nodiscard]] HRESULT _DrawGlyphRun(_In_opt_ void* clientDrawingContext,
|
||||
gsl::not_null<IDWriteTextRenderer*> renderer,
|
||||
D2D_POINT_2F& mutableOrigin,
|
||||
const Run& run) noexcept;
|
||||
|
||||
[[nodiscard]] static constexpr UINT32 _EstimateGlyphCount(const UINT32 textLength) noexcept;
|
||||
|
||||
private:
|
||||
// DirectWrite font render data
|
||||
DxFontRenderData* _fontRenderData;
|
||||
|
||||
// DirectWrite text formats
|
||||
IDWriteTextFormat* _formatInUse;
|
||||
|
||||
// DirectWrite font faces
|
||||
IDWriteFontFace1* _fontInUse;
|
||||
|
||||
// The text we're analyzing and processing into a layout
|
||||
std::wstring _text;
|
||||
std::vector<UINT16> _textClusterColumns;
|
||||
size_t _width;
|
||||
|
||||
// Properties of the text that might be relevant.
|
||||
std::wstring _localeName;
|
||||
::Microsoft::WRL::ComPtr<IDWriteNumberSubstitution> _numberSubstitution;
|
||||
DWRITE_READING_DIRECTION _readingDirection;
|
||||
|
||||
// Text analysis results
|
||||
std::vector<LinkedRun> _runs;
|
||||
std::vector<DWRITE_LINE_BREAKPOINT> _breakpoints;
|
||||
|
||||
// Text analysis interim status variable (to assist the Analyzer Sink in operations involving _runs)
|
||||
UINT32 _runIndex;
|
||||
|
||||
// Glyph shaping results
|
||||
|
||||
// Whether the entire text is determined to be simple and does not require full script shaping.
|
||||
bool _isEntireTextSimple;
|
||||
|
||||
std::vector<DWRITE_GLYPH_OFFSET> _glyphOffsets;
|
||||
|
||||
// Clusters are complicated. They're in respect to each individual run.
|
||||
// The offsets listed here are in respect to the _text string, but from the beginning index of
|
||||
// each run.
|
||||
// That means if we have two runs, we will see 0 1 2 3 4 0 1 2 3 4 5 6 7... in this clusters count.
|
||||
std::vector<UINT16> _glyphClusters;
|
||||
|
||||
// This appears to be the index of the glyph inside each font.
|
||||
std::vector<UINT16> _glyphIndices;
|
||||
|
||||
// This is for calculating glyph advances when the entire text is simple.
|
||||
std::vector<INT32> _glyphDesignUnitAdvances;
|
||||
|
||||
std::vector<float> _glyphAdvances;
|
||||
|
||||
struct ScaleCorrection
|
||||
{
|
||||
UINT32 textIndex;
|
||||
UINT32 textLength;
|
||||
float scale;
|
||||
};
|
||||
|
||||
// These are used to further break the runs apart and adjust the font size so glyphs fit inside the cells.
|
||||
std::vector<ScaleCorrection> _glyphScaleCorrections;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
public:
|
||||
CustomTextLayout() = default;
|
||||
|
||||
friend class CustomTextLayoutTests;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
@ -1,979 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#include "CustomTextRenderer.h"
|
||||
|
||||
#include "../../inc/DefaultSettings.h"
|
||||
|
||||
#include <wrl.h>
|
||||
#include <wrl/client.h>
|
||||
#include <VersionHelpers.h>
|
||||
|
||||
using namespace Microsoft::Console::Render;
|
||||
|
||||
#pragma region IDWritePixelSnapping methods
|
||||
// Routine Description:
|
||||
// - Implementation of IDWritePixelSnapping::IsPixelSnappingDisabled
|
||||
// - Determines if we're allowed to snap text to pixels for this particular drawing context
|
||||
// Arguments:
|
||||
// - clientDrawingContext - Pointer to structure of information required to draw
|
||||
// - isDisabled - TRUE if we do not snap to nearest pixels. FALSE otherwise.
|
||||
// Return Value:
|
||||
// - S_OK
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::IsPixelSnappingDisabled(void* /*clientDrawingContext*/,
|
||||
_Out_ BOOL* isDisabled) noexcept
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, isDisabled);
|
||||
|
||||
*isDisabled = false;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Implementation of IDWritePixelSnapping::GetPixelsPerDip
|
||||
// - Retrieves the number of real monitor pixels to use per device-independent-pixel (DIP)
|
||||
// - DIPs are used by DirectX all the way until the final drawing surface so things are only
|
||||
// scaled at the very end and the complexity can be abstracted.
|
||||
// Arguments:
|
||||
// - clientDrawingContext - Pointer to structure of information required to draw
|
||||
// - pixelsPerDip - The number of pixels per DIP. 96 is standard DPI.
|
||||
// Return Value:
|
||||
// - S_OK
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::GetPixelsPerDip(void* clientDrawingContext,
|
||||
_Out_ FLOAT* pixelsPerDip) noexcept
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, pixelsPerDip);
|
||||
|
||||
const DrawingContext* drawingContext = static_cast<DrawingContext*>(clientDrawingContext);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, drawingContext);
|
||||
|
||||
float dpiX, dpiY;
|
||||
drawingContext->renderTarget->GetDpi(&dpiX, &dpiY);
|
||||
*pixelsPerDip = dpiX / USER_DEFAULT_SCREEN_DPI;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Implementation of IDWritePixelSnapping::GetCurrentTransform
|
||||
// - Retrieves the matrix transform to be used while laying pixels onto the
|
||||
// drawing context
|
||||
// Arguments:
|
||||
// - clientDrawingContext - Pointer to structure of information required to draw
|
||||
// - transform - The matrix transform to use to adapt DIP representations into real monitor coordinates.
|
||||
// Return Value:
|
||||
// - S_OK
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::GetCurrentTransform(void* clientDrawingContext,
|
||||
DWRITE_MATRIX* transform) noexcept
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, transform);
|
||||
|
||||
const DrawingContext* drawingContext = static_cast<DrawingContext*>(clientDrawingContext);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, drawingContext);
|
||||
|
||||
// Retrieve as D2D1 matrix then copy into DWRITE matrix.
|
||||
D2D1_MATRIX_3X2_F d2d1Matrix{ 0 };
|
||||
drawingContext->renderTarget->GetTransform(&d2d1Matrix);
|
||||
|
||||
transform->dx = d2d1Matrix.dx;
|
||||
transform->dy = d2d1Matrix.dy;
|
||||
transform->m11 = d2d1Matrix.m11;
|
||||
transform->m12 = d2d1Matrix.m12;
|
||||
transform->m21 = d2d1Matrix.m21;
|
||||
transform->m22 = d2d1Matrix.m22;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IDWriteTextRenderer methods
|
||||
// Routine Description:
|
||||
// - Implementation of IDWriteTextRenderer::DrawUnderline
|
||||
// - Directs us to draw an underline on the given context at the given position.
|
||||
// Arguments:
|
||||
// - clientDrawingContext - Pointer to structure of information required to draw
|
||||
// - baselineOriginX - The text baseline position's X coordinate
|
||||
// - baselineOriginY - The text baseline position's Y coordinate
|
||||
// - The baseline is generally not the top nor the bottom of the "cell" that
|
||||
// text is drawn into. It's usually somewhere "in the middle" and depends on the
|
||||
// font and the glyphs. It can be calculated during layout and analysis in respect
|
||||
// to the given font and glyphs.
|
||||
// - underline - The properties of the underline that we should use for drawing
|
||||
// - clientDrawingEffect - any special effect to pass along for rendering
|
||||
// Return Value:
|
||||
// - S_OK
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::DrawUnderline(void* clientDrawingContext,
|
||||
FLOAT baselineOriginX,
|
||||
FLOAT baselineOriginY,
|
||||
_In_ const DWRITE_UNDERLINE* underline,
|
||||
IUnknown* clientDrawingEffect) noexcept
|
||||
{
|
||||
return _FillRectangle(clientDrawingContext,
|
||||
clientDrawingEffect,
|
||||
baselineOriginX,
|
||||
baselineOriginY + underline->offset,
|
||||
underline->width,
|
||||
underline->thickness,
|
||||
underline->readingDirection,
|
||||
underline->flowDirection);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Implementation of IDWriteTextRenderer::DrawStrikethrough
|
||||
// - Directs us to draw a strikethrough on the given context at the given position.
|
||||
// Arguments:
|
||||
// - clientDrawingContext - Pointer to structure of information required to draw
|
||||
// - baselineOriginX - The text baseline position's X coordinate
|
||||
// - baselineOriginY - The text baseline position's Y coordinate
|
||||
// - The baseline is generally not the top nor the bottom of the "cell" that
|
||||
// text is drawn into. It's usually somewhere "in the middle" and depends on the
|
||||
// font and the glyphs. It can be calculated during layout and analysis in respect
|
||||
// to the given font and glyphs.
|
||||
// - strikethrough - The properties of the strikethrough that we should use for drawing
|
||||
// - clientDrawingEffect - any special effect to pass along for rendering
|
||||
// Return Value:
|
||||
// - S_OK
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::DrawStrikethrough(void* clientDrawingContext,
|
||||
FLOAT baselineOriginX,
|
||||
FLOAT baselineOriginY,
|
||||
_In_ const DWRITE_STRIKETHROUGH* strikethrough,
|
||||
IUnknown* clientDrawingEffect) noexcept
|
||||
{
|
||||
return _FillRectangle(clientDrawingContext,
|
||||
clientDrawingEffect,
|
||||
baselineOriginX,
|
||||
baselineOriginY + strikethrough->offset,
|
||||
strikethrough->width,
|
||||
strikethrough->thickness,
|
||||
strikethrough->readingDirection,
|
||||
strikethrough->flowDirection);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Helper method to draw a line through our text.
|
||||
// Arguments:
|
||||
// - clientDrawingContext - Pointer to structure of information required to draw
|
||||
// - clientDrawingEffect - any special effect passed along for rendering
|
||||
// - x - The left coordinate of the rectangle
|
||||
// - y - The top coordinate of the rectangle
|
||||
// - width - The width of the rectangle (from X to the right)
|
||||
// - height - The height of the rectangle (from Y down)
|
||||
// - readingDirection - textual reading information that could affect the rectangle
|
||||
// - flowDirection - textual flow information that could affect the rectangle
|
||||
// Return Value:
|
||||
// - S_OK
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
|
||||
IUnknown* clientDrawingEffect,
|
||||
float x,
|
||||
float y,
|
||||
float width,
|
||||
float thickness,
|
||||
DWRITE_READING_DIRECTION /*readingDirection*/,
|
||||
DWRITE_FLOW_DIRECTION /*flowDirection*/) noexcept
|
||||
{
|
||||
auto drawingContext = static_cast<DrawingContext*>(clientDrawingContext);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, drawingContext);
|
||||
|
||||
// Get brush
|
||||
ID2D1Brush* brush = drawingContext->foregroundBrush;
|
||||
|
||||
if (clientDrawingEffect != nullptr)
|
||||
{
|
||||
brush = static_cast<ID2D1Brush*>(clientDrawingEffect);
|
||||
}
|
||||
|
||||
const auto rect = D2D1::RectF(x, y, x + width, y + thickness);
|
||||
drawingContext->renderTarget->FillRectangle(&rect, brush);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Implementation of IDWriteTextRenderer::DrawInlineObject
|
||||
// - Passes drawing control from the outer layout down into the context of an embedded object
|
||||
// which can have its own drawing layout and renderer properties at a given position
|
||||
// Arguments:
|
||||
// - clientDrawingContext - Pointer to structure of information required to draw
|
||||
// - originX - The left coordinate of the draw position
|
||||
// - originY - The top coordinate of the draw position
|
||||
// - inlineObject - The object to draw at the position
|
||||
// - isSideways - Should be drawn vertically instead of horizontally
|
||||
// - isRightToLeft - Should be drawn RTL (or bottom to top) instead of the default way
|
||||
// - clientDrawingEffect - any special effect passed along for rendering
|
||||
// Return Value:
|
||||
// - S_OK or appropriate error from the delegated inline object's draw call
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::DrawInlineObject(void* clientDrawingContext,
|
||||
FLOAT originX,
|
||||
FLOAT originY,
|
||||
IDWriteInlineObject* inlineObject,
|
||||
BOOL isSideways,
|
||||
BOOL isRightToLeft,
|
||||
IUnknown* clientDrawingEffect) noexcept
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, inlineObject);
|
||||
|
||||
return inlineObject->Draw(clientDrawingContext,
|
||||
this,
|
||||
originX,
|
||||
originY,
|
||||
isSideways,
|
||||
isRightToLeft,
|
||||
clientDrawingEffect);
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Attempt to draw the cursor. If the cursor isn't visible or on, this
|
||||
// function will do nothing. If the cursor isn't within the bounds of the
|
||||
// current run of text, then this function will do nothing.
|
||||
// - This function will get called twice during a run, once before the text is
|
||||
// drawn (underneath the text), and again after the text is drawn (above the
|
||||
// text). Depending on if the cursor wants to be drawn above or below the
|
||||
// text, this function will do nothing for the first/second pass
|
||||
// (respectively).
|
||||
// Arguments:
|
||||
// - d2dContext - Pointer to the current D2D drawing context
|
||||
// - textRunBounds - The bounds of the current run of text.
|
||||
// - drawingContext - Pointer to structure of information required to draw
|
||||
// - firstPass - true if we're being called before the text is drawn, false afterwards.
|
||||
// Return Value:
|
||||
// - S_FALSE if we did nothing, S_OK if we successfully painted, otherwise an appropriate HRESULT
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::DrawCursor(gsl::not_null<ID2D1DeviceContext*> d2dContext,
|
||||
D2D1_RECT_F textRunBounds,
|
||||
const DrawingContext& drawingContext,
|
||||
const bool firstPass)
|
||||
try
|
||||
{
|
||||
if (!drawingContext.cursorInfo.has_value())
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
const auto& options = drawingContext.cursorInfo.value();
|
||||
|
||||
// if the cursor is off, do nothing - it should not be visible.
|
||||
if (!options.isOn)
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
const auto fInvert = !options.fUseColor;
|
||||
// The normal, colored FullBox and legacy cursors are drawn in the first pass
|
||||
// so they go behind the text.
|
||||
// Inverted cursors are drawn in two passes.
|
||||
// All other cursors are drawn in the second pass only.
|
||||
if (!fInvert)
|
||||
{
|
||||
if (firstPass != (options.cursorType == CursorType::FullBox))
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO GH#6338: Add support for `"cursorTextColor": null` for letting the
|
||||
// cursor draw on top again.
|
||||
|
||||
// Create rectangular block representing where the cursor can fill.
|
||||
D2D1_RECT_F rect;
|
||||
rect.left = options.coordCursor.x * drawingContext.cellSize.width;
|
||||
rect.top = options.coordCursor.y * drawingContext.cellSize.height;
|
||||
rect.right = rect.left + drawingContext.cellSize.width;
|
||||
rect.bottom = rect.top + drawingContext.cellSize.height;
|
||||
|
||||
// If we're double-width, make it one extra glyph wider
|
||||
if (options.fIsDoubleWidth)
|
||||
{
|
||||
rect.right += drawingContext.cellSize.width;
|
||||
}
|
||||
|
||||
// If the cursor isn't within the bounds of this current run of text, do nothing.
|
||||
if (rect.top > textRunBounds.bottom ||
|
||||
rect.bottom <= textRunBounds.top ||
|
||||
rect.left > textRunBounds.right ||
|
||||
rect.right <= textRunBounds.left)
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
auto paintType = CursorPaintType::Fill;
|
||||
switch (options.cursorType)
|
||||
{
|
||||
case CursorType::Legacy:
|
||||
{
|
||||
// Enforce min/max cursor height
|
||||
auto ulHeight = std::clamp(options.ulCursorHeightPercent, MinCursorHeightPercent, MaxCursorHeightPercent);
|
||||
ulHeight = gsl::narrow_cast<ULONG>(drawingContext.cellSize.height * ulHeight) / 100;
|
||||
ulHeight = std::max(ulHeight, MinCursorHeightPixels); // No smaller than 1px
|
||||
|
||||
rect.top = rect.bottom - ulHeight;
|
||||
break;
|
||||
}
|
||||
case CursorType::VerticalBar:
|
||||
{
|
||||
// It can't be wider than one cell or we'll have problems in invalidation, so restrict here.
|
||||
// It's either the left + the proposed width from the ease of access setting, or
|
||||
// it's the right edge of the block cursor as a maximum.
|
||||
rect.right = std::min(rect.right, rect.left + options.cursorPixelWidth);
|
||||
break;
|
||||
}
|
||||
case CursorType::Underscore:
|
||||
{
|
||||
rect.top = rect.bottom - 1;
|
||||
break;
|
||||
}
|
||||
case CursorType::DoubleUnderscore:
|
||||
{
|
||||
// Use rect for lower line.
|
||||
rect.top = rect.bottom - 1;
|
||||
break;
|
||||
}
|
||||
case CursorType::EmptyBox:
|
||||
{
|
||||
paintType = CursorPaintType::Outline;
|
||||
break;
|
||||
}
|
||||
case CursorType::FullBox:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
// **DRAW** PHASE
|
||||
Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> brush;
|
||||
Microsoft::WRL::ComPtr<ID2D1Image> originalTarget;
|
||||
Microsoft::WRL::ComPtr<ID2D1CommandList> commandList;
|
||||
D2D1::Matrix3x2F originalTransform;
|
||||
if (!fInvert)
|
||||
{
|
||||
// Make sure to make the cursor opaque
|
||||
RETURN_IF_FAILED(d2dContext->CreateSolidColorBrush(til::color{ options.cursorColor }, &brush));
|
||||
}
|
||||
else
|
||||
{
|
||||
// CURSOR INVERSION
|
||||
// We're trying to invert the cursor and the character underneath it without redrawing the text (as
|
||||
// doing so would break up the run if it were part of a ligature). To do that, we're going to try
|
||||
// to invert the content of the screen where the cursor would have been.
|
||||
//
|
||||
// This renderer, however, supports transparency. In fact, in its default configuration it will not
|
||||
// have a background at all (it delegates background handling to somebody else.) You can't invert what
|
||||
// isn't there.
|
||||
//
|
||||
// To properly invert the cursor in such a configuration, then, we have to play some tricks. Examples
|
||||
// are given below for two cursor types, but this applies to all of them.
|
||||
//
|
||||
// First, we'll draw a "backplate" in the user's requested background color (with the alpha channel
|
||||
// set to 0xFF). (firstPass == true)
|
||||
//
|
||||
// EMPTY BOX FILLED BOX
|
||||
// ===== =====
|
||||
// = = =====
|
||||
// = = =====
|
||||
// = = =====
|
||||
// ===== =====
|
||||
//
|
||||
// Then, outside of DrawCursor, the glyph is drawn:
|
||||
//
|
||||
// EMPTY BOX FILLED BOX
|
||||
// ==A== ==A==
|
||||
// =A A= =A=A=
|
||||
// AAAAA AAAAA
|
||||
// A A A===A
|
||||
// A===A A===A
|
||||
//
|
||||
// Last, we'll draw the cursor again in all white and use that as the *mask* for inverting the already-
|
||||
// drawn pixels. (firstPass == false) (# = mask, a = inverted A)
|
||||
//
|
||||
// EMPTY BOX FILLED BOX
|
||||
// ##a## ##a##
|
||||
// #A A# #a#a#
|
||||
// aAAAa aaaaa
|
||||
// a a a###a
|
||||
// a###a a###a
|
||||
if (firstPass)
|
||||
{
|
||||
// Draw a backplate behind the cursor in the *background* color so that we can invert it later.
|
||||
// Make sure the cursor is always readable (see gh-3647)
|
||||
const til::color color{ til::color{ drawingContext.backgroundBrush->GetColor() } ^ RGB(63, 63, 63) };
|
||||
RETURN_IF_FAILED(d2dContext->CreateSolidColorBrush(color.with_alpha(255),
|
||||
&brush));
|
||||
}
|
||||
else
|
||||
{
|
||||
// When we're drawing an inverted cursor on the second pass (foreground), we want to draw it into a
|
||||
// command list, which we will then draw down with MASK_INVERT. We'll draw it in white,
|
||||
// which will ensure that every component is masked.
|
||||
RETURN_IF_FAILED(d2dContext->CreateCommandList(&commandList));
|
||||
d2dContext->GetTarget(&originalTarget);
|
||||
d2dContext->SetTarget(commandList.Get());
|
||||
// We use an identity transform here to avoid the active transform being applied twice.
|
||||
d2dContext->GetTransform(&originalTransform);
|
||||
d2dContext->SetTransform(D2D1::Matrix3x2F::Identity());
|
||||
RETURN_IF_FAILED(d2dContext->CreateSolidColorBrush(COLOR_WHITE, &brush));
|
||||
}
|
||||
}
|
||||
|
||||
switch (paintType)
|
||||
{
|
||||
case CursorPaintType::Fill:
|
||||
{
|
||||
d2dContext->FillRectangle(rect, brush.Get());
|
||||
break;
|
||||
}
|
||||
case CursorPaintType::Outline:
|
||||
{
|
||||
// DrawRectangle in straddles physical pixels in an attempt to draw a line
|
||||
// between them. To avoid this, bump the rectangle around by half the stroke width.
|
||||
rect.top += 0.5f;
|
||||
rect.left += 0.5f;
|
||||
rect.bottom -= 0.5f;
|
||||
rect.right -= 0.5f;
|
||||
|
||||
d2dContext->DrawRectangle(rect, brush.Get());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
if (options.cursorType == CursorType::DoubleUnderscore)
|
||||
{
|
||||
// Draw upper line directly.
|
||||
auto upperLine = rect;
|
||||
upperLine.top -= 2;
|
||||
upperLine.bottom -= 2;
|
||||
d2dContext->FillRectangle(upperLine, brush.Get());
|
||||
}
|
||||
|
||||
if (commandList)
|
||||
{
|
||||
// We drew the entire cursor in a command list
|
||||
// so now we draw that command list using MASK_INVERT over the existing image
|
||||
RETURN_IF_FAILED(commandList->Close());
|
||||
d2dContext->SetTarget(originalTarget.Get());
|
||||
d2dContext->SetTransform(originalTransform);
|
||||
d2dContext->DrawImage(commandList.Get(), D2D1_INTERPOLATION_MODE_LINEAR, D2D1_COMPOSITE_MODE_MASK_INVERT);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN()
|
||||
|
||||
// Routine Description:
|
||||
// - Implementation of IDWriteTextRenderer::DrawInlineObject
|
||||
// - Passes drawing control from the outer layout down into the context of an embedded object
|
||||
// which can have its own drawing layout and renderer properties at a given position
|
||||
// Arguments:
|
||||
// - clientDrawingContext - Pointer to structure of information required to draw
|
||||
// - baselineOriginX - The text baseline position's X coordinate
|
||||
// - baselineOriginY - The text baseline position's Y coordinate
|
||||
// - The baseline is generally not the top nor the bottom of the "cell" that
|
||||
// text is drawn into. It's usually somewhere "in the middle" and depends on the
|
||||
// font and the glyphs. It can be calculated during layout and analysis in respect
|
||||
// to the given font and glyphs.
|
||||
// - measuringMode - The mode to measure glyphs in the DirectWrite context
|
||||
// - glyphRun - Information on the glyphs
|
||||
// - glyphRunDescription - Further metadata about the glyphs used while drawing
|
||||
// - clientDrawingEffect - any special effect passed along for rendering
|
||||
// Return Value:
|
||||
// - S_OK, GSL/WIL/STL error, or appropriate DirectX/Direct2D/DirectWrite based error while drawing.
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::DrawGlyphRun(
|
||||
void* clientDrawingContext,
|
||||
FLOAT baselineOriginX,
|
||||
FLOAT baselineOriginY,
|
||||
DWRITE_MEASURING_MODE measuringMode,
|
||||
const DWRITE_GLYPH_RUN* glyphRun,
|
||||
const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription,
|
||||
IUnknown* clientDrawingEffect)
|
||||
{
|
||||
// Color glyph rendering sourced from https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/DWriteColorGlyph
|
||||
|
||||
#pragma warning(suppress : 26429) // Symbol 'drawingContext' is never tested for nullness, it can be marked as not_null (f.23).
|
||||
auto drawingContext = static_cast<DrawingContext*>(clientDrawingContext);
|
||||
|
||||
// Since we've delegated the drawing of the background of the text into this function, the origin passed in isn't actually the baseline.
|
||||
// It's the top left corner. Save that off first.
|
||||
const auto origin = D2D1::Point2F(baselineOriginX, baselineOriginY);
|
||||
|
||||
// Then make a copy for the baseline origin (which is part way down the left side of the text, not the top or bottom).
|
||||
// We'll use this baseline Origin for drawing the actual text.
|
||||
const D2D1_POINT_2F baselineOrigin{ origin.x, origin.y + drawingContext->spacing.baseline };
|
||||
|
||||
::Microsoft::WRL::ComPtr<ID2D1DeviceContext> d2dContext;
|
||||
RETURN_IF_FAILED(drawingContext->renderTarget->QueryInterface(d2dContext.GetAddressOf()));
|
||||
|
||||
// Determine clip rectangle
|
||||
D2D1_RECT_F clipRect;
|
||||
clipRect.top = origin.y + drawingContext->topClipOffset;
|
||||
clipRect.bottom = origin.y + drawingContext->cellSize.height - drawingContext->bottomClipOffset;
|
||||
clipRect.left = 0;
|
||||
clipRect.right = FLT_MAX;
|
||||
|
||||
// If we already have a clip rectangle, check if it different than the previous one.
|
||||
if (_clipRect.has_value())
|
||||
{
|
||||
const auto storedVal = _clipRect.value();
|
||||
// If it is different, pop off the old one and push the new one on.
|
||||
if (storedVal.top != clipRect.top || storedVal.bottom != clipRect.bottom ||
|
||||
storedVal.left != clipRect.left || storedVal.right != clipRect.right)
|
||||
{
|
||||
d2dContext->PopAxisAlignedClip();
|
||||
|
||||
// Clip all drawing in this glyph run to where we expect.
|
||||
// We need the AntialiasMode here to be Aliased to ensure
|
||||
// that background boxes line up with each other and don't leave behind
|
||||
// stray colors.
|
||||
// See GH#3626 for more details.
|
||||
d2dContext->PushAxisAlignedClip(clipRect, D2D1_ANTIALIAS_MODE_ALIASED);
|
||||
_clipRect = clipRect;
|
||||
}
|
||||
}
|
||||
// If we have no clip rectangle, it's easy. Push it on and go.
|
||||
else
|
||||
{
|
||||
// See above for aliased flag explanation.
|
||||
d2dContext->PushAxisAlignedClip(clipRect, D2D1_ANTIALIAS_MODE_ALIASED);
|
||||
_clipRect = clipRect;
|
||||
}
|
||||
|
||||
// Draw the background
|
||||
// The rectangle needs to be deduced based on the origin and the BidiDirection
|
||||
const auto advancesSpan = std::span{ glyphRun->glyphAdvances, glyphRun->glyphCount };
|
||||
const auto totalSpan = std::accumulate(advancesSpan.begin(), advancesSpan.end(), 0.0f);
|
||||
|
||||
D2D1_RECT_F rect;
|
||||
rect.top = origin.y;
|
||||
rect.bottom = rect.top + drawingContext->cellSize.height;
|
||||
rect.left = origin.x;
|
||||
// Check for RTL, if it is, move rect.left to the left from the baseline
|
||||
if (WI_IsFlagSet(glyphRun->bidiLevel, 1))
|
||||
{
|
||||
rect.left -= totalSpan;
|
||||
}
|
||||
rect.right = rect.left + totalSpan;
|
||||
|
||||
d2dContext->FillRectangle(rect, drawingContext->backgroundBrush);
|
||||
|
||||
RETURN_IF_FAILED(DrawCursor(d2dContext.Get(), rect, *drawingContext, true));
|
||||
|
||||
// GH#5098: If we're rendering with cleartype text, we need to always render
|
||||
// onto an opaque background. If our background _isn't_ opaque, then we need
|
||||
// to use grayscale AA for this run of text.
|
||||
//
|
||||
// We can force grayscale AA for just this run of text by pushing a new
|
||||
// layer onto the d2d context. We'll only need to do this for cleartype
|
||||
// text, when our eventual background isn't actually opaque. See
|
||||
// DxEngine::PaintBufferLine and DxEngine::UpdateDrawingBrushes for more
|
||||
// details.
|
||||
//
|
||||
// DANGER: Layers slow us down. Only do this in the specific case where
|
||||
// someone has chosen the slower ClearType antialiasing (versus the faster
|
||||
// grayscale antialiasing).
|
||||
|
||||
// First, create the scope_exit to pop the layer. If we don't need the
|
||||
// layer, we'll just gracefully release it.
|
||||
auto popLayer = wil::scope_exit([&d2dContext]() noexcept {
|
||||
d2dContext->PopLayer();
|
||||
});
|
||||
|
||||
if (drawingContext->forceGrayscaleAA)
|
||||
{
|
||||
// Mysteriously, D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE actually
|
||||
// gets us the behavior we want, which is grayscale.
|
||||
d2dContext->PushLayer(D2D1::LayerParameters(rect,
|
||||
nullptr,
|
||||
D2D1_ANTIALIAS_MODE_ALIASED,
|
||||
D2D1::IdentityMatrix(),
|
||||
1.0,
|
||||
nullptr,
|
||||
D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE),
|
||||
nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
popLayer.release();
|
||||
}
|
||||
// Now go onto drawing the text.
|
||||
|
||||
// First check if we want a color font and try to extract color emoji first.
|
||||
// Color emoji are only available on Windows 10+
|
||||
static const auto s_isWindows10OrGreater = IsWindows10OrGreater();
|
||||
if (WI_IsFlagSet(drawingContext->options, D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT) && s_isWindows10OrGreater)
|
||||
{
|
||||
::Microsoft::WRL::ComPtr<ID2D1DeviceContext4> d2dContext4;
|
||||
RETURN_IF_FAILED(d2dContext.As(&d2dContext4));
|
||||
|
||||
::Microsoft::WRL::ComPtr<IDWriteFactory4> dwriteFactory4;
|
||||
RETURN_IF_FAILED(drawingContext->dwriteFactory->QueryInterface(dwriteFactory4.GetAddressOf()));
|
||||
|
||||
// The list of glyph image formats this renderer is prepared to support.
|
||||
const auto supportedFormats =
|
||||
DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE |
|
||||
DWRITE_GLYPH_IMAGE_FORMATS_CFF |
|
||||
DWRITE_GLYPH_IMAGE_FORMATS_COLR |
|
||||
DWRITE_GLYPH_IMAGE_FORMATS_SVG |
|
||||
DWRITE_GLYPH_IMAGE_FORMATS_PNG |
|
||||
DWRITE_GLYPH_IMAGE_FORMATS_JPEG |
|
||||
DWRITE_GLYPH_IMAGE_FORMATS_TIFF |
|
||||
DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8;
|
||||
|
||||
// Determine whether there are any color glyph runs within glyphRun. If
|
||||
// there are, glyphRunEnumerator can be used to iterate through them.
|
||||
::Microsoft::WRL::ComPtr<IDWriteColorGlyphRunEnumerator1> glyphRunEnumerator;
|
||||
const auto hr = dwriteFactory4->TranslateColorGlyphRun(baselineOrigin,
|
||||
glyphRun,
|
||||
glyphRunDescription,
|
||||
supportedFormats,
|
||||
measuringMode,
|
||||
nullptr,
|
||||
0,
|
||||
&glyphRunEnumerator);
|
||||
|
||||
// If the analysis found no color glyphs in the run, just draw normally.
|
||||
if (hr == DWRITE_E_NOCOLOR)
|
||||
{
|
||||
RETURN_IF_FAILED(_DrawBasicGlyphRun(drawingContext,
|
||||
baselineOrigin,
|
||||
measuringMode,
|
||||
glyphRun,
|
||||
glyphRunDescription,
|
||||
drawingContext->foregroundBrush,
|
||||
clientDrawingEffect));
|
||||
}
|
||||
else
|
||||
{
|
||||
RETURN_IF_FAILED(hr);
|
||||
|
||||
::Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> tempBrush;
|
||||
|
||||
// Complex case: the run has one or more color runs within it. Iterate
|
||||
// over the sub-runs and draw them, depending on their format.
|
||||
for (;;)
|
||||
{
|
||||
BOOL haveRun;
|
||||
RETURN_IF_FAILED(glyphRunEnumerator->MoveNext(&haveRun));
|
||||
if (!haveRun)
|
||||
break;
|
||||
|
||||
DWRITE_COLOR_GLYPH_RUN1 const* colorRun;
|
||||
RETURN_IF_FAILED(glyphRunEnumerator->GetCurrentRun(&colorRun));
|
||||
|
||||
const auto currentBaselineOrigin = D2D1::Point2F(colorRun->baselineOriginX, colorRun->baselineOriginY);
|
||||
|
||||
switch (colorRun->glyphImageFormat)
|
||||
{
|
||||
case DWRITE_GLYPH_IMAGE_FORMATS_PNG:
|
||||
case DWRITE_GLYPH_IMAGE_FORMATS_JPEG:
|
||||
case DWRITE_GLYPH_IMAGE_FORMATS_TIFF:
|
||||
case DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8:
|
||||
{
|
||||
// This run is bitmap glyphs. Use Direct2D to draw them.
|
||||
d2dContext4->DrawColorBitmapGlyphRun(colorRun->glyphImageFormat,
|
||||
currentBaselineOrigin,
|
||||
&colorRun->glyphRun,
|
||||
measuringMode);
|
||||
}
|
||||
break;
|
||||
|
||||
case DWRITE_GLYPH_IMAGE_FORMATS_SVG:
|
||||
{
|
||||
// This run is SVG glyphs. Use Direct2D to draw them.
|
||||
d2dContext4->DrawSvgGlyphRun(currentBaselineOrigin,
|
||||
&colorRun->glyphRun,
|
||||
drawingContext->foregroundBrush,
|
||||
nullptr, // svgGlyphStyle
|
||||
0, // colorPaletteIndex
|
||||
measuringMode);
|
||||
}
|
||||
break;
|
||||
|
||||
case DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE:
|
||||
case DWRITE_GLYPH_IMAGE_FORMATS_CFF:
|
||||
case DWRITE_GLYPH_IMAGE_FORMATS_COLR:
|
||||
default:
|
||||
{
|
||||
// This run is solid-color outlines, either from non-color
|
||||
// glyphs or from COLR glyph layers. Use Direct2D to draw them.
|
||||
|
||||
ID2D1Brush* layerBrush{ nullptr };
|
||||
// The rule is "if 0xffff, use current brush." See:
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/dwrite_2/ns-dwrite_2-dwrite_color_glyph_run
|
||||
if (colorRun->paletteIndex == 0xFFFF)
|
||||
{
|
||||
// This run uses the current text color.
|
||||
layerBrush = drawingContext->foregroundBrush;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!tempBrush)
|
||||
{
|
||||
RETURN_IF_FAILED(d2dContext4->CreateSolidColorBrush(colorRun->runColor, &tempBrush));
|
||||
}
|
||||
else
|
||||
{
|
||||
// This run specifies its own color.
|
||||
tempBrush->SetColor(colorRun->runColor);
|
||||
}
|
||||
layerBrush = tempBrush.Get();
|
||||
}
|
||||
|
||||
// Draw the run with the selected color.
|
||||
RETURN_IF_FAILED(_DrawBasicGlyphRun(drawingContext,
|
||||
currentBaselineOrigin,
|
||||
measuringMode,
|
||||
&colorRun->glyphRun,
|
||||
colorRun->glyphRunDescription,
|
||||
layerBrush,
|
||||
clientDrawingEffect));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Simple case: the run has no color glyphs. Draw the main glyph run
|
||||
// using the current text color.
|
||||
RETURN_IF_FAILED(_DrawBasicGlyphRun(drawingContext,
|
||||
baselineOrigin,
|
||||
measuringMode,
|
||||
glyphRun,
|
||||
glyphRunDescription,
|
||||
drawingContext->foregroundBrush,
|
||||
clientDrawingEffect));
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(DrawCursor(d2dContext.Get(), rect, *drawingContext, false));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::EndClip(void* clientDrawingContext) noexcept
|
||||
try
|
||||
{
|
||||
auto drawingContext = static_cast<DrawingContext*>(clientDrawingContext);
|
||||
RETURN_HR_IF(E_INVALIDARG, !drawingContext);
|
||||
|
||||
if (_clipRect.has_value())
|
||||
{
|
||||
drawingContext->renderTarget->PopAxisAlignedClip();
|
||||
_clipRect = std::nullopt;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN()
|
||||
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::_DrawBasicGlyphRun(DrawingContext* clientDrawingContext,
|
||||
D2D1_POINT_2F baselineOrigin,
|
||||
DWRITE_MEASURING_MODE measuringMode,
|
||||
_In_ const DWRITE_GLYPH_RUN* glyphRun,
|
||||
_In_opt_ const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription,
|
||||
ID2D1Brush* brush,
|
||||
_In_opt_ IUnknown* clientDrawingEffect)
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, clientDrawingContext);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, glyphRun);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, brush);
|
||||
|
||||
::Microsoft::WRL::ComPtr<ID2D1DeviceContext> d2dContext;
|
||||
RETURN_IF_FAILED(clientDrawingContext->renderTarget->QueryInterface(d2dContext.GetAddressOf()));
|
||||
|
||||
// If a special drawing effect was specified, see if we know how to deal with it.
|
||||
if (clientDrawingEffect)
|
||||
{
|
||||
::Microsoft::WRL::ComPtr<IBoxDrawingEffect> boxEffect;
|
||||
if (SUCCEEDED(clientDrawingEffect->QueryInterface<IBoxDrawingEffect>(&boxEffect)))
|
||||
{
|
||||
return _DrawBoxRunManually(clientDrawingContext, baselineOrigin, measuringMode, glyphRun, glyphRunDescription, boxEffect.Get());
|
||||
}
|
||||
|
||||
//_DrawBasicGlyphRunManually(clientDrawingContext, baselineOrigin, measuringMode, glyphRun, glyphRunDescription);
|
||||
//_DrawGlowGlyphRun(clientDrawingContext, baselineOrigin, measuringMode, glyphRun, glyphRunDescription);
|
||||
}
|
||||
|
||||
// If we get down here, there either was no special effect or we don't know what to do with it. Use the standard GlyphRun drawing.
|
||||
|
||||
// Using the context is the easiest/default way of drawing.
|
||||
d2dContext->DrawGlyphRun(baselineOrigin, glyphRun, glyphRunDescription, brush, measuringMode);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::_DrawBoxRunManually(DrawingContext* clientDrawingContext,
|
||||
D2D1_POINT_2F baselineOrigin,
|
||||
DWRITE_MEASURING_MODE /*measuringMode*/,
|
||||
_In_ const DWRITE_GLYPH_RUN* glyphRun,
|
||||
_In_opt_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/,
|
||||
_In_ IBoxDrawingEffect* clientDrawingEffect) noexcept
|
||||
try
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, clientDrawingContext);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, glyphRun);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, clientDrawingEffect);
|
||||
|
||||
::Microsoft::WRL::ComPtr<ID2D1Factory> d2dFactory;
|
||||
clientDrawingContext->renderTarget->GetFactory(d2dFactory.GetAddressOf());
|
||||
|
||||
::Microsoft::WRL::ComPtr<ID2D1PathGeometry> pathGeometry;
|
||||
d2dFactory->CreatePathGeometry(pathGeometry.GetAddressOf());
|
||||
|
||||
::Microsoft::WRL::ComPtr<ID2D1GeometrySink> geometrySink;
|
||||
pathGeometry->Open(geometrySink.GetAddressOf());
|
||||
|
||||
glyphRun->fontFace->GetGlyphRunOutline(
|
||||
glyphRun->fontEmSize,
|
||||
glyphRun->glyphIndices,
|
||||
glyphRun->glyphAdvances,
|
||||
glyphRun->glyphOffsets,
|
||||
glyphRun->glyphCount,
|
||||
glyphRun->isSideways,
|
||||
glyphRun->bidiLevel % 2,
|
||||
geometrySink.Get());
|
||||
|
||||
geometrySink->Close();
|
||||
|
||||
// Can be used to see the dimensions of what is written.
|
||||
/*D2D1_RECT_F bounds;
|
||||
pathGeometry->GetBounds(D2D1::IdentityMatrix(), &bounds);*/
|
||||
// The bounds here are going to be centered around the baseline of the font.
|
||||
// That is, the DWRITE_GLYPH_METRICS property for this glyph's baseline is going
|
||||
// to be at the 0 point in the Y direction when we receive the geometry.
|
||||
// The ascent will go up negative from Y=0 and the descent will go down positive from Y=0.
|
||||
// As for the horizontal direction, I didn't study this in depth, but it appears to always be
|
||||
// positive X with both the left and right edges being positive and away from X=0.
|
||||
// For one particular instance, we might ask for the geometry for a U+2588 box and see the bounds as:
|
||||
//
|
||||
// Top=
|
||||
// -20.315
|
||||
// -----------
|
||||
// | |
|
||||
// | |
|
||||
// Left= | | Right=
|
||||
// 13.859 | | 26.135
|
||||
// | |
|
||||
// Origin --> X | |
|
||||
// (0,0) | |
|
||||
// -----------
|
||||
// Bottom=
|
||||
// 5.955
|
||||
|
||||
// Dig out the box drawing effect parameters.
|
||||
BoxScale scale;
|
||||
RETURN_IF_FAILED(clientDrawingEffect->GetScale(&scale));
|
||||
|
||||
// The scale transform will inflate the entire geometry first.
|
||||
// We want to do this before it moves out of its original location as generally our
|
||||
// algorithms for fitting cells will blow up the glyph to the size it needs to be first and then
|
||||
// nudge it into place with the translations.
|
||||
const auto scaleTransform = D2D1::Matrix3x2F::Scale(scale.HorizontalScale, scale.VerticalScale);
|
||||
|
||||
// Now shift it all the way to where the baseline says it should be.
|
||||
const auto baselineTransform = D2D1::Matrix3x2F::Translation(baselineOrigin.x, baselineOrigin.y);
|
||||
|
||||
// Finally apply the little "nudge" that we may have been directed to align it better with the cell.
|
||||
const auto offsetTransform = D2D1::Matrix3x2F::Translation(scale.HorizontalTranslation, scale.VerticalTranslation);
|
||||
|
||||
// The order is important here. Scale it first, then slide it into place.
|
||||
const auto matrixTransformation = scaleTransform * baselineTransform * offsetTransform;
|
||||
|
||||
::Microsoft::WRL::ComPtr<ID2D1TransformedGeometry> transformedGeometry;
|
||||
d2dFactory->CreateTransformedGeometry(pathGeometry.Get(),
|
||||
&matrixTransformation,
|
||||
transformedGeometry.GetAddressOf());
|
||||
|
||||
// Can be used to see the dimensions after translation.
|
||||
/*D2D1_RECT_F boundsAfter;
|
||||
transformedGeometry->GetBounds(D2D1::IdentityMatrix(), &boundsAfter);*/
|
||||
// Compare this to the original bounds above to see what the matrix did.
|
||||
// To make it useful, first visualize for yourself the pixel dimensions of the cell
|
||||
// based on the baselineOrigin and the exact integer cell width and heights that we're storing.
|
||||
// You'll also probably need the full-pixel ascent and descent because the point we're given
|
||||
// is the baseline, not the top left corner of the cell as we're used to.
|
||||
// Most of these metrics can be found in the initial font creation routines or in
|
||||
// the line spacing applied to the text format (member variables on the renderer).
|
||||
// baselineOrigin = (0, 567)
|
||||
// fullPixelAscent = 39
|
||||
// fullPixelDescent = 9
|
||||
// cell dimensions = 26 x 48 (notice 48 height is 39 + 9 or ascent + descent)
|
||||
// This means that our cell should be the rectangle
|
||||
//
|
||||
// T=528
|
||||
// |-------|
|
||||
// L=0 | |
|
||||
// | |
|
||||
// Baseline->x |
|
||||
// Origin | | R=26
|
||||
// |-------|
|
||||
// B=576
|
||||
//
|
||||
// And we'll want to check that the bounds after transform will fit the glyph nicely inside
|
||||
// this box.
|
||||
// If not? We didn't do the scaling or translation correctly. Oops.
|
||||
|
||||
// Fill in the geometry. Don't outline, it can leave stuff outside the area we expect.
|
||||
clientDrawingContext->renderTarget->FillGeometry(transformedGeometry.Get(), clientDrawingContext->foregroundBrush);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::_DrawGlowGlyphRun(DrawingContext* clientDrawingContext,
|
||||
D2D1_POINT_2F baselineOrigin,
|
||||
DWRITE_MEASURING_MODE /*measuringMode*/,
|
||||
_In_ const DWRITE_GLYPH_RUN* glyphRun,
|
||||
_In_opt_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/) noexcept
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, clientDrawingContext);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, glyphRun);
|
||||
|
||||
// This is glow text manually
|
||||
::Microsoft::WRL::ComPtr<ID2D1Factory> d2dFactory;
|
||||
clientDrawingContext->renderTarget->GetFactory(d2dFactory.GetAddressOf());
|
||||
|
||||
::Microsoft::WRL::ComPtr<ID2D1PathGeometry> pathGeometry;
|
||||
d2dFactory->CreatePathGeometry(pathGeometry.GetAddressOf());
|
||||
|
||||
::Microsoft::WRL::ComPtr<ID2D1GeometrySink> geometrySink;
|
||||
pathGeometry->Open(geometrySink.GetAddressOf());
|
||||
|
||||
glyphRun->fontFace->GetGlyphRunOutline(
|
||||
glyphRun->fontEmSize,
|
||||
glyphRun->glyphIndices,
|
||||
glyphRun->glyphAdvances,
|
||||
glyphRun->glyphOffsets,
|
||||
glyphRun->glyphCount,
|
||||
glyphRun->isSideways,
|
||||
glyphRun->bidiLevel % 2,
|
||||
geometrySink.Get());
|
||||
|
||||
geometrySink->Close();
|
||||
|
||||
const auto matrixAlign = D2D1::Matrix3x2F::Translation(baselineOrigin.x, baselineOrigin.y);
|
||||
|
||||
::Microsoft::WRL::ComPtr<ID2D1TransformedGeometry> transformedGeometry;
|
||||
d2dFactory->CreateTransformedGeometry(pathGeometry.Get(),
|
||||
&matrixAlign,
|
||||
transformedGeometry.GetAddressOf());
|
||||
|
||||
::Microsoft::WRL::ComPtr<ID2D1TransformedGeometry> alignedGeometry;
|
||||
d2dFactory->CreateTransformedGeometry(pathGeometry.Get(),
|
||||
&matrixAlign,
|
||||
alignedGeometry.GetAddressOf());
|
||||
|
||||
::Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> brush;
|
||||
::Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> outlineBrush;
|
||||
|
||||
clientDrawingContext->renderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White, 1.0f), brush.GetAddressOf());
|
||||
clientDrawingContext->renderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Red, 1.0f), outlineBrush.GetAddressOf());
|
||||
|
||||
clientDrawingContext->renderTarget->DrawGeometry(transformedGeometry.Get(), outlineBrush.Get(), 2.0f);
|
||||
|
||||
clientDrawingContext->renderTarget->FillGeometry(alignedGeometry.Get(), brush.Get());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@ -1,153 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <wrl/implements.h>
|
||||
#include "BoxDrawingEffect.h"
|
||||
#include "../../renderer/inc/CursorOptions.h"
|
||||
|
||||
namespace Microsoft::Console::Render
|
||||
{
|
||||
struct DrawingContext
|
||||
{
|
||||
DrawingContext(ID2D1RenderTarget* renderTarget,
|
||||
ID2D1SolidColorBrush* foregroundBrush,
|
||||
ID2D1SolidColorBrush* backgroundBrush,
|
||||
bool forceGrayscaleAA,
|
||||
IDWriteFactory* dwriteFactory,
|
||||
const DWRITE_LINE_SPACING spacing,
|
||||
const D2D_SIZE_F cellSize,
|
||||
const D2D_SIZE_F targetSize,
|
||||
const std::optional<CursorOptions>& cursorInfo,
|
||||
const D2D1_DRAW_TEXT_OPTIONS options = D2D1_DRAW_TEXT_OPTIONS_NONE) noexcept :
|
||||
renderTarget(renderTarget),
|
||||
foregroundBrush(foregroundBrush),
|
||||
backgroundBrush(backgroundBrush),
|
||||
useBoldFont(false),
|
||||
useItalicFont(false),
|
||||
forceGrayscaleAA(forceGrayscaleAA),
|
||||
dwriteFactory(dwriteFactory),
|
||||
spacing(spacing),
|
||||
cellSize(cellSize),
|
||||
targetSize(targetSize),
|
||||
cursorInfo(cursorInfo),
|
||||
options(options),
|
||||
topClipOffset(0),
|
||||
bottomClipOffset(0)
|
||||
{
|
||||
}
|
||||
|
||||
ID2D1RenderTarget* renderTarget;
|
||||
ID2D1SolidColorBrush* foregroundBrush;
|
||||
ID2D1SolidColorBrush* backgroundBrush;
|
||||
bool useBoldFont;
|
||||
bool useItalicFont;
|
||||
bool forceGrayscaleAA;
|
||||
IDWriteFactory* dwriteFactory;
|
||||
DWRITE_LINE_SPACING spacing;
|
||||
D2D_SIZE_F cellSize;
|
||||
D2D_SIZE_F targetSize;
|
||||
std::optional<CursorOptions> cursorInfo;
|
||||
D2D1_DRAW_TEXT_OPTIONS options;
|
||||
FLOAT topClipOffset;
|
||||
FLOAT bottomClipOffset;
|
||||
};
|
||||
|
||||
// Helper to choose which Direct2D method to use when drawing the cursor rectangle
|
||||
enum class CursorPaintType
|
||||
{
|
||||
Fill,
|
||||
Outline
|
||||
};
|
||||
|
||||
constexpr const ULONG MinCursorHeightPixels = 1;
|
||||
constexpr const ULONG MinCursorHeightPercent = 1;
|
||||
constexpr const ULONG MaxCursorHeightPercent = 100;
|
||||
|
||||
class CustomTextRenderer : public ::Microsoft::WRL::RuntimeClass<::Microsoft::WRL::RuntimeClassFlags<::Microsoft::WRL::ClassicCom | ::Microsoft::WRL::InhibitFtmBase>, IDWriteTextRenderer>
|
||||
{
|
||||
public:
|
||||
// http://www.charlespetzold.com/blog/2014/01/Character-Formatting-Extensions-with-DirectWrite.html
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/DirectWrite/how-to-implement-a-custom-text-renderer
|
||||
|
||||
// IDWritePixelSnapping methods
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE IsPixelSnappingDisabled(void* clientDrawingContext,
|
||||
_Out_ BOOL* isDisabled) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE GetPixelsPerDip(void* clientDrawingContext,
|
||||
_Out_ FLOAT* pixelsPerDip) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE GetCurrentTransform(void* clientDrawingContext,
|
||||
_Out_ DWRITE_MATRIX* transform) noexcept override;
|
||||
|
||||
// IDWriteTextRenderer methods
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE DrawGlyphRun(void* clientDrawingContext,
|
||||
FLOAT baselineOriginX,
|
||||
FLOAT baselineOriginY,
|
||||
DWRITE_MEASURING_MODE measuringMode,
|
||||
_In_ const DWRITE_GLYPH_RUN* glyphRun,
|
||||
_In_ const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription,
|
||||
IUnknown* clientDrawingEffect) override;
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE DrawUnderline(void* clientDrawingContext,
|
||||
FLOAT baselineOriginX,
|
||||
FLOAT baselineOriginY,
|
||||
_In_ const DWRITE_UNDERLINE* underline,
|
||||
IUnknown* clientDrawingEffect) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE DrawStrikethrough(void* clientDrawingContext,
|
||||
FLOAT baselineOriginX,
|
||||
FLOAT baselineOriginY,
|
||||
_In_ const DWRITE_STRIKETHROUGH* strikethrough,
|
||||
IUnknown* clientDrawingEffect) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE DrawInlineObject(void* clientDrawingContext,
|
||||
FLOAT originX,
|
||||
FLOAT originY,
|
||||
IDWriteInlineObject* inlineObject,
|
||||
BOOL isSideways,
|
||||
BOOL isRightToLeft,
|
||||
IUnknown* clientDrawingEffect) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE EndClip(void* clientDrawingContext) noexcept;
|
||||
|
||||
[[nodiscard]] static HRESULT DrawCursor(gsl::not_null<ID2D1DeviceContext*> d2dContext,
|
||||
D2D1_RECT_F textRunBounds,
|
||||
const DrawingContext& drawingContext,
|
||||
const bool firstPass);
|
||||
|
||||
private:
|
||||
[[nodiscard]] HRESULT _FillRectangle(void* clientDrawingContext,
|
||||
IUnknown* clientDrawingEffect,
|
||||
float x,
|
||||
float y,
|
||||
float width,
|
||||
float thickness,
|
||||
DWRITE_READING_DIRECTION readingDirection,
|
||||
DWRITE_FLOW_DIRECTION flowDirection) noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT _DrawBasicGlyphRun(DrawingContext* clientDrawingContext,
|
||||
D2D1_POINT_2F baselineOrigin,
|
||||
DWRITE_MEASURING_MODE measuringMode,
|
||||
_In_ const DWRITE_GLYPH_RUN* glyphRun,
|
||||
_In_opt_ const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription,
|
||||
ID2D1Brush* brush,
|
||||
_In_opt_ IUnknown* clientDrawingEffect);
|
||||
|
||||
[[nodiscard]] HRESULT _DrawBoxRunManually(DrawingContext* clientDrawingContext,
|
||||
D2D1_POINT_2F baselineOrigin,
|
||||
DWRITE_MEASURING_MODE measuringMode,
|
||||
_In_ const DWRITE_GLYPH_RUN* glyphRun,
|
||||
_In_opt_ const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription,
|
||||
_In_ IBoxDrawingEffect* clientDrawingEffect) noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT _DrawGlowGlyphRun(DrawingContext* clientDrawingContext,
|
||||
D2D1_POINT_2F baselineOrigin,
|
||||
DWRITE_MEASURING_MODE measuringMode,
|
||||
_In_ const DWRITE_GLYPH_RUN* glyphRun,
|
||||
_In_opt_ const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription) noexcept;
|
||||
|
||||
std::optional<D2D1_RECT_F> _clipRect;
|
||||
};
|
||||
}
|
||||
@ -1,313 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
#include "DxFontInfo.h"
|
||||
|
||||
#include <unicode.hpp>
|
||||
#include <VersionHelpers.h>
|
||||
|
||||
#include "../base/FontCache.h"
|
||||
|
||||
static constexpr std::wstring_view FALLBACK_FONT_FACES[] = { L"Consolas", L"Lucida Console", L"Courier New" };
|
||||
|
||||
using namespace Microsoft::Console::Render;
|
||||
|
||||
DxFontInfo::DxFontInfo(IDWriteFactory1* dwriteFactory) :
|
||||
DxFontInfo{ dwriteFactory, {}, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL }
|
||||
{
|
||||
}
|
||||
|
||||
DxFontInfo::DxFontInfo(
|
||||
IDWriteFactory1* dwriteFactory,
|
||||
std::wstring_view familyName,
|
||||
DWRITE_FONT_WEIGHT weight,
|
||||
DWRITE_FONT_STYLE style,
|
||||
DWRITE_FONT_STRETCH stretch) :
|
||||
_familyName(familyName),
|
||||
_weight(weight),
|
||||
_style(style),
|
||||
_stretch(stretch),
|
||||
_didFallback(false)
|
||||
{
|
||||
__assume(dwriteFactory != nullptr);
|
||||
THROW_IF_FAILED(dwriteFactory->GetSystemFontCollection(_fontCollection.addressof(), FALSE));
|
||||
}
|
||||
|
||||
bool DxFontInfo::operator==(const DxFontInfo& other) const noexcept
|
||||
{
|
||||
return (_familyName == other._familyName &&
|
||||
_weight == other._weight &&
|
||||
_style == other._style &&
|
||||
_stretch == other._stretch &&
|
||||
_didFallback == other._didFallback);
|
||||
}
|
||||
|
||||
std::wstring_view DxFontInfo::GetFamilyName() const noexcept
|
||||
{
|
||||
return _familyName;
|
||||
}
|
||||
|
||||
void DxFontInfo::SetFamilyName(const std::wstring_view familyName)
|
||||
{
|
||||
_familyName = familyName;
|
||||
}
|
||||
|
||||
DWRITE_FONT_WEIGHT DxFontInfo::GetWeight() const noexcept
|
||||
{
|
||||
return _weight;
|
||||
}
|
||||
|
||||
void DxFontInfo::SetWeight(const DWRITE_FONT_WEIGHT weight) noexcept
|
||||
{
|
||||
_weight = weight;
|
||||
}
|
||||
|
||||
DWRITE_FONT_STYLE DxFontInfo::GetStyle() const noexcept
|
||||
{
|
||||
return _style;
|
||||
}
|
||||
|
||||
void DxFontInfo::SetStyle(const DWRITE_FONT_STYLE style) noexcept
|
||||
{
|
||||
_style = style;
|
||||
}
|
||||
|
||||
DWRITE_FONT_STRETCH DxFontInfo::GetStretch() const noexcept
|
||||
{
|
||||
return _stretch;
|
||||
}
|
||||
|
||||
void DxFontInfo::SetStretch(const DWRITE_FONT_STRETCH stretch) noexcept
|
||||
{
|
||||
_stretch = stretch;
|
||||
}
|
||||
|
||||
bool DxFontInfo::GetFallback() const noexcept
|
||||
{
|
||||
return _didFallback;
|
||||
}
|
||||
|
||||
IDWriteFontCollection* DxFontInfo::GetFontCollection() const noexcept
|
||||
{
|
||||
return _fontCollection.get();
|
||||
}
|
||||
|
||||
void DxFontInfo::SetFromEngine(const std::wstring_view familyName,
|
||||
const DWRITE_FONT_WEIGHT weight,
|
||||
const DWRITE_FONT_STYLE style,
|
||||
const DWRITE_FONT_STRETCH stretch)
|
||||
{
|
||||
_familyName = familyName;
|
||||
_weight = weight;
|
||||
_style = style;
|
||||
_stretch = stretch;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Attempts to locate the font given, but then begins falling back if we cannot find it.
|
||||
// - We'll try to fall back to Consolas with the given weight/stretch/style first,
|
||||
// then try Consolas again with normal weight/stretch/style,
|
||||
// and if nothing works, then we'll throw an error.
|
||||
// Arguments:
|
||||
// - dwriteFactory - The DWrite factory to use
|
||||
// - localeName - Locale to search for appropriate fonts
|
||||
// Return Value:
|
||||
// - Smart pointer holding interface reference for queryable font data.
|
||||
[[nodiscard]] Microsoft::WRL::ComPtr<IDWriteFontFace1> DxFontInfo::ResolveFontFaceWithFallback(std::wstring& localeName)
|
||||
{
|
||||
// First attempt to find exactly what the user asked for.
|
||||
_didFallback = false;
|
||||
Microsoft::WRL::ComPtr<IDWriteFontFace1> face{ nullptr };
|
||||
|
||||
// GH#10211 - wrap this all up in a try/catch. If the nearby fonts are
|
||||
// corrupted, then we don't want to throw out of this top half of this
|
||||
// method. We still want to fall back to a font that's reasonable, below.
|
||||
try
|
||||
{
|
||||
face = _FindFontFace(localeName);
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
if constexpr (Feature_NearbyFontLoading::IsEnabled())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!face)
|
||||
{
|
||||
_fontCollection = FontCache::GetCached();
|
||||
face = _FindFontFace(localeName);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
if (!face)
|
||||
{
|
||||
// If we missed, try looking a little more by trimming the last word off the requested family name a few times.
|
||||
// Quite often, folks are specifying weights or something in the familyName and it causes failed resolution and
|
||||
// an unexpected error dialog. We theoretically could detect the weight words and convert them, but this
|
||||
// is the quick fix for the majority scenario.
|
||||
// The long/full fix is backlogged to GH#9744
|
||||
// Also this doesn't count as a fallback because we don't want to annoy folks with the warning dialog over
|
||||
// this resolution.
|
||||
while (!face && !_familyName.empty())
|
||||
{
|
||||
const auto lastSpace = _familyName.find_last_of(UNICODE_SPACE);
|
||||
|
||||
// value is unsigned and npos will be greater than size.
|
||||
// if we didn't find anything to trim, leave.
|
||||
if (lastSpace >= _familyName.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// trim string down to just before the found space
|
||||
// (space found at 6... trim from 0 for 6 length will give us 0-5 as the new string)
|
||||
_familyName = _familyName.substr(0, lastSpace);
|
||||
|
||||
try
|
||||
{
|
||||
// Try to find it with the shortened family name
|
||||
face = _FindFontFace(localeName);
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
}
|
||||
|
||||
// Alright, if our quick shot at trimming didn't work either...
|
||||
// move onto looking up a font from our hard-coded list of fonts
|
||||
// that should really always be available.
|
||||
if (!face)
|
||||
{
|
||||
for (const auto fallbackFace : FALLBACK_FONT_FACES)
|
||||
{
|
||||
_familyName = fallbackFace;
|
||||
|
||||
try
|
||||
{
|
||||
face = _FindFontFace(localeName);
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
if (face)
|
||||
{
|
||||
_didFallback = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
THROW_HR_IF_NULL(E_FAIL, face);
|
||||
|
||||
return face;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Locates a suitable font face from the given information
|
||||
// Arguments:
|
||||
// - dwriteFactory - The DWrite factory to use
|
||||
// - localeName - Locale to search for appropriate fonts
|
||||
// Return Value:
|
||||
// - Smart pointer holding interface reference for queryable font data.
|
||||
#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<IDWriteFontFace1> DxFontInfo::_FindFontFace(std::wstring& localeName)
|
||||
{
|
||||
Microsoft::WRL::ComPtr<IDWriteFontFace1> fontFace;
|
||||
|
||||
UINT32 familyIndex;
|
||||
BOOL familyExists;
|
||||
|
||||
THROW_IF_FAILED(_fontCollection->FindFamilyName(_familyName.data(), &familyIndex, &familyExists));
|
||||
|
||||
if (familyExists)
|
||||
{
|
||||
Microsoft::WRL::ComPtr<IDWriteFontFamily> fontFamily;
|
||||
THROW_IF_FAILED(_fontCollection->GetFontFamily(familyIndex, &fontFamily));
|
||||
|
||||
Microsoft::WRL::ComPtr<IDWriteFont> font;
|
||||
THROW_IF_FAILED(fontFamily->GetFirstMatchingFont(GetWeight(), GetStretch(), GetStyle(), &font));
|
||||
|
||||
Microsoft::WRL::ComPtr<IDWriteFontFace> fontFace0;
|
||||
THROW_IF_FAILED(font->CreateFontFace(&fontFace0));
|
||||
|
||||
THROW_IF_FAILED(fontFace0.As(&fontFace));
|
||||
|
||||
// Retrieve metrics in case the font we created was different than what was requested.
|
||||
_weight = font->GetWeight();
|
||||
_stretch = font->GetStretch();
|
||||
_style = font->GetStyle();
|
||||
|
||||
// Dig the family name out at the end to return it.
|
||||
_familyName = _GetFontFamilyName(fontFamily.Get(), localeName);
|
||||
}
|
||||
|
||||
return fontFace;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Retrieves the font family name out of the given object in the given locale.
|
||||
// - If we can't find a valid name for the given locale, we'll fallback and report it back.
|
||||
// Arguments:
|
||||
// - fontFamily - DirectWrite font family object
|
||||
// - localeName - The locale in which the name should be retrieved.
|
||||
// - If fallback occurred, this is updated to what we retrieved instead.
|
||||
// Return Value:
|
||||
// - Localized string name of the font family
|
||||
[[nodiscard]] std::wstring DxFontInfo::_GetFontFamilyName(const gsl::not_null<IDWriteFontFamily*> fontFamily,
|
||||
std::wstring& localeName)
|
||||
{
|
||||
// See: https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nn-dwrite-idwritefontcollection
|
||||
Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> familyNames;
|
||||
THROW_IF_FAILED(fontFamily->GetFamilyNames(&familyNames));
|
||||
|
||||
// First we have to find the right family name for the locale. We're going to bias toward what the caller
|
||||
// requested, but fallback if we need to and reply with the locale we ended up choosing.
|
||||
UINT32 index = 0;
|
||||
BOOL exists = false;
|
||||
|
||||
// This returns S_OK whether or not it finds a locale name. Check exists field instead.
|
||||
// If it returns an error, it's a real problem, not an absence of this locale name.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritelocalizedstrings-findlocalename
|
||||
THROW_IF_FAILED(familyNames->FindLocaleName(localeName.data(), &index, &exists));
|
||||
|
||||
// If we tried and it still doesn't exist, try with the fallback locale.
|
||||
if (!exists)
|
||||
{
|
||||
localeName = L"en-us";
|
||||
THROW_IF_FAILED(familyNames->FindLocaleName(localeName.data(), &index, &exists));
|
||||
}
|
||||
|
||||
// If it still doesn't exist, we're going to try index 0.
|
||||
if (!exists)
|
||||
{
|
||||
index = 0;
|
||||
|
||||
// Get the locale name out so at least the caller knows what locale this name goes with.
|
||||
UINT32 length = 0;
|
||||
THROW_IF_FAILED(familyNames->GetLocaleNameLength(index, &length));
|
||||
localeName.resize(length);
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritelocalizedstrings-getlocalenamelength
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritelocalizedstrings-getlocalename
|
||||
// GetLocaleNameLength does not include space for null terminator, but GetLocaleName needs it so add one.
|
||||
THROW_IF_FAILED(familyNames->GetLocaleName(index, localeName.data(), length + 1));
|
||||
}
|
||||
|
||||
// OK, now that we've decided which family name and the locale that it's in... let's go get it.
|
||||
UINT32 length = 0;
|
||||
THROW_IF_FAILED(familyNames->GetStringLength(index, &length));
|
||||
|
||||
// Make our output buffer and resize it so it is allocated.
|
||||
std::wstring retVal;
|
||||
retVal.resize(length);
|
||||
|
||||
// FINALLY, go fetch the string name.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritelocalizedstrings-getstringlength
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritelocalizedstrings-getstring
|
||||
// Once again, GetStringLength is without the null, but GetString needs the null. So add one.
|
||||
THROW_IF_FAILED(familyNames->GetString(index, retVal.data(), length + 1));
|
||||
|
||||
// and return it.
|
||||
return retVal;
|
||||
}
|
||||
@ -1,72 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dwrite.h>
|
||||
#include <dwrite_1.h>
|
||||
#include <dwrite_2.h>
|
||||
#include <dwrite_3.h>
|
||||
|
||||
namespace Microsoft::Console::Render
|
||||
{
|
||||
class DxFontInfo
|
||||
{
|
||||
public:
|
||||
DxFontInfo(IDWriteFactory1* dwriteFactory);
|
||||
|
||||
DxFontInfo(
|
||||
IDWriteFactory1* dwriteFactory,
|
||||
std::wstring_view familyName,
|
||||
DWRITE_FONT_WEIGHT weight,
|
||||
DWRITE_FONT_STYLE style,
|
||||
DWRITE_FONT_STRETCH stretch);
|
||||
|
||||
bool operator==(const DxFontInfo& other) const noexcept;
|
||||
|
||||
std::wstring_view GetFamilyName() const noexcept;
|
||||
void SetFamilyName(const std::wstring_view familyName);
|
||||
|
||||
DWRITE_FONT_WEIGHT GetWeight() const noexcept;
|
||||
void SetWeight(const DWRITE_FONT_WEIGHT weight) noexcept;
|
||||
|
||||
DWRITE_FONT_STYLE GetStyle() const noexcept;
|
||||
void SetStyle(const DWRITE_FONT_STYLE style) noexcept;
|
||||
|
||||
DWRITE_FONT_STRETCH GetStretch() const noexcept;
|
||||
void SetStretch(const DWRITE_FONT_STRETCH stretch) noexcept;
|
||||
|
||||
bool GetFallback() const noexcept;
|
||||
IDWriteFontCollection* GetFontCollection() 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<IDWriteFontFace1> ResolveFontFaceWithFallback(std::wstring& localeName);
|
||||
|
||||
private:
|
||||
[[nodiscard]] ::Microsoft::WRL::ComPtr<IDWriteFontFace1> _FindFontFace(std::wstring& localeName);
|
||||
|
||||
[[nodiscard]] std::wstring _GetFontFamilyName(const gsl::not_null<IDWriteFontFamily*> fontFamily,
|
||||
std::wstring& localeName);
|
||||
|
||||
// The font name we should be looking for
|
||||
std::wstring _familyName;
|
||||
|
||||
// The weight (bold, light, etc.)
|
||||
DWRITE_FONT_WEIGHT _weight;
|
||||
|
||||
// Normal, italic, etc.
|
||||
DWRITE_FONT_STYLE _style;
|
||||
|
||||
// The stretch of the font is the spacing between each letter
|
||||
DWRITE_FONT_STRETCH _stretch;
|
||||
|
||||
wil::com_ptr<IDWriteFontCollection> _fontCollection;
|
||||
|
||||
// Indicates whether we couldn't match the user request and had to choose from a hard-coded default list.
|
||||
bool _didFallback;
|
||||
};
|
||||
}
|
||||
@ -1,923 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
#include "DxFontRenderData.h"
|
||||
|
||||
#include <VersionHelpers.h>
|
||||
|
||||
static constexpr float POINTS_PER_INCH = 72.0f;
|
||||
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<IDWriteFactory1> dwriteFactory) :
|
||||
_dwriteFactory(std::move(dwriteFactory)),
|
||||
_defaultFontInfo{ _dwriteFactory.Get() },
|
||||
_lineSpacing{},
|
||||
_lineMetrics{},
|
||||
_fontSize{}
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] Microsoft::WRL::ComPtr<IDWriteTextAnalyzer1> DxFontRenderData::Analyzer()
|
||||
{
|
||||
if (!_dwriteTextAnalyzer)
|
||||
{
|
||||
Microsoft::WRL::ComPtr<IDWriteTextAnalyzer> analyzer;
|
||||
THROW_IF_FAILED(_dwriteFactory->CreateTextAnalyzer(&analyzer));
|
||||
THROW_IF_FAILED(analyzer.As(&_dwriteTextAnalyzer));
|
||||
}
|
||||
|
||||
return _dwriteTextAnalyzer;
|
||||
}
|
||||
|
||||
[[nodiscard]] Microsoft::WRL::ComPtr<IDWriteFontFallback> DxFontRenderData::SystemFontFallback()
|
||||
{
|
||||
if (!_systemFontFallback)
|
||||
{
|
||||
::Microsoft::WRL::ComPtr<IDWriteFactory2> factory2;
|
||||
THROW_IF_FAILED(_dwriteFactory.As(&factory2));
|
||||
factory2->GetSystemFontFallback(&_systemFontFallback);
|
||||
}
|
||||
|
||||
return _systemFontFallback;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::wstring DxFontRenderData::UserLocaleName()
|
||||
{
|
||||
if (_userLocaleName.empty())
|
||||
{
|
||||
std::array<wchar_t, LOCALE_NAME_MAX_LENGTH> localeName;
|
||||
|
||||
const auto returnCode = GetUserDefaultLocaleName(localeName.data(), gsl::narrow<int>(localeName.size()));
|
||||
if (returnCode)
|
||||
{
|
||||
_userLocaleName = { localeName.data() };
|
||||
}
|
||||
else
|
||||
{
|
||||
_userLocaleName = { FALLBACK_LOCALE.data(), FALLBACK_LOCALE.size() };
|
||||
}
|
||||
}
|
||||
|
||||
return _userLocaleName;
|
||||
}
|
||||
|
||||
[[nodiscard]] til::size DxFontRenderData::GlyphCell() noexcept
|
||||
{
|
||||
return _glyphCell;
|
||||
}
|
||||
|
||||
[[nodiscard]] DxFontRenderData::LineMetrics DxFontRenderData::GetLineMetrics() noexcept
|
||||
{
|
||||
return _lineMetrics;
|
||||
}
|
||||
|
||||
[[nodiscard]] DWRITE_FONT_WEIGHT DxFontRenderData::DefaultFontWeight() noexcept
|
||||
{
|
||||
return _defaultFontInfo.GetWeight();
|
||||
}
|
||||
|
||||
[[nodiscard]] DWRITE_FONT_STYLE DxFontRenderData::DefaultFontStyle() noexcept
|
||||
{
|
||||
return _defaultFontInfo.GetStyle();
|
||||
}
|
||||
|
||||
[[nodiscard]] DWRITE_FONT_STRETCH DxFontRenderData::DefaultFontStretch() noexcept
|
||||
{
|
||||
return _defaultFontInfo.GetStretch();
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<DWRITE_FONT_FEATURE>& DxFontRenderData::DefaultFontFeatures() const noexcept
|
||||
{
|
||||
return _featureVector;
|
||||
}
|
||||
|
||||
[[nodiscard]] Microsoft::WRL::ComPtr<IDWriteTextFormat> DxFontRenderData::DefaultTextFormat()
|
||||
{
|
||||
return TextFormatWithAttribute(_defaultFontInfo.GetWeight(), _defaultFontInfo.GetStyle(), _defaultFontInfo.GetStretch());
|
||||
}
|
||||
|
||||
[[nodiscard]] Microsoft::WRL::ComPtr<IDWriteFontFace1> DxFontRenderData::DefaultFontFace()
|
||||
{
|
||||
return FontFaceWithAttribute(_defaultFontInfo.GetWeight(), _defaultFontInfo.GetStyle(), _defaultFontInfo.GetStretch());
|
||||
}
|
||||
|
||||
[[nodiscard]] Microsoft::WRL::ComPtr<IBoxDrawingEffect> DxFontRenderData::DefaultBoxDrawingEffect()
|
||||
{
|
||||
if (!_boxDrawingEffect)
|
||||
{
|
||||
// Calculate and cache the box effect for the base font. Scale is 1.0f because the base font is exactly the scale we want already.
|
||||
THROW_IF_FAILED(s_CalculateBoxEffect(DefaultTextFormat().Get(), _glyphCell.width, DefaultFontFace().Get(), 1.0f, &_boxDrawingEffect));
|
||||
}
|
||||
|
||||
return _boxDrawingEffect;
|
||||
}
|
||||
|
||||
[[nodiscard]] Microsoft::WRL::ComPtr<IDWriteTextFormat> DxFontRenderData::TextFormatWithAttribute(DWRITE_FONT_WEIGHT weight,
|
||||
DWRITE_FONT_STYLE style,
|
||||
DWRITE_FONT_STRETCH stretch)
|
||||
{
|
||||
const auto textFormatIt = _textFormatMap.find(_ToMapKey(weight, style, stretch));
|
||||
if (textFormatIt == _textFormatMap.end())
|
||||
{
|
||||
auto fontInfo = _defaultFontInfo;
|
||||
fontInfo.SetWeight(weight);
|
||||
fontInfo.SetStyle(style);
|
||||
fontInfo.SetStretch(stretch);
|
||||
|
||||
// Create the font with the fractional pixel height size.
|
||||
// It should have an integer pixel width by our math.
|
||||
// Then below, apply the line spacing to the format to position the floating point pixel height characters
|
||||
// into a cell that has an integer pixel height leaving some padding above/below as necessary to round them out.
|
||||
auto localeName = UserLocaleName();
|
||||
Microsoft::WRL::ComPtr<IDWriteTextFormat> textFormat;
|
||||
THROW_IF_FAILED(_BuildTextFormat(fontInfo, localeName).As(&textFormat));
|
||||
THROW_IF_FAILED(textFormat->SetLineSpacing(_lineSpacing.method, _lineSpacing.height, _lineSpacing.baseline));
|
||||
THROW_IF_FAILED(textFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR));
|
||||
THROW_IF_FAILED(textFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP));
|
||||
|
||||
_textFormatMap.emplace(_ToMapKey(weight, style, stretch), textFormat);
|
||||
return textFormat;
|
||||
}
|
||||
else
|
||||
{
|
||||
return textFormatIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] Microsoft::WRL::ComPtr<IDWriteFontFace1> DxFontRenderData::FontFaceWithAttribute(DWRITE_FONT_WEIGHT weight,
|
||||
DWRITE_FONT_STYLE style,
|
||||
DWRITE_FONT_STRETCH stretch)
|
||||
{
|
||||
const auto fontFaceIt = _fontFaceMap.find(_ToMapKey(weight, style, stretch));
|
||||
if (fontFaceIt == _fontFaceMap.end())
|
||||
{
|
||||
auto fontInfo = _defaultFontInfo;
|
||||
fontInfo.SetWeight(weight);
|
||||
fontInfo.SetStyle(style);
|
||||
fontInfo.SetStretch(stretch);
|
||||
|
||||
auto fontLocaleName = UserLocaleName();
|
||||
auto fontFace = fontInfo.ResolveFontFaceWithFallback(fontLocaleName);
|
||||
|
||||
_fontFaceMap.emplace(_ToMapKey(weight, style, stretch), fontFace);
|
||||
return fontFace;
|
||||
}
|
||||
else
|
||||
{
|
||||
return fontFaceIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Updates the font used for drawing
|
||||
// Arguments:
|
||||
// - desired - Information specifying the font that is requested
|
||||
// - actual - Filled with the nearest font actually chosen for drawing
|
||||
// - dpi - The DPI of the screen
|
||||
// Return Value:
|
||||
// - S_OK or relevant DirectX error
|
||||
[[nodiscard]] HRESULT DxFontRenderData::UpdateFont(const FontInfoDesired& desired, FontInfo& actual, const int dpi, const std::unordered_map<std::wstring_view, uint32_t>& features, const std::unordered_map<std::wstring_view, float>& axes) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
_userLocaleName.clear();
|
||||
_textFormatMap.clear();
|
||||
_fontFaceMap.clear();
|
||||
_boxDrawingEffect.Reset();
|
||||
|
||||
// Initialize the default font info and build everything from here.
|
||||
_defaultFontInfo = DxFontInfo(
|
||||
_dwriteFactory.Get(),
|
||||
desired.GetFaceName(),
|
||||
static_cast<DWRITE_FONT_WEIGHT>(desired.GetWeight()),
|
||||
DWRITE_FONT_STYLE_NORMAL,
|
||||
DWRITE_FONT_STRETCH_NORMAL);
|
||||
|
||||
_SetFeatures(features);
|
||||
_SetAxes(axes);
|
||||
|
||||
_BuildFontRenderData(desired, actual, dpi);
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Calculates the box drawing scale/translate matrix values to fit a box glyph into the cell as perfectly as possible.
|
||||
// Arguments:
|
||||
// - format - Text format used to determine line spacing (height including ascent & descent) as calculated from the base font.
|
||||
// - widthPixels - The pixel width of the available cell.
|
||||
// - face - The font face that is currently being used, may differ from the base font from the layout.
|
||||
// - fontScale - if the given font face is going to be scaled versus the format, we need to know so we can compensate for that. pass 1.0f for no scaling.
|
||||
// - effect - Receives the effect to apply to box drawing characters. If no effect is received, special treatment isn't required.
|
||||
// Return Value:
|
||||
// - S_OK, GSL/WIL errors, DirectWrite errors, or math errors.
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE DxFontRenderData::s_CalculateBoxEffect(IDWriteTextFormat* format, size_t widthPixels, IDWriteFontFace1* face, float fontScale, IBoxDrawingEffect** effect) noexcept
|
||||
try
|
||||
{
|
||||
// Check for bad in parameters.
|
||||
RETURN_HR_IF(E_INVALIDARG, !format);
|
||||
RETURN_HR_IF(E_INVALIDARG, !face);
|
||||
|
||||
// Check the out parameter and fill it up with null.
|
||||
RETURN_HR_IF(E_INVALIDARG, !effect);
|
||||
*effect = nullptr;
|
||||
|
||||
// The format is based around the main font that was specified by the user.
|
||||
// We need to know its size as well as the final spacing that was calculated around
|
||||
// it when it was first selected to get an idea of how large the bounding box is.
|
||||
const auto fontSize = format->GetFontSize();
|
||||
|
||||
DWRITE_LINE_SPACING_METHOD spacingMethod;
|
||||
float lineSpacing; // total height of the cells
|
||||
float baseline; // vertical position counted down from the top where the characters "sit"
|
||||
RETURN_IF_FAILED(format->GetLineSpacing(&spacingMethod, &lineSpacing, &baseline));
|
||||
|
||||
const auto ascentPixels = baseline;
|
||||
const auto descentPixels = lineSpacing - baseline;
|
||||
|
||||
// We need this for the designUnitsPerEm which will be required to move back and forth between
|
||||
// Design Units and Pixels. I'll elaborate below.
|
||||
DWRITE_FONT_METRICS1 fontMetrics;
|
||||
face->GetMetrics(&fontMetrics);
|
||||
|
||||
// If we had font fallback occur, the size of the font given to us (IDWriteFontFace1) can be different
|
||||
// than the font size used for the original format (IDWriteTextFormat).
|
||||
const auto scaledFontSize = fontScale * fontSize;
|
||||
|
||||
// This is Unicode FULL BLOCK U+2588.
|
||||
// We presume that FULL BLOCK should be filling its entire cell in all directions so it should provide a good basis
|
||||
// in knowing exactly where to touch every single edge.
|
||||
// We're also presuming that the other box/line drawing glyphs were authored in this font to perfectly inscribe
|
||||
// inside of FULL BLOCK, with the same left/top/right/bottom bearings so they would look great when drawn adjacent.
|
||||
const UINT32 blockCodepoint = L'\x2588';
|
||||
|
||||
// Get the index of the block out of the font.
|
||||
UINT16 glyphIndex;
|
||||
RETURN_IF_FAILED(face->GetGlyphIndicesW(&blockCodepoint, 1, &glyphIndex));
|
||||
|
||||
// If it was 0, it wasn't found in the font. We're going to try again with
|
||||
// Unicode BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL U+253C which should be touching
|
||||
// all the edges of the possible rectangle, much like a full block should.
|
||||
if (glyphIndex == 0)
|
||||
{
|
||||
const UINT32 alternateCp = L'\x253C';
|
||||
RETURN_IF_FAILED(face->GetGlyphIndicesW(&alternateCp, 1, &glyphIndex));
|
||||
}
|
||||
|
||||
// If we still didn't find the glyph index, we haven't implemented any further logic to figure out the box dimensions.
|
||||
// So we're just going to leave successfully as is and apply no scaling factor. It might look not-right, but it won't
|
||||
// stop the rendering pipeline.
|
||||
RETURN_HR_IF(S_FALSE, glyphIndex == 0);
|
||||
|
||||
// Get the metrics of the given glyph, which we're going to treat as the outline box in which all line/block drawing
|
||||
// glyphs will be inscribed within, perfectly touching each edge as to align when two cells meet.
|
||||
DWRITE_GLYPH_METRICS boxMetrics = { 0 };
|
||||
RETURN_IF_FAILED(face->GetDesignGlyphMetrics(&glyphIndex, 1, &boxMetrics));
|
||||
|
||||
// NOTE: All metrics we receive from DWRITE are going to be in "design units" which are a somewhat agnostic
|
||||
// way of describing proportions.
|
||||
// Converting back and forth between real pixels and design units is possible using
|
||||
// any font's specific fontSize and the designUnitsPerEm FONT_METRIC value.
|
||||
//
|
||||
// Here's what to know about the boxMetrics:
|
||||
//
|
||||
//
|
||||
//
|
||||
// topLeft --> +--------------------------------+ ---
|
||||
// | ^ | |
|
||||
// | | topSide | |
|
||||
// | | Bearing | |
|
||||
// | v | |
|
||||
// | +-----------------+ | |
|
||||
// | | | | |
|
||||
// | | | | | a
|
||||
// | | | | | d
|
||||
// | | | | | v
|
||||
// +<---->+ | | | a
|
||||
// | | | | | n
|
||||
// | left | | | | c
|
||||
// | Side | | | | e
|
||||
// | Bea- | | | | H
|
||||
// | ring | | right | | e
|
||||
// vertical | | | Side | | i
|
||||
// OriginY --> x | | Bea- | | g
|
||||
// | | | ring | | h
|
||||
// | | | | | t
|
||||
// | | +<----->+ |
|
||||
// | +-----------------+ | |
|
||||
// | ^ | |
|
||||
// | bottomSide | | |
|
||||
// | Bearing | | |
|
||||
// | v | |
|
||||
// +--------------------------------+ ---
|
||||
//
|
||||
//
|
||||
// | |
|
||||
// +--------------------------------+
|
||||
// | advanceWidth |
|
||||
//
|
||||
//
|
||||
// NOTE: The bearings can be negative, in which case it is specifying that the glyphs overhang the box
|
||||
// as defined by the advanceHeight/width.
|
||||
// See also: https://docs.microsoft.com/en-us/windows/win32/api/dwrite/ns-dwrite-dwrite_glyph_metrics
|
||||
|
||||
// The scale is a multiplier and the translation is addition. So *1 and +0 will mean nothing happens.
|
||||
const auto defaultBoxVerticalScaleFactor = 1.0f;
|
||||
auto boxVerticalScaleFactor = defaultBoxVerticalScaleFactor;
|
||||
const auto defaultBoxVerticalTranslation = 0.0f;
|
||||
auto boxVerticalTranslation = defaultBoxVerticalTranslation;
|
||||
{
|
||||
// First, find the dimensions of the glyph representing our fully filled box.
|
||||
|
||||
// Ascent is how far up from the baseline we'll draw.
|
||||
// verticalOriginY is the measure from the topLeft corner of the bounding box down to where
|
||||
// the glyph's version of the baseline is.
|
||||
// topSideBearing is how much "gap space" is left between that topLeft and where the glyph
|
||||
// starts drawing. Subtract the gap space to find how far is drawn upward from baseline.
|
||||
const auto boxAscentDesignUnits = boxMetrics.verticalOriginY - boxMetrics.topSideBearing;
|
||||
|
||||
// Descent is how far down from the baseline we'll draw.
|
||||
// advanceHeight is the total height of the drawn bounding box.
|
||||
// verticalOriginY is how much was given to the ascent, so subtract that out.
|
||||
// What remains is then the descent value. Remove the
|
||||
// bottomSideBearing as the "gap space" on the bottom to find how far is drawn downward from baseline.
|
||||
const auto boxDescentDesignUnits = boxMetrics.advanceHeight - boxMetrics.verticalOriginY - boxMetrics.bottomSideBearing;
|
||||
|
||||
// The height, then, of the entire box is just the sum of the ascent above the baseline and the descent below.
|
||||
const auto boxHeightDesignUnits = boxAscentDesignUnits + boxDescentDesignUnits;
|
||||
|
||||
// Second, find the dimensions of the cell we're going to attempt to fit within.
|
||||
// We know about the exact ascent/descent units in pixels as calculated when we chose a font and
|
||||
// adjusted the ascent/descent for a nice perfect baseline and integer total height.
|
||||
// All we need to do is adapt it into Design Units so it meshes nicely with the Design Units above.
|
||||
// Use the formula: Pixels * Design Units Per Em / Font Size = Design Units
|
||||
const auto cellAscentDesignUnits = ascentPixels * fontMetrics.designUnitsPerEm / scaledFontSize;
|
||||
const auto cellDescentDesignUnits = descentPixels * fontMetrics.designUnitsPerEm / scaledFontSize;
|
||||
const auto cellHeightDesignUnits = cellAscentDesignUnits + cellDescentDesignUnits;
|
||||
|
||||
// OK, now do a few checks. If the drawn box touches the top and bottom of the cell
|
||||
// and the box is overall tall enough, then we'll not bother adjusting.
|
||||
// We will presume the font author has set things as they wish them to be.
|
||||
const auto boxTouchesCellTop = boxAscentDesignUnits >= cellAscentDesignUnits;
|
||||
const auto boxTouchesCellBottom = boxDescentDesignUnits >= cellDescentDesignUnits;
|
||||
const auto boxIsTallEnoughForCell = boxHeightDesignUnits >= cellHeightDesignUnits;
|
||||
|
||||
// If not...
|
||||
if (!(boxTouchesCellTop && boxTouchesCellBottom && boxIsTallEnoughForCell))
|
||||
{
|
||||
// Find a scaling factor that will make the total height drawn of this box
|
||||
// perfectly fit the same number of design units as the cell.
|
||||
// Since scale factor is a multiplier, it doesn't matter that this is design units.
|
||||
// The fraction between the two heights in pixels should be exactly the same
|
||||
// (which is what will matter when we go to actually render it... the pixels that is.)
|
||||
// Don't scale below 1.0. If it'd shrink, just center it at the prescribed scale.
|
||||
boxVerticalScaleFactor = std::max(cellHeightDesignUnits / boxHeightDesignUnits, 1.0f);
|
||||
|
||||
// The box as scaled might be hanging over the top or bottom of the cell (or both).
|
||||
// We find out the amount of overhang/underhang on both the top and the bottom.
|
||||
const auto extraAscent = boxAscentDesignUnits * boxVerticalScaleFactor - cellAscentDesignUnits;
|
||||
const auto extraDescent = boxDescentDesignUnits * boxVerticalScaleFactor - cellDescentDesignUnits;
|
||||
|
||||
// This took a bit of time and effort and it's difficult to put into words, but here goes.
|
||||
// We want the average of the two magnitudes to find out how much to "take" from one and "give"
|
||||
// to the other such that both are equal. We presume the glyphs are designed to be drawn
|
||||
// centered in their box vertically to look good.
|
||||
// The ordering around subtraction is required to ensure that the direction is correct with a negative
|
||||
// translation moving up (taking excess descent and adding to ascent) and positive is the opposite.
|
||||
const auto boxVerticalTranslationDesignUnits = (extraAscent - extraDescent) / 2;
|
||||
|
||||
// The translation is just a raw movement of pixels up or down. Since we were working in Design Units,
|
||||
// we need to run the opposite algorithm shown above to go from Design Units to Pixels.
|
||||
boxVerticalTranslation = boxVerticalTranslationDesignUnits * scaledFontSize / fontMetrics.designUnitsPerEm;
|
||||
}
|
||||
}
|
||||
|
||||
// The horizontal adjustments follow the exact same logic as the vertical ones.
|
||||
const auto defaultBoxHorizontalScaleFactor = 1.0f;
|
||||
auto boxHorizontalScaleFactor = defaultBoxHorizontalScaleFactor;
|
||||
const auto defaultBoxHorizontalTranslation = 0.0f;
|
||||
auto boxHorizontalTranslation = defaultBoxHorizontalTranslation;
|
||||
{
|
||||
// This is the only difference. We don't have a horizontalOriginX from the metrics.
|
||||
// However, https://docs.microsoft.com/en-us/windows/win32/api/dwrite/ns-dwrite-dwrite_glyph_metrics says
|
||||
// the X coordinate is specified by half the advanceWidth to the right of the horizontalOrigin.
|
||||
// So we'll use that as the "center" and apply it the role that verticalOriginY had above.
|
||||
|
||||
const auto boxCenterDesignUnits = boxMetrics.advanceWidth / 2;
|
||||
const auto boxLeftDesignUnits = boxCenterDesignUnits - boxMetrics.leftSideBearing;
|
||||
const auto boxRightDesignUnits = boxMetrics.advanceWidth - boxMetrics.rightSideBearing - boxCenterDesignUnits;
|
||||
const auto boxWidthDesignUnits = boxLeftDesignUnits + boxRightDesignUnits;
|
||||
|
||||
const auto cellWidthDesignUnits = widthPixels * fontMetrics.designUnitsPerEm / scaledFontSize;
|
||||
const auto cellLeftDesignUnits = cellWidthDesignUnits / 2;
|
||||
const auto cellRightDesignUnits = cellLeftDesignUnits;
|
||||
|
||||
const auto boxTouchesCellLeft = boxLeftDesignUnits >= cellLeftDesignUnits;
|
||||
const auto boxTouchesCellRight = boxRightDesignUnits >= cellRightDesignUnits;
|
||||
const auto boxIsWideEnoughForCell = boxWidthDesignUnits >= cellWidthDesignUnits;
|
||||
|
||||
if (!(boxTouchesCellLeft && boxTouchesCellRight && boxIsWideEnoughForCell))
|
||||
{
|
||||
boxHorizontalScaleFactor = std::max(cellWidthDesignUnits / boxWidthDesignUnits, 1.0f);
|
||||
const auto extraLeft = boxLeftDesignUnits * boxHorizontalScaleFactor - cellLeftDesignUnits;
|
||||
const auto extraRight = boxRightDesignUnits * boxHorizontalScaleFactor - cellRightDesignUnits;
|
||||
|
||||
const auto boxHorizontalTranslationDesignUnits = (extraLeft - extraRight) / 2;
|
||||
|
||||
boxHorizontalTranslation = boxHorizontalTranslationDesignUnits * scaledFontSize / fontMetrics.designUnitsPerEm;
|
||||
}
|
||||
}
|
||||
|
||||
// If we set anything, make a drawing effect. Otherwise, there isn't one.
|
||||
if (defaultBoxVerticalScaleFactor != boxVerticalScaleFactor ||
|
||||
defaultBoxVerticalTranslation != boxVerticalTranslation ||
|
||||
defaultBoxHorizontalScaleFactor != boxHorizontalScaleFactor ||
|
||||
defaultBoxHorizontalTranslation != boxHorizontalTranslation)
|
||||
{
|
||||
// OK, make the object that will represent our effect, stuff the metrics into it, and return it.
|
||||
RETURN_IF_FAILED(WRL::MakeAndInitialize<BoxDrawingEffect>(effect, boxVerticalScaleFactor, boxVerticalTranslation, boxHorizontalScaleFactor, boxHorizontalTranslation));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN()
|
||||
|
||||
// Routine Description:
|
||||
// - Returns whether the user set or updated any of the font features to be applied
|
||||
bool DxFontRenderData::DidUserSetFeatures() const noexcept
|
||||
{
|
||||
return _didUserSetFeatures;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Returns whether the user set or updated any of the font axes to be applied
|
||||
bool DxFontRenderData::DidUserSetAxes() const noexcept
|
||||
{
|
||||
return _didUserSetAxes;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Function called to inform us whether to use the user set weight
|
||||
// in the font axes
|
||||
// - Called by CustomTextLayout, when the text attribute is intense we should
|
||||
// ignore the user set weight, otherwise setting the bold font axis
|
||||
// breaks the bold font attribute
|
||||
// Arguments:
|
||||
// - inhibitUserWeight: boolean that tells us if we should use the user set weight
|
||||
// in the font axes
|
||||
void DxFontRenderData::InhibitUserWeight(bool inhibitUserWeight) noexcept
|
||||
{
|
||||
_inhibitUserWeight = inhibitUserWeight;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Returns whether the set italic in the font axes
|
||||
// Return Value:
|
||||
// - True if the user set the italic axis to 1,
|
||||
// false if the italic axis is not present or the italic axis is set to 0
|
||||
bool DxFontRenderData::DidUserSetItalic() const noexcept
|
||||
{
|
||||
return _didUserSetItalic;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Updates our internal map of font features with the given features
|
||||
// - NOTE TO CALLER: Make sure to call _BuildFontRenderData after calling this for the feature changes
|
||||
// to take place
|
||||
// Arguments:
|
||||
// - features - the features to update our map with
|
||||
void DxFontRenderData::_SetFeatures(const std::unordered_map<std::wstring_view, uint32_t>& features)
|
||||
{
|
||||
// Populate the feature map with the standard list first
|
||||
std::unordered_map<DWRITE_FONT_FEATURE_TAG, uint32_t> featureMap{
|
||||
{ DWRITE_MAKE_FONT_FEATURE_TAG('c', 'a', 'l', 't'), 1 }, // Contextual Alternates
|
||||
{ DWRITE_MAKE_FONT_FEATURE_TAG('l', 'i', 'g', 'a'), 1 }, // Standard Ligatures
|
||||
{ DWRITE_MAKE_FONT_FEATURE_TAG('c', 'l', 'i', 'g'), 1 }, // Contextual Ligatures
|
||||
{ DWRITE_MAKE_FONT_FEATURE_TAG('k', 'e', 'r', 'n'), 1 } // Kerning
|
||||
};
|
||||
|
||||
// Update our feature map with the provided features
|
||||
if (!features.empty())
|
||||
{
|
||||
for (const auto& [tag, param] : features)
|
||||
{
|
||||
if (tag.length() == TAG_LENGTH)
|
||||
{
|
||||
featureMap.insert_or_assign(DWRITE_MAKE_FONT_FEATURE_TAG(til::at(tag, 0), til::at(tag, 1), til::at(tag, 2), til::at(tag, 3)), param);
|
||||
}
|
||||
}
|
||||
_didUserSetFeatures = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_didUserSetFeatures = false;
|
||||
}
|
||||
|
||||
// Convert the data to DWRITE_FONT_FEATURE and store it in a vector for CustomTextLayout
|
||||
_featureVector.clear();
|
||||
for (const auto [tag, param] : featureMap)
|
||||
{
|
||||
_featureVector.push_back(DWRITE_FONT_FEATURE{ tag, param });
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Updates our internal map of font axes with the given axes
|
||||
// - NOTE TO CALLER: Make sure to call _BuildFontRenderData after calling this for the axes changes
|
||||
// to take place
|
||||
// Arguments:
|
||||
// - axes - the axes to update our map with
|
||||
void DxFontRenderData::_SetAxes(const std::unordered_map<std::wstring_view, float>& axes)
|
||||
{
|
||||
// Clear out the old vector and booleans in case this is a hot reload
|
||||
_axesVector = std::vector<DWRITE_FONT_AXIS_VALUE>{};
|
||||
_didUserSetAxes = false;
|
||||
_didUserSetItalic = false;
|
||||
|
||||
// Update our axis map with the provided axes
|
||||
if (!axes.empty())
|
||||
{
|
||||
// Store the weight aside: we will be creating a span of all the axes in the vector except the weight,
|
||||
// and then we will add the weight to the vector
|
||||
// We are doing this so that when the text attribute is intense, we can apply all the axes except the weight
|
||||
std::optional<DWRITE_FONT_AXIS_VALUE> weightAxis;
|
||||
|
||||
// Since we are calling an 'emplace_back' after creating the span,
|
||||
// there is a chance a reallocation happens (if the vector needs to grow), which would make the span point to
|
||||
// deallocated memory. To avoid this, make sure to reserve enough memory in the vector.
|
||||
_axesVector.reserve(axes.size());
|
||||
|
||||
#pragma warning(suppress : 26445) // the analyzer doesn't like reference to string_view
|
||||
for (const auto& [axis, value] : axes)
|
||||
{
|
||||
if (axis.length() == TAG_LENGTH)
|
||||
{
|
||||
const auto dwriteFontAxis = DWRITE_FONT_AXIS_VALUE{ DWRITE_MAKE_FONT_AXIS_TAG(til::at(axis, 0), til::at(axis, 1), til::at(axis, 2), til::at(axis, 3)), value };
|
||||
if (dwriteFontAxis.axisTag != DWRITE_FONT_AXIS_TAG_WEIGHT)
|
||||
{
|
||||
_axesVector.emplace_back(dwriteFontAxis);
|
||||
}
|
||||
else
|
||||
{
|
||||
weightAxis = dwriteFontAxis;
|
||||
}
|
||||
_didUserSetItalic |= dwriteFontAxis.axisTag == DWRITE_FONT_AXIS_TAG_ITALIC && value == 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Make the span, which has all the axes except the weight
|
||||
_axesVectorWithoutWeight = std::span{ _axesVector };
|
||||
|
||||
// Add the weight axis to the vector if needed
|
||||
if (weightAxis)
|
||||
{
|
||||
_axesVector.emplace_back(weightAxis.value());
|
||||
}
|
||||
_didUserSetAxes = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Converts a DWRITE_FONT_STRETCH enum into the corresponding float value to
|
||||
// create a DWRITE_FONT_AXIS_VALUE with
|
||||
// Arguments:
|
||||
// - fontStretch: the old DWRITE_FONT_STRETCH enum to be converted into an axis value
|
||||
// Return value:
|
||||
// - The float value corresponding to the passed in fontStretch
|
||||
float DxFontRenderData::_FontStretchToWidthAxisValue(DWRITE_FONT_STRETCH fontStretch) noexcept
|
||||
{
|
||||
// 10 elements from DWRITE_FONT_STRETCH_UNDEFINED (0) to DWRITE_FONT_STRETCH_ULTRA_EXPANDED (9)
|
||||
static constexpr auto fontStretchEnumToVal = std::array{ 100.0f, 50.0f, 62.5f, 75.0f, 87.5f, 100.0f, 112.5f, 125.0f, 150.0f, 200.0f };
|
||||
|
||||
if (gsl::narrow_cast<size_t>(fontStretch) > fontStretchEnumToVal.size())
|
||||
{
|
||||
fontStretch = DWRITE_FONT_STRETCH_NORMAL;
|
||||
}
|
||||
|
||||
return til::at(fontStretchEnumToVal, fontStretch);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Converts a DWRITE_FONT_STYLE enum into the corresponding float value to
|
||||
// create a DWRITE_FONT_AXIS_VALUE with
|
||||
// Arguments:
|
||||
// - fontStyle: the old DWRITE_FONT_STYLE enum to be converted into an axis value
|
||||
// Return value:
|
||||
// - The float value corresponding to the passed in fontStyle
|
||||
float DxFontRenderData::_FontStyleToSlantFixedAxisValue(DWRITE_FONT_STYLE fontStyle) noexcept
|
||||
{
|
||||
// DWRITE_FONT_STYLE_NORMAL (0), DWRITE_FONT_STYLE_OBLIQUE (1), DWRITE_FONT_STYLE_ITALIC (2)
|
||||
static constexpr auto fontStyleEnumToVal = std::array{ 0.0f, -20.0f, -12.0f };
|
||||
|
||||
// Both DWRITE_FONT_STYLE_OBLIQUE and DWRITE_FONT_STYLE_ITALIC default to having slant.
|
||||
// Though an italic font technically need not have slant (there exist upright ones), the
|
||||
// vast majority of italic fonts are also slanted. Ideally the slant comes from the
|
||||
// 'slnt' value in the STAT or fvar table, or the post table italic angle.
|
||||
|
||||
if (gsl::narrow_cast<size_t>(fontStyle) > fontStyleEnumToVal.size())
|
||||
{
|
||||
fontStyle = DWRITE_FONT_STYLE_NORMAL;
|
||||
}
|
||||
|
||||
return til::at(fontStyleEnumToVal, fontStyle);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Fill any missing axis values that might be known but were unspecified, such as omitting
|
||||
// the 'wght' axis tag but specifying the old DWRITE_FONT_WEIGHT enum
|
||||
// - This function will only be called with a valid IDWriteTextFormat3
|
||||
// (on platforms where IDWriteTextFormat3 is supported)
|
||||
// Arguments:
|
||||
// - fontWeight: the old DWRITE_FONT_WEIGHT enum to be converted into an axis value
|
||||
// - fontStretch: the old DWRITE_FONT_STRETCH enum to be converted into an axis value
|
||||
// - fontStyle: the old DWRITE_FONT_STYLE enum to be converted into an axis value
|
||||
// - fontSize: the number to convert into an axis value
|
||||
// - format: the IDWriteTextFormat3 to get the defined axes from
|
||||
// Return value:
|
||||
// - The fully formed axes vector
|
||||
#pragma warning(suppress : 26429) // the analyzer doesn't detect that our FAIL_FAST_IF_NULL macro \
|
||||
// checks format for nullness
|
||||
std::vector<DWRITE_FONT_AXIS_VALUE> DxFontRenderData::GetAxisVector(const DWRITE_FONT_WEIGHT fontWeight,
|
||||
const DWRITE_FONT_STRETCH fontStretch,
|
||||
const DWRITE_FONT_STYLE fontStyle,
|
||||
IDWriteTextFormat3* format)
|
||||
{
|
||||
FAIL_FAST_IF_NULL(format);
|
||||
|
||||
const auto axesCount = format->GetFontAxisValueCount();
|
||||
std::vector<DWRITE_FONT_AXIS_VALUE> axesVector;
|
||||
axesVector.resize(axesCount);
|
||||
format->GetFontAxisValues(axesVector.data(), axesCount);
|
||||
|
||||
auto axisTagPresence = AxisTagPresence::None;
|
||||
for (const auto& fontAxisValue : axesVector)
|
||||
{
|
||||
switch (fontAxisValue.axisTag)
|
||||
{
|
||||
case DWRITE_FONT_AXIS_TAG_WEIGHT:
|
||||
WI_SetFlag(axisTagPresence, AxisTagPresence::Weight);
|
||||
break;
|
||||
case DWRITE_FONT_AXIS_TAG_WIDTH:
|
||||
WI_SetFlag(axisTagPresence, AxisTagPresence::Width);
|
||||
break;
|
||||
case DWRITE_FONT_AXIS_TAG_ITALIC:
|
||||
WI_SetFlag(axisTagPresence, AxisTagPresence::Italic);
|
||||
break;
|
||||
case DWRITE_FONT_AXIS_TAG_SLANT:
|
||||
WI_SetFlag(axisTagPresence, AxisTagPresence::Slant);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (WI_IsFlagClear(axisTagPresence, AxisTagPresence::Weight))
|
||||
{
|
||||
axesVector.emplace_back(DWRITE_FONT_AXIS_VALUE{ DWRITE_FONT_AXIS_TAG_WEIGHT, gsl::narrow<float>(fontWeight) });
|
||||
}
|
||||
if (WI_IsFlagClear(axisTagPresence, AxisTagPresence::Width))
|
||||
{
|
||||
axesVector.emplace_back(DWRITE_FONT_AXIS_VALUE{ DWRITE_FONT_AXIS_TAG_WIDTH, _FontStretchToWidthAxisValue(fontStretch) });
|
||||
}
|
||||
if (WI_IsFlagClear(axisTagPresence, AxisTagPresence::Italic))
|
||||
{
|
||||
axesVector.emplace_back(DWRITE_FONT_AXIS_VALUE{ DWRITE_FONT_AXIS_TAG_ITALIC, (fontStyle == DWRITE_FONT_STYLE_ITALIC ? 1.0f : 0.0f) });
|
||||
}
|
||||
if (WI_IsFlagClear(axisTagPresence, AxisTagPresence::Slant))
|
||||
{
|
||||
axesVector.emplace_back(DWRITE_FONT_AXIS_VALUE{ DWRITE_FONT_AXIS_TAG_SLANT, _FontStyleToSlantFixedAxisValue(fontStyle) });
|
||||
}
|
||||
|
||||
return axesVector;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Build the needed data for rendering according to the font used
|
||||
// Arguments:
|
||||
// - desired - Information specifying the font that is requested
|
||||
// - actual - Filled with the nearest font actually chosen for drawing
|
||||
// - dpi - The DPI of the screen
|
||||
// Return Value:
|
||||
// - None
|
||||
void DxFontRenderData::_BuildFontRenderData(const FontInfoDesired& desired, FontInfo& actual, const int dpi)
|
||||
{
|
||||
const auto dpiF = static_cast<float>(dpi);
|
||||
auto fontLocaleName = UserLocaleName();
|
||||
// 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 auto face = _defaultFontInfo.ResolveFontFaceWithFallback(fontLocaleName);
|
||||
|
||||
DWRITE_FONT_METRICS1 fontMetrics;
|
||||
face->GetMetrics(&fontMetrics);
|
||||
|
||||
const UINT32 spaceCodePoint = L'M';
|
||||
UINT16 spaceGlyphIndex;
|
||||
THROW_IF_FAILED(face->GetGlyphIndicesW(&spaceCodePoint, 1, &spaceGlyphIndex));
|
||||
|
||||
INT32 advanceInDesignUnits;
|
||||
THROW_IF_FAILED(face->GetDesignGlyphAdvances(1, &spaceGlyphIndex, &advanceInDesignUnits));
|
||||
|
||||
DWRITE_GLYPH_METRICS spaceMetrics = { 0 };
|
||||
THROW_IF_FAILED(face->GetDesignGlyphMetrics(&spaceGlyphIndex, 1, &spaceMetrics));
|
||||
|
||||
// The math here is actually:
|
||||
// Requested Size in Points * DPI scaling factor * Points to Pixels scaling factor.
|
||||
// - DPI = dots per inch
|
||||
// - PPI = points per inch or "points" as usually seen when choosing a font size
|
||||
// - The DPI scaling factor is the current monitor DPI divided by 96, the default DPI.
|
||||
// - The Points to Pixels factor is based on the typography definition of 72 points per inch.
|
||||
// As such, converting requires taking the 96 pixel per inch default and dividing by the 72 points per inch
|
||||
// to get a factor of 1 and 1/3.
|
||||
// This turns into something like:
|
||||
// - 12 ppi font * (96 dpi / 96 dpi) * (96 dpi / 72 points per inch) = 16 pixels tall font for 100% display (96 dpi is 100%)
|
||||
// - 12 ppi font * (144 dpi / 96 dpi) * (96 dpi / 72 points per inch) = 24 pixels tall font for 150% display (144 dpi is 150%)
|
||||
// - 12 ppi font * (192 dpi / 96 dpi) * (96 dpi / 72 points per inch) = 32 pixels tall font for 200% display (192 dpi is 200%)
|
||||
const auto heightDesired = desired.GetEngineSize().height / POINTS_PER_INCH * dpiF;
|
||||
|
||||
// The advance is the number of pixels left-to-right (X dimension) for the given font.
|
||||
// We're finding a proportional factor here with the design units in "ems", not an actual pixel measurement.
|
||||
const auto widthAdvance = static_cast<float>(advanceInDesignUnits) / fontMetrics.designUnitsPerEm;
|
||||
|
||||
// Use the real pixel height desired by the "em" factor for the width to get the number of pixels
|
||||
// we will need per character in width. This will almost certainly result in fractional X-dimension pixels.
|
||||
const auto widthAdvanceInPx = heightDesired * widthAdvance;
|
||||
|
||||
// Now reverse the "em" factor from above to turn the exact pixel width into a (probably) fractional
|
||||
// height in pixels of each character. It's easier for us to pad out height and align vertically
|
||||
// than it is horizontally.
|
||||
const auto fontSize = roundf(widthAdvanceInPx) / widthAdvance;
|
||||
_fontSize = fontSize;
|
||||
|
||||
// Now figure out the basic properties of the character height which include ascent and descent
|
||||
// for this specific font size.
|
||||
const auto ascent = (fontSize * fontMetrics.ascent) / fontMetrics.designUnitsPerEm;
|
||||
const auto descent = (fontSize * fontMetrics.descent) / fontMetrics.designUnitsPerEm;
|
||||
|
||||
// Get the gap.
|
||||
const auto gap = (fontSize * fontMetrics.lineGap) / fontMetrics.designUnitsPerEm;
|
||||
const auto halfGap = gap / 2;
|
||||
|
||||
// We're going to build a line spacing object here to track all of this data in our format.
|
||||
DWRITE_LINE_SPACING lineSpacing = {};
|
||||
lineSpacing.method = DWRITE_LINE_SPACING_METHOD_UNIFORM;
|
||||
|
||||
// We need to make sure the baseline falls on a round pixel (not a fractional pixel).
|
||||
// If the baseline is fractional, the text appears blurry, especially at small scales.
|
||||
// Since we also need to make sure the bounding box as a whole is round pixels
|
||||
// (because the entire console system maths in full cell units),
|
||||
// we're just going to ceiling up the ascent and descent to make a full pixel amount
|
||||
// and set the baseline to the full round pixel ascent value.
|
||||
//
|
||||
// For reference, for the letters "ag":
|
||||
// ...
|
||||
// gggggg bottom of previous line
|
||||
//
|
||||
// ----------------- <===========================================|
|
||||
// | topSideBearing | 1/2 lineGap |
|
||||
// aaaaaa ggggggg <-------------------------|-------------| |
|
||||
// a g g | | |
|
||||
// aaaaa ggggg |<-ascent | |
|
||||
// a a g | | |---- lineHeight
|
||||
// aaaaa a gggggg <----baseline, verticalOriginY----------|---|
|
||||
// g g |<-descent | |
|
||||
// gggggg <-------------------------|-------------| |
|
||||
// | bottomSideBearing | 1/2 lineGap |
|
||||
// ----------------- <===========================================|
|
||||
//
|
||||
// aaaaaa ggggggg top of next line
|
||||
// ...
|
||||
//
|
||||
// Also note...
|
||||
// We're going to add half the line gap to the ascent and half the line gap to the descent
|
||||
// to ensure that the spacing is balanced vertically.
|
||||
// Generally speaking, the line gap is added to the ascent by DirectWrite itself for
|
||||
// horizontally drawn text which can place the baseline and glyphs "lower" in the drawing
|
||||
// box than would be desired for proper alignment of things like line and box characters
|
||||
// which will try to sit centered in the area and touch perfectly with their neighbors.
|
||||
|
||||
const auto fullPixelAscent = ceil(ascent + halfGap);
|
||||
const auto fullPixelDescent = ceil(descent + halfGap);
|
||||
const auto defaultHeight = fullPixelAscent + fullPixelDescent;
|
||||
const auto lineHeight = desired.GetCellHeight().Resolve(defaultHeight, dpiF, heightDesired, widthAdvanceInPx);
|
||||
const auto baseline = fullPixelAscent + (lineHeight - defaultHeight) / 2.0f;
|
||||
|
||||
lineSpacing.height = roundf(lineHeight);
|
||||
lineSpacing.baseline = roundf(baseline);
|
||||
|
||||
// According to MSDN (https://docs.microsoft.com/en-us/windows/win32/api/dwrite_3/ne-dwrite_3-dwrite_font_line_gap_usage)
|
||||
// Setting "ENABLED" means we've included the line gapping in the spacing numbers given.
|
||||
lineSpacing.fontLineGapUsage = DWRITE_FONT_LINE_GAP_USAGE_ENABLED;
|
||||
|
||||
_lineSpacing = lineSpacing;
|
||||
|
||||
const auto widthApprox = desired.GetCellWidth().Resolve(widthAdvanceInPx, dpiF, heightDesired, widthAdvanceInPx);
|
||||
const auto widthExact = roundf(widthApprox);
|
||||
|
||||
// The scaled size needs to represent the pixel box that each character will fit within for the purposes
|
||||
// of hit testing math and other such multiplication/division.
|
||||
til::size coordSize;
|
||||
coordSize.width = static_cast<til::CoordType>(widthExact);
|
||||
coordSize.height = static_cast<til::CoordType>(lineSpacing.height);
|
||||
|
||||
// Unscaled is for the purposes of re-communicating this font back to the renderer again later.
|
||||
// As such, we need to give the same original size parameter back here without padding
|
||||
// or rounding or scaling manipulation.
|
||||
const auto unscaled = desired.GetEngineSize();
|
||||
|
||||
const auto scaled = coordSize;
|
||||
|
||||
actual.SetFromEngine(_defaultFontInfo.GetFamilyName(),
|
||||
desired.GetFamily(),
|
||||
DefaultTextFormat()->GetFontWeight(),
|
||||
false,
|
||||
scaled,
|
||||
unscaled);
|
||||
|
||||
actual.SetFallback(_defaultFontInfo.GetFallback());
|
||||
|
||||
LineMetrics lineMetrics;
|
||||
// There is no font metric for the grid line width, so we use a small
|
||||
// multiple of the font size, which typically rounds to a pixel.
|
||||
lineMetrics.gridlineWidth = std::round(fontSize * 0.025f);
|
||||
|
||||
// All other line metrics are in design units, so to get a pixel value,
|
||||
// we scale by the font size divided by the design-units-per-em.
|
||||
const auto scale = fontSize / fontMetrics.designUnitsPerEm;
|
||||
lineMetrics.underlineOffset = std::round(fontMetrics.underlinePosition * scale);
|
||||
lineMetrics.underlineWidth = std::round(fontMetrics.underlineThickness * scale);
|
||||
lineMetrics.strikethroughOffset = std::round(fontMetrics.strikethroughPosition * scale);
|
||||
lineMetrics.strikethroughWidth = std::round(fontMetrics.strikethroughThickness * scale);
|
||||
|
||||
// We always want the lines to be visible, so if a stroke width ends up
|
||||
// at zero after rounding, we need to make it at least 1 pixel.
|
||||
lineMetrics.gridlineWidth = std::max(lineMetrics.gridlineWidth, 1.0f);
|
||||
lineMetrics.underlineWidth = std::max(lineMetrics.underlineWidth, 1.0f);
|
||||
lineMetrics.strikethroughWidth = std::max(lineMetrics.strikethroughWidth, 1.0f);
|
||||
|
||||
// Offsets are relative to the base line of the font, so we subtract
|
||||
// from the ascent to get an offset relative to the top of the cell.
|
||||
lineMetrics.underlineOffset = lineSpacing.baseline - lineMetrics.underlineOffset;
|
||||
lineMetrics.strikethroughOffset = lineSpacing.baseline - lineMetrics.strikethroughOffset;
|
||||
|
||||
// For double underlines we need a second offset, just below the first,
|
||||
// but with a bit of a gap (about double the grid line width).
|
||||
lineMetrics.underlineOffset2 = lineMetrics.underlineOffset +
|
||||
lineMetrics.underlineWidth +
|
||||
std::round(fontSize * 0.05f);
|
||||
|
||||
// However, we don't want the underline to extend past the bottom of the
|
||||
// cell, so we clamp the offset to fit just inside.
|
||||
const auto maxUnderlineOffset = lineSpacing.height - lineMetrics.underlineWidth;
|
||||
lineMetrics.underlineOffset2 = std::min(lineMetrics.underlineOffset2, maxUnderlineOffset);
|
||||
|
||||
// But if the resulting gap isn't big enough even to register as a thicker
|
||||
// line, it's better to place the second line slightly above the first.
|
||||
if (lineMetrics.underlineOffset2 < lineMetrics.underlineOffset + lineMetrics.gridlineWidth)
|
||||
{
|
||||
lineMetrics.underlineOffset2 = lineMetrics.underlineOffset - lineMetrics.gridlineWidth;
|
||||
}
|
||||
|
||||
// We also add half the stroke width to the offsets, since the line
|
||||
// coordinates designate the center of the line.
|
||||
lineMetrics.underlineOffset += lineMetrics.underlineWidth / 2.0f;
|
||||
lineMetrics.underlineOffset2 += lineMetrics.underlineWidth / 2.0f;
|
||||
lineMetrics.strikethroughOffset += lineMetrics.strikethroughWidth / 2.0f;
|
||||
|
||||
_lineMetrics = lineMetrics;
|
||||
|
||||
_glyphCell = actual.GetSize();
|
||||
}
|
||||
|
||||
Microsoft::WRL::ComPtr<IDWriteTextFormat> DxFontRenderData::_BuildTextFormat(const DxFontInfo& fontInfo, const std::wstring_view localeName)
|
||||
{
|
||||
Microsoft::WRL::ComPtr<IDWriteTextFormat> format;
|
||||
THROW_IF_FAILED(_dwriteFactory->CreateTextFormat(fontInfo.GetFamilyName().data(),
|
||||
fontInfo.GetFontCollection(),
|
||||
fontInfo.GetWeight(),
|
||||
fontInfo.GetStyle(),
|
||||
fontInfo.GetStretch(),
|
||||
_fontSize,
|
||||
localeName.data(),
|
||||
&format));
|
||||
|
||||
// If the OS supports IDWriteTextFormat3, set the font axes
|
||||
::Microsoft::WRL::ComPtr<IDWriteTextFormat3> format3;
|
||||
if (!FAILED(format->QueryInterface(IID_PPV_ARGS(&format3))))
|
||||
{
|
||||
if (_inhibitUserWeight && !_axesVectorWithoutWeight.empty())
|
||||
{
|
||||
format3->SetFontAxisValues(_axesVectorWithoutWeight.data(), gsl::narrow<uint32_t>(_axesVectorWithoutWeight.size()));
|
||||
}
|
||||
else if (!_inhibitUserWeight && !_axesVector.empty())
|
||||
{
|
||||
format3->SetFontAxisValues(_axesVector.data(), gsl::narrow<uint32_t>(_axesVector.size()));
|
||||
}
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
@ -1,142 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../renderer/inc/FontInfoDesired.hpp"
|
||||
#include "DxFontInfo.h"
|
||||
#include "BoxDrawingEffect.h"
|
||||
|
||||
#include <dwrite.h>
|
||||
#include <dwrite_1.h>
|
||||
#include <dwrite_2.h>
|
||||
#include <dwrite_3.h>
|
||||
|
||||
#include <wrl.h>
|
||||
|
||||
namespace Microsoft::Console::Render
|
||||
{
|
||||
enum class AxisTagPresence : BYTE
|
||||
{
|
||||
None = 0x00,
|
||||
Weight = 0x01,
|
||||
Width = 0x02,
|
||||
Italic = 0x04,
|
||||
Slant = 0x08,
|
||||
};
|
||||
DEFINE_ENUM_FLAG_OPERATORS(AxisTagPresence);
|
||||
|
||||
class DxFontRenderData
|
||||
{
|
||||
public:
|
||||
struct LineMetrics
|
||||
{
|
||||
float gridlineWidth;
|
||||
float underlineOffset;
|
||||
float underlineOffset2;
|
||||
float underlineWidth;
|
||||
float strikethroughOffset;
|
||||
float strikethroughWidth;
|
||||
};
|
||||
|
||||
DxFontRenderData(::Microsoft::WRL::ComPtr<IDWriteFactory1> dwriteFactory);
|
||||
|
||||
// DirectWrite text analyzer from the factory
|
||||
[[nodiscard]] Microsoft::WRL::ComPtr<IDWriteTextAnalyzer1> Analyzer();
|
||||
|
||||
[[nodiscard]] Microsoft::WRL::ComPtr<IDWriteFontFallback> SystemFontFallback();
|
||||
|
||||
// A locale that can be used on construction of assorted DX objects that want to know one.
|
||||
[[nodiscard]] std::wstring UserLocaleName();
|
||||
|
||||
[[nodiscard]] til::size GlyphCell() noexcept;
|
||||
[[nodiscard]] LineMetrics GetLineMetrics() noexcept;
|
||||
|
||||
// The weight of default font
|
||||
[[nodiscard]] DWRITE_FONT_WEIGHT DefaultFontWeight() noexcept;
|
||||
|
||||
// The style of default font
|
||||
[[nodiscard]] DWRITE_FONT_STYLE DefaultFontStyle() noexcept;
|
||||
|
||||
// The stretch of default font
|
||||
[[nodiscard]] DWRITE_FONT_STRETCH DefaultFontStretch() noexcept;
|
||||
|
||||
// The font features of the default font
|
||||
[[nodiscard]] const std::vector<DWRITE_FONT_FEATURE>& DefaultFontFeatures() const noexcept;
|
||||
|
||||
// The DirectWrite format object representing the size and other text properties to be applied (by default)
|
||||
[[nodiscard]] Microsoft::WRL::ComPtr<IDWriteTextFormat> DefaultTextFormat();
|
||||
|
||||
// The DirectWrite font face to use while calculating layout (by default)
|
||||
[[nodiscard]] Microsoft::WRL::ComPtr<IDWriteFontFace1> DefaultFontFace();
|
||||
|
||||
// Box drawing scaling effects that are cached for the base font across layouts
|
||||
[[nodiscard]] Microsoft::WRL::ComPtr<IBoxDrawingEffect> DefaultBoxDrawingEffect();
|
||||
|
||||
// The attributed variants of the format object representing the size and other text properties
|
||||
[[nodiscard]] Microsoft::WRL::ComPtr<IDWriteTextFormat> TextFormatWithAttribute(DWRITE_FONT_WEIGHT weight,
|
||||
DWRITE_FONT_STYLE style,
|
||||
DWRITE_FONT_STRETCH stretch);
|
||||
|
||||
// The attributed variants of the font face to use while calculating layout
|
||||
[[nodiscard]] Microsoft::WRL::ComPtr<IDWriteFontFace1> FontFaceWithAttribute(DWRITE_FONT_WEIGHT weight,
|
||||
DWRITE_FONT_STYLE style,
|
||||
DWRITE_FONT_STRETCH stretch);
|
||||
|
||||
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& desired, FontInfo& fiFontInfo, const int dpi, const std::unordered_map<std::wstring_view, uint32_t>& features = {}, const std::unordered_map<std::wstring_view, float>& axes = {}) noexcept;
|
||||
|
||||
[[nodiscard]] static HRESULT STDMETHODCALLTYPE s_CalculateBoxEffect(IDWriteTextFormat* format, size_t widthPixels, IDWriteFontFace1* face, float fontScale, IBoxDrawingEffect** effect) noexcept;
|
||||
|
||||
bool DidUserSetFeatures() const noexcept;
|
||||
bool DidUserSetAxes() const noexcept;
|
||||
void InhibitUserWeight(bool inhibitUserWeight) noexcept;
|
||||
bool DidUserSetItalic() const noexcept;
|
||||
|
||||
std::vector<DWRITE_FONT_AXIS_VALUE> GetAxisVector(const DWRITE_FONT_WEIGHT fontWeight,
|
||||
const DWRITE_FONT_STRETCH fontStretch,
|
||||
const DWRITE_FONT_STYLE fontStyle,
|
||||
IDWriteTextFormat3* format);
|
||||
|
||||
private:
|
||||
using FontAttributeMapKey = uint32_t;
|
||||
|
||||
bool _inhibitUserWeight{ false };
|
||||
bool _didUserSetItalic{ false };
|
||||
bool _didUserSetFeatures{ false };
|
||||
bool _didUserSetAxes{ false };
|
||||
// The font features to apply to the text
|
||||
std::vector<DWRITE_FONT_FEATURE> _featureVector;
|
||||
|
||||
// The font axes to apply to the text
|
||||
std::vector<DWRITE_FONT_AXIS_VALUE> _axesVector;
|
||||
std::span<DWRITE_FONT_AXIS_VALUE> _axesVectorWithoutWeight;
|
||||
|
||||
// We use this to identify font variants with different attributes.
|
||||
static FontAttributeMapKey _ToMapKey(DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch) noexcept
|
||||
{
|
||||
return (weight << 16) | (style << 8) | stretch;
|
||||
};
|
||||
|
||||
void _SetFeatures(const std::unordered_map<std::wstring_view, uint32_t>& features);
|
||||
void _SetAxes(const std::unordered_map<std::wstring_view, float>& axes);
|
||||
float _FontStretchToWidthAxisValue(DWRITE_FONT_STRETCH fontStretch) noexcept;
|
||||
float _FontStyleToSlantFixedAxisValue(DWRITE_FONT_STYLE fontStyle) noexcept;
|
||||
void _BuildFontRenderData(const FontInfoDesired& desired, FontInfo& actual, const int dpi);
|
||||
Microsoft::WRL::ComPtr<IDWriteTextFormat> _BuildTextFormat(const DxFontInfo& fontInfo, const std::wstring_view localeName);
|
||||
|
||||
std::unordered_map<FontAttributeMapKey, ::Microsoft::WRL::ComPtr<IDWriteTextFormat>> _textFormatMap;
|
||||
std::unordered_map<FontAttributeMapKey, ::Microsoft::WRL::ComPtr<IDWriteFontFace1>> _fontFaceMap;
|
||||
|
||||
::Microsoft::WRL::ComPtr<IBoxDrawingEffect> _boxDrawingEffect;
|
||||
::Microsoft::WRL::ComPtr<IDWriteFontFallback> _systemFontFallback;
|
||||
::Microsoft::WRL::ComPtr<IDWriteFactory1> _dwriteFactory;
|
||||
::Microsoft::WRL::ComPtr<IDWriteTextAnalyzer1> _dwriteTextAnalyzer;
|
||||
|
||||
std::wstring _userLocaleName;
|
||||
DxFontInfo _defaultFontInfo;
|
||||
til::size _glyphCell;
|
||||
DWRITE_LINE_SPACING _lineSpacing;
|
||||
LineMetrics _lineMetrics;
|
||||
float _fontSize;
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,327 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../renderer/inc/RenderEngineBase.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <dxgi.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <dxgi1_3.h>
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <d2d1.h>
|
||||
#include <d2d1_1.h>
|
||||
#include <d2d1helper.h>
|
||||
#include <DirectXMath.h>
|
||||
#include <dwrite.h>
|
||||
#include <dwrite_1.h>
|
||||
#include <dwrite_2.h>
|
||||
#include <dwrite_3.h>
|
||||
|
||||
#include <wrl.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include "CustomTextLayout.h"
|
||||
#include "CustomTextRenderer.h"
|
||||
#include "DxFontRenderData.h"
|
||||
#include "DxSoftFont.h"
|
||||
|
||||
#include "../../types/inc/Viewport.hpp"
|
||||
|
||||
#include <TraceLoggingProvider.h>
|
||||
|
||||
TRACELOGGING_DECLARE_PROVIDER(g_hDxRenderProvider);
|
||||
|
||||
namespace Microsoft::Console::Render
|
||||
{
|
||||
class DxEngine final : public RenderEngineBase
|
||||
{
|
||||
public:
|
||||
DxEngine();
|
||||
~DxEngine();
|
||||
DxEngine(const DxEngine&) = default;
|
||||
DxEngine(DxEngine&&) = default;
|
||||
DxEngine& operator=(const DxEngine&) = default;
|
||||
DxEngine& operator=(DxEngine&&) = default;
|
||||
|
||||
// Used to release device resources so that another instance of
|
||||
// conhost can render to the screen (i.e. only one DirectX
|
||||
// application may control the screen at a time.)
|
||||
[[nodiscard]] HRESULT Enable() noexcept override;
|
||||
[[nodiscard]] HRESULT Disable() noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT SetHwnd(const HWND hwnd) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT SetWindowSize(const til::size pixels) noexcept override;
|
||||
|
||||
void SetCallback(std::function<void(const HANDLE)> pfn) noexcept override;
|
||||
void SetWarningCallback(std::function<void(const HRESULT)> pfn) noexcept override;
|
||||
|
||||
bool GetRetroTerminalEffect() const noexcept override;
|
||||
void SetRetroTerminalEffect(bool enable) noexcept override;
|
||||
|
||||
std::wstring_view GetPixelShaderPath() noexcept override;
|
||||
void SetPixelShaderPath(std::wstring_view value) noexcept override;
|
||||
|
||||
void SetForceFullRepaintRendering(bool enable) noexcept override;
|
||||
|
||||
void SetSoftwareRendering(bool enable) noexcept override;
|
||||
|
||||
// IRenderEngine Members
|
||||
[[nodiscard]] HRESULT Invalidate(const til::rect* const psrRegion) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateCursor(const til::rect* const psrRegion) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateSystem(const til::rect* const prcDirtyClient) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateSelection(const std::vector<til::rect>& rectangles) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateScroll(const til::point* const pcoordDelta) noexcept override;
|
||||
[[nodiscard]] HRESULT InvalidateAll() noexcept override;
|
||||
[[nodiscard]] HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT StartPaint() noexcept override;
|
||||
[[nodiscard]] HRESULT EndPaint() noexcept override;
|
||||
|
||||
[[nodiscard]] bool RequiresContinuousRedraw() noexcept override;
|
||||
|
||||
void WaitUntilCanRender() noexcept override;
|
||||
[[nodiscard]] HRESULT Present() noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT ScrollFrame() noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT UpdateSoftFont(const std::span<const uint16_t> bitPattern,
|
||||
const til::size cellSize,
|
||||
const size_t centeringHint) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT ResetLineTransform() noexcept override;
|
||||
[[nodiscard]] HRESULT PrepareLineTransform(const LineRendition lineRendition,
|
||||
const til::CoordType targetRow,
|
||||
const til::CoordType viewportLeft) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT PaintBackground() noexcept override;
|
||||
[[nodiscard]] HRESULT PaintBufferLine(const std::span<const Cluster> clusters,
|
||||
const til::point coord,
|
||||
const bool fTrimLeft,
|
||||
const bool lineWrapped) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override;
|
||||
[[nodiscard]] HRESULT PaintSelections(const std::vector<til::rect>& rect) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes,
|
||||
const RenderSettings& renderSettings,
|
||||
const gsl::not_null<IRenderData*> pData,
|
||||
const bool usingSoftFont,
|
||||
const bool isSettingDefaultBrushes) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, const std::unordered_map<std::wstring_view, uint32_t>& features, const std::unordered_map<std::wstring_view, float>& axes) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateDpi(const int iDpi) noexcept override;
|
||||
[[nodiscard]] HRESULT UpdateViewport(const til::inclusive_rect& srNewViewport) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, const int iDpi) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT GetDirtyArea(std::span<const til::rect>& area) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT GetFontSize(_Out_ til::size* pFontSize) noexcept override;
|
||||
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
|
||||
|
||||
[[nodiscard]] ::Microsoft::Console::Types::Viewport GetViewportInCharacters(const ::Microsoft::Console::Types::Viewport& viewInPixels) const noexcept override;
|
||||
[[nodiscard]] ::Microsoft::Console::Types::Viewport GetViewportInPixels(const ::Microsoft::Console::Types::Viewport& viewInCharacters) const noexcept override;
|
||||
|
||||
float GetScaling() const noexcept override;
|
||||
|
||||
void SetSelectionBackground(const COLORREF color, const float alpha = 0.5f) noexcept override;
|
||||
void SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept override;
|
||||
void EnableTransparentBackground(const bool isTransparent) noexcept override;
|
||||
|
||||
void UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept override;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] HRESULT _DoUpdateTitle(_In_ const std::wstring_view newTitle) noexcept override;
|
||||
[[nodiscard]] HRESULT _PaintTerminalEffects() noexcept;
|
||||
[[nodiscard]] bool _FullRepaintNeeded() const noexcept;
|
||||
|
||||
private:
|
||||
enum class SwapChainMode
|
||||
{
|
||||
ForHwnd,
|
||||
ForComposition
|
||||
};
|
||||
|
||||
SwapChainMode _chainMode;
|
||||
|
||||
HWND _hwndTarget;
|
||||
til::size _sizeTarget;
|
||||
int _dpi;
|
||||
float _scale;
|
||||
float _prevScale;
|
||||
|
||||
std::function<void(const HANDLE)> _pfn;
|
||||
std::function<void(const HRESULT)> _pfnWarningCallback;
|
||||
|
||||
bool _isEnabled;
|
||||
bool _isPainting;
|
||||
|
||||
til::size _displaySizePixels;
|
||||
|
||||
D2D1_COLOR_F _defaultForegroundColor;
|
||||
D2D1_COLOR_F _defaultBackgroundColor;
|
||||
|
||||
D2D1_COLOR_F _foregroundColor;
|
||||
D2D1_COLOR_F _backgroundColor;
|
||||
D2D1_COLOR_F _selectionBackground;
|
||||
|
||||
LineRendition _currentLineRendition;
|
||||
D2D1::Matrix3x2F _currentLineTransform;
|
||||
|
||||
uint16_t _hyperlinkHoveredId;
|
||||
|
||||
bool _firstFrame;
|
||||
std::pmr::unsynchronized_pool_resource _pool;
|
||||
til::pmr::bitmap _invalidMap;
|
||||
til::point _invalidScroll;
|
||||
bool _allInvalid;
|
||||
|
||||
bool _presentReady;
|
||||
std::vector<til::rect> _presentDirty;
|
||||
RECT _presentScroll;
|
||||
POINT _presentOffset;
|
||||
DXGI_PRESENT_PARAMETERS _presentParams;
|
||||
|
||||
static std::atomic<size_t> _tracelogCount;
|
||||
|
||||
wil::unique_handle _swapChainHandle;
|
||||
|
||||
// Device-Independent Resources
|
||||
::Microsoft::WRL::ComPtr<ID2D1Factory1> _d2dFactory;
|
||||
|
||||
::Microsoft::WRL::ComPtr<IDWriteFactory1> _dwriteFactory;
|
||||
::Microsoft::WRL::ComPtr<CustomTextLayout> _customLayout;
|
||||
::Microsoft::WRL::ComPtr<CustomTextRenderer> _customRenderer;
|
||||
::Microsoft::WRL::ComPtr<ID2D1StrokeStyle> _strokeStyle;
|
||||
::Microsoft::WRL::ComPtr<ID2D1StrokeStyle> _dashStrokeStyle;
|
||||
|
||||
std::unique_ptr<DxFontRenderData> _fontRenderData;
|
||||
DxSoftFont _softFont;
|
||||
bool _usingSoftFont;
|
||||
|
||||
D2D1_STROKE_STYLE_PROPERTIES _strokeStyleProperties;
|
||||
D2D1_STROKE_STYLE_PROPERTIES _dashStrokeStyleProperties;
|
||||
|
||||
// Device-Dependent Resources
|
||||
bool _recreateDeviceRequested;
|
||||
bool _haveDeviceResources;
|
||||
::Microsoft::WRL::ComPtr<ID3D11Device> _d3dDevice;
|
||||
::Microsoft::WRL::ComPtr<ID3D11DeviceContext> _d3dDeviceContext;
|
||||
|
||||
::Microsoft::WRL::ComPtr<ID2D1Device> _d2dDevice;
|
||||
::Microsoft::WRL::ComPtr<ID2D1DeviceContext> _d2dDeviceContext;
|
||||
::Microsoft::WRL::ComPtr<ID2D1Bitmap1> _d2dBitmap;
|
||||
::Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> _d2dBrushForeground;
|
||||
::Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> _d2dBrushBackground;
|
||||
|
||||
::Microsoft::WRL::ComPtr<IDXGIFactory2> _dxgiFactory2;
|
||||
::Microsoft::WRL::ComPtr<IDXGIFactoryMedia> _dxgiFactoryMedia;
|
||||
::Microsoft::WRL::ComPtr<IDXGIDevice> _dxgiDevice;
|
||||
::Microsoft::WRL::ComPtr<IDXGISurface> _dxgiSurface;
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC1 _swapChainDesc;
|
||||
::Microsoft::WRL::ComPtr<IDXGISwapChain1> _dxgiSwapChain;
|
||||
wil::unique_handle _swapChainFrameLatencyWaitableObject;
|
||||
std::unique_ptr<DrawingContext> _drawingContext;
|
||||
|
||||
// Terminal effects resources.
|
||||
|
||||
// Controls if configured terminal effects are enabled
|
||||
bool _terminalEffectsEnabled;
|
||||
|
||||
// Experimental and deprecated retro terminal effect
|
||||
// Preserved for backwards compatibility
|
||||
// Implemented in terms of the more generic pixel shader effect
|
||||
// Has precedence over pixel shader effect
|
||||
bool _retroTerminalEffect;
|
||||
|
||||
// Experimental and pixel shader effect
|
||||
// Allows user to load a pixel shader from a few presets or from a file path
|
||||
std::wstring _pixelShaderPath;
|
||||
bool _pixelShaderLoaded{ false };
|
||||
|
||||
std::chrono::steady_clock::time_point _shaderStartTime;
|
||||
|
||||
// DX resources needed for terminal effects
|
||||
::Microsoft::WRL::ComPtr<ID3D11RenderTargetView> _renderTargetView;
|
||||
::Microsoft::WRL::ComPtr<ID3D11VertexShader> _vertexShader;
|
||||
::Microsoft::WRL::ComPtr<ID3D11PixelShader> _pixelShader;
|
||||
::Microsoft::WRL::ComPtr<ID3D11InputLayout> _vertexLayout;
|
||||
::Microsoft::WRL::ComPtr<ID3D11Buffer> _screenQuadVertexBuffer;
|
||||
::Microsoft::WRL::ComPtr<ID3D11Buffer> _pixelShaderSettingsBuffer;
|
||||
::Microsoft::WRL::ComPtr<ID3D11SamplerState> _samplerState;
|
||||
::Microsoft::WRL::ComPtr<ID3D11Texture2D> _framebufferCapture;
|
||||
|
||||
// Preferences and overrides
|
||||
bool _softwareRendering;
|
||||
bool _forceFullRepaintRendering;
|
||||
|
||||
D2D1_TEXT_ANTIALIAS_MODE _antialiasingMode;
|
||||
|
||||
bool _defaultBackgroundIsTransparent;
|
||||
|
||||
// DirectX constant buffers need to be a multiple of 16; align to pad the size.
|
||||
__declspec(align(16)) struct
|
||||
{
|
||||
// Note: This can be seen as API endpoint towards user provided pixel shaders.
|
||||
// Changes here can break existing pixel shaders so be careful with changing datatypes
|
||||
// and order of parameters
|
||||
float Time;
|
||||
float Scale;
|
||||
DirectX::XMFLOAT2 Resolution;
|
||||
DirectX::XMFLOAT4 Background;
|
||||
#pragma warning(suppress : 4324) // structure was padded due to __declspec(align())
|
||||
} _pixelShaderSettings;
|
||||
|
||||
[[nodiscard]] HRESULT _CreateDeviceResources(const bool createSwapChain) noexcept;
|
||||
[[nodiscard]] HRESULT _CreateSurfaceHandle() noexcept;
|
||||
|
||||
bool _HasTerminalEffects() const noexcept;
|
||||
std::string _LoadPixelShaderFile() const;
|
||||
HRESULT _SetupTerminalEffects();
|
||||
void _ComputePixelShaderSettings() noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT _PrepareRenderTarget() noexcept;
|
||||
|
||||
void _ReleaseDeviceResources() noexcept;
|
||||
|
||||
bool _ShouldForceGrayscaleAA() noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT _CreateTextLayout(
|
||||
_In_reads_(StringLength) PCWCHAR String,
|
||||
_In_ size_t StringLength,
|
||||
_Out_ IDWriteTextLayout** ppTextLayout) noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT _CopyFrontToBack() noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT _EnableDisplayAccess(const bool outputEnabled) noexcept;
|
||||
|
||||
[[nodiscard]] til::size _GetClientSize() const;
|
||||
|
||||
void _InvalidateRectangle(const til::rect& rc);
|
||||
bool _IsAllInvalid() const noexcept;
|
||||
|
||||
[[nodiscard]] D2D1_COLOR_F _ColorFFromColorRef(const COLORREF color) noexcept;
|
||||
|
||||
// Routine Description:
|
||||
// - Helps convert a Direct2D ColorF into a DXGI RGBA
|
||||
// Arguments:
|
||||
// - color - Direct2D Color F
|
||||
// Return Value:
|
||||
// - DXGI RGBA
|
||||
[[nodiscard]] constexpr DXGI_RGBA s_RgbaFromColorF(const D2D1_COLOR_F color) noexcept
|
||||
{
|
||||
return { color.r, color.g, color.b, color.a };
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1,245 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#include "DxSoftFont.h"
|
||||
#include "CustomTextRenderer.h"
|
||||
|
||||
#include <d2d1effects.h>
|
||||
|
||||
#pragma comment(lib, "dxguid.lib")
|
||||
|
||||
using namespace Microsoft::Console::Render;
|
||||
using Microsoft::WRL::ComPtr;
|
||||
|
||||
// The soft font is rendered into a bitmap laid out in a 12x8 grid, which is
|
||||
// enough space for the 96 characters expected in the font, and which minimizes
|
||||
// the dimensions for a typical 2:1 cell size. Each position in the grid is
|
||||
// surrounded by a 2 pixel border which helps avoid bleed across the character
|
||||
// boundaries when the output is scaled.
|
||||
|
||||
constexpr size_t BITMAP_GRID_WIDTH = 12;
|
||||
constexpr size_t BITMAP_GRID_HEIGHT = 8;
|
||||
constexpr size_t PADDING = 2;
|
||||
|
||||
void DxSoftFont::SetFont(const std::span<const uint16_t> bitPattern,
|
||||
const til::size sourceSize,
|
||||
const til::size targetSize,
|
||||
const size_t centeringHint)
|
||||
{
|
||||
Reset();
|
||||
|
||||
// If the font is being reset, just free up the memory and return.
|
||||
if (bitPattern.empty())
|
||||
{
|
||||
_bitmapBits.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto maxGlyphCount = BITMAP_GRID_WIDTH * BITMAP_GRID_HEIGHT;
|
||||
_glyphCount = std::min<size_t>(bitPattern.size() / sourceSize.height, maxGlyphCount);
|
||||
_sourceSize = sourceSize;
|
||||
_targetSize = targetSize;
|
||||
_centeringHint = centeringHint;
|
||||
|
||||
const auto bitmapWidth = BITMAP_GRID_WIDTH * (_sourceSize.width + PADDING * 2);
|
||||
const auto bitmapHeight = BITMAP_GRID_HEIGHT * (_sourceSize.height + PADDING * 2);
|
||||
_bitmapBits = std::vector<byte>(bitmapWidth * bitmapHeight);
|
||||
_bitmapSize = { gsl::narrow_cast<UINT32>(bitmapWidth), gsl::narrow_cast<UINT32>(bitmapHeight) };
|
||||
|
||||
const auto bitmapScanline = [=](const auto lineNumber) noexcept {
|
||||
return _bitmapBits.begin() + lineNumber * bitmapWidth;
|
||||
};
|
||||
|
||||
// The source bitPattern is just a list of the scanlines making up the
|
||||
// glyphs one after the other, but we want to lay them out in a grid, so
|
||||
// we need to process each glyph individually.
|
||||
auto srcPointer = bitPattern.begin();
|
||||
for (auto glyphNumber = 0u; glyphNumber < _glyphCount; glyphNumber++)
|
||||
{
|
||||
// We start by calculating the position in the bitmap where the glyph
|
||||
// needs to be stored.
|
||||
const auto xOffset = _xOffsetForGlyph<size_t>(glyphNumber);
|
||||
const auto yOffset = _yOffsetForGlyph<size_t>(glyphNumber);
|
||||
auto dstPointer = bitmapScanline(yOffset) + xOffset;
|
||||
for (auto y = 0; y < sourceSize.height; y++)
|
||||
{
|
||||
// Then for each scanline in the source, we need to expand the bits
|
||||
// into 8-bit values. For every bit that is set we write out an FF
|
||||
// value, and if not set, we write out 00. In the end, all we care
|
||||
// about is a single red component for the R8_UNORM bitmap format,
|
||||
// since we'll later remap that to RGBA with a color matrix.
|
||||
auto srcBits = *(srcPointer++);
|
||||
for (auto x = 0; x < sourceSize.width; x++)
|
||||
{
|
||||
const auto srcBitIsSet = (srcBits & 0x8000) != 0;
|
||||
*(dstPointer++) = srcBitIsSet ? 0xFF : 0x00;
|
||||
srcBits <<= 1;
|
||||
}
|
||||
// When glyphs in this bitmap are output, they will typically need
|
||||
// to scaled, and this can result in some bleed from the surrounding
|
||||
// pixels. So to keep the borders clean, we pad the areas to the left
|
||||
// and right by repeating the first and last pixels of each scanline.
|
||||
std::fill_n(dstPointer, PADDING, til::at(dstPointer, -1));
|
||||
dstPointer -= sourceSize.width;
|
||||
std::fill_n(dstPointer - PADDING, PADDING, til::at(dstPointer, 0));
|
||||
dstPointer += bitmapWidth;
|
||||
}
|
||||
}
|
||||
|
||||
// In the same way that we padded the left and right of each glyph in the
|
||||
// code above, we also need to pad the top and bottom. But in this case we
|
||||
// can simply do a whole row of glyphs from the grid at the same time.
|
||||
for (auto gridRow = 0u; gridRow < BITMAP_GRID_HEIGHT; gridRow++)
|
||||
{
|
||||
const auto rowOffset = _yOffsetForGlyph<size_t>(gridRow);
|
||||
const auto rowTop = bitmapScanline(rowOffset);
|
||||
const auto rowBottom = bitmapScanline(rowOffset + _sourceSize.height - 1);
|
||||
for (auto i = 1; i <= PADDING; i++)
|
||||
{
|
||||
std::copy_n(rowTop, bitmapWidth, rowTop - i * bitmapWidth);
|
||||
std::copy_n(rowBottom, bitmapWidth, rowBottom + i * bitmapWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT DxSoftFont::SetTargetSize(const til::size targetSize)
|
||||
{
|
||||
_targetSize = targetSize;
|
||||
return _scaleEffect ? _scaleEffect->SetValue(D2D1_SCALE_PROP_SCALE, _scaleForTargetSize()) : S_OK;
|
||||
}
|
||||
|
||||
HRESULT DxSoftFont::SetAntialiasing(const bool antialiased)
|
||||
{
|
||||
_interpolation = (antialiased ? ANTIALIASED_INTERPOLATION : ALIASED_INTERPOLATION);
|
||||
return _scaleEffect ? _scaleEffect->SetValue(D2D1_SCALE_PROP_INTERPOLATION_MODE, _interpolation) : S_OK;
|
||||
}
|
||||
|
||||
HRESULT DxSoftFont::SetColor(const D2D1_COLOR_F& color)
|
||||
{
|
||||
// Since our source image is monochrome, we don't care about the
|
||||
// individual color components. We just multiply the red component
|
||||
// by the active color value to get the output color. And note that
|
||||
// the alpha matrix entry is already set to 1 in the constructor,
|
||||
// so we don't need to keep updating it here.
|
||||
_colorMatrix.m[0][0] = color.r;
|
||||
_colorMatrix.m[0][1] = color.g;
|
||||
_colorMatrix.m[0][2] = color.b;
|
||||
return _colorEffect ? _colorEffect->SetValue(D2D1_COLORMATRIX_PROP_COLOR_MATRIX, _colorMatrix) : S_OK;
|
||||
}
|
||||
|
||||
HRESULT DxSoftFont::Draw(const DrawingContext& drawingContext,
|
||||
const std::span<const Cluster> clusters,
|
||||
const float originX,
|
||||
const float originY)
|
||||
{
|
||||
ComPtr<ID2D1DeviceContext> d2dContext;
|
||||
RETURN_IF_FAILED(drawingContext.renderTarget->QueryInterface(d2dContext.GetAddressOf()));
|
||||
|
||||
// We start by creating a clipping rectangle for the region we're going to
|
||||
// draw, and this is initially filled with the active background color.
|
||||
D2D1_RECT_F rect;
|
||||
rect.top = originY + drawingContext.topClipOffset;
|
||||
rect.bottom = originY + _targetSize.height - drawingContext.bottomClipOffset;
|
||||
rect.left = originX;
|
||||
rect.right = originX + _targetSize.width * clusters.size();
|
||||
d2dContext->FillRectangle(rect, drawingContext.backgroundBrush);
|
||||
d2dContext->PushAxisAlignedClip(rect, D2D1_ANTIALIAS_MODE_ALIASED);
|
||||
auto resetClippingRect = wil::scope_exit([&]() noexcept { d2dContext->PopAxisAlignedClip(); });
|
||||
|
||||
// The bitmap and associated scaling/coloring effects are created on demand
|
||||
// so we need make sure they're generated now.
|
||||
RETURN_IF_FAILED(_createResources(d2dContext.Get()));
|
||||
|
||||
// We use the CustomTextRenderer to draw the first pass of the cursor.
|
||||
RETURN_IF_FAILED(CustomTextRenderer::DrawCursor(d2dContext.Get(), rect, drawingContext, true));
|
||||
|
||||
// Then we draw the associated glyph for each entry in the cluster list.
|
||||
auto targetPoint = D2D1_POINT_2F{ originX, originY };
|
||||
for (auto& cluster : clusters)
|
||||
{
|
||||
// For DRCS, we only care about the character's lower 7 bits, then
|
||||
// codepoint 0x20 will be the first glyph in the set.
|
||||
const auto glyphNumber = (cluster.GetTextAsSingle() & 0x7f) - 0x20;
|
||||
const auto x = _xOffsetForGlyph<float>(glyphNumber);
|
||||
const auto y = _yOffsetForGlyph<float>(glyphNumber);
|
||||
const auto sourceRect = D2D1_RECT_F{ x, y, x + _targetSize.width, y + _targetSize.height };
|
||||
LOG_IF_FAILED(_scaleEffect->SetValue(D2D1_SCALE_PROP_CENTER_POINT, D2D1::Point2F(x, y)));
|
||||
d2dContext->DrawImage(_colorEffect.Get(), targetPoint, sourceRect);
|
||||
targetPoint.x += _targetSize.width;
|
||||
}
|
||||
|
||||
// We finish by the drawing the second pass of the cursor.
|
||||
return CustomTextRenderer::DrawCursor(d2dContext.Get(), rect, drawingContext, false);
|
||||
}
|
||||
|
||||
void DxSoftFont::Reset()
|
||||
{
|
||||
_colorEffect.Reset();
|
||||
_scaleEffect.Reset();
|
||||
_bitmap.Reset();
|
||||
}
|
||||
|
||||
HRESULT DxSoftFont::_createResources(gsl::not_null<ID2D1DeviceContext*> d2dContext)
|
||||
{
|
||||
if (!_bitmap)
|
||||
{
|
||||
D2D1_BITMAP_PROPERTIES bitmapProperties{};
|
||||
bitmapProperties.pixelFormat.format = DXGI_FORMAT_R8_UNORM;
|
||||
bitmapProperties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE;
|
||||
const auto bitmapPitch = gsl::narrow_cast<UINT32>(_bitmapSize.width);
|
||||
RETURN_IF_FAILED(d2dContext->CreateBitmap(_bitmapSize, _bitmapBits.data(), bitmapPitch, bitmapProperties, _bitmap.GetAddressOf()));
|
||||
}
|
||||
|
||||
if (!_scaleEffect)
|
||||
{
|
||||
RETURN_IF_FAILED(d2dContext->CreateEffect(CLSID_D2D1Scale, _scaleEffect.GetAddressOf()));
|
||||
RETURN_IF_FAILED(_scaleEffect->SetValue(D2D1_SCALE_PROP_INTERPOLATION_MODE, _interpolation));
|
||||
RETURN_IF_FAILED(_scaleEffect->SetValue(D2D1_SCALE_PROP_SCALE, _scaleForTargetSize()));
|
||||
_scaleEffect->SetInput(0, _bitmap.Get());
|
||||
|
||||
if (_colorEffect)
|
||||
{
|
||||
_colorEffect->SetInputEffect(0, _scaleEffect.Get());
|
||||
}
|
||||
}
|
||||
|
||||
if (!_colorEffect)
|
||||
{
|
||||
RETURN_IF_FAILED(d2dContext->CreateEffect(CLSID_D2D1ColorMatrix, _colorEffect.GetAddressOf()));
|
||||
RETURN_IF_FAILED(_colorEffect->SetValue(D2D1_COLORMATRIX_PROP_COLOR_MATRIX, _colorMatrix));
|
||||
_colorEffect->SetInputEffect(0, _scaleEffect.Get());
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
D2D1_VECTOR_2F DxSoftFont::_scaleForTargetSize() const noexcept
|
||||
{
|
||||
// If the text in the font is not perfectly centered, the _centeringHint
|
||||
// gives us the offset needed to correct that misalignment. So to ensure
|
||||
// the scaling is evenly balanced around the center point of the glyphs,
|
||||
// we can use that hint to adjust the dimensions of our source and target
|
||||
// widths when calculating the horizontal scale.
|
||||
const auto targetCenteringHint = std::lround((float)_centeringHint * _targetSize.width / _sourceSize.width);
|
||||
const auto xScale = gsl::narrow_cast<float>(_targetSize.width - targetCenteringHint) / (_sourceSize.width - _centeringHint);
|
||||
const auto yScale = gsl::narrow_cast<float>(_targetSize.height) / _sourceSize.height;
|
||||
return D2D1::Vector2F(xScale, yScale);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T DxSoftFont::_xOffsetForGlyph(const size_t glyphNumber) const noexcept
|
||||
{
|
||||
const auto xOffsetInGrid = glyphNumber / BITMAP_GRID_HEIGHT;
|
||||
const auto paddedGlyphWidth = _sourceSize.width + PADDING * 2;
|
||||
return gsl::narrow_cast<T>(xOffsetInGrid * paddedGlyphWidth + PADDING);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T DxSoftFont::_yOffsetForGlyph(const size_t glyphNumber) const noexcept
|
||||
{
|
||||
const auto yOffsetInGrid = glyphNumber % BITMAP_GRID_HEIGHT;
|
||||
const auto paddedGlyphHeight = _sourceSize.height + PADDING * 2;
|
||||
return gsl::narrow_cast<T>(yOffsetInGrid * paddedGlyphHeight + PADDING);
|
||||
}
|
||||
@ -1,56 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../inc/Cluster.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <d2d1_1.h>
|
||||
#include <wrl.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
namespace Microsoft::Console::Render
|
||||
{
|
||||
struct DrawingContext;
|
||||
|
||||
class DxSoftFont
|
||||
{
|
||||
public:
|
||||
void SetFont(const std::span<const uint16_t> bitPattern,
|
||||
const til::size sourceSize,
|
||||
const til::size targetSize,
|
||||
const size_t centeringHint);
|
||||
HRESULT SetTargetSize(const til::size targetSize);
|
||||
HRESULT SetAntialiasing(const bool antialiased);
|
||||
HRESULT SetColor(const D2D1_COLOR_F& color);
|
||||
HRESULT Draw(const DrawingContext& drawingContext,
|
||||
const std::span<const Cluster> clusters,
|
||||
const float originX,
|
||||
const float originY);
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
static constexpr auto ANTIALIASED_INTERPOLATION = D2D1_SCALE_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC;
|
||||
static constexpr auto ALIASED_INTERPOLATION = D2D1_SCALE_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
|
||||
|
||||
HRESULT _createResources(gsl::not_null<ID2D1DeviceContext*> d2dContext);
|
||||
D2D1_VECTOR_2F _scaleForTargetSize() const noexcept;
|
||||
template<typename T>
|
||||
T _xOffsetForGlyph(const size_t glyphNumber) const noexcept;
|
||||
template<typename T>
|
||||
T _yOffsetForGlyph(const size_t glyphNumber) const noexcept;
|
||||
|
||||
size_t _glyphCount = 0;
|
||||
til::size _sourceSize;
|
||||
til::size _targetSize;
|
||||
size_t _centeringHint = 0;
|
||||
D2D1_SCALE_INTERPOLATION_MODE _interpolation = ALIASED_INTERPOLATION;
|
||||
D2D1_MATRIX_5X4_F _colorMatrix{ ._14 = 1 };
|
||||
D2D1_SIZE_U _bitmapSize{};
|
||||
std::vector<byte> _bitmapBits;
|
||||
::Microsoft::WRL::ComPtr<ID2D1Bitmap> _bitmap;
|
||||
::Microsoft::WRL::ComPtr<ID2D1Effect> _scaleEffect;
|
||||
::Microsoft::WRL::ComPtr<ID2D1Effect> _colorEffect;
|
||||
};
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
import "oaidl.idl";
|
||||
import "ocidl.idl";
|
||||
|
||||
typedef struct BoxScale
|
||||
{
|
||||
float VerticalScale;
|
||||
float VerticalTranslation;
|
||||
float HorizontalScale;
|
||||
float HorizontalTranslation;
|
||||
} BoxScale;
|
||||
|
||||
[
|
||||
uuid("C164926F-1A4D-470D-BB8A-3D2CC4B035E4"),
|
||||
object,
|
||||
local
|
||||
]
|
||||
interface IBoxDrawingEffect : IUnknown
|
||||
{
|
||||
HRESULT GetScale([out] BoxScale* scale);
|
||||
};
|
||||
@ -1,91 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#if !TIL_FEATURE_DXENGINESHADERSUPPORT_ENABLED
|
||||
constexpr std::string_view retroPixelShaderString{ "" };
|
||||
#else
|
||||
constexpr std::string_view retroPixelShaderString{ R"(
|
||||
// The original retro pixel shader
|
||||
Texture2D shaderTexture;
|
||||
SamplerState samplerState;
|
||||
|
||||
cbuffer PixelShaderSettings {
|
||||
float Time;
|
||||
float Scale;
|
||||
float2 Resolution;
|
||||
float4 Background;
|
||||
};
|
||||
|
||||
#define SCANLINE_FACTOR 0.5
|
||||
#define SCALED_SCANLINE_PERIOD Scale
|
||||
#define SCALED_GAUSSIAN_SIGMA (2.0*Scale)
|
||||
|
||||
static const float M_PI = 3.14159265f;
|
||||
|
||||
float Gaussian2D(float x, float y, float sigma)
|
||||
{
|
||||
return 1/(sigma*sqrt(2*M_PI)) * exp(-0.5*(x*x + y*y)/sigma/sigma);
|
||||
}
|
||||
|
||||
float4 Blur(Texture2D input, float2 tex_coord, float sigma)
|
||||
{
|
||||
uint width, height;
|
||||
shaderTexture.GetDimensions(width, height);
|
||||
|
||||
float texelWidth = 1.0f/width;
|
||||
float texelHeight = 1.0f/height;
|
||||
|
||||
float4 color = { 0, 0, 0, 0 };
|
||||
|
||||
int sampleCount = 13;
|
||||
|
||||
for (int x = 0; x < sampleCount; x++)
|
||||
{
|
||||
float2 samplePos = { 0, 0 };
|
||||
|
||||
samplePos.x = tex_coord.x + (x - sampleCount/2) * texelWidth;
|
||||
for (int y = 0; y < sampleCount; y++)
|
||||
{
|
||||
samplePos.y = tex_coord.y + (y - sampleCount/2) * texelHeight;
|
||||
if (samplePos.x <= 0 || samplePos.y <= 0 || samplePos.x >= width || samplePos.y >= height) continue;
|
||||
|
||||
color += input.Sample(samplerState, samplePos) * Gaussian2D((x - sampleCount/2), (y - sampleCount/2), sigma);
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
float SquareWave(float y)
|
||||
{
|
||||
return 1 - (floor(y / SCALED_SCANLINE_PERIOD) % 2) * SCANLINE_FACTOR;
|
||||
}
|
||||
|
||||
float4 Scanline(float4 color, float4 pos)
|
||||
{
|
||||
float wave = SquareWave(pos.y);
|
||||
|
||||
// TODO:GH#3929 make this configurable.
|
||||
// Remove the && false to draw scanlines everywhere.
|
||||
if (length(color.rgb) < 0.2 && false)
|
||||
{
|
||||
return color + wave*0.1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return color * wave;
|
||||
}
|
||||
}
|
||||
|
||||
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
|
||||
{
|
||||
Texture2D input = shaderTexture;
|
||||
|
||||
// TODO:GH#3930 Make these configurable in some way.
|
||||
float4 color = input.Sample(samplerState, tex);
|
||||
color += Blur(input, tex, SCALED_GAUSSIAN_SIGMA)*0.3;
|
||||
color = Scanline(color, pos);
|
||||
|
||||
return color;
|
||||
}
|
||||
)" };
|
||||
#endif
|
||||
@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#if !TIL_FEATURE_DXENGINESHADERSUPPORT_ENABLED
|
||||
const char screenVertexShaderString[] = "";
|
||||
#else
|
||||
const char screenVertexShaderString[] = R"(
|
||||
struct VS_OUTPUT
|
||||
{
|
||||
float4 pos : SV_POSITION;
|
||||
float2 tex : TEXCOORD;
|
||||
};
|
||||
VS_OUTPUT main(float4 pos : POSITION, float2 tex : TEXCOORD)
|
||||
{
|
||||
VS_OUTPUT output;
|
||||
output.pos = pos;
|
||||
output.tex = tex;
|
||||
return output;
|
||||
}
|
||||
)";
|
||||
#endif
|
||||
@ -1,2 +0,0 @@
|
||||
DIRS= \
|
||||
lib \
|
||||
@ -1,48 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{48D21369-3D7B-4431-9967-24E81292CF62}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>dx</RootNamespace>
|
||||
<ProjectName>RendererDx</ProjectName>
|
||||
<TargetName>ConRenderDx</TargetName>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<Midl>
|
||||
<OutputDirectory>$(SolutionDir)src\renderer\dx\</OutputDirectory>
|
||||
</Midl>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<Import Project="$(SolutionDir)src\common.nugetversions.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\BoxDrawingEffect.cpp" />
|
||||
<ClCompile Include="..\CustomTextLayout.cpp" />
|
||||
<ClCompile Include="..\CustomTextRenderer.cpp" />
|
||||
<ClCompile Include="..\DxFontInfo.cpp" />
|
||||
<ClCompile Include="..\DxFontRenderData.cpp" />
|
||||
<ClCompile Include="..\DxRenderer.cpp" />
|
||||
<ClCompile Include="..\DxSoftFont.cpp" />
|
||||
<ClCompile Include="..\precomp.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\BoxDrawingEffect.h" />
|
||||
<ClInclude Include="..\CustomTextLayout.h" />
|
||||
<ClInclude Include="..\CustomTextRenderer.h" />
|
||||
<ClInclude Include="..\DxFontInfo.h" />
|
||||
<ClInclude Include="..\DxFontRenderData.h" />
|
||||
<ClInclude Include="..\DxRenderer.hpp" />
|
||||
<ClInclude Include="..\DxSoftFont.h" />
|
||||
<ClInclude Include="..\precomp.h" />
|
||||
<ClInclude Include="..\ScreenPixelShader.h" />
|
||||
<ClInclude Include="..\ScreenVertexShader.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="..\IBoxDrawingEffect.idl" />
|
||||
</ItemGroup>
|
||||
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
|
||||
<Import Project="$(SolutionDir)src\common.build.post.props" />
|
||||
<Import Project="$(SolutionDir)src\common.nugetversions.targets" />
|
||||
</Project>
|
||||
@ -1,31 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\BoxDrawingEffect.cpp" />
|
||||
<ClCompile Include="..\CustomTextLayout.cpp" />
|
||||
<ClCompile Include="..\CustomTextRenderer.cpp" />
|
||||
<ClCompile Include="..\DxFontInfo.cpp" />
|
||||
<ClCompile Include="..\DxFontRenderData.cpp" />
|
||||
<ClCompile Include="..\DxRenderer.cpp" />
|
||||
<ClCompile Include="..\DxSoftFont.cpp" />
|
||||
<ClCompile Include="..\precomp.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\BoxDrawingEffect.h" />
|
||||
<ClInclude Include="..\CustomTextLayout.h" />
|
||||
<ClInclude Include="..\CustomTextRenderer.h" />
|
||||
<ClInclude Include="..\DxFontInfo.h" />
|
||||
<ClInclude Include="..\DxFontRenderData.h" />
|
||||
<ClInclude Include="..\DxRenderer.hpp" />
|
||||
<ClInclude Include="..\DxSoftFont.h" />
|
||||
<ClInclude Include="..\precomp.h" />
|
||||
<ClInclude Include="..\ScreenPixelShader.h" />
|
||||
<ClInclude Include="..\ScreenVertexShader.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="..\IBoxDrawingEffect.idl" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@ -1,8 +0,0 @@
|
||||
!include ..\sources.inc
|
||||
|
||||
# -------------------------------------
|
||||
# Program Information
|
||||
# -------------------------------------
|
||||
|
||||
TARGETNAME = ConRenderDx
|
||||
TARGETTYPE = LIBRARY
|
||||
@ -1,4 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
@ -1,41 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
// This includes support libraries from the CRT, STL, WIL, and GSL
|
||||
#define BLOCK_TIL // We want to include it later, after DX.
|
||||
#include "LibraryIncludes.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <winmeta.h>
|
||||
|
||||
#include "../host/conddkrefs.h"
|
||||
#include <condrv.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <exception>
|
||||
#include <typeinfo>
|
||||
|
||||
#include <dcomp.h>
|
||||
|
||||
#include <dxgi.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <dxgi1_3.h>
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <d2d1.h>
|
||||
#include <d2d1_1.h>
|
||||
#include <d2d1_2.h>
|
||||
#include <d2d1_3.h>
|
||||
#include <d2d1helper.h>
|
||||
#include <dwrite.h>
|
||||
#include <dwrite_1.h>
|
||||
#include <dwrite_2.h>
|
||||
#include <dwrite_3.h>
|
||||
|
||||
// Re-include TIL at the bottom to gain DX superpowers.
|
||||
#include "til.h"
|
||||
|
||||
#pragma hdrstop
|
||||
@ -1,42 +0,0 @@
|
||||
!include ..\..\..\project.inc
|
||||
|
||||
# -------------------------------------
|
||||
# Windows Console
|
||||
# - Console Renderer for DirectX
|
||||
# -------------------------------------
|
||||
|
||||
# This module provides a rendering engine implementation that
|
||||
# draws to a DirectX surface.
|
||||
|
||||
# -------------------------------------
|
||||
# CRT Configuration
|
||||
# -------------------------------------
|
||||
|
||||
BUILD_FOR_CORESYSTEM = 1
|
||||
|
||||
# -------------------------------------
|
||||
# Sources, Headers, and Libraries
|
||||
# -------------------------------------
|
||||
|
||||
PRECOMPILED_CXX = 1
|
||||
PRECOMPILED_INCLUDE = ..\precomp.h
|
||||
|
||||
INCLUDES = \
|
||||
$(INCLUDES); \
|
||||
..; \
|
||||
..\..\inc; \
|
||||
..\..\..\inc; \
|
||||
..\..\..\host; \
|
||||
$(MINWIN_INTERNAL_PRIV_SDK_INC_PATH_L); \
|
||||
$(MINWIN_RESTRICTED_PRIV_SDK_INC_PATH_L); \
|
||||
|
||||
SOURCES = \
|
||||
$(SOURCES) \
|
||||
..\DxRenderer.cpp \
|
||||
..\DxFontInfo.cpp \
|
||||
..\DxFontRenderData.cpp \
|
||||
..\DxSoftFont.cpp \
|
||||
..\CustomTextRenderer.cpp \
|
||||
..\CustomTextLayout.cpp \
|
||||
|
||||
C_DEFINES=$(C_DEFINES) -D__INSIDE_WINDOWS
|
||||
@ -1,100 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
#include "WexTestClass.h"
|
||||
#include "../../inc/consoletaeftemplates.hpp"
|
||||
|
||||
#include "../CustomTextLayout.h"
|
||||
|
||||
using namespace WEX::Common;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
|
||||
using namespace Microsoft::Console::Render;
|
||||
|
||||
class Microsoft::Console::Render::CustomTextLayoutTests
|
||||
{
|
||||
TEST_CLASS(CustomTextLayoutTests);
|
||||
|
||||
TEST_METHOD(OrderRuns)
|
||||
{
|
||||
CustomTextLayout layout;
|
||||
|
||||
// Create linked list runs where a --> c --> b
|
||||
CustomTextLayout::LinkedRun a;
|
||||
a.nextRunIndex = 2;
|
||||
a.textStart = 0;
|
||||
CustomTextLayout::LinkedRun b;
|
||||
b.nextRunIndex = 0;
|
||||
b.textStart = 20;
|
||||
CustomTextLayout::LinkedRun c;
|
||||
c.nextRunIndex = 1;
|
||||
c.textStart = 10;
|
||||
|
||||
// but insert them into the runs as a, b, c
|
||||
layout._runs.push_back(a);
|
||||
layout._runs.push_back(b);
|
||||
layout._runs.push_back(c);
|
||||
|
||||
// Now order them.
|
||||
layout._OrderRuns();
|
||||
|
||||
// Validate that they've been reordered to a, c, b by index so they can be iterated to go in order.
|
||||
|
||||
// The text starts should be in order 0, 10, 20.
|
||||
// The next run indexes should point at each other.
|
||||
VERIFY_ARE_EQUAL(a.textStart, layout._runs.at(0).textStart);
|
||||
VERIFY_ARE_EQUAL(1u, layout._runs.at(0).nextRunIndex);
|
||||
VERIFY_ARE_EQUAL(c.textStart, layout._runs.at(1).textStart);
|
||||
VERIFY_ARE_EQUAL(2u, layout._runs.at(1).nextRunIndex);
|
||||
VERIFY_ARE_EQUAL(b.textStart, layout._runs.at(2).textStart);
|
||||
VERIFY_ARE_EQUAL(0u, layout._runs.at(2).nextRunIndex);
|
||||
}
|
||||
|
||||
TEST_METHOD(SplitCurrentRunIncludingGlyphs)
|
||||
{
|
||||
CustomTextLayout layout;
|
||||
|
||||
// Put glyph data into the layout as if we've already gone through analysis.
|
||||
// This data matches the verbose comment from the CustomTextLayout.cpp file
|
||||
// and is derived from
|
||||
// https://social.msdn.microsoft.com/Forums/en-US/993365bc-8689-45ff-a675-c5ed0c011788/dwriteglyphrundescriptionclustermap-explained
|
||||
|
||||
layout._text = L"fiñe";
|
||||
|
||||
layout._glyphIndices.push_back(19);
|
||||
layout._glyphIndices.push_back(81);
|
||||
layout._glyphIndices.push_back(23);
|
||||
layout._glyphIndices.push_back(72);
|
||||
|
||||
layout._glyphClusters.push_back(0);
|
||||
layout._glyphClusters.push_back(0);
|
||||
layout._glyphClusters.push_back(1);
|
||||
layout._glyphClusters.push_back(3);
|
||||
|
||||
// Set up the layout to have a run that already has glyph data inside of it.
|
||||
CustomTextLayout::LinkedRun run;
|
||||
run.textStart = 0;
|
||||
run.textLength = 4;
|
||||
run.glyphStart = 0;
|
||||
run.glyphCount = 4;
|
||||
|
||||
layout._runs.push_back(run);
|
||||
|
||||
// Now split it in the middle per the comment example
|
||||
layout._SetCurrentRun(2);
|
||||
layout._SplitCurrentRun(2);
|
||||
|
||||
// And validate that the split state matches what we expected.
|
||||
VERIFY_ARE_EQUAL(0u, layout._runs.at(0).textStart);
|
||||
VERIFY_ARE_EQUAL(2u, layout._runs.at(0).textLength);
|
||||
VERIFY_ARE_EQUAL(0u, layout._runs.at(0).glyphStart);
|
||||
VERIFY_ARE_EQUAL(1u, layout._runs.at(0).glyphCount);
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, layout._runs.at(1).textStart);
|
||||
VERIFY_ARE_EQUAL(2u, layout._runs.at(1).textLength);
|
||||
VERIFY_ARE_EQUAL(1u, layout._runs.at(1).glyphStart);
|
||||
VERIFY_ARE_EQUAL(3u, layout._runs.at(1).glyphCount);
|
||||
}
|
||||
};
|
||||
@ -1,12 +0,0 @@
|
||||
//Autogenerated file name + version resource file for Device Guard whitelisting effort
|
||||
|
||||
#include <windows.h>
|
||||
#include <ntverp.h>
|
||||
|
||||
#define VER_FILETYPE VFT_UNKNOWN
|
||||
#define VER_FILESUBTYPE VFT2_UNKNOWN
|
||||
#define VER_FILEDESCRIPTION_STR ___TARGETNAME
|
||||
#define VER_INTERNALNAME_STR ___TARGETNAME
|
||||
#define VER_ORIGINALFILENAME_STR ___TARGETNAME
|
||||
|
||||
#include "common.ver"
|
||||
@ -1,42 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{95B136F9-B238-490C-A7C5-5843C1FECAC4}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>DxUnitTests</RootNamespace>
|
||||
<ProjectName>Dx.Unit.Tests</ProjectName>
|
||||
<TargetName>Dx.Unit.Tests</TargetName>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<Import Project="$(SolutionDir)\src\common.nugetversions.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="CustomTextLayoutTests.cpp" />
|
||||
<ClCompile Include="..\precomp.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\types\lib\types.vcxproj">
|
||||
<Project>{18d09a24-8240-42d6-8cb6-236eee820263}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\base\lib\base.vcxproj">
|
||||
<Project>{af0a096a-8b3a-4949-81ef-7df8f0fee91f}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\lib\dx.vcxproj">
|
||||
<Project>{48D21369-3D7B-4431-9967-24E81292CF62}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\precomp.h" />
|
||||
</ItemGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..;$(SolutionDir)src\inc;$(SolutionDir)src\inc\test;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
|
||||
<Import Project="$(SolutionDir)src\common.build.post.props" />
|
||||
<Import Project="$(SolutionDir)src\common.build.tests.props" />
|
||||
<Import Project="$(SolutionDir)src\common.nugetversions.targets" />
|
||||
</Project>
|
||||
@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="ProductBuild" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(NTMAKEENV)\UniversalTest\Microsoft.TestInfrastructure.UniversalTest.props" />
|
||||
</Project>
|
||||
@ -1,36 +0,0 @@
|
||||
!include ..\..\..\project.unittest.inc
|
||||
|
||||
# -------------------------------------
|
||||
# Program Information
|
||||
# -------------------------------------
|
||||
|
||||
TARGETNAME = Microsoft.Console.Renderer.Dx.UnitTests
|
||||
TARGETTYPE = DYNLINK
|
||||
DLLDEF =
|
||||
|
||||
# -------------------------------------
|
||||
# Sources, Headers, and Libraries
|
||||
# -------------------------------------
|
||||
|
||||
SOURCES = \
|
||||
$(SOURCES) \
|
||||
CustomTextLayoutTests.cpp \
|
||||
DefaultResource.rc \
|
||||
|
||||
INCLUDES = \
|
||||
.. \
|
||||
$(INCLUDES) \
|
||||
|
||||
TARGETLIBS = \
|
||||
$(WINCORE_OBJ_PATH)\console\open\src\renderer\dx\lib\$(O)\ConRenderDx.lib \
|
||||
$(WINCORE_OBJ_PATH)\console\open\src\renderer\base\lib\$(O)\ConRenderBase.lib \
|
||||
$(WINCORE_OBJ_PATH)\console\open\src\types\lib\$(O)\ConTypes.lib \
|
||||
$(TARGETLIBS) \
|
||||
|
||||
# -------------------------------------
|
||||
# Localization
|
||||
# -------------------------------------
|
||||
|
||||
# Autogenerated. Sets file name for Device Guard whitelisting effort, used in RC.exe.
|
||||
C_DEFINES = $(C_DEFINES) -D___TARGETNAME="""$(TARGETNAME).$(TARGETTYPE)"""
|
||||
MUI_VERIFY_NO_LOC_RESOURCE = 1
|
||||
Loading…
x
Reference in New Issue
Block a user