Skip to content

Commit 15d350a

Browse files
committed
fix(text): draw Fabric text with grayscale antialiasing on transparent surfaces
RenderText calls ID2D1RenderTarget::DrawTextLayout without setting a text antialias mode, so the draw inherits the device context default - ClearType subpixel antialiasing on a normal machine. The target it draws onto is transparent: ParagraphComponentView creates the drawing surface with DirectXAlphaMode::Premultiplied and clears it to ColorF(Black, 0.0f) when the view has no backgroundColor. ClearType computes independent R/G/B coverage and needs an opaque, known background to filter against. With none, that per-channel coverage lands in the surface colour channels and shows as dark or colour-tinted fringes along thin glyph stems once composited. D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE is the documented mode for transparent render targets. Saves the current mode, sets GRAYSCALE for the draw, and restores it - mirroring the GetAntialiasMode/SetAntialiasMode and GetDpi/SetDpi save/restore idioms already used in this function. Neither SetTextAntialiasMode nor D2D1_TEXT_ANTIALIAS_MODE appeared anywhere in the tree before this change. Addresses #16340.
1 parent c69cf55 commit 15d350a

2 files changed

Lines changed: 19 additions & 0 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "Draw Fabric text with grayscale antialiasing instead of inheriting ClearType, which fringes thin glyphs on transparent composition surfaces",
4+
"packageName": "react-native-windows",
5+
"email": "collindanielschneide@gmail.com",
6+
"dependentChangeType": "patch"
7+
}

vnext/Microsoft.ReactNative/Fabric/Composition/TextDrawing.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,24 @@ void RenderText(
165165
position += length;
166166
}
167167

168+
// The composition drawing surface this text lands on is premultiplied-alpha and is transparent
169+
// wherever the view has no opaque background; the surface is then composited by the visual tree,
170+
// possibly under an opacity or a transform. ClearType subpixel antialiasing has no valid opaque
171+
// background to filter against in that situation, and its per-channel coverage survives into the
172+
// surface's color channels as dark/colored fringes along thin glyph stems. Grayscale antialiasing
173+
// is the correct mode for transparent render targets, so ask for it explicitly rather than
174+
// inheriting whatever the device context defaults to.
175+
auto oldTextAntialiasMode = deviceContext.GetTextAntialiasMode();
176+
deviceContext.SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
177+
168178
// Draw the line of text at the specified offset, which corresponds to the top-left
169179
// corner of our drawing surface. Notice we don't call BeginDraw on the D2D device
170180
// context; this has already been done for us by the composition API.
171181
deviceContext.DrawTextLayout(
172182
D2D1::Point2F(offsetX, offsetY), &textLayout, brush.get(), D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT);
173183

184+
deviceContext.SetTextAntialiasMode(oldTextAntialiasMode);
185+
174186
// restore dpi to old state
175187
deviceContext.SetDpi(oldDpiX, oldDpiY);
176188
}

0 commit comments

Comments
 (0)