Problem Description
On the new architecture, RenderText — the single function every <Text> render goes through —
calls ID2D1RenderTarget::DrawTextLayout without ever setting a text antialias mode, so the draw
inherits the device context's default. On a normal Windows machine that default is ClearType
subpixel antialiasing.
The surface it draws onto is transparent:
ParagraphComponentView::updateVisualBrush creates it as
CreateDrawingSurfaceBrush(surfaceSize, DirectXPixelFormat::B8G8R8A8UIntNormalized, DirectXAlphaMode::Premultiplied)
(vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp, ~L367-370).
ParagraphComponentView::DrawText clears it to D2D1::ColorF(D2D1::ColorF::Black, 0.0f) —
transparent black — whenever the view has no backgroundColor (same file, ~L521-523).
- The surface is composited by the visual tree afterwards, possibly under an opacity or a transform.
ClearType computes independent R/G/B coverage and needs an opaque, known background to filter
against. There is none here, so that per-channel coverage lands in the surface's colour channels and
shows up as dark or colour-tinted fringes along thin glyph stems once the surface is composited.
D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE is the documented mode for transparent render targets.
The gap is repo-wide, not local: at c69cf55f67f9b03f467502dac1007ac2d9ebe209, neither
SetTextAntialiasMode nor D2D1_TEXT_ANTIALIAS_MODE appears anywhere in the tree. The other two
DrawTextLayout call sites have the same omission —
ScrollViewComponentView.cpp ~L529 (scrollbar arrow glyphs) and
TextInput/WindowsTextInputComponentView.cpp ~L1870 (placeholder text).
Confidence, stated honestly. The missing call is a fact — the source is quoted above and the
repo-wide search returned zero hits. The visual claim is a source-level diagnosis that I have not
captured as before/after pixels: I have not built or run RNW main, and I cannot rule out that D2D
already coerces toward grayscale on some premultiplied targets, in which case the practical effect
of setting it explicitly is determinism (no dependence on the user's font-smoothing setting) rather
than a pixel change. I did not want to file a "verified" claim I had not measured.
Steps To Reproduce
- New-arch RNW app, default
<Text> (no backgroundColor), light background behind it.
- Use thin glyph strokes — a light font weight, a small icon font, or hairline separators — at a
fractional/high display scale (we see it clearly at 250%).
- Zoom a screenshot to the pixel level along vertical stems.
- Observe coloured/dark fringing rather than clean grayscale edges.
Control: set D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE on the device context immediately before the
DrawTextLayout in RenderText and repeat.
Expected Results
Text on the new architecture is antialiased in grayscale, matching what other frameworks do for text
on transparent composition surfaces, and does not depend on the machine's ClearType setting.
Fix
PR attached: save / set D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE / restore around the DrawTextLayout
call in RenderText, matching the GetAntialiasMode/SetAntialiasMode/restore and
GetDpi/SetDpi/restore idioms already present in that same function. One file, twelve added
lines, no signature or header changes. Not compiled and not run — lint/format/build gates are unrun.
CLI version
18.0.0
Environment
System:
OS: Windows 11 10.0.26200 (ARM64 device; app builds and runs ARM64)
CPU: (6) x64 Apple Silicon (Windows-on-ARM)
Memory: 6.49 GB / 15.99 GB
Binaries:
Node: 22.15.0
Yarn: 4.5.1
npm: 10.9.2
SDKs:
Windows SDK versions: 10.0.19041.0, 10.0.22621.0, 10.0.26100.0
IDEs:
Visual Studio: 18.6.11822.322 (Community 2026), 17.14.37314.3 (Community 2022)
npmPackages (yarn 4 workspaces — `cli info` reports Not Found in-workspace):
react-native: 0.83.2
react-native-windows: 0.83.2 (New Architecture / Fabric composition)
Microsoft.WindowsAppSDK: 1.8
Source analysis above is against main at c69cf55f67f9b03f467502dac1007ac2d9ebe209, not 0.83.2 —
the code in question is unchanged between them as far as this function is concerned.
Community Modules
None involved. Core <Text> on the Composition/Fabric path.
Target React Native Architecture
New Architecture (WinAppSDK) Only
Target Platform Version
10.0.22621
Visual Studio Version
Visual Studio 2026
Build Configuration
Debug
Snack, code example, screenshot, or link to a repository
The decisive artifact is the source itself — TextDrawing.cpp L171-172 has no antialias-mode call
and D2D1_TEXT_ANTIALIAS_MODE occurs nowhere in the repo. Happy to produce zoomed before/after
captures from our app once we have the patched build, and to test any alternative the team prefers
(e.g. conditional grayscale only when the paragraph has no opaque background).
Context: found during a Windows hardening pass of a production RNW app (Facilitron FIT — RNW 0.83.2
new-arch, Windows 11 ARM64, WinAppSDK 1.8, 250% display scale). Related font-path issues from the
same investigation: #16306, #16308. Sibling PRs: #16302, #16303, #16304.
Problem Description
On the new architecture,
RenderText— the single function every<Text>render goes through —calls
ID2D1RenderTarget::DrawTextLayoutwithout ever setting a text antialias mode, so the drawinherits the device context's default. On a normal Windows machine that default is ClearType
subpixel antialiasing.
The surface it draws onto is transparent:
ParagraphComponentView::updateVisualBrushcreates it asCreateDrawingSurfaceBrush(surfaceSize, DirectXPixelFormat::B8G8R8A8UIntNormalized, DirectXAlphaMode::Premultiplied)(
vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp, ~L367-370).ParagraphComponentView::DrawTextclears it toD2D1::ColorF(D2D1::ColorF::Black, 0.0f)—transparent black — whenever the view has no
backgroundColor(same file, ~L521-523).ClearType computes independent R/G/B coverage and needs an opaque, known background to filter
against. There is none here, so that per-channel coverage lands in the surface's colour channels and
shows up as dark or colour-tinted fringes along thin glyph stems once the surface is composited.
D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALEis the documented mode for transparent render targets.The gap is repo-wide, not local: at
c69cf55f67f9b03f467502dac1007ac2d9ebe209, neitherSetTextAntialiasModenorD2D1_TEXT_ANTIALIAS_MODEappears anywhere in the tree. The other twoDrawTextLayoutcall sites have the same omission —ScrollViewComponentView.cpp~L529 (scrollbar arrow glyphs) andTextInput/WindowsTextInputComponentView.cpp~L1870 (placeholder text).Confidence, stated honestly. The missing call is a fact — the source is quoted above and the
repo-wide search returned zero hits. The visual claim is a source-level diagnosis that I have not
captured as before/after pixels: I have not built or run RNW
main, and I cannot rule out that D2Dalready coerces toward grayscale on some premultiplied targets, in which case the practical effect
of setting it explicitly is determinism (no dependence on the user's font-smoothing setting) rather
than a pixel change. I did not want to file a "verified" claim I had not measured.
Steps To Reproduce
<Text>(nobackgroundColor), light background behind it.fractional/high display scale (we see it clearly at 250%).
Control: set
D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALEon the device context immediately before theDrawTextLayoutinRenderTextand repeat.Expected Results
Text on the new architecture is antialiased in grayscale, matching what other frameworks do for text
on transparent composition surfaces, and does not depend on the machine's ClearType setting.
Fix
PR attached: save / set
D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE/ restore around theDrawTextLayoutcall in
RenderText, matching theGetAntialiasMode/SetAntialiasMode/restore andGetDpi/SetDpi/restore idioms already present in that same function. One file, twelve addedlines, no signature or header changes. Not compiled and not run — lint/format/build gates are unrun.
CLI version
18.0.0
Environment
Source analysis above is against
mainatc69cf55f67f9b03f467502dac1007ac2d9ebe209, not 0.83.2 —the code in question is unchanged between them as far as this function is concerned.
Community Modules
None involved. Core
<Text>on the Composition/Fabric path.Target React Native Architecture
New Architecture (WinAppSDK) Only
Target Platform Version
10.0.22621
Visual Studio Version
Visual Studio 2026
Build Configuration
Debug
Snack, code example, screenshot, or link to a repository
The decisive artifact is the source itself —
TextDrawing.cppL171-172 has no antialias-mode calland
D2D1_TEXT_ANTIALIAS_MODEoccurs nowhere in the repo. Happy to produce zoomed before/aftercaptures from our app once we have the patched build, and to test any alternative the team prefers
(e.g. conditional grayscale only when the paragraph has no opaque background).
Context: found during a Windows hardening pass of a production RNW app (Facilitron FIT — RNW 0.83.2
new-arch, Windows 11 ARM64, WinAppSDK 1.8, 250% display scale). Related font-path issues from the
same investigation: #16306, #16308. Sibling PRs: #16302, #16303, #16304.