Compare commits

...

1 Commits

Author SHA1 Message Date
Leonard Hecker
0ade1583ac Add COLRv1 support 2023-05-29 15:59:56 +02:00
10 changed files with 398 additions and 54 deletions

View File

@@ -53,7 +53,7 @@ Settings::Settings() :
_fUseWindowSizePixels(false),
// window size pixels initialized below
_fInterceptCopyPaste(0),
_fUseDx(UseDx::Disabled),
_fUseDx(UseDx::AtlasEngine),
_fCopyColor(false)
{
_dwScreenBufferSize.X = 80;

View File

@@ -35,7 +35,8 @@ AtlasEngine::AtlasEngine()
#endif
THROW_IF_FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(_p.dwriteFactory), reinterpret_cast<::IUnknown**>(_p.dwriteFactory.addressof())));
_p.dwriteFactory4 = _p.dwriteFactory.try_query<IDWriteFactory4>();
_p.dwriteFactory.try_query_to(_p.dwriteFactory4.addressof());
_p.dwriteFactory.try_query_to(_p.dwriteFactory8.addressof());
THROW_IF_FAILED(_p.dwriteFactory->GetSystemFontFallback(_p.systemFontFallback.addressof()));
_p.systemFontFallback1 = _p.systemFontFallback.try_query<IDWriteFontFallback1>();

View File

@@ -4,6 +4,7 @@
#pragma once
#include <dwrite_3.h>
#include <d2d1_3.h>
#include <d3d11_2.h>
#include <dxgi1_3.h>

View File

