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[]} palette - current working palette, as Atari 8-bit color values (low nybble luminance, high nybble hue)
|
||||||
* @param {number} n - target color count
|
* @param {number} n - target color count
|
||||||
* @param {RGB[]} inputError
|
* @param {RGB[]} inputError
|
||||||
|
* @param {number} y
|
||||||
* @returns {{output: Uint8Array, palette: number[], error: RGB[]}}
|
* @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:
|
// to brute-force, the possible palettes are:
|
||||||
// 255 * 254 * 253 = 16,386,810
|
// 255 * 254 * 253 = 16,386,810
|
||||||
//
|
//
|
||||||
|
@ -408,7 +409,7 @@ function decimate(input, palette, n, inputError) {
|
||||||
let inputPixel = (x, error) => {
|
let inputPixel = (x, error) => {
|
||||||
let rgb = input[x].clone();
|
let rgb = input[x].clone();
|
||||||
if (error) {
|
if (error) {
|
||||||
rgb.add(error.cur[x]);
|
rgb.inc(error.cur[x]);
|
||||||
}
|
}
|
||||||
if (inputError) {
|
if (inputError) {
|
||||||
rgb.inc(inputError[x]);
|
rgb.inc(inputError[x]);
|
||||||
|
@ -499,39 +500,52 @@ function decimate(input, palette, n, inputError) {
|
||||||
//reserved = [0, 5, 10, 15]; // grayscale
|
//reserved = [0, 5, 10, 15]; // grayscale
|
||||||
//reserved = [0, 0x48, 0x78, 15]; // vaporwave
|
//reserved = [0, 0x48, 0x78, 15]; // vaporwave
|
||||||
//reserved = [0, 0x3c, 0x78, 15]; // red/blue/white
|
//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);
|
let keepers = new Uint8Array(256);
|
||||||
for (let i of reserved) {
|
for (let i of reserved) {
|
||||||
keepers[i & 0xfe] = 1; // drop that 0 luminance bit!
|
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) {
|
while (decimated.length > n) {
|
||||||
// Try dithering to every possible subset
|
let {popularity} = dither(decimated);
|
||||||
// See which has the worst overall fit and drop it
|
let hues = zeros(16);
|
||||||
let farthest = -1;
|
let lumas = zeros(16);
|
||||||
let worstIndex = -1;
|
|
||||||
for (let i = 0; i < decimated.length; i++) {
|
for (let i = 0; i < decimated.length; i++) {
|
||||||
let color = decimated[i];
|
let color = decimated[i];
|
||||||
if (keepers[color]) {
|
let hue = color >> 4;
|
||||||
continue;
|
let luma = color & 0xe;
|
||||||
|
hues[hue] += popularity[i];
|
||||||
|
if (luma > lumas[hue]) {
|
||||||
|
lumas[hue] = luma;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let shorter = decimated.filter((x) => x != i);
|
let a = hues;
|
||||||
let {distance2} = dither(shorter);
|
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);
|
||||||
|
|
||||||
if (distance2 >= farthest) {
|
decimated = reserved.concat(a);
|
||||||
farthest = distance2;
|
|
||||||
worstIndex = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
console.log('end', decimated);
|
console.log('end', decimated);
|
||||||
|
|
||||||
|
@ -626,10 +640,9 @@ async function convert(source, nbits) {
|
||||||
|
|
||||||
let lines = [];
|
let lines = [];
|
||||||
for (let y = 0; y < height; y++) {
|
for (let y = 0; y < height; y++) {
|
||||||
console.log(`y ${y}`);
|
|
||||||
let error = lines[y - 1]?.error;
|
let error = lines[y - 1]?.error;
|
||||||
let inputLine = input.slice(y * width, (y + 1) * width);
|
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);
|
lines.push(line);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|
Loading…
Reference in a new issue