Skip to content

fix(macos): match GNU font weight by extracting masks with dark-on-light smoothing - #637

Merged
eval-exec merged 1 commit into
eval-exec:mainfrom
vritser:fix/macos-font-smoothing-polarity
Oct 30, 2026
Merged

eval-exec merged 1 commit into
eval-exec:mainfrom
vritser:fix/macos-font-smoothing-polarity

Conversation

@vritser

@vritser vritser commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Follow-up to #622.

Problem

#622 moved macOS outline rasterization to Core Graphics and enabled font smoothing, but extracted glyph coverage from white glyphs on black. Core Graphics font smoothing applies a contrast-dependent gamma, so that inflates coverage on the way into the atlas. After the merge, @yibie measured Neomacs roughly 30% heavier than GNU Emacs on a 1x (non-Retina) display at Monaco 16.

Fix

Rasterize the mask the way GNU draws ordinary text: dark glyphs on white, then invert the luminance. Font smoothing stays enabled, matching GNU's window context default.

Measurements

Same four ASCII lines, Monaco 16, 1x 1920x1080, GNU Emacs 31.1.50. Ink is Σ(255 - luminance) over the identical crop:

Build ink vs GNU
GNU Emacs 31.1.50 1,111,924 —
#622 (white-on-black) 1,345,897 +21.0%
this PR 1,095,669 −1.5%

An earlier attempt that simply disabled smoothing measured −14.9%: right idea, wrong weight.

Tests

  • cargo test -p neomacs-renderer-wgpu --lib macos_: 9 passed, 1 ignored (the manual visual probe), macOS 26 aarch64.
  • New macos_native_masks_use_dark_on_light_smoothing renders the inverse polarity as a control and requires the production path to stay lighter.
  • cargo fmt --check and git diff --check pass; no new clippy warnings in the changed files.

Caveats

  • The atlas stores one polarity-independent mask, so light-on-dark themes still reuse the dark-on-light smoothing gamma; text on dark backgrounds may differ by a few percent. Making the polarity face-color aware would need another cache dimension.
  • Measurements come from a single 1x display; no 2x reference was available on this machine.

@coderabbitai

coderabbitai Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: aa206546-ef02-43db-9321-680c546c3e77
Commits

Reviewing files that changed from the base of the PR and between e8ecf80 and e7b944e.

Files selected for processing: 2
  • crates/neomacs-renderer-wgpu/src/glyph_atlas/macos.rs
  • crates/neomacs-renderer-wgpu/src/glyph_atlas/tests/macos_test.rs

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

Walkthrough

The macOS native glyph rasterizer now defaults to dark-on-light masks. It selects bitmap and glyph colors by polarity and adjusts luminance-to-coverage conversion. A test compares coverage for the default and light-on-dark polarities at two sizes.

Changes: macOS Glyph Masks

Layer / File(s) Summary
Polarity rasterization and coverage validation
crates/neomacs-renderer-wgpu/src/glyph_atlas/macos.rs, crates/neomacs-renderer-wgpu/src/glyph_atlas/tests/macos_test.rs
The rasterizer defaults to dark-on-light polarity. Bitmap initialization, glyph color, and luminance-to-coverage conversion depend on polarity. A test compares summed coverage at 16 and 32 pixels.

Priority: Normal

Estimated code review effort: 2 (Simple) | ~12 minutes

Change: Bug fix

Possibly related PRs

Suggested reviewers: eval-exec

Merge Risk: Minimal · up to e7b94

