This commit is contained in:
Brooke Vibber 2023-03-22 23:24:11 -07:00
parent 23e60ae668
commit 0dcfa88c29

View file

@ -28,6 +28,17 @@ function fromLinear(val) {
return unit * 255;
}
function toSRGB(val) {
val /= 255;
if (val <= 0.0031308) {
val *= 12.92;
} else {
val = (val * 1.055) ** (1.0 / 2.4) - 0.055;
}
val *= 255;
return val;
}
class RGB {
constructor(r, g, b) {
this.r = r;
@ -46,41 +57,32 @@ class RGB {
return new RGB(r,g,b);
}
toLinear() {
map(callback) {
return new RGB(
toLinear(this.r),
toLinear(this.g),
toLinear(this.b)
callback(this.r),
callback(this.g),
callback(this.b)
);
}
toLinear() {
return this.map(toLinear);
}
fromLinear() {
return new RGB(
fromLinear(this.r),
fromLinear(this.g),
fromLinear(this.b)
);
return this.map(fromLinear);
}
cap() {
if (this.r < 0) {
this.r = 0;
}
if (this.g < 0) {
this.g = 0;
}
if (this.b < 0) {
this.b = 0;
}
if (this.r > 255) {
this.r = 255;
}
if (this.g > 255) {
this.g = 255;
}
if (this.b > 255) {
this.b = 255;
toSRGB() {
return this.map(toSRGB);
}
clamp() {
return this.map((val) => {
if (val < 0) return 0;
if (val > 255) return 255;
return val;
});
}
inc(other) {
@ -90,11 +92,11 @@ class RGB {
return this;
}
static add(a, b) {
add(other) {
return new RGB(
a.r + b.r,
a.g + b.g,
a.b + b.b
this.r + other.r,
this.g + other.g,
this.b + other.b
);
}
@ -399,6 +401,7 @@ let atariRGB = [
0xfffa84,
0xffff99,
].map((hex) => RGB.fromHex(hex).toLinear());
//].map((hex) => RGB.fromHex(hex));
/**
* Dither RGB input data with a target palette size.
@ -430,10 +433,9 @@ function decimate(input, palette, n) {
let inputPixel = (x, error) => {
let rgb = input[x].clone();
if (error) {
rgb.inc(error.cur[x]);
rgb = rgb.add(error.cur[x]);
}
rgb.cap();
return rgb;
return rgb.clamp();
};
// Apply dithering with given palette and collect color usage stats
@ -585,7 +587,7 @@ function decimate(input, palette, n) {
let lumas = bucket.map((rgb) => rgb.luma());
let brightest = Math.max(...lumas);
if (avg_luma > 0) {
rgb = rgb.multiply(brightest / avg_luma);
rgb = rgb.multiply(brightest / avg_luma).clamp();
}
@ -740,7 +742,7 @@ async function convert(source) {
.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]));
inputLine = inputLine.map((rgb, x) => rgb.add(error[x]));
}
let line = decimate(inputLine, allColors, 4, y);
lines.push(line);
@ -897,11 +899,12 @@ async function saveImage(width, height, lines, dest) {
if (i >= width) {
throw new Error('i >= width');
}
let rgb = atariRGB[palette[output[i]]];
//let rgb = atariRGB[palette[output[i]]].fromLinear();
let rgb = atariRGB[palette[output[i]]].toSRGB();
rgba[y * stride + x * 4 + 0] = fromLinear(rgb.r);
rgba[y * stride + x * 4 + 1] = fromLinear(rgb.g);
rgba[y * stride + x * 4 + 2] = fromLinear(rgb.b);
rgba[y * stride + x * 4 + 0] = rgb.r;
rgba[y * stride + x * 4 + 1] = rgb.g;
rgba[y * stride + x * 4 + 2] = rgb.b;
rgba[y * stride + x * 4 + 3] = 255;
}
}