directwrite - Direct2D - 如何绘制尽可能接近 GDI 渲染的文本
问题描述
我正在使用 Embarcadero RAD Studio XE7 编译器(C++ Builder),我正在尝试提供的 Direct2D API。我的目的是创建提供与 GDI 完全相同的渲染的应用程序,但也可能使 Direct2D 提供的新功能受益,例如 Window 10 上的彩色表情符号。
在这种情况下,我编写了一个小的文本绘制函数,它使用 Direct2D 来完成这项工作。我还搜索了如何配置 Direct2D/DirectWrite 以绘制尽可能接近 GDI 所做的文本。
这导致了以下功能:
void DrawText_Direct2D(const std::wstring& text, const TRect& rect, TColor bgColor,
TFont* pFont, TCanvas* pCanvas)
{
if (!pFont || !pCanvas)
return;
// fill destination canvas background with provided color
WGDIHelper::Fill(pCanvas, rect, bgColor);
::D2D1_RECT_F drawRect;
drawRect.left = rect.Left;
drawRect.top = rect.Top;
drawRect.right = rect.Right;
drawRect.bottom = rect.Bottom;
// get Direct2D destination canvas
std::unique_ptr<TDirect2DCanvas> pD2DCanvas(new TDirect2DCanvas(pCanvas->Handle, rect));
// configure Direct2D font
pD2DCanvas->Font->Height = pFont->Height;
pD2DCanvas->Font->Name = pFont->Name;
pD2DCanvas->Font->Orientation = pFont->Orientation;
pD2DCanvas->Font->Pitch = pFont->Pitch;
pD2DCanvas->Font->Style = pFont->Style;
// get DirectWrite text format object
_di_IDWriteTextFormat pFormat = pD2DCanvas->Font->Handle;
// found it?
if (!pFormat)
return;
// get (or create) the DirectWrite factory
_di_IDWriteFactory pDirectWrite = ::DWriteFactory(DWRITE_FACTORY_TYPE_SHARED);
if (!pDirectWrite)
return;
// configure and apply new rendering parameters for DirectWrite
_di_IDWriteRenderingParams renderParams;
pDirectWrite->CreateCustomRenderingParams(1.0f, 0.0f, 0.0f, DWRITE_PIXEL_GEOMETRY_RGB,
DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL, &renderParams);
pD2DCanvas->RenderTarget->SetTextRenderingParams(renderParams);
// set antialiasing mode
pD2DCanvas->RenderTarget->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
::ID2D1SolidColorBrush* pBrush = NULL;
WColor color(pFont->Color);
// create solid color brush
pD2DCanvas->RenderTarget->CreateSolidColorBrush(color.GetD2DColor(), &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_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);
try
{
pD2DCanvas->BeginDraw();
// draw the text
pD2DCanvas->RenderTarget->DrawText(text.c_str(), text.length(), pFormat, drawRect, pBrush,
D2D1_DRAW_TEXT_OPTIONS_NONE);
}
__finally
{
pD2DCanvas->EndDraw();
}
}
上面的代码是我可以做的更接近 GDI 文本绘图。但是,有一些我无法纠正的小差异。这是使用 GDI 绘制的文本的屏幕截图,并使用上述功能(请注意,下面的图像是拉伸的,应该以他的真实尺寸打开):
如您所见,D2D 文本比 GDI 文本长,并且两个版本之间的几个词,如“humanum”,明显不同。此外,抗锯齿在几个词周围有些不同,例如第一个“文本”词,包括以下逗号,这导致这些词再次显得有些模糊。
但是,对我来说,绘制相似但可见差异很小的图形并不是一个可以接受的选择。我需要一张没有差异的图纸,或者至少在几乎看不到差异的地方(即没有彻底比较)。
AFAIK 使用相同的字体,并将正确的 cleartype 模式应用于 DirectWrite。那么如何改进我的 Direct2D 渲染,使其看起来更接近旧的 GDI 呢?有没有办法做到这一点?
注意我已经找到并阅读了几个关于 Direct2D 和 GDI 之间文本质量差异的主题,例如
为什么 DirectX/DirectWrite/Direct2D 文本渲染不能像 GDI 一样清晰?
不幸的是,就我而言,我已经启用了建议的选项以允许 Direct2D 像 GDI 一样绘制文本(至少我认为我这样做是正确的),但结果仍然不足以让我在我的项目中使用它.
解决方案
我建议改用CreateGdiCompatibleTextLayout,因为 Direct2D 中没有DrawTextInGdiCompatibleWay函数变体。它不一定完全匹配 gdi 输出,但希望会更接近。
推荐阅读
- c - kill 函数中 sig 的可能值
- python - 在 python 中的 globals() 字典中添加一个新的键值对是否被认为是“定义的行为”?
- google-cloud-platform - Google AutoML“.txt to .jsonl”脚本所需的数据格式是什么?
- java - 在 Android Studio 中使用 Switch 启用/禁用 editText
- javascript - 尝试使用 moment.js 格式化字符串并计算时差时奇怪的输出
- javascript - 石头剪刀布编码挑战
- r - 在具有通用列引用的函数中使用 sqldf
- wxpython - 如何删除 MenuBar 中的 Window 下拉菜单?
- python - Python 中的网络爬虫,用于同一网页的多篇文章
- php - 删除 URL 中的空格并使用 htaccess 将所有内容变为小写