GDI - 我可以在 DrawText 中使用新的 Windows 10 Segoe UI Emoji 彩色字体吗?

Posted

技术标签:

【中文标题】GDI - 我可以在 DrawText 中使用新的 Windows 10 Segoe UI Emoji 彩色字体吗?【英文标题】:GDI - Can I use the new Windows 10 Segoe UI Emoji colored font with DrawText? 【发布时间】:2021-06-27 03:50:41 【问题描述】:

我正在使用 Embarcadero RAD Studio (10.2 Tokyo starter) 和 Windows GDI 创建一个 c++ 项目,通过 DrawText() 函数绘制文本。

我最近看到 Windows 10 提供了一种新的“Segoe UI Emoji”字体,它可能允许文本函数绘制彩色表情符号。我发现了几个使用 Direct2D 的示例,但没有一个使用纯 GDI 函数。

我也试过一个简单的代码,像这样:

HDC hDC = ::GetDC(Handle);

std::auto_ptr<TCanvas> pCanvas(new TCanvas());
pCanvas->Handle = hDC;

pCanvas->Brush->Color = clWhite;
pCanvas->Brush->Style = bsSolid;
pCanvas->FillRect(TRect(0, 0, ClientWidth, ClientHeight));

const std::wstring text = L"Test ???? ???? ???? ???? ???? ???? ???? ????";

TRect textRect(10, 10, ClientWidth - 10, ClientHeight - 10);

hFont = ::CreateFont(-40,
                      0, 
                      0,
                      0,
                      FW_DONTCARE,
                      FALSE,
                      FALSE,
                      FALSE,
                      DEFAULT_CHARSET,
                      OUT_OUTLINE_PRECIS,
                      CLIP_DEFAULT_PRECIS,
                      CLEARTYPE_QUALITY,
                      VARIABLE_PITCH,
                      L"Segoe UI Emoji");

::SelectObject(hDC, hFont);

::DrawTextW(hDC,
            text.c_str(),
            text.length(),
            &textRect,
            DT_LEFT | DT_TOP | DT_SINGLELINE);

::DeleteObject(hFont);

输出结果在符号方面听起来不错,但它们是黑白绘制的,没有颜色,如下面的屏幕截图所示:

我找不到任何其他选项可以允许使用彩色符号而不是黑白符号来绘制文本。有没有办法在 GDI DrawText() 函数中激活对颜色的支持,如果是,怎么做?还是只有 Direct2D 可以绘制彩色表情符号?

于 2017 年 10 月 30 日编辑

由于 GDI 无法完成这项工作(不幸的是,正如我所想的那样)我在这里发布了上述代码的 Direct2D 版本,它对我有用。

const std::wstring text = L"Test ???? ???? ???? ???? ???? ???? ???? ????";

HDC hDC = ::GetDC(Handle);

std::auto_ptr<TCanvas> pGDICanvas(new TCanvas());
pGDICanvas->Handle = hDC;

pGDICanvas->Brush->Color = clWhite;
pGDICanvas->Brush->Style = bsSolid;
pGDICanvas->FillRect(TRect(0, 0, ClientWidth, ClientHeight));

::D2D1_RECT_F textRect;
textRect.left   = 10;
textRect.top    = 10;
textRect.right  = ClientWidth  - 10;
textRect.bottom = ClientHeight - 10;

std::auto_ptr<TDirect2DCanvas> pCanvas(new TDirect2DCanvas(hDC, TRect(0, 0, ClientWidth, ClientHeight)));

// configure Direct2D font
pCanvas->Font->Size        = 40;
pCanvas->Font->Name        = L"Segoe UI Emoji";
pCanvas->Font->Orientation = 0;
pCanvas->Font->Pitch       = System::Uitypes::TFontPitch::fpVariable;
pCanvas->Font->Style       = TFontStyles();

// get DirectWrite text format object
_di_IDWriteTextFormat pFormat = pCanvas->Font->Handle;

if (!pFormat)
    return;

pCanvas->RenderTarget->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);

::D2D1_COLOR_F color;
color.r = 0.0f;
color.g = 0.0f;
color.b = 0.0f;
color.a = 1.0f;

::ID2D1SolidColorBrush* pBrush = NULL;

// create solid color brush, use pen color if rect is completely filled with outline
pCanvas->RenderTarget->CreateSolidColorBrush(color, &pBrush);

