Skip to content

feat: font_thicken ("auto", true or false) and font_thicken_strength draw terminal text alike for every text color - #547

Open
abhijit-s wants to merge 7 commits into
herdrdev:masterfrom
abhijit-s:feat/font-thicken-setting
Open

abhijit-s wants to merge 7 commits into
herdrdev:masterfrom
abhijit-s:feat/font-thicken-setting

Conversation

@abhijit-s

@abhijit-s abhijit-s commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

TL;DR: font_thicken ("auto", true or false) and font_thicken_strength (settings.json) control how macOS font smoothing draws terminal text. true thickens it the same for every text color, as Ghostty's font-thicken does; false turns smoothing off, as Ghostty's font-thicken = false; "auto", the default, keeps today's behaviour. Closes #414: dark-on-light text can be drawn as heavy as light-on-dark, and, for the dark-theme case in the #414 comments, light-on-dark can be drawn as light as dark-on-light, or lighter still with smoothing off.

Why

  • GPUI picks macOS font smoothing from the text color's luminance (glyph_dilation_for_color): dark text on a light background gets the lightest level, light text on dark the heaviest. The same terminal text inks ~21% heavier on a dark theme than on a light one.
  • Ghostty instead rasterises every glyph with smoothing on at a gray of font-thicken-strength / 255 in linear gray, then tints it, so both polarities ink alike. With font-thicken = false (its default) it doesn't smooth at all.

