diff --git a/api/derive.ts b/api/derive.ts index 82b848e..9c5cf83 100644 --- a/api/derive.ts +++ b/api/derive.ts @@ -164,6 +164,9 @@ const THUMB_MAX_WIDTH = 640; */ const SQUARE_TOLERANCE = 0.02; +/** Where the drawing-or-photograph question is asked. See prefersLossless for why not smaller. */ +const PROBE_WIDTH = 480; + /** * Is this a DRAWING rather than a photograph? * @@ -191,8 +194,28 @@ const SQUARE_TOLERANCE = 0.02; * * Ties and failures go to lossy, which is the right default for a library that is mostly photographs. */ -async function prefersLossless(sourcePath: string, staging: string, email: string): Promise { - const probe = LADDER[0]!; +async function prefersLossless( + sourcePath: string, + staging: string, + email: string, + sourceWidth: number, +): Promise { + /* + Measured near the source's own size, NOT at the smallest rung. + + The first attempt probed at 96 pixels and got the answer wrong on exactly the file it was written + for. Downscaling a logo to 96 resamples its flat areas into gradients, so by the time the probe + sees it, it already looks like a photograph — lossy wins the comparison, and that verdict is then + applied to every rung above, where the flat areas are still flat and lossless would have won by a + wide margin. fofos-logo-saffron came out with a 13,074-byte 160-pixel rung beside a 9,144-byte + 300-pixel original. + + 480 rather than the full size: a picture is still recognisably itself at 480 — flat stays flat and + photographic stays photographic — and probing a 4000x6000 photograph losslessly to learn something + already obvious is minutes of encoding across a library. Anything smaller than 480 is probed at its + own size, which is the case that matters, because that is what a logo is. + */ + const probe = Math.min(PROBE_WIDTH, sourceWidth); const lossyPath = join(staging, '.probe-lossy.webp'); const losslessPath = join(staging, '.probe-lossless.webp'); @@ -554,7 +577,7 @@ async function buildImage( photograph want opposite encoders, and getting it wrong on a logo produced a smaller rung two and a half times the size of the original. */ - const lossless = await prefersLossless(master, staging, email); + const lossless = await prefersLossless(master, staging, email, width); const variants: Variant[] = [];