wip
This commit is contained in:
parent
dfd35658ee
commit
6af5d43943
1 changed files with 39 additions and 26 deletions
|
@ -386,9 +386,10 @@ let atariRGB = [
|
|||
* @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: Uint8Array, palette: number[], error: RGB[]}}
|
||||
*/
|
||||
function decimate(input, palette, n, inputError) {
|
||||
function decimate(input, palette, n, inputError, y) {
|
||||
// to brute-force, the possible palettes are:
|
||||
// 255 * 254 * 253 = 16,386,810
|
||||
//
|
||||
|
@ -408,7 +409,7 @@ function decimate(input, palette, n, inputError) {
|
|||
let inputPixel = (x, error) => {
|
||||
let rgb = input[x].clone();
|
||||
if (error) {
|
||||
rgb.add(error.cur[x]);
|
||||
rgb.inc(error.cur[x]);
|
||||
}
|
||||
if (inputError) {
|
||||
rgb.inc(inputError[x]);
|
||||
|
@ -499,39 +500,52 @@ function decimate(input, palette, n, inputError) {
|
|||
//reserved = [0, 5, 10, 15]; // grayscale
|
||||
//reserved = [0, 0x48, 0x78, 15]; // vaporwave
|
||||
//reserved = [0, 0x3c, 0x78, 15]; // red/blue/white
|
||||
/*
|
||||
if (( y & 1 ) === 0) {
|
||||
reserved = [0, 0x3c, 0x1a, 15]; // red/yellow/white
|
||||
} else {
|
||||
reserved = [0, 0x76, 0x9a, 15]; // blue/cyan/white
|
||||
}
|
||||
*/
|
||||
|
||||
let keepers = new Uint8Array(256);
|
||||
for (let i of reserved) {
|
||||
keepers[i & 0xfe] = 1; // drop that 0 luminance bit!
|
||||
}
|
||||
|
||||
let zeros = (n) => {
|
||||
let arr = [];
|
||||
for (let i = 0; i < n; i++) {
|
||||
arr.push(0);
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
// first, dither to the total atari palette
|
||||
while (decimated.length > n) {
|
||||
// Try dithering to every possible subset
|
||||
// See which has the worst overall fit and drop it
|
||||
let farthest = -1;
|
||||
let worstIndex = -1;
|
||||
let {popularity} = dither(decimated);
|
||||
let hues = zeros(16);
|
||||
let lumas = zeros(16);
|
||||
for (let i = 0; i < decimated.length; i++) {
|
||||
let color = decimated[i];
|
||||
if (keepers[color]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let shorter = decimated.filter((x) => x != i);
|
||||
let {distance2} = dither(shorter);
|
||||
|
||||
if (distance2 >= farthest) {
|
||||
farthest = distance2;
|
||||
worstIndex = i;
|
||||
let hue = color >> 4;
|
||||
let luma = color & 0xe;
|
||||
hues[hue] += popularity[i];
|
||||
if (luma > lumas[hue]) {
|
||||
lumas[hue] = luma;
|
||||
}
|
||||
}
|
||||
console.log(`worstIndex ${worstIndex} farthest ${farthest}`);
|
||||
let foo = decimated.length;
|
||||
decimated.splice(worstIndex, 1);
|
||||
//console.log(decimated.length);
|
||||
if (decimated.length == foo) {
|
||||
throw new Error('this should not happen');
|
||||
}
|
||||
//console.log('n ', decimated);
|
||||
|
||||
let a = hues;
|
||||
a = a.map((count, hue) => { return {count, hue} });
|
||||
a = a.sort((a, b) => b.count - a.count);
|
||||
a = a.map(({hue}) => hue);
|
||||
a = a.filter((color) => !keepers[color]);
|
||||
a = a.slice(0, n - reserved.length);
|
||||
a = a.map((hue) => (hue << 4) | lumas[hue]);
|
||||
a = a.sort((a, b) => a - b);
|
||||
|
||||
decimated = reserved.concat(a);
|
||||
}
|
||||
console.log('end', decimated);
|
||||
|
||||
|
@ -626,10 +640,9 @@ async function convert(source, nbits) {
|
|||
|
||||
let lines = [];
|
||||
for (let y = 0; y < height; y++) {
|
||||
console.log(`y ${y}`);
|
||||
let error = lines[y - 1]?.error;
|
||||
let inputLine = input.slice(y * width, (y + 1) * width);
|
||||
let line = decimate(inputLine, allColors, 4, error);
|
||||
let line = decimate(inputLine, allColors, 4, error, y);
|
||||
lines.push(line);
|
||||
}
|
||||
return {
|
||||
|
|
Loading…
Reference in a new issue