What changes

  • gpui-pre: GlyphThicken (Auto, Strength(u8), Off) in RenderGlyphParams::thicken, which is in the glyph atlas key, so differently smoothed glyphs never share a tile; and Window::with_glyph_thicken(thicken, f). On macOS the rasteriser draws Strength as Ghostty does, with one more pixel of raster room, as Ghostty pads thickened glyphs, and Off with setShouldSmoothFonts(false), as Ghostty's coretext.zig does. Other platforms take the field and ignore it. Everything outside with_glyph_thicken keeps GPUI's color-based smoothing.
  • The terminal grid paints inside with_glyph_thicken; the row cache key carries the setting, so a change redraws kept rows.
  • font_thicken: absent or "auto" (the default) leaves smoothing to GPUI, true thickens at font_thicken_strength, false turns smoothing off. The default stays out of settings.json and true/false round-trip as JSON booleans. Any other string is reported by problems() and draws as "auto".
  • font_thicken_strength (0..255, default 255, Ghostty's default), validated in problems(). Both keys are documented in defaults/AGENTS.md. Panels keep the system's smoothing.
  • THIRD-PARTY-NOTICES regenerated with just notices (the Cargo.toml hash, plus the 0.1.13 version it was behind on).

Numbers

tests/thicken_ink.rs on this branch (upstream/master e77f112 plus this change): real CoreText and GPU readback, headless Metal, JetBrains Mono 400 at 12 device px. Ink is the mean of (pixel − background) / (text − background) over the text's box, so polarities compare directly.

Light on dark Dark on light
"auto" (today, the default) 0.0711 0.0588
true, strength 30 0.0588 0.0588
true, strength 255 0.0711 0.0711
false (smoothing off) 0.0511 0.0511
  • macOS applies the strength in a few steps (about 112, 192, 224 and 255); 0 to ~100 all draw as its lightest smoothing.
  • The test asserts: auto, light ink > dark ink × 1.05; true at either strength and false, both polarities within 1%; 255 thicker than 30; false lighter than auto's dark on light.

Not in this pull request

  • A Settings window row (upstream's window has none for font_ligatures either) and the same choice for panel text (ui_font_thicken), which my fork carries.

Tests

  • settings::tests::font_thicken_defaults_auto_at_ghosttys_strength_and_validates_it, settings::tests::font_thicken_is_auto_true_or_false_and_names_anything_else, tests/thicken_ink.rs (new [[test]], macOS only), and the_guide_explains_every_setting covering both keys.
  • just check passes.

…text

Issue herdrdev#414 asked for a way to turn smoothing fully off, as Ghostty's `font-thicken = false` (its default) does. Strength 30 only reaches CoreGraphics' lightest smoothing level, which still dilates glyphs; with smoothing off they draw as plain antialiased coverage, lighter still and the same for every text color.

`font_thicken` takes `"auto"` (absent, the default: GPUI's color-based smoothing), `true` (thicken at `font_thicken_strength`) or `false` (smoothing off). The default stays out of settings.json and `true`/`false` round-trip as JSON booleans, so existing files keep their meaning. Any other string is kept and named in `problems()`, and draws as `"auto"`.

GPUI carries the three states as `GlyphThicken` (`Auto`, `Strength(u8)`, `Off`) in `RenderGlyphParams::thicken`, so it stays in the glyph atlas key and differently smoothed glyphs never share a tile. The macOS rasteriser draws `Off` with `setShouldSmoothFonts(false)`, as Ghostty's coretext.zig does, without the extra pixel of raster room thickening needs. Other backends take the field and ignore it.

tests/thicken_ink.rs gains the off rows: JetBrains Mono 12 device px inks 0.0511 in both polarities, against 0.0588 for the lightest smoothing and 0.0711 for light text on dark under `"auto"`.
@abhijit-s abhijit-s changed the title feat: font_thicken and font_thicken_strength thicken terminal text alike for every text color feat: font_thicken ("auto", true or false) and font_thicken_strength draw terminal text alike for every text color Nov 5, 2026
@abhijit-s

Copy link
Copy Markdown
Contributor Author

TL;DR: fixed the Linux CI failure and merged master.

  • Linux: tests/colrv1.rs (Linux-only) built a RenderGlyphParams without the new thicken field, so the colrv1 test target didn't compile. It now sets thicken: gpui::GlyphThicken::Auto, as the wgpu and DirectWrite callers do (c10c4b1).
  • Windows: the earlier run failed window::tests::hidden_tabs_follow_the_window with the test scheduler's "Detected activity on thread … Your test is not deterministic" panic. This PR doesn't touch that test or the window's tab code; the new run will show whether it repeats.
  • Merged master (31 commits, no conflicts). just check passes locally on macOS.

@Tom-Caz

Tom-Caz commented Nov 11, 2026

Copy link
Copy Markdown

+1 for font_thicken: false. On a dark theme, Herdr's terminal text is noticeably heavier and softer than Ghostty's, which has font-thicken = false by default.

Measured from a side-by-side with the same font (JetBrains Mono Nerd Font Mono) and colors on a 2x display: the "T" stroke is 0.155 of the cap height in Herdr and 0.122 in Ghostty. That matches CoreText rendering with smoothing on (0.143–0.150) and off (0.122).

Workaround until this lands: defaults write dev.herdr.app AppleFontSmoothing -int 0, then restart. font_smoothing_allowed_by_user honors it, and the stroke went from 3.2px to 2.6px here. It affects the whole app, though, so it would be nice if false covered panel text too, not only terminals.

@abhijit-s

Copy link
Copy Markdown
Contributor Author

Thanks @Tom-Caz, those measurements line up with mine: false here is setShouldSmoothFonts(false), the same as Ghostty's font-thicken = false.

On panel text: this PR covers terminal text only. My fork has a second pair of settings for everything else in the window (panels, tabs, overlays, toasts, mods and the Settings window):

  • ui_font_thicken ("auto", true or false) and ui_font_thicken_strength (0–255), the same values and defaults as the terminal pair.
  • The window sets it once per frame and the terminal grid paints inside its own value, so the two never mix. The glyph atlas key already carries the thickening, so the two looks never share a tile.
  • Measured (JetBrains Mono 400 at 12 device px, headless 2x window): ui_font_thicken: false takes panel dark-on-light text from 0.0588 to 0.0511 ink, while a terminal grid inside it stays at 0.0588.
  • Fork commits: 63cc98a and 024d51d on abhijit-s/herdr-app.

@ogulcancelik would you take ui_font_thicken in this PR, or as a follow-up once this one is settled? It adds two settings keys and one with_glyph_thicken scope at the window's paint; it doesn't change how terminals draw.

Until then, the AppleFontSmoothing workaround above is the way to get the whole app unsmoothed.

…-setting

# Conflicts:
#	src/settings.rs
#	src/terminal.rs
@tumenbaev

Copy link
Copy Markdown

@ogulcancelik any chance this could be merged? would be great to have thickness configurable.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

macOS: dark text on a light background draws thinner than light on dark, with no setting like Ghostty's font-thicken

4 participants