diff --git a/dither-image.js b/dither-image.js index 080ad74..2aac164 100644 --- a/dither-image.js +++ b/dither-image.js @@ -540,10 +540,15 @@ 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); + throw new Error('short bucket'); + } + //console.log('medianCut', bucket, range); // Sort by the channel with the greatest range, // then cut the bucket in two at the median. if (range.g >= range.r && range.g >= range.b) { @@ -554,6 +559,7 @@ function decimate(input, palette, n, inputError, y) { bucket.sort((a, b) => b.b - a.b); } let half = bucket.length >> 1; + //console.log('cutting', half, bucket.length); return [bucket.slice(0, half), bucket.slice(half)]; }; while (buckets.length < n) { @@ -579,9 +585,25 @@ function decimate(input, palette, n, inputError, y) { //let rgb = bucket // .reduce((acc, rgb) => acc.inc(rgb), new RGB(0, 0, 0)) // .divide(bucket.length); + + // scale to the max luma + let lumas = bucket.map((rgb) => rgb.luma()); + console.log(lumas); + let luma = Math.max(...lumas); + let rgb = bucket[lumas.indexOf(luma)]; + console.log(rgb, luma); + let from = rgb.luma(); + if (from > 0) { + rgb = rgb.multiply(luma / rgb.luma()); + } + console.log(rgb, luma); + if (!rgb) { + throw new Error('xxx'); + } // Take the channel-brightest color in the bucket - //let rgb = bucket[bucket.length - 1]; + // bad + //rgb = bucket[bucket.length - 1]; // Take the luma-brightest color in the bucket //let rgb = bucket.slice().sort((a, b) => b.luma() - a.luma())[bucket.length - 1]; @@ -591,11 +613,22 @@ function decimate(input, palette, n, inputError, y) { // Combine the brightest of each channel // this is kinda good + /* let rgb = new RGB( Math.max(...bucket.map((rgb) => rgb.r)), Math.max(...bucket.map((rgb) => rgb.g)), Math.max(...bucket.map((rgb) => rgb.b)) ); + */ + // combine the median of each channel + // sux + /* + let rgb = new RGB( + bucket.map((rgb) => rgb.r).sort((a, b) => b - a)[bucket.length >> 1], + bucket.map((rgb) => rgb.g).sort((a, b) => b - a)[bucket.length >> 1], + bucket.map((rgb) => rgb.b).sort((a, b) => b - a)[bucket.length >> 1] + ); + */ // Take the luma-median color in the bucket //let rgb = bucket.slice().sort((a, b) => b.luma() - a.luma())[bucket.length >> 1]; @@ -605,14 +638,14 @@ function decimate(input, palette, n, inputError, y) { // .sort((a, b) => Math.max(b.r, b.g, b.b) - Math.max(a.r, b.g, b.b))[bucket.length >> 1]; // And map into the Atari palette - let dists = palette.map(( i) => rgb.difference(atariRGB[i]).magnitude()); + let dists = palette.map((i) => rgb.difference(atariRGB[i]).magnitude()); let closest = Math.min(...dists); let index = dists.indexOf(closest); return palette[index]; }); // hack decimated.sort((a, b) => a - b); - console.log(decimated); + //console.log(decimated); decimated[0] = 0; // Palette fits