The mask polarity change has no identified merge-blocking issue.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title names the macOS glyph-mask change: extracting masks with dark-on-light smoothing to match GNU font weight. The raw summary and objectives confirm the change rasterizes dark glyphs on white, …
Description check Passed The description states the problem (#622 extracted white-on-black glyph coverage, which inflated weight through Core Graphics contrast-dependent gamma), the fix (dark-on-white rasterization with lumin…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title names the macOS glyph-mask change: extracting masks with dark-on-light smoothing to match GNU font weight. The raw summary and objectives confirm the change rasterizes dark glyphs on white, inverts luminance, and keeps font smoothing enabled. The title is specific and matches the main change. It uses the conventional fix(macos) prefix, and it is 82 characters, which is slightly long but readable.

Full details: Description check

Explanation

The description states the problem (#622 extracted white-on-black glyph coverage, which inflated weight through Core Graphics contrast-dependent gamma), the fix (dark-on-white rasterization with luminance inversion and smoothing kept enabled), measurements, test results, and caveats. It directly relates to the changeset, so it passes this lenient check.

Full details: Docstring Coverage

Explanation

Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 3 functions across 2 files.

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't add verification image into git track.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

…rity

PR eval-exec#622 enabled Core Graphics font smoothing for native outline masks but
extracted coverage from white glyphs on black. Font smoothing's gamma is
contrast-dependent, so that inflated coverage and made 1x text about 21%
heavier than GNU Emacs, as reported after the merge.

Draw dark glyphs on white and invert the coverage, matching GNU's ordinary
on-screen contrast, and keep smoothing enabled. Measured on a 1x display
with Monaco 16, ink over the same four ASCII lines is +21% before and -1.5%
after, relative to GNU Emacs 31.1.50.

Keep the inverse polarity as a test control and add a regression test that
requires the production path to stay lighter.
@vritser
vritser force-pushed the fix/macos-font-smoothing-polarity branch from a45bc5d to e7b944e Compare October 30, 2026 07:27
@coderabbitai
coderabbitai Bot requested a review from eval-exec October 30, 2026 07:29

@eval-exec eval-exec left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved as a stopgap (e7b944e3bd).

The light-theme fix is verified end-to-end. I rebuilt GNU Emacs 31.1 from the pinned a360712c9d27 with --with-ns and captured the frame content view through a small Emacs module at the display's 2x scale (Monaco 16 pt, the same four lines, no chrome):

GNU GUI at 2x ink PR mask at 32 px
light (black on white) 4,420,610 production dark-on-light: 4,420,610
dark (white on black) 4,806,838 old light-on-dark: 4,806,838

The production polarity is therefore GNU-exact for the default light face, and the old mask is exactly GNU's dark rendering. The 1x −1.5% residual is a 1x/crop/context effect, not a mask-polarity error.

Approving as the interim fix: default light theme correct; dark-theme/light-foreground text is 8.7% lighter than GNU at 2x and ~20% at 1x (16 px masks), accepted as the known stopgap. The long-term direction is a polarity-independent coverage mask with renderer-owned stem darkening/gamma and gamma-correct compositing, which makes this whole polarity dimension obsolete; reproducing Core Graphics' per-color smoothing gamma is not the target architecture.

Follow-ups, not blockers:

  • Correct the caveat: the dark-theme swing is 8.7% at 32 px and 22.6% at 16 px, not "a few percent"; GNU's dark rendering is exactly the old control mask.
  • The new test is not run by CI — the macOS jobs run neovm-core/Doom, the display-stack job is Ubuntu. Adding cargo nextest run -p neomacs-renderer-wgpu --lib macos_ to the macOS job keeps the guard alive (~0.1 s after build).
  • The test only asserts strict ordering; a margin (e.g. ≥1.05 at 16 px, ≥1.02 at 32 px) or logged inks would be stronger.
  • MaskPolarity's "polarity-independent mask" wording is inverted: the mask is polarity-dependent, the atlas stores one shared mask.

Verification: macos_ 9/9; full neomacs-renderer-wgpu 897/900 with only environment/network failures (4 CJK tests reproduce with pre-PR files — missing Noto CJK, installed by the Linux display job; 3 color-font tests timed out downloading pinned fixtures from raw.githubusercontent.com; 3 aborted by the download); cargo fmt --all --check and git diff --check clean. The Neomacs wgpu surface could not be captured in this locked console session; the GNU-vs-mask equality and the PR's own 1x screen measurement cover that gap.

@eval-exec
eval-exec merged commit d1560e9 into eval-exec:main Oct 30, 2026
4 checks passed
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.

2 participants