Fabric text is drawn with ClearType onto transparent composition surfaces, fringing thin glyphs
Los mantenedores suelen responder en 2 días
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 82/100
Línea de trabajo
Comienza en TextDrawing.cpp, especialmente en RenderText y sus patrones existentes de GetAntialiasMode/SetAntialiasMode/restore; revisa los sitios nombrados de DrawTextLayout en ParagraphComponentView.cpp, ScrollViewComponentView.cpp y TextInput/WindowsTextInputComponentView.cpp. Ejecuta la compilación o las comprobaciones de RNW disponibles, y considera terminado el trabajo cuando el texto sobre superficies transparentes use antialiasing explícito en escala de grises sin cambiar el estado del contexto de dispositivo circundante.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
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::updateVisualBrushcreates it as
CreateDrawingSurfaceBrush(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 nobackgroundColor(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>(nobackgroundColor), 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.
- Lenguaje dominante
- C++
- Estrellas
- 17.4k
- Forks
- 1.2k
- Merge medio
- 2 d 17 h
- PR fusionados (30 d)
- 13
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de microsoft/react-native-windows
-
bug Needs: Triage :mag:
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
microsoft/react-native-windows#16321 · 1 comentario ·
Los mantenedores suelen responder en 2 días
-
Needs: Triage :mag:
Dificultad 5/5 Más de una semana Aptitud para principiantes 30/100
microsoft/react-native-windows#16442 · 1 comentario ·
Los mantenedores suelen responder en 2 días
-
Fabric: activating the window doesn't announce the window or the focused control to a screen readerAbiertoNeeds: Triage :mag:
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
microsoft/react-native-windows#16435 ·
Los mantenedores suelen responder en 2 días
-
bug Needs: Triage :mag:
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
microsoft/react-native-windows#16410 ·
Los mantenedores suelen responder en 2 días
-
bug Invalid Triage Needs: Triage :mag:
Dificultad 4/5 3-5 días Aptitud para principiantes 55/100
microsoft/react-native-windows#16368 ·
Los mantenedores suelen responder en 2 días
Todos los issues de microsoft/react-native-windows
Issues similares
-
libasr headers?Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
Los mantenedores suelen responder en 1 día
-
bug iOS 🍎 ui/ux
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
MerginMaps/mobile#4744 ·
Los mantenedores suelen responder en 1 día
-
Component: Ruby Type: bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
Los mantenedores suelen responder en 1 día
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 88/100
Los mantenedores suelen responder en 3 días
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 92/100
kokkos/kokkos-kernels#3328 ·
Los mantenedores suelen responder en 1 día