Skip to content

Commit

Permalink
Add support for soft fonts in the DX renderer (#13362)
Browse files Browse the repository at this point in the history
## Summary of the Pull Request

This PR adds support for downloadable soft fonts in the DirectX
renderer, potentially enabling them to be used in Windows Terminal.

## References

Soft fonts were first implemented in conhost (with the GDI renderer) in
PR #10011.

## PR Checklist
* [ ] Closes #xxx
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already. If not
checked, I'm ready to accept this work might be rejected in favor of a
different grand plan. Issue number where discussion took place: #xxx

## Detailed Description of the Pull Request / Additional comments

The way the DirectX implementation works is by building up a bitmap
containing all of the glyphs, and then drawing an appropriate subsection
of that bitmap for each character that needs to be rendered. The current
text color is applied with a color matrix effect, and the glyphs are
automatically scaled up to the current font size with a scaling effect.

By default the scaling uses a high quality cubic interpolation, which
gives it a smoother antialiased effect. But if the *Text antialiasing*
option is configured as *Aliased*, we use a simpler nearest-neighbor
interpolation, which more closely matches the rendering of the original
GDI implementation.

## Validation Steps Performed

I've manually tested the renderer in conhost with the `UseDx` registry
entry. I've also tested in Windows Terminal using the experimental
passthrough mode.
  • Loading branch information
j4james committed Jul 14, 2022
1 parent bc79867 commit 9ae68cc
Show file tree
Hide file tree
Showing 10 changed files with 383 additions and 14 deletions.
1 change: 1 addition & 0 deletions .github/actions/spelling/expect/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ codeproject
coinit
COLLECTIONURI
colorizing
COLORMATRIX
colororacle
colorref
colorscheme
Expand Down
14 changes: 7 additions & 7 deletions src/renderer/dx/CustomTextRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,10 @@ using namespace Microsoft::Console::Render;
// - 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 _drawCursor(gsl::not_null<ID2D1DeviceContext*> d2dContext,
D2D1_RECT_F textRunBounds,
const DrawingContext& drawingContext,
const bool firstPass)
[[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())
Expand Down Expand Up @@ -374,7 +374,7 @@ try
// = = =====
// ===== =====
//
// Then, outside of _drawCursor, the glyph is drawn:
// Then, outside of DrawCursor, the glyph is drawn:
//
// EMPTY BOX FILLED BOX
// ==A== ==A==
Expand Down Expand Up @@ -556,7 +556,7 @@ CATCH_RETURN()

d2dContext->FillRectangle(rect, drawingContext->backgroundBrush);

RETURN_IF_FAILED(_drawCursor(d2dContext.Get(), rect, *drawingContext, true));
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
Expand Down Expand Up @@ -746,7 +746,7 @@ CATCH_RETURN()
clientDrawingEffect));
}

RETURN_IF_FAILED(_drawCursor(d2dContext.Get(), rect, *drawingContext, false));
RETURN_IF_FAILED(DrawCursor(d2dContext.Get(), rect, *drawingContext, false));

return S_OK;
}
Expand Down
5 changes: 5 additions & 0 deletions src/renderer/dx/CustomTextRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ namespace Microsoft::Console::Render

