Files
booth/tests/test_thumbs_browser.py
T
vh c2b1454358 fix(thumbs): size thumbnails for the tile's width at 2x, not 512 on the long side
The operator on sindra-nude-final: "the images look blurry until they're
selected and blown up." The cap was 512px on the LONGEST side, which the
comment called "comfortably above any tile size", and it was, for a square. A
gallery tile is sized by its WIDTH, though, and a 704x1408 portrait got 256px
of width for a tile Chromium renders at 361 CSS px. That is 1.4x stretched at
1x density and 2.8x on a 2x screen. The review stage serves the original,
which is why it looked sharp once opened.

- THUMB_WIDTH = 768: the widest desktop tile (3 columns, 1440px and up,
  measured at 321-361 CSS px across viewports) doubled for a 2x screen.
  THUMB_HEIGHT_MAX = 4096 stops a long screenshot going through at full height.
- An original that fits the bounds is served as-is only when it is also light
  (<= 64 KB; 768-wide thumbnails average 39 KB over the 381 live images) or
  animated, since a thumbnail is one frame. Fitting a tile in pixels is not
  being cheap in bytes: these portraits are ~1.1 MB PNGs.
- The size rule is in the cache name (`<rel>.768w.webp`). The live 512-cap
  thumbnails are newer than their sources, so the mtime check alone would have
  served them forever. The old files are orphans, swept with their booth.
- tests/test_thumbs_browser.py holds THUMB_WIDTH against the rendered grid at
  1440, 1920 and 2560. The constant is a layout number, and a redesign that
  widens the tiles turns it red instead of soft.

Measured cost, all 381 live images: 4.8 MB -> 14.2 MB of thumbnails, still ~27x
under the 386 MB of originals. Known limit: the 2-column (<=472px) and 1-column
(<=650px) reflows are softer than 768 covers at 2x. tests/mutations/thumbs.toml
proves 7 falsifiers.
2026-09-23 22:23:03 -07:00

51 lines
2.2 KiB
Python

"""Thumbnails against the tile they are drawn into, in a real browser.
`THUMB_WIDTH` is derived from a LAYOUT number: the widest gallery tile on the
desktop grid, doubled for a 2x screen. A Python test cannot see a CSS width, so
without this file the constant and the grid could drift apart silently, which
is how the tiles went soft in the first place. If the grid widens its tiles,
this goes red and the constant is revisited, rather than the operator finding
it by eye.
Scoped to the desktop layout (3 columns, 1440px and up), which is where tiles
are measured at 321-361 CSS px. Narrower windows reflow to 2 columns (up to
472 px) or 1 (up to 650 px): at 2x density those are softer than this bound
covers, and that is a known limit, not a defect this file asserts against.
"""
from __future__ import annotations
import os
import pytest
from test_embed_browser import browser, live # noqa: F401 (fixtures)
from booth.thumbs import THUMB_WIDTH
PIL = pytest.importorskip("PIL.Image", reason="Pillow is not installed")
@pytest.mark.parametrize("viewport", [(1440, 900), (1920, 1080), (2560, 1440)])
def test_a_thumbnail_covers_its_tile_at_2x_density(browser, live, viewport): # noqa: F811
base, root = live
b = root / "g"
b.mkdir()
for n in ("a.png", "b.png", "c.png"):
PIL.frombytes("RGB", (1024, 1024), os.urandom(1024 * 1024 * 3)).save(b / n, "PNG")
pg = browser.new_page(viewport={"width": viewport[0], "height": viewport[1]})
try:
pg.goto(f"{base}/b/g/", wait_until="load")
pg.wait_for_function(
"Array.from(document.querySelectorAll('.gallery .item img'))"
".every(i => i.complete && i.naturalWidth)", timeout=15000)
tiles = pg.evaluate(
"Array.from(document.querySelectorAll('.gallery .item img'))"
".map(i => [i.currentSrc, i.clientWidth, i.naturalWidth])")
finally:
pg.close()
assert tiles, "no gallery tiles rendered"
for src, shown, natural in tiles:
assert "thumb=1" in src, f"{src} is not the thumbnail"
assert natural >= 2 * shown, (
f"{src}: a {shown}px tile needs {2 * shown}px at 2x, the thumbnail has {natural}px"
f" (THUMB_WIDTH={THUMB_WIDTH})")