Skip to content

fix(desktop): bake the Classic face on whole pixels at density 1 - #29

Merged
doodlewind merged 1 commit into
mainfrom
fix/desktop-classic-face-1x
Oct 4, 2026
Merged

doodlewind merged 1 commit into
mainfrom
fix/desktop-classic-face-1x

Conversation

@doodlewind

@doodlewind doodlewind commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

What changed

At rasterDensity 1 the Classic theme's 12.5px face drew no comma and no semicolon, and nine other marks lost or misplaced pixels. src/system-ui/gen-assets.ts now bakes the 12.5px slots at density 1 from outlines snapped to whole device pixels.

Cause. The generator snaps W95FA's outlines to a 40-unit grid with a 10-unit x phase. That is the device pixel grid of the 12.5px slots at density 2 (40 units per device pixel). At density 1 a device pixel is 80 units, and the font's vertical edges sit at 50 + 80k units (1768 of the 1790 x coordinates of the ASCII glyphs), so the 40-unit snap left every vertical edge in the middle of a device pixel. A few glyphs are also drawn a fraction of a pixel off in y. The bake logged slot19 … (AA 86.13%) for density 1, and the hard threshold then dropped whatever covered half a pixel.

Fix. loadSnapped(step, phase) takes the grid. The density-2 bakes and the 25px slot keep (40, 10); the 12.5px slots at density 1 use (80, 50). The generator's alignment assertion (AA ≤ 2%) now runs at both densities, and a new assertion fails the bake when any mapped glyph but the space has no ink.

What moves. w95fa-19.bin and w95fa-20.bin (density 1). The other eight atlases are byte-identical (sha256 compared before and after). In the two that change, no glyph's advance or left-bearing shift changes, so text measures and wraps as before; the header's cell width drops by one pixel (12 to 11, and 13 to 12 for the bold slot).

Eleven glyphs change their density-1 bitmap: " ' ) * , - ; = ^ ` ~. The comma and the semicolon were empty; the others gain the pixels the threshold dropped or move to the nearest whole pixel. Cell rows of slot 19, main on the left:

 ','  row  9  .......   .#.....      '-'  row 6  .####..   .......
      row 10  .......   #......           row 7  .......   #####..

 ';'  row  7  .......   .#.....      '"'  row 0  .......   #.#....
      row  9  .......   .#.....           row 1  #.#....   #.#....
      row 10  .......   #......           row 2  #.#....   #.#....

Why

Pocket Studio runs the System UI in a browser canvas at density 1. Its Classic theme showed "Your games the workbench and your settings open here." in About and "evan Draft" in its lists. The same holds for any density-1 host of this shell, including the browser preview.

Verification

Run in a clean clone of this branch with the pinned PocketJS submodule (0ca1b362), after bun run setup.

Command Result
bun shells/desktop/src/system-ui/gen-assets.ts density 1: slot19 … (AA 0.00%), was 86.13%; density 2 unchanged
sha256 of src/system-ui/fonts/*.bin before and after 2 of 10 differ: w95fa-19.bin, w95fa-20.bin
bun run check exit 0: 3DS 40, iPod 72, desktop unit 61, sim 6, wrap 7 (4 new), text 6, touch 66 pass; 0 fail

New coverage in test/wrap-op.test.ts, which already loads the generated atlas: every ASCII glyph but the space has ink in slots 19 and 20 at both densities; no coverage sample is partial; each glyph's advance is equal at both densities; the comma and the semicolon draw at density 1. With gen-assets.ts from main, the first and the last of these fail, naming , and ;.

Looked at in Pocket Studio's laptop (800×600 canvas, density 1, Classic): the commas are back in the list rows and in the About dialog, and the rest of the text is unchanged.

Not run locally, left to CI: desktop test:rust, desktop build, desktop package:linux, desktop test:web, the site build and its smoke check.

🤖 Generated with Claude Code

gen-assets.ts snaps W95FA's outlines to the 40-unit half-pixel grid, which is
the device pixel grid of the 12.5px slots at rasterDensity 2. At density 1 a
device pixel is 80 units. The font's vertical edges sit at 50 + 80k units, so
that snap left every one of them in the middle of a device pixel, and a few
glyphs are drawn a fraction of a pixel off in y. The bake reported 86% of its
ink as partial coverage, and the threshold dropped what covered half a pixel:
the comma and the semicolon baked empty, and the quotes, the backtick, the
closing parenthesis, the asterisk, the hyphen, the equals sign, the caret and
the tilde lost or misplaced pixels.

The 12.5px slots at density 1 now bake from a second snap, to the 80-unit grid
with the 50-unit phase. Slots 19 and 20 at density 1 change; the density-2
atlases and the 25px slot are byte-identical, and no advance changes at either
density. The generator asserts at both densities that the snap aligned and
that every mapped glyph but the space has ink.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@doodlewind
doodlewind force-pushed the fix/desktop-classic-face-1x branch from 0b8bf80 to 0e89d93 Compare October 4, 2026 09:30
@doodlewind
doodlewind marked this pull request as ready for review October 4, 2026 10:00
@doodlewind
doodlewind merged commit b5252cf into main Oct 4, 2026
2 checks passed
@doodlewind
doodlewind deleted the fix/desktop-classic-face-1x branch October 4, 2026 10:00
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.

1 participant