diff --git a/dither-image.js b/dither-image.js index a0eda76..f2f7af9 100644 --- a/dither-image.js +++ b/dither-image.js @@ -407,11 +407,10 @@ let atariRGB = [ * @param {RGB[]} input source scanline data, in linear RGB * @param {number[]} palette - current working palette, as Atari 8-bit color values (low nybble luminance, high nybble hue) * @param {number} n - target color count - * @param {RGB[]} inputError * @param {number} y * @returns {{output: number[], palette: number[], error: RGB[]}} */ -function decimate(input, palette, n, inputError, y) { +function decimate(input, palette, n) { // to brute-force, the possible palettes are: // 255 * 254 * 253 = 16,386,810 // @@ -433,9 +432,6 @@ function decimate(input, palette, n, inputError, y) { if (error) { rgb.inc(error.cur[x]); } - if (inputError) { - rgb.inc(inputError[x]); - } rgb.cap(); return rgb; }; @@ -540,9 +536,9 @@ function decimate(input, palette, n, inputError, y) { // Median cut! // https://en.wikipedia.org/wiki/Median_cut - //let buckets = [input.slice()]; - let initial = dither(palette); - let buckets = [initial.output.map((i) => atariRGB[palette[i]])]; + let buckets = [input.slice()]; + //let initial = dither(palette); + //let buckets = [initial.output.map((i) => atariRGB[palette[i]])]; let medianCut = (bucket, range) => { if (bucket.length < 2) { console.log(bucket); @@ -582,19 +578,25 @@ function decimate(input, palette, n, inputError, y) { } decimated = buckets.map((bucket) => { // Average the RGB colors in this chunk - //let rgb = bucket - // .reduce((acc, rgb) => acc.inc(rgb), new RGB(0, 0, 0)) - // .divide(bucket.length); - - // scale to the max luma + let rgb = bucket + .reduce((acc, rgb) => acc.inc(rgb), new RGB(0, 0, 0)) + .divide(bucket.length); + + let avg_luma = rgb.luma(); + let lumas = bucket.map((rgb) => rgb.luma()); + let brightest = Math.max(...lumas); + if (avg_luma > 0) { + rgb = rgb.multiply(brightest / avg_luma); + } + + + // pick the luma-brightest color in the bucket // kinda nice but really aggressive with the colors + /* let lumas = bucket.map((rgb) => rgb.luma()); let luma = Math.max(...lumas); let rgb = bucket[lumas.indexOf(luma)]; - let from = rgb.luma(); - if (from > 0) { - rgb = rgb.multiply(luma / rgb.luma()); - } + */ // Take the channel-brightest color in the bucket // bad @@ -615,6 +617,7 @@ function decimate(input, palette, n, inputError, y) { Math.max(...bucket.map((rgb) => rgb.b)) ); */ + // combine the median of each channel // sux /* @@ -734,9 +737,13 @@ async function convert(source) { let lines = []; for (let y = 0; y < height; y++) { - let error = lines[y - 1]?.error; - let inputLine = input.slice(y * width, (y + 1) * width); - let line = decimate(inputLine, allColors, 4, error, y); + let inputLine = input + .slice(y * width, (y + 1) * width); + if (y > 0) { + let error = lines[y - 1].error; + inputLine = inputLine.map((rgb, x) => RGB.add(rgb, error[x])); + } + let line = decimate(inputLine, allColors, 4, y); lines.push(line); } return {