if (!pBrush)
    return;

// set horiz alignment
pFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);

// set vert alignment
pFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR);

// set reading direction
pFormat->SetReadingDirection(DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);

// set word wrapping mode
pFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);

IDWriteInlineObject* pInlineObject = NULL;

::DWRITE_TRIMMING trimming;
trimming.delimiter      = 0;
trimming.delimiterCount = 0;
trimming.granularity    = DWRITE_TRIMMING_GRANULARITY_NONE;

// set text trimming
pFormat->SetTrimming(&trimming, pInlineObject);

pCanvas->BeginDraw();

pCanvas->RenderTarget->DrawText(text.c_str(), text.length(), pFormat, textRect, pBrush,
            D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT);

pCanvas->EndDraw();

当然,此代码只会在当前最新版本的 Windows 10 及更高版本上绘制彩色表情符号。在以前的版本中,文本将如上绘制(并且代码可能无法编译)。

阅读奖励

MSDN:Color Fonts with DirectWrite, Direct2D, and Win2D

【问题讨论】:

使用 DirectWrite,您必须为彩色字体选择颜色渲染,以避免破坏未准备好的程序。如果 GDI 有可能,他们可能也会选择加入。鉴于 DirectWrite 是唯一的“当前”文本呈现堆栈,我认为 GDI 没有更新让您可以选择加入。 谢谢你的回答,不幸的是我是这么想的 这并不排除您可以使用 GDI 手动执行此操作的可能性。我想可能有一种方法可以将各种颜色层实例化为不同的字体。然后你必须设置文本颜色,为当前图层绘制文本,并为每种颜色重复。 再次感谢您的回答。事实上,我已经找到了一种方法来解决 GDI 以获得我想要的效果。但是我的代码很复杂,而且我对使用新的 Windows 10 功能很感兴趣,这似乎做了完全相同的事情,以免重新发明***。不幸的是,实现这一目标的唯一方法似乎是重写我的代码以使用 Direct2D 而不是 GDI 【参考方案1】:

GDI 不支持彩色字体(即使你走完整的 Uniscribe 路线),如果你想要彩色字体支持,你必须使用 Direct2D。更简单的 GDI API 不支持彩色字体是有道理的,因为彩色字体需要使用 OpenType 标签,并且 DrawText/TextOut 都没有提供这种级别的控制,Uniscribe 允许这样的标签,但根本没有扩展到支持彩色字体。

【讨论】:

【参考方案2】:

您可以使用 DirectWrite 将彩色表情符号绘制到内存 DC 中的位图上,然后将 BitBlt() 绘制到目标 DC。基本上,您需要实现一个自定义 IDWriteTextRenderer 类并使用您的渲染器调用 IDWriteTextLayout::Draw(),然后复制结果。在您的课程中,您从 IDWriteFactory 检索 IDWriteGdiInterop 并调用 IDWriteGdiInterop::CreateBitmapRenderTarget() 以获取位图渲染目标;调用 IDWriteFactory::CreateMonitorRenderingParams() 来获取渲染参数,调用 IDWriteFactory::CreateTextFormat() 来设置你的文本格式。唯一重要的方法是 DrawGlyphRun(),您可以在其中使用 IDWriteFactory2::TranslateColorGlyphRun() 获得 IDWriteColorGlyphRunEnumerator,并且在每次颜色运行时,调用 IDWriteBitmapRenderTarget::DrawGlyphRun() 来为您完成工作。 只要记住在窗口大小/位置发生变化时更新渲染目标/参数。 你可以参考这个 MSDN 文档:

渲染到 GDI 表面 https://msdn.microsoft.com/en-us/library/windows/desktop/ff485856(v=vs.85).aspx

【讨论】:

以上是关于GDI - 我可以在 DrawText 中使用新的 Windows 10 Segoe UI Emoji 彩色字体吗?的主要内容,如果未能解决你的问题,请参考以下文章

Delphi中的GDI行距?

MFC DrawText,垂直,DT_CALCRECT with lf_escapement = 900

DirectWrite GDI 互操作:使用“IDWriteFontFace”绘制文本的简单方法

c++ 使用新的图像 gdi+

使用 ffmpeg drawtext 突出显示文本中的一些单词

android中使用canvas.drawText函数可否设置旋转