[[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,
Expand Down
54 changes: 47 additions & 7 deletions src/renderer/dx/DxRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,8 @@ void DxEngine::_ReleaseDeviceResources() noexcept

_d2dBitmap.Reset();

_softFont.Reset();

if (nullptr != _d2dDeviceContext.Get() && _isPainting)
{
_d2dDeviceContext->EndDraw();
Expand Down Expand Up @@ -1689,12 +1691,22 @@ try
// Calculate positioning of our origin.
const auto origin = (coord * _fontRenderData->GlyphCell()).to_d2d_point();

// Create the text layout
RETURN_IF_FAILED(_customLayout->Reset());
RETURN_IF_FAILED(_customLayout->AppendClusters(clusters));
if (_usingSoftFont)
{
// We need to reset the clipping rect applied by the CustomTextRenderer,
// since the soft font will want to set its own clipping rect.
RETURN_IF_FAILED(_customRenderer->EndClip(_drawingContext.get()));
RETURN_IF_FAILED(_softFont.Draw(*_drawingContext, clusters, origin.x, origin.y));
}
else
{
// Create the text layout
RETURN_IF_FAILED(_customLayout->Reset());
RETURN_IF_FAILED(_customLayout->AppendClusters(clusters));

// Layout then render the text
RETURN_IF_FAILED(_customLayout->Draw(_drawingContext.get(), _customRenderer.Get(), origin.x, origin.y));
// Layout then render the text
RETURN_IF_FAILED(_customLayout->Draw(_drawingContext.get(), _customRenderer.Get(), origin.x, origin.y));
}

return S_OK;
}
Expand Down Expand Up @@ -1933,8 +1945,9 @@ CATCH_RETURN()
[[nodiscard]] HRESULT DxEngine::UpdateDrawingBrushes(const TextAttribute& textAttributes,
const RenderSettings& renderSettings,
const gsl::not_null<IRenderData*> /*pData*/,
const bool /*usingSoftFont*/,
const bool usingSoftFont,
const bool isSettingDefaultBrushes) noexcept
try
{
const auto [colorForeground, colorBackground] = renderSettings.GetAttributeColorsWithAlpha(textAttributes);

Expand All @@ -1951,6 +1964,12 @@ CATCH_RETURN()
_d2dBrushForeground->SetColor(_foregroundColor);
_d2dBrushBackground->SetColor(_backgroundColor);

_usingSoftFont = usingSoftFont;
if (_usingSoftFont)
{
_softFont.SetColor(_foregroundColor);
}

// If this flag is set, then we need to update the default brushes too and the swap chain background.
if (isSettingDefaultBrushes)
{
Expand Down Expand Up @@ -1990,6 +2009,7 @@ CATCH_RETURN()

return S_OK;
}
CATCH_RETURN();

// Routine Description:
// - Updates the font used for drawing
Expand Down Expand Up @@ -2021,7 +2041,8 @@ try
// Prepare the text layout.
_customLayout = WRL::Make<CustomTextLayout>(_fontRenderData.get());

return S_OK;
// Inform the soft font of the new cell size so it can scale appropriately.
return _softFont.SetTargetSize(_fontRenderData->GlyphCell());
}
CATCH_RETURN();

Expand Down Expand Up @@ -2233,6 +2254,7 @@ try
{
_antialiasingMode = antialiasingMode;
_recreateDeviceRequested = true;
LOG_IF_FAILED(_softFont.SetAntialiasing(antialiasingMode != D2D1_TEXT_ANTIALIAS_MODE_ALIASED));
LOG_IF_FAILED(InvalidateAll());
}
}
Expand Down Expand Up @@ -2275,6 +2297,24 @@ void DxEngine::UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept
_hyperlinkHoveredId = hoveredId;
}

// Routine Description:
// - This method will replace the active soft font with the given bit pattern.
// Arguments:
// - bitPattern - An array of scanlines representing all the glyphs in the font.
// - cellSize - The cell size for an individual glyph.
// - centeringHint - The horizontal extent that glyphs are offset from center.
// Return Value:
// - S_OK if successful. E_FAIL if there was an error.
HRESULT DxEngine::UpdateSoftFont(const gsl::span<const uint16_t> bitPattern,
const til::size cellSize,
const size_t centeringHint) noexcept
try
{
_softFont.SetFont(bitPattern, cellSize, _fontRenderData->GlyphCell(), centeringHint);
return S_OK;
}
CATCH_RETURN();

// Method Description:
// - Informs this render engine about certain state for this frame at the
// beginning of this frame. We'll use it to get information about the cursor
Expand Down
7 changes: 7 additions & 0 deletions src/renderer/dx/DxRenderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "CustomTextLayout.h"
#include "CustomTextRenderer.h"
#include "DxFontRenderData.h"
#include "DxSoftFont.h"

#include "../../types/inc/Viewport.hpp"

Expand Down Expand Up @@ -91,6 +92,10 @@ namespace Microsoft::Console::Render

[[nodiscard]] HRESULT ScrollFrame() noexcept override;

[[nodiscard]] HRESULT UpdateSoftFont(const gsl::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;
Expand Down Expand Up @@ -204,6 +209,8 @@ namespace Microsoft::Console::Render
::Microsoft::WRL::ComPtr<ID2D1StrokeStyle> _hyperlinkStrokeStyle;

std::unique_ptr<DxFontRenderData> _fontRenderData;
DxSoftFont _softFont;
bool _usingSoftFont;

D2D1_STROKE_STYLE_PROPERTIES _strokeStyleProperties;
D2D1_STROKE_STYLE_PROPERTIES _dashStrokeStyleProperties;
Expand Down
Loading

0 comments on commit 9ae68cc

Please sign in to comment.