@@ -28,28 +28,93 @@ void Microsoft::Console::Render::Atlas::GlyphRunAccumulateBounds(const ID2D1Devi
}
}
wil::com_ptr<IDWriteColorGlyphRunEnumerator1> Microsoft::Console::Render::Atlas::TranslateColorGlyphRun(IDWriteFactory4* dwriteFactory4, D2D_POINT_2F baselineOrigin, const DWRITE_GLYPH_RUN* glyphRun) noexcept
wil::com_ptr<IDWriteColorGlyphRunEnumerator1> Microsoft::Console::Render::Atlas::TranslateColorGlyphRun(IDWriteFactory4* dwriteFactory4, IDWriteFactory8* dwriteFactory8, D2D_POINT_2F baselineOrigin, const DWRITE_GLYPH_RUN* glyphRun)
{
static constexpr auto formats =
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;
wil::com_ptr<IDWriteColorGlyphRunEnumerator1> enumerator;
if (dwriteFactory4)
if (dwriteFactory8)
{
static constexpr auto formats =
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 |
DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE;
std::ignore = dwriteFactory8->TranslateColorGlyphRun(baselineOrigin, glyphRun, nullptr, formats, DWRITE_PAINT_FEATURE_LEVEL_COLR_V1, DWRITE_MEASURING_MODE_NATURAL, nullptr, 0, enumerator.addressof());
}
else if (dwriteFactory4)
{
static constexpr auto formats =
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;
std::ignore = dwriteFactory4->TranslateColorGlyphRun(baselineOrigin, glyphRun, nullptr, formats, DWRITE_MEASURING_MODE_NATURAL, nullptr, 0, enumerator.addressof());
}
return enumerator;
}
void Microsoft::Console::Render::Atlas::DrawColorGlyphRunEnumerator(ID2D1DeviceContext4* d2dRenderTarget4, ID2D1DeviceContext7* d2dRenderTarget7, IDWriteColorGlyphRunEnumerator1* enumerator, ID2D1SolidColorBrush* emojiBrush, ID2D1SolidColorBrush* foregroundBrush)
{
for (;;)
{
BOOL hasRun;
THROW_IF_FAILED(enumerator->MoveNext(&hasRun));
if (!hasRun)
{
break;
}
const DWRITE_COLOR_GLYPH_RUN1* colorGlyphRun = nullptr;
THROW_IF_FAILED(enumerator->GetCurrentRun(&colorGlyphRun));
ID2D1Brush* runBrush = nullptr;
if (colorGlyphRun->paletteIndex == /*DWRITE_NO_PALETTE_INDEX*/ 0xffff)
{
runBrush = foregroundBrush;
}
else
{
emojiBrush->SetColor(&colorGlyphRun->runColor);
runBrush = emojiBrush;
}
const D2D1_POINT_2F origin{ colorGlyphRun->baselineOriginX, colorGlyphRun->baselineOriginY };
switch (static_cast<int>(colorGlyphRun->glyphImageFormat))
{
case DWRITE_GLYPH_IMAGE_FORMATS_NONE:
break;
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:
d2dRenderTarget4->DrawColorBitmapGlyphRun(colorGlyphRun->glyphImageFormat, origin, &colorGlyphRun->glyphRun, colorGlyphRun->measuringMode, D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION_DEFAULT);
break;
case DWRITE_GLYPH_IMAGE_FORMATS_SVG:
d2dRenderTarget4->DrawSvgGlyphRun(origin, &colorGlyphRun->glyphRun, runBrush, nullptr, 0, colorGlyphRun->measuringMode);
break;
case DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE:
d2dRenderTarget7->DrawPaintGlyphRun(origin, &colorGlyphRun->glyphRun, runBrush, 0, colorGlyphRun->measuringMode);
break;
default:
d2dRenderTarget4->DrawGlyphRun(origin, &colorGlyphRun->glyphRun, colorGlyphRun->glyphRunDescription, runBrush, colorGlyphRun->measuringMode);
break;
}
}
}
bool Microsoft::Console::Render::Atlas::ColorGlyphRunMoveNext(IDWriteColorGlyphRunEnumerator1* enumerator)
{
BOOL hasRun;
@@ -70,7 +135,7 @@ void Microsoft::Console::Render::Atlas::ColorGlyphRunAccumulateBounds(const ID2D
GlyphRunAccumulateBounds(d2dRenderTarget, baselineOrigin, &colorGlyphRun->glyphRun, bounds);
}
void Microsoft::Console::Render::Atlas::ColorGlyphRunDraw(ID2D1DeviceContext4* d2dRenderTarget4, ID2D1SolidColorBrush* emojiBrush, ID2D1SolidColorBrush* foregroundBrush, const DWRITE_COLOR_GLYPH_RUN1* colorGlyphRun) noexcept
void Microsoft::Console::Render::Atlas::ColorGlyphRunDraw(ID2D1DeviceContext4* d2dRenderTarget4, ID2D1DeviceContext7* d2dRenderTarget7, ID2D1SolidColorBrush* emojiBrush, ID2D1SolidColorBrush* foregroundBrush, const DWRITE_COLOR_GLYPH_RUN1* colorGlyphRun) noexcept
{
ID2D1Brush* runBrush = nullptr;
if (colorGlyphRun->paletteIndex == /*DWRITE_NO_PALETTE_INDEX*/ 0xffff)
@@ -85,7 +150,7 @@ void Microsoft::Console::Render::Atlas::ColorGlyphRunDraw(ID2D1DeviceContext4* d
const D2D1_POINT_2F baselineOrigin{ colorGlyphRun->baselineOriginX, colorGlyphRun->baselineOriginY };
switch (colorGlyphRun->glyphImageFormat)
switch (static_cast<int>(colorGlyphRun->glyphImageFormat))
{
case DWRITE_GLYPH_IMAGE_FORMATS_NONE:
break;
@@ -98,6 +163,9 @@ void Microsoft::Console::Render::Atlas::ColorGlyphRunDraw(ID2D1DeviceContext4* d
case DWRITE_GLYPH_IMAGE_FORMATS_SVG:
d2dRenderTarget4->DrawSvgGlyphRun(baselineOrigin, &colorGlyphRun->glyphRun, runBrush, nullptr, 0, colorGlyphRun->measuringMode);
break;
case DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE:
d2dRenderTarget7->DrawPaintGlyphRun(baselineOrigin, &colorGlyphRun->glyphRun, runBrush, 0, colorGlyphRun->measuringMode);
break;
default:
d2dRenderTarget4->DrawGlyphRun(baselineOrigin, &colorGlyphRun->glyphRun, colorGlyphRun->glyphRunDescription, runBrush, colorGlyphRun->measuringMode);
break;

View File

@@ -77,9 +77,10 @@ namespace Microsoft::Console::Render::Atlas
inline constexpr D2D1_RECT_F GlyphRunEmptyBounds{ 1e38f, 1e38f, -1e38f, -1e38f };
void GlyphRunAccumulateBounds(const ID2D1DeviceContext* d2dRenderTarget, D2D1_POINT_2F baselineOrigin, const DWRITE_GLYPH_RUN* glyphRun, D2D1_RECT_F& bounds);
wil::com_ptr<IDWriteColorGlyphRunEnumerator1> TranslateColorGlyphRun(IDWriteFactory4* dwriteFactory4, D2D_POINT_2F baselineOrigin, const DWRITE_GLYPH_RUN* glyphRun) noexcept;
wil::com_ptr<IDWriteColorGlyphRunEnumerator1> TranslateColorGlyphRun(IDWriteFactory4* dwriteFactory4, IDWriteFactory8* dwriteFactory8, D2D_POINT_2F baselineOrigin, const DWRITE_GLYPH_RUN* glyphRun);
void DrawColorGlyphRunEnumerator(ID2D1DeviceContext4* d2dRenderTarget4, ID2D1DeviceContext7* d2dRenderTarget7, IDWriteColorGlyphRunEnumerator1* enumerator, ID2D1SolidColorBrush* emojiBrush, ID2D1SolidColorBrush* foregroundBrush);
bool ColorGlyphRunMoveNext(IDWriteColorGlyphRunEnumerator1* enumerator);
const DWRITE_COLOR_GLYPH_RUN1* ColorGlyphRunGetCurrentRun(IDWriteColorGlyphRunEnumerator1* enumerator);
void ColorGlyphRunAccumulateBounds(const ID2D1DeviceContext* d2dRenderTarget, const DWRITE_COLOR_GLYPH_RUN1* colorGlyphRun, D2D1_RECT_F& bounds);
void ColorGlyphRunDraw(ID2D1DeviceContext4* d2dRenderTarget4, ID2D1SolidColorBrush* emojiBrush, ID2D1SolidColorBrush* foregroundBrush, const DWRITE_COLOR_GLYPH_RUN1* colorGlyphRun) noexcept;
void ColorGlyphRunDraw(ID2D1DeviceContext4* d2dRenderTarget4, ID2D1DeviceContext7* d2dRenderTarget7, ID2D1SolidColorBrush* emojiBrush, ID2D1SolidColorBrush* foregroundBrush, const DWRITE_COLOR_GLYPH_RUN1* colorGlyphRun) noexcept;
}

View File

@@ -27,6 +27,7 @@ void BackendD2D::ReleaseResources() noexcept
{
_renderTarget.reset();
_renderTarget4.reset();
_renderTarget7.reset();
// Ensure _handleSettingsUpdate() is called so that _renderTarget gets recreated.
_generation = {};
}
@@ -85,6 +86,7 @@ void BackendD2D::_handleSettingsUpdate(const RenderingPayload& p)
// ID2D1RenderTarget and ID2D1DeviceContext are the same and I'm tired of pretending they're not.
THROW_IF_FAILED(p.d2dFactory->CreateDxgiSurfaceRenderTarget(buffer.query<IDXGISurface>().get(), &props, reinterpret_cast<ID2D1RenderTarget**>(_renderTarget.addressof())));
_renderTarget.query_to(_renderTarget4.addressof());
_renderTarget.query_to(_renderTarget7.addressof());
_renderTarget->SetUnitMode(D2D1_UNIT_MODE_PIXELS);
_renderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
@@ -220,17 +222,7 @@ void BackendD2D::_drawText(RenderingPayload& p)
if (glyphRun.fontFace)
{
D2D1_RECT_F bounds = GlyphRunEmptyBounds;
if (const auto enumerator = TranslateColorGlyphRun(p.dwriteFactory4.get(), baselineOrigin, &glyphRun))
{
while (ColorGlyphRunMoveNext(enumerator.get()))
{
const auto colorGlyphRun = ColorGlyphRunGetCurrentRun(enumerator.get());
ColorGlyphRunDraw(_renderTarget4.get(), _emojiBrush.get(), brush, colorGlyphRun);
ColorGlyphRunAccumulateBounds(_renderTarget.get(), colorGlyphRun, bounds);
}
}
else
{
_renderTarget->DrawGlyphRun(baselineOrigin, &glyphRun, brush, DWRITE_MEASURING_MODE_NATURAL);
GlyphRunAccumulateBounds(_renderTarget.get(), baselineOrigin, &glyphRun, bounds);

View File

@@ -36,6 +36,7 @@ namespace Microsoft::Console::Render::Atlas
wil::com_ptr<ID2D1DeviceContext> _renderTarget;
wil::com_ptr<ID2D1DeviceContext4> _renderTarget4; // Optional. Supported since Windows 10 14393.
wil::com_ptr<ID2D1DeviceContext7> _renderTarget7; // Optional. Supported since Windows 11 <unknown>. 25357 maybe?
wil::com_ptr<ID2D1StrokeStyle> _dottedStrokeStyle;
wil::com_ptr<ID2D1Bitmap> _backgroundBitmap;
wil::com_ptr<ID2D1BitmapBrush> _backgroundBrush;

View File

@@ -9,6 +9,8 @@
#include <shader_ps.h>
#include <shader_vs.h>
#include <wincodec.h>
#include "dwrite.h"
#include "../../types/inc/ColorFix.hpp"
@@ -24,6 +26,7 @@ TIL_FAST_MATH_BEGIN
// This code packs various data into smaller-than-int types to save both CPU and GPU memory. This warning would force
// us to add dozens upon dozens of gsl::narrow_cast<>s throughout the file which is more annoying than helpful.
#pragma warning(disable : 4100)
#pragma warning(disable : 4242) // '=': conversion from '...' to '...', possible loss of data
#pragma warning(disable : 4244) // 'initializing': conversion from '...' to '...', possible loss of data
#pragma warning(disable : 4267) // 'argument': conversion from '...' to '...', possible loss of data
@@ -42,6 +45,149 @@ TIL_FAST_MATH_BEGIN
using namespace Microsoft::Console::Render::Atlas;
class SharedWicBitmap sealed : public IWICBitmap, public IWICBitmapLock
{
public:
static HRESULT Create(_In_ u32 width, _In_ u32 height, _In_ u32 stride, _In_ WICPixelFormatGUID const& pixelFormat, _In_opt_ u8* pBuffer, _Outptr_ SharedWicBitmap** ppWICBitmap)
{
if (pBuffer == nullptr)
{
const u32 size = stride * height;
pBuffer = new u8[size]{};
}
*ppWICBitmap = new SharedWicBitmap(width, height, stride, pixelFormat, pBuffer, pBuffer != nullptr);
return S_OK;
}
STDMETHODIMP QueryInterface(_In_ REFIID riid, _Outptr_ void** ppvObject) override
{
if (__uuidof(IUnknown) == riid)
{
*ppvObject = static_cast<IUnknown*>(static_cast<IWICBitmap*>(this));
}
else if (__uuidof(IWICBitmap) == riid)
{
*ppvObject = static_cast<IWICBitmap*>(this);
}
else if (__uuidof(IWICBitmapLock) == riid)
{
*ppvObject = static_cast<IWICBitmapLock*>(this);
}
else
{
*ppvObject = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
ULONG STDMETHODCALLTYPE AddRef() override
{
return ++m_refCount;
}
ULONG STDMETHODCALLTYPE Release() override
{
u32 newRefCount = --m_refCount;
if (newRefCount == 0)
{
delete this;
}
return newRefCount;
}
STDMETHODIMP GetSize(_Out_ UINT* pWidth, _Out_ UINT* pHeight) override
{
*pWidth = m_width;
*pHeight = m_height;
return S_OK;
}
STDMETHODIMP GetStride(_Out_ UINT* pStride) override
{
*pStride = m_stride;
return S_OK;
}
STDMETHODIMP GetPixelFormat(_Out_ WICPixelFormatGUID* pPixelFormat) override
{
*pPixelFormat = m_pixelFormat;
return S_OK;
}
STDMETHODIMP GetResolution(_Out_ double* pDpiX, _Out_ double* pDpiY) override
{
*pDpiX = 96.0;
*pDpiY = 96.0;
return S_OK;
}
STDMETHODIMP SetResolution(double dpiX, double dpiY) override
{
assert(false);
return E_NOTIMPL;
}
STDMETHODIMP CopyPalette(_In_opt_ IWICPalette* pIPalette) override
{
assert(false);
return E_NOTIMPL;
}
STDMETHODIMP SetPalette(_In_opt_ IWICPalette* pIPalette) override
{
assert(false);
return E_NOTIMPL;
}
STDMETHODIMP CopyPixels(_In_opt_ const WICRect* prc, UINT cbStride, UINT cbBufferSize, _Out_writes_all_(cbBufferSize) BYTE* pbBuffer) override
{
assert(false);
return E_NOTIMPL;
}
STDMETHODIMP Lock(_In_opt_ const WICRect* prcLock, DWORD flags, _Outptr_result_maybenull_ IWICBitmapLock** ppILock) override
{
*ppILock = this;
AddRef();
return S_OK;
}
STDMETHODIMP GetDataPointer(_Out_ UINT* pcbBufferSize, _Outptr_result_buffer_all_maybenull_(*pcbBufferSize) BYTE** ppData) override
{
*pcbBufferSize = m_stride * m_height;
*ppData = m_pBuffer;
return S_OK;
}
private:
u32 m_refCount;
u32 m_width;
u32 m_height;
u32 m_stride;
WICPixelFormatGUID m_pixelFormat;
u8* m_pBuffer;
bool m_usesSharedMemory;
SharedWicBitmap(_In_ u32 width, _In_ u32 height, _In_ u32 stride, _In_ WICPixelFormatGUID pixelFormat, _In_ u8* pBuffer, _In_ bool usesSharedMemory) :
m_refCount(1), m_width(width), m_height(height), m_stride(stride), m_pixelFormat(pixelFormat), m_pBuffer(pBuffer), m_usesSharedMemory(usesSharedMemory)
{
}
~SharedWicBitmap()
{
if (!m_usesSharedMemory)
{
delete[] m_pBuffer;
}
}
};
template<>
struct std::hash<u16>
{
@@ -191,6 +337,22 @@ BackendD3D::BackendD3D(const RenderingPayload& p)
THROW_IF_FAILED(p.device->CreateBlendState(&desc, _blendState.addressof()));
}
SharedWicBitmap* pSharedWicBitmap = nullptr;
THROW_IF_FAILED(SharedWicBitmap::Create(1, 1, 4, GUID_WICPixelFormat8bppAlpha, nullptr, &pSharedWicBitmap));
const auto renderTargetProperties = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_SOFTWARE,
D2D1::PixelFormat(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
96.0f,
96.0f,
D2D1_RENDER_TARGET_USAGE_NONE,
D2D1_FEATURE_LEVEL_DEFAULT);
wil::com_ptr<ID2D1RenderTarget> renderTarget;
THROW_IF_FAILED(p.d2dFactory->CreateWicBitmapRenderTarget(pSharedWicBitmap, &renderTargetProperties, renderTarget.addressof()));
renderTarget.query_to(_d2dRenderTarget4WIC.addressof());
renderTarget.query_to(_d2dRenderTarget7WIC.addressof());
#ifndef NDEBUG
_sourceDirectory = std::filesystem::path{ __FILE__ }.parent_path();
_sourceCodeWatcher = wil::make_folder_change_reader_nothrow(_sourceDirectory.c_str(), false, wil::FolderChangeEvents::FileName | wil::FolderChangeEvents::LastWriteTime, [this](wil::FolderChangeEvent, PCWSTR path) {
@@ -742,6 +904,7 @@ void BackendD3D::_resizeGlyphAtlas(const RenderingPayload& p, const u16 u, const
{
_d2dRenderTarget.reset();
_d2dRenderTarget4.reset();
_d2dRenderTarget7.reset();
_glyphAtlas.reset();
_glyphAtlasView.reset();
@@ -769,6 +932,7 @@ void BackendD3D::_resizeGlyphAtlas(const RenderingPayload& p, const u16 u, const
// ID2D1RenderTarget and ID2D1DeviceContext are the same and I'm tired of pretending they're not.
THROW_IF_FAILED(p.d2dFactory->CreateDxgiSurfaceRenderTarget(surface.get(), &props, reinterpret_cast<ID2D1RenderTarget**>(_d2dRenderTarget.addressof())));
_d2dRenderTarget.try_query_to(_d2dRenderTarget4.addressof());
_d2dRenderTarget.try_query_to(_d2dRenderTarget7.addressof());
_d2dRenderTarget->SetUnitMode(D2D1_UNIT_MODE_PIXELS);
// We don't really use D2D for anything except DWrite, but it
@@ -1254,7 +1418,7 @@ bool BackendD3D::_drawGlyph(const RenderingPayload& p, const AtlasFontFaceEntryI
#endif
const auto lineRendition = static_cast<LineRendition>(fontFaceEntry.lineRendition);
const auto needsTransform = lineRendition != LineRendition::SingleWidth;
auto needsTransform = lineRendition != LineRendition::SingleWidth;
static constexpr D2D1_MATRIX_3X2_F identityTransform{ .m11 = 1, .m22 = 1 };
D2D1_MATRIX_3X2_F transform = identityTransform;
@@ -1298,24 +1462,39 @@ bool BackendD3D::_drawGlyph(const RenderingPayload& p, const AtlasFontFaceEntryI
}
});
auto enumerator = TranslateColorGlyphRun(p.dwriteFactory4.get(), p.dwriteFactory8.get(), {}, &glyphRun);
isColorGlyph = !!enumerator;
if (!enumerator)
{
const auto enumerator = TranslateColorGlyphRun(p.dwriteFactory4.get(), {}, &glyphRun);
THROW_IF_FAILED(_d2dRenderTarget4->GetGlyphRunWorldBounds({}, &glyphRun, DWRITE_MEASURING_MODE_NATURAL, &bounds));
}
else
{
wil::com_ptr<ID2D1SolidColorBrush> emojiBrush;
wil::com_ptr<ID2D1SolidColorBrush> brush;
if (!enumerator)
{
THROW_IF_FAILED(_d2dRenderTarget->GetGlyphRunWorldBounds({}, &glyphRun, DWRITE_MEASURING_MODE_NATURAL, &bounds));
}
else
{
isColorGlyph = true;
_d2dRenderTarget4->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
static constexpr D2D1_COLOR_F color{ 1, 1, 1, 1 };
THROW_IF_FAILED(_d2dRenderTarget4WIC->CreateSolidColorBrush(&color, nullptr, emojiBrush.put()));
THROW_IF_FAILED(_d2dRenderTarget4WIC->CreateSolidColorBrush(&color, nullptr, brush.put()));
while (ColorGlyphRunMoveNext(enumerator.get()))
{
const auto colorGlyphRun = ColorGlyphRunGetCurrentRun(enumerator.get());
ColorGlyphRunAccumulateBounds(_d2dRenderTarget.get(), colorGlyphRun, bounds);
}
}
wil::com_ptr<ID2D1CommandList> commandList;
THROW_IF_FAILED(_d2dRenderTarget4WIC->CreateCommandList(commandList.addressof()));
wil::com_ptr<ID2D1Image> previousTarget;
_d2dRenderTarget4WIC->GetTarget(previousTarget.addressof());
_d2dRenderTarget4WIC->SetTarget(commandList.get());
const auto cleanup = wil::scope_exit([&]() {
_d2dRenderTarget4WIC->SetTarget(previousTarget.get());
});
_d2dRenderTarget4WIC->BeginDraw();
DrawColorGlyphRunEnumerator(_d2dRenderTarget4WIC.get(), _d2dRenderTarget7WIC.get(), enumerator.get(), emojiBrush.get(), brush.get());
THROW_IF_FAILED(_d2dRenderTarget4WIC->EndDraw());
THROW_IF_FAILED(commandList->Close());
THROW_IF_FAILED(_d2dRenderTarget4WIC->GetImageWorldBounds(commandList.get(), &bounds));
}
// Overhangs for box glyphs can produce unsightly effects, where the antialiased edges of horizontal
@@ -1386,18 +1565,14 @@ bool BackendD3D::_drawGlyph(const RenderingPayload& p, const AtlasFontFaceEntryI
_d2dRenderTarget->SetTransform(&transform);
}
if (!isColorGlyph)
if (!enumerator)
{
_d2dRenderTarget->DrawGlyphRun(baselineOrigin, &glyphRun, _brush.get(), DWRITE_MEASURING_MODE_NATURAL);
}
else
{
const auto enumerator = TranslateColorGlyphRun(p.dwriteFactory4.get(), baselineOrigin, &glyphRun);
while (ColorGlyphRunMoveNext(enumerator.get()))
{
const auto colorGlyphRun = ColorGlyphRunGetCurrentRun(enumerator.get());
ColorGlyphRunDraw(_d2dRenderTarget4.get(), _emojiBrush.get(), _brush.get(), colorGlyphRun);
}
enumerator = TranslateColorGlyphRun(p.dwriteFactory4.get(), p.dwriteFactory8.get(), baselineOrigin, &glyphRun);
DrawColorGlyphRunEnumerator(_d2dRenderTarget4.get(), _d2dRenderTarget7.get(), enumerator.get(), _emojiBrush.get(), _brush.get());
}
// Ligatures are drawn with strict cell-wise foreground color, while other text allows colors to overhang

View File

@@ -263,9 +263,12 @@ namespace Microsoft::Console::Render::Atlas
stbrp_context _rectPacker{};
til::CoordType _ligatureOverhangTriggerLeft = 0;
til::CoordType _ligatureOverhangTriggerRight = 0;
wil::com_ptr<ID2D1DeviceContext> _d2dRenderTarget;
wil::com_ptr<ID2D1DeviceContext4> _d2dRenderTarget4; // Optional. Supported since Windows 10 14393.
wil::com_ptr<ID2D1DeviceContext7> _d2dRenderTarget7; // Optional. Supported since Windows 11 <unknown>. 25357 maybe?
wil::com_ptr<ID2D1DeviceContext4> _d2dRenderTarget4WIC;
wil::com_ptr<ID2D1DeviceContext7> _d2dRenderTarget7WIC;
wil::com_ptr<ID2D1SolidColorBrush> _emojiBrush;
wil::com_ptr<ID2D1SolidColorBrush> _brush;
wil::com_ptr<ID2D1Bitmap1> _softFontBitmap;

View File

@@ -7,6 +7,107 @@
#include "../../renderer/inc/IRenderEngine.hpp"
// vvvv copied out of microsoft.windows.sdk.cpp.10.0.25363-preview vvvv
#define DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE static_cast<DWRITE_GLYPH_IMAGE_FORMATS>(0x00000100)
enum DWRITE_PAINT_FEATURE_LEVEL
{
DWRITE_PAINT_FEATURE_LEVEL_NONE = 0,
DWRITE_PAINT_FEATURE_LEVEL_COLR_V0 = 1,
DWRITE_PAINT_FEATURE_LEVEL_COLR_V1 = 2
};
interface DWRITE_DECLARE_INTERFACE("EE0A7FB5-DEF4-4C23-A454-C9C7DC878398") IDWriteFactory8 : public IDWriteFactory7
{
/// <summary>
/// Translates a glyph run to a sequence of color glyph runs, which can be
/// rendered to produce a color representation of the original "base" run.
/// </summary>
/// <param name="baselineOriginX">Horizontal and vertical origin of the base glyph run in
/// pre-transform coordinates.</param>
/// <param name="glyphRun">Pointer to the original "base" glyph run.</param>
/// <param name="glyphRunDescription">Optional glyph run description.</param>
/// <param name="desiredGlyphImageFormats">Which data formats TranslateColorGlyphRun
/// should split the runs into.</param>
/// <param name="paintFeatureLevel">Paint feature level supported by the caller. Used
/// when desiredGlyphImageFormats includes DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE. See
/// DWRITE_PAINT_FEATURE_LEVEL for more information.</param>
/// <param name="measuringMode">Measuring mode, needed to compute the origins
/// of each glyph.</param>
/// <param name="worldToDeviceTransform">Matrix converting from the client's
/// coordinate space to device coordinates (pixels), i.e., the world transform
/// multiplied by any DPI scaling.</param>
/// <param name="colorPaletteIndex">Zero-based index of the color palette to use.
/// Valid indices are less than the number of palettes in the font, as returned
/// by IDWriteFontFace2::GetColorPaletteCount.</param>
/// <param name="colorEnumerator">If the function succeeds, receives a pointer
/// to an enumerator object that can be used to obtain the color glyph runs.
/// If the base run has no color glyphs, then the output pointer is NULL
/// and the method returns DWRITE_E_NOCOLOR.</param>
/// <returns>
/// Returns DWRITE_E_NOCOLOR if the font has no color information, the glyph run
/// does not contain any color glyphs, or the specified color palette index
/// is out of range. In this case, the client should render the original glyph
/// run. Otherwise, returns a standard HRESULT error code.
/// </returns>
/// <remarks>
/// The old IDWriteFactory2::TranslateColorGlyphRun is equivalent to passing
/// DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE|CFF|COLR.
/// </remarks>
STDMETHOD(TranslateColorGlyphRun)
(
D2D1_POINT_2F baselineOrigin,
_In_ DWRITE_GLYPH_RUN const* glyphRun,
_In_opt_ DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
DWRITE_GLYPH_IMAGE_FORMATS desiredGlyphImageFormats,
DWRITE_PAINT_FEATURE_LEVEL paintFeatureLevel,
DWRITE_MEASURING_MODE measuringMode,
_In_opt_ DWRITE_MATRIX const* worldAndDpiTransform,
UINT32 colorPaletteIndex,
_COM_Outptr_ IDWriteColorGlyphRunEnumerator1** colorEnumerator) PURE;
};
interface DX_DECLARE_INTERFACE("ec891cf7-9b69-4851-9def-4e0915771e62") ID2D1DeviceContext7 : public ID2D1DeviceContext6
{
/// <summary>
/// Get the maximum paint feature level supported by DrawPaintGlyphRun.
/// </summary>
STDMETHOD_(DWRITE_PAINT_FEATURE_LEVEL, GetPaintFeatureLevel)
() PURE;
/// <summary>
/// Draws a color glyph run that has the format of
/// DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE.
/// </summary>
/// <param name="colorPaletteIndex">The index used to select a color palette within
/// a color font. Note that this not the same as the paletteIndex in the
/// DWRITE_COLOR_GLYPH_RUN struct, which is not relevant for paint glyphs.</param>
STDMETHOD_(void, DrawPaintGlyphRun)
(
D2D1_POINT_2F baselineOrigin,
_In_ CONST DWRITE_GLYPH_RUN* glyphRun,
_In_opt_ ID2D1Brush* defaultFillBrush = NULL,
UINT32 colorPaletteIndex = 0,
DWRITE_MEASURING_MODE measuringMode = DWRITE_MEASURING_MODE_NATURAL) PURE;
/// <summary>
/// Draws a glyph run, using color representations of glyphs if available.
/// </summary>
STDMETHOD_(void, DrawGlyphRunWithColorSupport)
(
D2D1_POINT_2F baselineOrigin,
_In_ CONST DWRITE_GLYPH_RUN* glyphRun,
_In_opt_ CONST DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription,
_In_opt_ ID2D1Brush* foregroundBrush,
_In_opt_ ID2D1SvgGlyphStyle* svgGlyphStyle,
UINT32 colorPaletteIndex = 0,
DWRITE_MEASURING_MODE measuringMode = DWRITE_MEASURING_MODE_NATURAL,
D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION bitmapSnapOption = D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION_DEFAULT) PURE;
}; // interface ID2D1DeviceContext7
// ^^^^ copied out of microsoft.windows.sdk.cpp.10.0.25363-preview ^^^^
namespace Microsoft::Console::Render::Atlas
{
#define ATLAS_FLAG_OPS(type, underlying) \
@@ -462,7 +563,8 @@ namespace Microsoft::Console::Render::Atlas
//// Parameters which are constant across backends.
wil::com_ptr<ID2D1Factory> d2dFactory;
wil::com_ptr<IDWriteFactory2> dwriteFactory;
wil::com_ptr<IDWriteFactory4> dwriteFactory4; // optional, might be nullptr
wil::com_ptr<IDWriteFactory4> dwriteFactory4; // Optional. Supported since Windows 10 14393.
wil::com_ptr<IDWriteFactory8> dwriteFactory8; // Optional. Supported since Windows 11 <unknown>. 25357 maybe?
wil::com_ptr<IDWriteFontFallback> systemFontFallback;
wil::com_ptr<IDWriteFontFallback1> systemFontFallback1; // optional, might be nullptr
wil::com_ptr<IDWriteTextAnalyzer1> textAnalyzer;