Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strawman: Dumb implementation of text D2D effects. #817

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/cascadia/TerminalControl/TerminalControl.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
</ItemGroup>
<ItemDefinitionGroup>
<Link>
<AdditionalDependencies>dwrite.lib;dxgi.lib;d2d1.lib;d3d11.lib;shcore.lib;winmm.lib;pathcch.lib;propsys.lib;uiautomationcore.lib;Shlwapi.lib;ntdll.lib;user32.lib;kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>dwrite.lib;dxgi.lib;dxguid.lib;d2d1.lib;d3d11.lib;shcore.lib;winmm.lib;pathcch.lib;propsys.lib;uiautomationcore.lib;Shlwapi.lib;ntdll.lib;user32.lib;kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<ClCompile>
<AdditionalIncludeDirectories>$(OpenConsoleDir)src\types\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
Expand All @@ -66,4 +66,4 @@
</PropertyGroup>
<Import Project="$(OpenConsoleDir)src\common.build.post.props" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
</Project>
</Project>
5 changes: 0 additions & 5 deletions src/cascadia/TerminalControl/TerminalControl.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,15 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp" />
<ClCompile Include="KeyChord.cpp" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's going on here? Why are these getting removed? (and below)

It doesn't look like you otherwise manipulated this module. Were these moved out by someone else and the filters wasn't updated?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea! I guess so, as the .vcxproj wasn't changed.

<ClCompile Include="TermControl.cpp" />
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="KeyChord.h" />
<ClInclude Include="TermControl.h" />
</ItemGroup>
<ItemGroup>
<Midl Include="TermControl.idl" />
<Midl Include="KeyChord.idl" />
<Midl Include="IKeyBindings.idl" />
<Midl Include="IControlSettings.idl" />
</ItemGroup>
<ItemGroup>
<None Include="TerminalControl.def" />
Expand Down
42 changes: 24 additions & 18 deletions src/renderer/dx/CustomTextRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,8 @@ HRESULT CustomTextRenderer::DrawGlyphRun(
D2D1_POINT_2F baselineOrigin = origin;
baselineOrigin.y += drawingContext->spacing.baseline;

::Microsoft::WRL::ComPtr<ID2D1DeviceContext4> d2dContext4;
RETURN_IF_FAILED(drawingContext->renderTarget->QueryInterface(d2dContext4.GetAddressOf()));
::Microsoft::WRL::ComPtr<ID2D1DeviceContext4> backgroundContext;
RETURN_IF_FAILED(drawingContext->renderTarget->QueryInterface(backgroundContext.GetAddressOf()));

// Draw the background
D2D1_RECT_F rect;
Expand All @@ -261,7 +261,13 @@ HRESULT CustomTextRenderer::DrawGlyphRun(
rect.right += glyphRun->glyphAdvances[i];
}

d2dContext4->FillRectangle(rect, drawingContext->backgroundBrush);
backgroundContext->FillRectangle(rect, drawingContext->backgroundBrush);
if (drawingContext->textEffect)
{
drawingContext->textRenderContext->PushAxisAlignedClip(rect, D2D1_ANTIALIAS_MODE_ALIASED);
drawingContext->textRenderContext->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f));
drawingContext->textRenderContext->PopAxisAlignedClip();
}

// Now go onto drawing the text.

Expand Down Expand Up @@ -332,22 +338,24 @@ HRESULT CustomTextRenderer::DrawGlyphRun(
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);
drawingContext->textRenderContext->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);
drawingContext->textRenderContext->DrawSvgGlyphRun(
currentBaselineOrigin,
&colorRun->glyphRun,
drawingContext->foregroundBrush,
nullptr, // svgGlyphStyle
0, // colorPaletteIndex
measuringMode);
}
break;

