UNPKG

@thi.ng/pixel-dither

Version:

Extensible image dithering w/ various algorithm presets

54 lines (53 loc) 1.62 kB
import { isNumber } from "@thi.ng/checks/is-number"; import { clamp } from "@thi.ng/math/interval"; const __init = (x, y, size, val, step, mat) => { if (size === 1) { !mat[y] && (mat[y] = []); mat[y][x] = val; return mat; } size >>= 1; const step4 = step << 2; __init(x, y, size, val, step4, mat); __init(x + size, y + size, size, val + step, step4, mat); __init(x + size, y, size, val + step * 2, step4, mat); __init(x, y + size, size, val + step * 3, step4, mat); return mat; }; const defBayer = (size) => ({ mat: __init(0, 0, size, 0, 1, []), invSize: 1 / (size * size), mask: size - 1 }); const __orderedDither1 = ({ mat, mask, invSize }, dsteps, drange, srange, x, y, val) => { val = dsteps * (val / srange) + mat[y & mask][x & mask] * invSize - 0.5 | 0; dsteps--; return clamp(val, 0, dsteps) * ((drange - 1) / dsteps); }; const orderedDither = (img, size, numColors) => { const { data, format, width } = img; const steps = isNumber(numColors) ? new Array(format.channels.length).fill(numColors) : numColors; const mat = isNumber(size) ? defBayer(size) : size; for (let i = 0, n = data.length, nc = format.channels.length, x = 0, y = 0; i < n; i++) { let col = data[i]; for (let j = 0; j < nc; j++) { const ch = format.channels[j]; const num = ch.num; const cs = steps[j]; cs > 0 && (col = ch.setInt( col, __orderedDither1(mat, cs, num, num, x, y, ch.int(col)) )); } data[i] = col; if (++x === width) { x = 0; y++; } } return img; }; export { defBayer, orderedDither };