import { render, TEXTURE_FILTER } from '../renderer' import { hslToRgb, rgbToHsl } from '../utils' import { Vec3 } from '../utils/math' // const PALETTE_16: Vec3[] = [ // [0.1498, 0.7753, 0.8255], // [0.1024, 0.9387, 0.6804], // [0.0699, 0.6976, 0.598], // [0.9567, 0.2212, 0.4431], // [0.8718, 0.2321, 0.2196], // [0.3833, 0.3623, 0.2706], // [0.2965, 0.3277, 0.4608], // [0.175, 0.5405, 0.5647], // [0.5355, 0.5341, 0.6549], // [0.5996, 0.368, 0.5098], // [0.6795, 0.134, 0.3804], // [0.5333, 0.1643, 0.5824], // [0.0764, 0.2791, 0.8314], // [0, 0.8324, 0.649], // [0.9194, 0.4819, 0.3784], // [0.9423, 0.7605, 0.6725] // ] // const BIAS_16 = 0.11 // WINDOWS 95 - 256 COLOURS PALETTE const PALETTE_256: Vec3[] = [ [0, 0, 0], [0, 1, 0.251], [0.3333, 1, 0.251], [0.1667, 1, 0.251], [0.6667, 1, 0.251], [0.8333, 1, 0.251], [0.5, 1, 0.251], [0, 0, 0.7529], [0.3333, 0.2857, 0.8078], [0.5856, 0.7115, 0.7961], [0.6393, 0.6038, 0.4157], [0.6502, 1, 0.5824], [0.2596, 1, 0.1863], [0.4686, 0.3869, 0.2686], [0.5977, 0.6038, 0.4157], [0.6252, 1, 0.5824], [0.2782, 1, 0.249], [0.4176, 0.503, 0.3314], [0.556, 0.6038, 0.4157], [0.6002, 1, 0.5824], [0.2893, 1, 0.3118], [0.3946, 0.5821, 0.3941], [0.5143, 0.6038, 0.4157], [0.5751, 1, 0.5824], [0.2967, 1, 0.3745], [0.3814, 0.6395, 0.4569], [0.4765, 0.6395, 0.4569], [0.5501, 1, 0.5824], [0.3019, 1, 0.4373], [0.3729, 0.7388, 0.5196], [0.4512, 0.7388, 0.5196], [0.525, 1, 0.5824], [0.3059, 1, 0.5], [0.367, 1, 0.5824], [0.4335, 1, 0.5824], [0.5, 1, 0.5824], [0, 1, 0.1667], [0.8333, 1, 0.1667], [0.75, 1, 0.3333], [0.7222, 1, 0.5], [0.0608, 1, 0.1667], [0.8333, 0.4655, 0.2275], [0.7314, 0.6915, 0.3941], [0.7068, 1, 0.5608], [0.1235, 1, 0.1667], [0.8333, 0.1486, 0.2902], [0.7009, 0.4592, 0.4569], [0.6858, 1, 0.6235], [0.1842, 1, 0.1863], [0.3333, 0.0556, 0.3529], [0.6471, 0.3333, 0.5], [0.6569, 1, 0.6667], [0.2218, 1, 0.249], [0.3333, 0.1981, 0.4157], [0.5843, 0.3333, 0.5], [0.6255, 1, 0.6667], [0.2442, 1, 0.3118], [0.3333, 0.3033, 0.4784], [0.5216, 0.3333, 0.5], [0.5941, 1, 0.6667], [0.2592, 1, 0.3745], [0.3333, 0.453, 0.5412], [0.467, 0.453, 0.5412], [0.5627, 1, 0.6667], [0.2698, 1, 0.4373], [0.3333, 0.6832, 0.6039], [0.436, 0.6832, 0.6039], [0.5314, 1, 0.6667], [0.2778, 1, 0.5], [0.3333, 1, 0.6667], [0.4167, 1, 0.6667], [0.5, 1, 0.6667], [0, 1, 0.249], [0.8885, 1, 0.249], [0.7912, 1, 0.3333], [0.7497, 1, 0.5], [0.0407, 1, 0.249], [0.9063, 0.6076, 0.3098], [0.7818, 0.6915, 0.3941], [0.7381, 1, 0.5608], [0.0827, 1, 0.249], [0.9427, 0.3368, 0.3725], [0.7664, 0.4592, 0.4569], [0.7222, 1, 0.6235], [0.1247, 1, 0.249], [0.0397, 0.1981, 0.4157], [0.7378, 0.3061, 0.5196], [0.7, 1, 0.6863], [0.1667, 1, 0.249], [0.1667, 0.1981, 0.4157], [0.6667, 0.2019, 0.5824], [0.6667, 1, 0.749], [0.2002, 1, 0.3118], [0.2387, 0.3033, 0.4784], [0.5426, 0.2019, 0.5824], [0.625, 1, 0.749], [0.2225, 1, 0.3745], [0.2673, 0.453, 0.5412], [0.4453, 0.3333, 0.6235], [0.5833, 1, 0.749], [0.2384, 1, 0.4373], [0.2826, 0.6832, 0.6039], [0.408, 0.6, 0.6863], [0.5417, 1, 0.749], [0.2503, 1, 0.5], [0.2922, 1, 0.6667], [0.3893, 1, 0.749], [0.5, 1, 0.749], [0, 1, 0.3333], [0.9167, 1, 0.3333], [0.8333, 1, 0.3333], [0.7778, 1, 0.5], [0.0304, 1, 0.3333], [0.9353, 0.6915, 0.3941], [0.8333, 0.6915, 0.3941], [0.7701, 1, 0.5608], [0.0618, 1, 0.3333], [0.9657, 0.4592, 0.4569], [0.8333, 0.4592, 0.4569], [0.7595, 1, 0.6235], [0.0931, 1, 0.3333], [0.0196, 0.3333, 0.5], [0.8333, 0.3061, 0.5196], [0.7448, 1, 0.6863], [0.1245, 1, 0.3333], [0.0824, 0.3333, 0.5], [0.8333, 0.2019, 0.5824], [0.7227, 1, 0.749], [0.1559, 1, 0.3333], [0.1451, 0.3333, 0.5], [0.8333, 0.0608, 0.6451], [0.6858, 1, 0.8118], [0.185, 1, 0.3745], [0.1997, 0.453, 0.5412], [0.3333, 0.1409, 0.7078], [0.6255, 1, 0.8333], [0.2063, 1, 0.4373], [0.2307, 0.6832, 0.6039], [0.3333, 0.453, 0.7706], [0.5627, 1, 0.8333], [0.2222, 1, 0.5], [0.25, 1, 0.6667], [0.3333, 1, 0.8333], [0.5, 1, 0.8333], [0, 1, 0.4157], [0.9332, 1, 0.4157], [0.8664, 1, 0.4157], [0.8052, 1, 0.5], [0.0244, 1, 0.4157], [0.9503, 0.7449, 0.4765], [0.872, 0.7449, 0.4765], [0.8013, 1, 0.5608], [0.0495, 1, 0.4157], [0.9754, 0.634, 0.5392], [0.8803, 0.634, 0.5392], [0.796, 1, 0.6235], [0.0747, 1, 0.4157], [0.0131, 0.5962, 0.5824], [0.8932, 0.5764, 0.602], [0.7885, 1, 0.6863], [0.0998, 1, 0.4157], [0.0551, 0.5962, 0.5824], [0.9157, 0.4971, 0.6647], [0.7773, 1, 0.749], [0.125, 1, 0.4157], [0.0971, 0.5962, 0.5824], [0.9654, 0.3813, 0.7275], [0.7587, 1, 0.8118], [0.1502, 1, 0.4157], [0.1391, 0.5962, 0.5824], [0.0833, 0.3281, 0.749], [0.7214, 1, 0.8745], [0.1749, 1, 0.4373], [0.18, 0.6832, 0.6039], [0.2013, 0.453, 0.7706], [0.624, 1, 0.9157], [0.1948, 1, 0.5], [0.2088, 1, 0.6667], [0.251, 1, 0.8333], [0.5, 1, 0.9157], [0.9444, 1, 0.5], [0.8889, 1, 0.5], [0.0203, 1, 0.5], [0.9598, 1, 0.5608], [0.8966, 1, 0.5608], [0.8333, 1, 0.5608], [0.0412, 1, 0.5], [0.9809, 1, 0.6235], [0.9071, 1, 0.6235], [0.8333, 1, 0.6235], [0.0621, 1, 0.5], [0.0098, 1, 0.6667], [0.9219, 1, 0.6863], [0.8333, 1, 0.6863], [0.083, 1, 0.5], [0.0412, 1, 0.6667], [0.944, 1, 0.749], [0.8333, 1, 0.749], [0.1039, 1, 0.5], [0.0725, 1, 0.6667], [0.9809, 1, 0.8118], [0.8333, 1, 0.8118], [0.1248, 1, 0.5], [0.1039, 1, 0.6667], [0.0412, 1, 0.8333], [0.8333, 1, 0.8745], [0.1458, 1, 0.5], [0.1353, 1, 0.6667], [0.1039, 1, 0.8333], [0.8333, 1, 0.9373], [0.1667, 1, 0.6667], [0.1667, 1, 0.8333], [0.6667, 1, 0.9], [0.8333, 1, 0.9], [0.5, 1, 0.6], [0.5, 1, 0.7], [0.5, 1, 0.8], [0.5, 1, 0.9], [0.3333, 1, 0.249], [0.4449, 1, 0.249], [0.5422, 1, 0.3333], [0.5837, 1, 0.5], [0.3333, 1, 0.3118], [0.4224, 1, 0.3118], [0.5108, 1, 0.3333], [0.5627, 1, 0.5], [0.3333, 1, 0.3745], [0.4075, 1, 0.3745], [0.4817, 1, 0.3745], [0.5418, 1, 0.5], [0.3333, 1, 0.4373], [0.3969, 1, 0.4373], [0.4604, 1, 0.4373], [0.5209, 1, 0.5], [0.3889, 1, 0.5], [0.4444, 1, 0.5], [0, 1, 0.0824], [0.749, 1, 0.1667], [0.7078, 1, 0.3333], [0.6941, 1, 0.5], [0.123, 1, 0.0824], [0.7006, 0.4655, 0.2275], [0.6799, 0.6915, 0.3941], [0.6749, 1, 0.5608], [0.2222, 1, 0.1235], [0.5853, 0.3386, 0.249], [0.1222, 1, 0.9706], [0.6667, 0.0215, 0.6353], [0, 0, 0.502], [0, 1, 0.5], [0.3333, 1, 0.5], [0.1667, 1, 0.5], [0.6667, 1, 0.5], [0.8333, 1, 0.5], [0.5, 1, 0.5], [0, 0, 1] ] const BIAS_256 = 0 const lightnessSteps = 4 const saturationSteps = 4 /* eslint-disable prettier/prettier */ const ditherTable = new Uint8Array([ 0, 48, 12, 60, 3, 51, 15, 63, 32, 16, 44, 28, 35, 19, 47, 31, 8, 56, 4, 52, 11, 59, 7, 55, 40, 24, 36, 20, 43, 27, 39, 23, 2, 50, 14, 62, 1, 49, 13, 61, 34, 18, 46, 30, 33, 17, 45, 29, 10, 58, 6, 54, 9, 57, 5, 53, 42, 26, 38, 22, 41, 25, 37, 21 ]) /* eslint-enable prettier/prettier */ function hueDistance(h1: number, h2: number): number { const diff = Math.abs(h1 - h2) return diff < 0.5 ? diff : 1 - diff } const lightnessStep = (l: number) => Math.round(l * lightnessSteps) / lightnessSteps const saturationStep = (s: number) => Math.round(s * saturationSteps) / saturationSteps const closestColorsCache = new Map() function closestColors(hue: number): [Vec3, Vec3] { const cachedResult = closestColorsCache.get(hue) if (cachedResult) return cachedResult let closest: Vec3 = [-2, 0, 0] let secondClosest: Vec3 = [-2, 0, 0] let minDist = Infinity let secondMinDist = Infinity for (const color of PALETTE_256) { const dist = hueDistance(color[0], hue) if (dist < minDist) { secondClosest = closest secondMinDist = minDist closest = color minDist = dist } else if (dist < secondMinDist) { secondClosest = color secondMinDist = dist } } const result: [Vec3, Vec3] = [closest, secondClosest] closestColorsCache.set(hue, result) return result } function dither(pos: [number, number], color: Vec3): Vec3 { const index = (pos[0] & 7) + ((pos[1] & 7) << 3) const limit = (ditherTable[index] + 1) / 64 + BIAS_256 const [closest, secondClosest] = closestColors(color[0]) const hueDiff = hueDistance(color[0], closest[0]) / hueDistance(secondClosest[0], closest[0]) const l1 = lightnessStep(Math.max(color[2] - 0.125, 0)) const l2 = lightnessStep(Math.min(color[2] + 0.124, 1)) const lightnessDiff = (color[2] - l1) / (l2 - l1) const resultColor: Vec3 = hueDiff < limit ? [...closest] : [...secondClosest] resultColor[2] = lightnessDiff < limit ? l1 : l2 const s1 = saturationStep(Math.max(color[1] - 0.125, 0)) const s2 = saturationStep(Math.min(color[1] + 0.124, 1)) const saturationDiff = (color[1] - s1) / (s2 - s1) resultColor[1] = saturationDiff < limit ? s1 : s2 return hslToRgb(resultColor) } export function orderedBayer(source: Buffer, width: number, height: number): Buffer { return render( source, width, height, (pixelCoords, texture) => { const color = texture(pixelCoords) const ditheredColor = dither(pixelCoords, rgbToHsl(color)) return [...ditheredColor, color[3]] }, { textureFilter: TEXTURE_FILTER.NEAREST } ) }