smaller stage, half steps above 2x — bigger, still even
Trimming the stage alone does nothing, and that is worth writing down: the scale is snapped, so at 3x the game is 1512 device pixels wide whether the stage is 560 or 512. Shrinking the surround only pays when it crosses a step. So both halves land together. ── the stage ── 560x400 down to 512x352, which is expert (504x342, measured) plus four pixels a side. There is nothing left to give — intermediate is 342 tall too, so anything shorter clips a board people actually play. ── half steps from 2x ── Not a retreat from whole-number scaling. The unit that has to stay regular is the CELL, not the pixel: this is a 16px grid, so at 3.5x a cell is exactly 56 screen pixels — as even as 3x or 4x — and only sub-cell pixels alternate between three and four. Below 2x that alternation is a third of a pixel rather than a seventh, so whole numbers still apply there. The case that started all this, a fractional devicePixelRatio at nominal 1:1, put the CELL boundaries at 26.4px, which is a different thing entirely. Together, on a 1800x1400 device-pixel panel: 3.0x becomes 3.5x, and the game goes from 1512 to 1764 device pixels wide. Known cost, and it is in the README: the Extras > Auto Options dialog is 328 wide and opens 25px right of the game, so it needs a 554-wide stage to fit whole from beginner. It cannot be closed from the panel anyway — no window manager, no title bar, no OK button — so that is the thing to fix before this number is revisited. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Officer-Session: de3ed4e6-f88a-429c-94e6-a760336ba83b
This commit is contained in:
+20
-12
@@ -20,7 +20,7 @@ type SessionStatus = {
|
||||
//
|
||||
// Only a placeholder until `onReady`, which sizes the canvas from the real framebuffer. Drift with the
|
||||
// sidecar's stage costs one frame of the wrong aspect ratio and nothing else.
|
||||
const PLACEHOLDER = { width: 560, height: 400 };
|
||||
const PLACEHOLDER = { width: 512, height: 352 };
|
||||
|
||||
const BUTTON_MASK: Record<number, number> = { 0: 1, 1: 2, 2: 4 };
|
||||
|
||||
@@ -82,25 +82,33 @@ export const MinesweeperPanel = () => {
|
||||
}, [status?.running]);
|
||||
|
||||
/**
|
||||
* How big to draw the stage — and the answer is always a WHOLE number of screen pixels per source
|
||||
* pixel, or a whole number of source pixels per screen pixel.
|
||||
* How big to draw the stage. Computed in DEVICE pixels, so a 2x display gets twice the detail rather
|
||||
* than the same picture twice the size — and allowed to scale UP past the framebuffer's own size,
|
||||
* without which a wide panel leaves the canvas sitting at a fraction of the pixels available to it.
|
||||
*
|
||||
* This is a 16px-per-cell bitmap from 1995. At a fractional scale some cells get three screen pixels
|
||||
* and their neighbours get two, so the grid goes uneven and the digits wobble: sharp everywhere and
|
||||
* wrong overall, which is what "not quite right" looks like on pixel art. `image-rendering: pixelated`
|
||||
* does not save you — it removes the blur, not the unevenness.
|
||||
* ── Why the scale is snapped, and to what ──
|
||||
*
|
||||
* Computed in DEVICE pixels, so a 2x display gets 2x the detail rather than the same picture twice as
|
||||
* large. And it is allowed to scale UP past the framebuffer's own size, which is the part that was
|
||||
* missing: on a 1x display the canvas sat at 800 CSS px in a panel twice that wide, using a quarter of
|
||||
* the pixels available to it.
|
||||
* This is a 16px-per-cell bitmap from 1995. At an arbitrary scale a cell is 26.4 screen pixels, so the
|
||||
* grid lines themselves land unevenly and the digits wobble: sharp everywhere and wrong overall, which
|
||||
* is what "not quite right" looks like on pixel art. `image-rendering: pixelated` does not save you —
|
||||
* it removes the blur, not the unevenness. A fractional devicePixelRatio produces this at what looks
|
||||
* like 1:1, which is the case that is easiest to miss.
|
||||
*
|
||||
* HALF steps are allowed from 2x up, and that is not a compromise on the above. The unit that has to
|
||||
* stay regular is the CELL, not the pixel: at 3.5x a cell is exactly 56 screen pixels, so the grid is
|
||||
* as even as it is at 3x or 4x, and only sub-cell pixels alternate between three and four. Below 2x
|
||||
* that alternation is a third of the pixel rather than a seventh, so it stays whole-number there.
|
||||
*
|
||||
* The steps are what make the size quantised — at 3x the game is the same width whether the stage is
|
||||
* 512 or 560 wide. Shrinking the stage is only worth anything when it crosses a step, which is why
|
||||
* these two changes landed together.
|
||||
*/
|
||||
const stage = useMemo(() => {
|
||||
if (!frame || !box || box.width <= 0 || box.height <= 0) return null;
|
||||
const fit = Math.min((box.width * box.dpr) / frame.width, (box.height * box.dpr) / frame.height);
|
||||
// Below 1 there is nothing to floor to, so take a unit fraction: the rows that survive are then
|
||||
// evenly spaced instead of dropped in clumps.
|
||||
const scale = fit >= 1 ? Math.floor(fit) : 1 / Math.ceil(1 / fit);
|
||||
const scale = fit < 1 ? 1 / Math.ceil(1 / fit) : fit < 2 ? 1 : Math.floor(fit * 2) / 2;
|
||||
return { width: (frame.width * scale) / box.dpr, height: (frame.height * scale) / box.dpr };
|
||||
}, [frame, box]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user