Expand All @@ -371,7 +379,7 @@ HRESULT CustomTextRenderer::DrawGlyphRun(
{
if (!tempBrush)
{
RETURN_IF_FAILED(d2dContext4->CreateSolidColorBrush(colorRun->runColor, &tempBrush));
RETURN_IF_FAILED(drawingContext->textRenderContext->CreateSolidColorBrush(colorRun->runColor, &tempBrush));
}
else
{
Expand Down Expand Up @@ -406,6 +414,7 @@ HRESULT CustomTextRenderer::DrawGlyphRun(
glyphRunDescription,
drawingContext->foregroundBrush));
}

return S_OK;
}
#pragma endregion
Expand All @@ -418,11 +427,8 @@ HRESULT CustomTextRenderer::_DrawBasicGlyphRun(DrawingContext* clientDrawingCont
_In_ const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription,
ID2D1Brush* brush)
{
::Microsoft::WRL::ComPtr<ID2D1DeviceContext4> d2dContext4;
RETURN_IF_FAILED(clientDrawingContext->renderTarget->QueryInterface(d2dContext4.GetAddressOf()));

// Using the context is the easiest/default way of drawing.
d2dContext4->DrawGlyphRun(baselineOrigin, glyphRun, glyphRunDescription, brush, measuringMode);
clientDrawingContext->textRenderContext->DrawGlyphRun(baselineOrigin, glyphRun, glyphRunDescription, brush, measuringMode);

// However, we could probably add options here and switch out to one of these other drawing methods (making it
// conditional based on the IUnknown* clientDrawingEffect or on some other switches and try these out instead:
Expand Down
8 changes: 8 additions & 0 deletions src/renderer/dx/CustomTextRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@

#pragma once

#include <d2d1_3.h>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this probably belongs in a precomp

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weirdly, it is in the precomp.h for all the projects I found that should be using it, but without it the build failed, but I didn't look too much into it. Definitely a FIXME.

Copy link
Author

@simonbuchan simonbuchan May 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction: It is explicitly #included in the precomp.h for RendererDx, but this file ends up included in a whole bunch of other projects. I couldn't find an appropriate file to add it, e.g. one with other #include <d2d1*.h> lines.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this end up included in other projects? Shouldn't it just be scoped inside DxRenderer?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's included in DxRenderer.hpp rather than forwarded, and that is included in InteractivityWin32's Window.cpp and TerminalControls TermControl.{h,cpp}. Seems like the right place to put it going by current style would be DxRenderer.hpp then, but better for those to have a pimpl pattern or get them onto IRenderEngine.


#include <wrl/implements.h>

namespace Microsoft::Console::Render
{
struct DrawingContext
{
DrawingContext(ID2D1RenderTarget* renderTarget,
ID2D1DeviceContext4* textRenderContext,
ID2D1Effect* textEffect,
ID2D1Brush* foregroundBrush,
ID2D1Brush* backgroundBrush,
IDWriteFactory* dwriteFactory,
Expand All @@ -18,6 +22,8 @@ namespace Microsoft::Console::Render
const D2D1_DRAW_TEXT_OPTIONS options = D2D1_DRAW_TEXT_OPTIONS_NONE)
{
this->renderTarget = renderTarget;
this->textRenderContext = textRenderContext;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're using tabs. we use 4 spaces.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, missed one!

this->textEffect = textEffect;
this->foregroundBrush = foregroundBrush;
this->backgroundBrush = backgroundBrush;
this->dwriteFactory = dwriteFactory;
Expand All @@ -27,6 +33,8 @@ namespace Microsoft::Console::Render
}

ID2D1RenderTarget* renderTarget;
ID2D1DeviceContext4* textRenderContext;
ID2D1Effect* textEffect;
ID2D1Brush* foregroundBrush;
ID2D1Brush* backgroundBrush;
IDWriteFactory* dwriteFactory;
Expand Down
61 changes: 57 additions & 4 deletions src/renderer/dx/DxRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ HRESULT DxEngine::_CreateDeviceResources(const bool createSwapChain) noexcept
if (_isPainting) {
// TODO: MSFT: 21169176 - remove this or restore the "try a few times to render" code... I think
_d2dRenderTarget->BeginDraw();
if (_d2dTextEffect) {
_d2dTextRenderContext->BeginDraw();
}
}

freeOnFail.release(); // don't need to release if we made it to the bottom and everything was good.
Expand All @@ -253,17 +256,53 @@ HRESULT DxEngine::_PrepareRenderTarget() noexcept
{
RETURN_IF_FAILED(_dxgiSwapChain->GetBuffer(0, IID_PPV_ARGS(&_dxgiSurface)));

D2D1_RENDER_TARGET_PROPERTIES props =
D2D1_RENDER_TARGET_PROPERTIES renderTargetProps =
D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
0.0f,
0.0f);

RETURN_IF_FAILED(_d2dFactory->CreateDxgiSurfaceRenderTarget(_dxgiSurface.Get(),
&props,
&renderTargetProps,
&_d2dRenderTarget));

const auto fdpi = static_cast<float>(_dpi);

#if false // direct render
RETURN_IF_FAILED(_d2dRenderTarget.As(&_d2dTargetRenderContext));
_d2dTextRenderContext = _d2dTargetRenderContext;
#else // effect render
RETURN_IF_FAILED(_d2dRenderTarget.As(&_d2dTargetRenderContext));
::Microsoft::WRL::ComPtr<ID2D1BitmapRenderTarget> bitmapRenderTarget;
RETURN_IF_FAILED(_d2dRenderTarget->CreateCompatibleRenderTarget(&bitmapRenderTarget));
RETURN_IF_FAILED(bitmapRenderTarget.As(&_d2dTextRenderContext));
::Microsoft::WRL::ComPtr<ID2D1Bitmap> bitmap;
RETURN_IF_FAILED(bitmapRenderTarget->GetBitmap(&bitmap));

#if false // blur
RETURN_IF_FAILED(_d2dTargetRenderContext->CreateEffect(CLSID_D2D1GaussianBlur, &_d2dTextEffect));
_d2dTextEffect->SetInput(0, bitmap.Get());
#elif true // drop-shadow
::Microsoft::WRL::ComPtr<ID2D1Effect> shadowEffect;
RETURN_IF_FAILED(_d2dTargetRenderContext->CreateEffect(CLSID_D2D1Shadow, &shadowEffect));
shadowEffect->SetInput(0, bitmap.Get());
RETURN_IF_FAILED(_d2dTargetRenderContext->CreateEffect(CLSID_D2D1Composite, &_d2dTextEffect));
_d2dTextEffect->SetInputEffect(0, shadowEffect.Get());
_d2dTextEffect->SetInput(1, bitmap.Get());
#else // "water" effect (only affects a 500x500-ish square in the top-left?)
::Microsoft::WRL::ComPtr<ID2D1Effect> turbulenceEffect;
RETURN_IF_FAILED(_d2dTargetRenderContext->CreateEffect(CLSID_D2D1Turbulence, &turbulenceEffect));
RETURN_IF_FAILED(_d2dTargetRenderContext->CreateEffect(CLSID_D2D1DisplacementMap, &_d2dTextEffect));
_d2dTextEffect->SetValue(D2D1_DISPLACEMENTMAP_PROP_SCALE, 50.0f);
_d2dTextEffect->SetValue(D2D1_DISPLACEMENTMAP_PROP_X_CHANNEL_SELECT, D2D1_CHANNEL_SELECTOR_R);
_d2dTextEffect->SetValue(D2D1_DISPLACEMENTMAP_PROP_Y_CHANNEL_SELECT, D2D1_CHANNEL_SELECTOR_G);
_d2dTextEffect->SetInput(0, bitmap.Get());
_d2dTextEffect->SetInputEffect(1, turbulenceEffect.Get());
#endif

#endif

_d2dRenderTarget->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
RETURN_IF_FAILED(_d2dRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::DarkRed),
&_d2dBrushBackground));
Expand All @@ -274,7 +313,6 @@ HRESULT DxEngine::_PrepareRenderTarget() noexcept
// If in composition mode, apply scaling factor matrix
if (_chainMode == SwapChainMode::ForComposition)
{
const auto fdpi = static_cast<float>(_dpi);
_d2dRenderTarget->SetDpi(fdpi, fdpi);

DXGI_MATRIX_3X2_F inverseScale = { 0 };
Expand Down Expand Up @@ -304,6 +342,9 @@ void DxEngine::_ReleaseDeviceResources() noexcept

if (nullptr != _d2dRenderTarget.Get() && _isPainting)
{
if (_d2dTextEffect) {
_d2dTextRenderContext->EndDraw();
}
_d2dRenderTarget->EndDraw();
}

Expand Down Expand Up @@ -702,6 +743,9 @@ HRESULT DxEngine::StartPaint() noexcept
}

_d2dRenderTarget->BeginDraw();
if (_d2dTextEffect) {
_d2dTextRenderContext->BeginDraw();
}
_isPainting = true;
}

