直接用 rendertarget 的 DrawText 方法显示文字的话无法为文字增加描边,用路径对象配合字体字形进行显示,并在内部用画刷填充的话可以达到描边的效果。
首先初始化的时候创建字体,要显示中文的话需要选择支持中文的字体,否则会因为找不到字模而显示为”囗”,这里选择了黑体作为样例。渲染的时候根据要显示的内容获取对应字模,并以此创建路径对象,创建完成后将其绘制到合适的位置,同时用指定颜色填充内部即可。
附上代码:
void Init() { ... hr = DWriteCreateFactory( DWRITE_FACTORY_TYPE_SHARED, __uuidof(m_pDWriteFactory), reinterpret_cast<IUnknown **>(&m_pDWriteFactory) ); IDWriteFontFile *pFontFiles{ nullptr }; hr = m_pDWriteFactory->CreateFontFileReference( L"simhei.ttf", NULL, &pFontFiles ); assert(SUCCEEDED(hr)); IDWriteFontFile *fontFileArray[] = { pFontFiles }; hr = m_pDWriteFactory->CreateFontFace( DWRITE_FONT_FACE_TYPE_TRUETYPE, ARRAYSIZE(fontFileArray), // file count fontFileArray, 0, DWRITE_FONT_SIMULATIONS_NONE, &m_pFontFace ); assert(SUCCEEDED(hr)); SafeRelease(&pFontFiles); ... } void DrawText( const wchar_t * string, unsigned int stringLength, IDWriteTextFormat * textFormat, const Rectangle & layoutRect ) { UINT* pCodePoints = new UINT[stringLength]; UINT16* pGlyphIndices = new UINT16[stringLength]; ZeroMemory(pCodePoints, sizeof(UINT) * stringLength); ZeroMemory(pGlyphIndices, sizeof(UINT16) * stringLength); for (int i = 0; i < stringLength; ++i) { pCodePoints[i] = string[i]; } HRESULT hr; // 确认字形是否存在 hr = m_pFontFace->GetGlyphIndicesW(pCodePoints, stringLength, pGlyphIndices); // 将文字描边储存至路径几何对象, 18.0f 为字号大小 hr = m_pDirect2dFactory->CreatePathGeometry(&m_pPathGeometry); hr = m_pPathGeometry->Open(&m_pGeometrySink); hr = m_pFontFace->GetGlyphRunOutline(18.0f, pGlyphIndices, NULL, NULL, stringLength, FALSE, FALSE, m_pGeometrySink ); hr = m_pGeometrySink->Close(); IDWriteTextLayout* pTextLayout = NULL; // 获取文本尺寸 hr = m_pDWriteFactory->CreateTextLayout(string, stringLength, textFormat, 0.0f, 0.0f, &pTextLayout); DWRITE_TEXT_METRICS textMetrics; hr = pTextLayout->GetMetrics(&textMetrics); SafeRelease(&pTextLayout); D2D1_MATRIX_3X2_F old; D2D1_RECT_F bounds; m_pRenderTarget->GetTransform(&old); m_pPathGeometry->GetBounds(old, &bounds); m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Translation( layoutRect.GetLeft() + (layoutRect.GetWidth() - bounds.right + bounds.left + textMetrics.left) / 2.0f, layoutRect.GetBottom() - (layoutRect.GetHeight() - bounds.bottom + bounds.top) / 2.0f)); m_pRenderTarget->DrawGeometry(m_pPathGeometry, m_pBlackBrush, 1.0f); m_pRenderTarget->FillGeometry(m_pPathGeometry, m_pWhiteBrush); m_pRenderTarget->SetTransform(old); SafeDeleteArray(&pCodePoints); SafeDeleteArray(&pGlyphIndices); SafeRelease(&m_pGeometrySink); SafeRelease(&m_pPathGeometry); }