feat(tao): LCD/ClearType text on Windows - #626
Conversation
|
@takke Excuse me, could you review this PR? Windows 10 is still widely used, and I think it's essential, but I had to modify the bytecode. I'm not sure if it's worth it. I don't want any reflections at all in Nucleus, so I didn't really have a choice. |
|
Up front: I don't have a Windows 10 machine at hand and font rasterization isn't my area of expertise, so I delegated this review to Claude (Fable 5) — including the design question of whether the bytecode patch is justified. The review below is Claude's; the verification runs were done on my Windows 11 machine (ClearType on, RGB), and I've read through and agree with the conclusions. Verdict on "was the bytecode patch worth it?"Yes — and I'd argue this is the right shape for it, not a compromise:
Code-level checks (all clean)
Verified on a real machine (Windows 11)
On "Windows 10 is essential": agreed — the gain is largest at 96-DPI / 100% scaling, which is exactly where the Windows 10 install base skews; on high-DPI displays the difference shrinks. This is a real fidelity gap Nucleus is well placed to close. Non-blocking notes
|
|
Thanks, yes, that seems correct to me too, but I can't merge something like that without checking end-to-end first; I need to install a VM. |
Compose hardcodes grayscale as the Windows font-smoothing default. Enable ClearType end to end without runtime reflection: - plugin: LcdTextDefaultTransform (artifact transform + ASM) patches FontRasterizationSettings.PlatformDefault in ui-text-desktop jars on non-test runtime classpaths — SubpixelAntiAlias on Windows, opt-outs -Dnucleus.text.lcd=false (runtime) / -Pnucleus.text.lcd.patch=false (build). Android/HotReload/KMP guards mirror the CleanNativeLibs transform; referenced ctor/enum fields are verified so Compose layout drift fails the build. Canary test patches both the plugin's Compose and the consumer version from the root version catalog. - tao: lcdSurfaceProps attaches the OS-queried pixel geometry (cached, RGB/BGR, grayscale on any unknown) to opaque Windows window surfaces only; per-pixel-alpha surfaces (popups, NativeView overlay, Mica or Acrylic backdrops, transparent windows) keep unknown geometry so Skia falls back to grayscale. renderGlFrame now requires windowTransparent. - jewel-demo: use JewelDecoratedWindow instead of hand-rolled theming.
598300b to
a72259c
Compare
`LcdTextTest > Compose LCD text on an RGB surface has chromatic edges` has been failing the macOS tao-tests job since #626 merged, which leaves every PR targeting this branch red. Skia can only fringe where the platform font host produces subpixel glyph masks: DirectWrite and FreeType do, CoreText does not — macOS dropped subpixel antialiasing in Mojave and renders grayscale whatever the surface's PixelGeometry says. So `lcdScore == grayScore` there, which is this feature's documented behaviour (`macOS and Linux stay grayscale` asserts the same thing on the surface-props side) rather than a regression. Skip the pixel assertion on macOS only; Windows and Linux keep it.
Summary
Compose Desktop hardcodes grayscale antialiasing as the Windows font-smoothing default (JetBrains/compose-multiplatform#875, tracked upstream as CMP-5359 — their own source comments they want ClearType but never wired the OS query). This PR enables LCD/ClearType text end to end, with no runtime reflection, working identically under HotSpot, ProGuard, and GraalVM native-image.
Paragraph half — Gradle plugin bytecode patch
LcdTextDefaultTransform: a Gradle artifact transform that rewritesFontRasterizationSettings$Companion.getPlatformDefault()inui-text-desktopjars via ASM — the original getter is kept (renamed) and a cached wrapper returnsSubpixelAntiAliason Windows, delegating everywhere else.-Dnucleus.text.lcd=false(runtime),-Pnucleus.text.lcd.patch=false(build-time, skips the transform entirely).*RuntimeClasspathonly, with the same Android / Compose Hot Reload / KMP-jar-variant guards asregisterCleanNativeLibsTransform(verified::examples:cmp-demoresolves).Surface half — Tao backend
lcdSurfacePropsattaches the OS-queried pixel geometry (newSPI_GETFONTSMOOTHING*native query; cached; RGB/BGR; grayscale on any unknown, including a failed orientation query) to opaque Windows window surfaces only.transparentBackgroundState), and transparent windows.renderGlFramenow requireswindowTransparentexplicitly — no unsafe default.Misc
JewelDecoratedWindow(theme outside the window, ~20 lines of hand-rolled deco theming removed).__pycache__/gitignored.Test plan
LcdTextDefaultTransformTest— patches real ui-text-desktop jars (Compose 1.10.0 + 1.12.0), asserts SubpixelAntiAlias on Windows, cache stability, runtime opt-out, non-Windows delegationLcdTextTest— surface-props gating (transparent/off-Windows/ClearType-off ⇒ null) + pixel-level chromatic-fringe assertionLcdTextCaptureTest— grayscale vs ClearType comparison PNG:examples:cmp-demo:dependenciesresolves (Android + Hot Reload guards):examples:jewel-demo:createDistributableships the patchedui-text-desktop-*-nucleus-lcd.jar