From 365d9684484fb9eff9e61ef0e242d8d5df85fbbf Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Sat, 18 Mar 2023 21:56:36 -0700 Subject: [PATCH] wip colors --- dither-image.js | 76 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/dither-image.js b/dither-image.js index fb5130e..1b8f8ee 100644 --- a/dither-image.js +++ b/dither-image.js @@ -407,6 +407,18 @@ function decimate(input, palette, n, inputError) { let width = input.length; + let inputPixel = (x, error) => { + let rgb = input[x]; + if (error) { + rgb.add(error.cur[x]); + } + if (inputError) { + rgb.inc(inputError[x]); + } + rgb.cap(); + return rgb; + }; + // Apply dithering with given palette and collect color usage stats let dither = (palette) => { let fitness = new Float64Array(width); @@ -426,11 +438,7 @@ function decimate(input, palette, n, inputError) { // Try dithering with this palette. for (let x = 0; x < width; x++) { - let rgb = RGB.add(input[x], error.cur[x]); - if (inputError) { - rgb.inc(inputError[x]); - } - rgb.cap(); + let rgb = inputPixel(x, error); // find the closest possible color // @todo consider doing the difference scoring in luminance and hue spaces @@ -487,16 +495,62 @@ function decimate(input, palette, n, inputError) { //decimated = [0, 0x36, 0x0f, 0x86]; let reserved = [0]; // black - //let reserved = [0, 15]; // black, white - //let reserved = [0, 5, 10, 15]; // grayscale - //let reserved = [0, 0x48, 0x78, 15]; // vaporwave - //let reserved = [0, 0x3c, 0x78, 15]; // red/blue/white + //reserved = [0, 15]; // black, white + //reserved = [0, 5, 10, 15]; // grayscale + //reserved = [0, 0x48, 0x78, 15]; // vaporwave + //reserved = [0, 0x3c, 0x78, 15]; // red/blue/white let keepers = new Uint8Array(256); for (let i of reserved) { keepers[i & 0xfe] = 1; // drop that 0 luminance bit! } + + // not happy with this alt yet + + let colorCount = new Int32Array(256); + let hues = new Int32Array(16); + let lumaByHue = new Int32Array(16); + + const bestColor = (rgb) => { + let best = -1; + let closest = Infinity; + for (let i = 0; i < 256; i += 2) { + let distance = rgb.difference(atariRGB[i]).magnitude(); + if (distance < closest) { + closest = distance; + best = i; + } + } + return best; + }; + for (let x = 0; x < width; x++ ) { + let rgb = inputPixel(x); + let i = bestColor(rgb); + colorCount[i]++; + let hue = i >> 4; + hues[hue]++; + let luma = i & 0xf; + lumaByHue[hue] = Math.max(luma, lumaByHue[hue]); + } + + let xhues = []; + xhues.push.apply(xhues, hues); + + let popularHues = xhues.map((count, hue) => {return {count, hue}}).sort((a, b) => b.count - a.count); + decimated = [0]; + for (let {count, hue} of popularHues) { + let luma = lumaByHue[hue]; + if (luma > 0 && count > 0) { + decimated.push((hue << 4) | luma); + } + if (decimated.length == n) { + break; + } + } + + /* + while (decimated.length > n) { let {popularity, fitness, output} = dither(decimated); @@ -509,8 +563,9 @@ function decimate(input, palette, n, inputError) { continue; } + let coolFactor = fitness[i] * popularity[i]; //let coolFactor = popularity[i]; - let coolFactor = (fitness[i] ** 2) * popularity[i]; + //let coolFactor = (fitness[i] ** 2) * popularity[i]; if (coolFactor < least) { pick = i; @@ -534,6 +589,7 @@ function decimate(input, palette, n, inputError) { throw new Error('this should not happen'); } } + */ // Palette fits return dither(decimated);