Expand All @@ -724,7 +768,14 @@ HRESULT DxEngine::EndPaint() noexcept
if (_haveDeviceResources) {
_isPainting = false;

hr = _d2dRenderTarget->EndDraw();
if (_d2dTextEffect) {
_d2dTargetRenderContext->DrawImage(_d2dTextEffect.Get());
hr = _d2dTextRenderContext->EndDraw();
}

if (SUCCEEDED(hr)) {
hr = _d2dRenderTarget->EndDraw();
}

if (SUCCEEDED(hr)) {

Expand Down Expand Up @@ -885,6 +936,8 @@ HRESULT DxEngine::PaintBufferLine(std::basic_string_view<Cluster> const clusters

// Assemble the drawing context information
DrawingContext context(_d2dRenderTarget.Get(),
_d2dTextRenderContext.Get(),
_d2dTextEffect.Get(),
_d2dBrushForeground.Get(),
_d2dBrushBackground.Get(),
_dwriteFactory.Get(),
Expand Down
5 changes: 5 additions & 0 deletions src/renderer/dx/DxRenderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ namespace Microsoft::Console::Render
::Microsoft::WRL::ComPtr<IDXGIOutput> _dxgiOutput;
::Microsoft::WRL::ComPtr<IDXGISurface> _dxgiSurface;
::Microsoft::WRL::ComPtr<ID2D1RenderTarget> _d2dRenderTarget;
::Microsoft::WRL::ComPtr<ID2D1DeviceContext1> _d2dTargetRenderContext;
// If there is a text effect, this is a ID2D1BitmapRenderTarget, otherwise it is _d2DRenderTarget
::Microsoft::WRL::ComPtr<ID2D1DeviceContext4> _d2dTextRenderContext;
// Can be null, but otherwise should use _d2dTextRenderContext as an input
::Microsoft::WRL::ComPtr<ID2D1Effect> _d2dTextEffect;
::Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> _d2dBrushForeground;
::Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> _d2dBrushBackground;
::Microsoft::WRL::ComPtr<IDXGISwapChain1> _dxgiSwapChain;
Expand Down