~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/knotless

git that lives in your Atmosphere account

Forked from chadtmiller.com/knotless

0
0
66 lines · 2.7 KB Raw History
// A screenshot that failed, as a check's output: WebP, small enough for
// the runner to keep (at most 1,000,000 bytes, the lexicon's maxSize).
//
//   node harness/shots/outputs.mjs <png> <out, less its extension> before|after|difference
//
// Before and after are lossy, at quality 75 and the shot's own scale: at
// 2× the text is as crisp as the PNG's, at a third of its size or less. The
// difference is lossless, so the pixels it marks stay exact. One still too
// big is tried smaller (before and after at quality 50, then at half the
// scale), keeping the smallest; one still over is written anyway, and
// said, as the runner leaves it out. Any failure exits non-zero, having
// written nothing, and linux.sh keeps the PNG instead.

import { readFile, writeFile } from 'node:fs/promises'
import { basename } from 'node:path'

const [png, out, kind] = process.argv.slice(2)
const most = 1_000_000
// WebP's largest side, in pixels.
const side = 16383

const tries =
  kind === 'difference'
    ? // Lossless is smallest for a difference's faded page, even against
      // lossy at half the scale, but a change of a different kind may not be.
      [
        { lossless: true },
        {
          quality: 50,
          half: true,
          note: 'lossy, at half the scale: the pixels it marks are blurred',
        },
      ]
    : [
        { quality: 75 },
        { quality: 50, note: 'at quality 50' },
        { quality: 75, half: true, note: 'at half the scale' },
      ]

const name = basename(out) + '.webp'
process.on('uncaughtException', (e) => {
  console.log(`==> ${name}: none, as ${e.message}`)
  process.exit(1)
})
// Imported here, so its absence is said as any other failure is.
const { default: sharp } = await import('sharp')
const input = await readFile(png)
const { width, height } = await sharp(input).metadata()
let smallest
for (const t of tries) {
  const scale = t.half ? 0.5 : 1
  if (Math.max(width, height) * scale > side) continue
  let image = sharp(input)
  if (t.half) image = image.resize({ width: Math.round(width / 2) })
  const options = t.lossless ? { lossless: true } : { quality: t.quality, smartSubsample: true }
  const bytes = await image.webp(options).toBuffer()
  if (!smallest || bytes.length < smallest.bytes.length) smallest = { bytes, t }
  if (bytes.length <= most) break
}
if (!smallest) throw new Error(`${width}×${height} is too big for WebP, even at half the scale`)
await writeFile(out + '.webp', smallest.bytes)
const kb = (n) => `${Math.round(n / 1000)} kB`
const how = smallest.t.note ? `, ${smallest.t.note}` : ''
console.log(`==> ${name}: ${kb(smallest.bytes.length)}, from a ${kb(input.length)} PNG${how}`)
if (smallest.bytes.length > most)
  console.log(`==> ${name}: still over ${most} bytes, so the runner will not keep it`)