diff --git a/dither4.js b/dither4.js index 00978fd..a110b70 100644 --- a/dither4.js +++ b/dither4.js @@ -1,3 +1,23 @@ +function sRGBToLinear(val) { + let unit = val / 255; + if (unit < 0.04045) { + unit /= 12.92; + } else { + unit = ((unit + 0.055) / 1.055) ** 2.4; + } + return unit * 255; +} + +function linearTosRGB(val) { + let unit = val / 255; + if (unit < 0.0031308) { + unit *= 12.92; + } else { + unit = 1.055 * unit ** (1 / 2.4) - 0.055; + } + return unit * 255; +} + class RGB { constructor(r, g, b) { this.r = r; @@ -12,6 +32,22 @@ class RGB { return new RGB(r,g,b); } + sRGBToLinear() { + return new RGB( + sRGBToLinear(this.r), + sRGBToLinear(this.g), + sRGBToLinear(this.b) + ); + } + + linearTosRGB() { + return new RGB( + linearTosRGB(this.r), + linearTosRGB(this.g), + linearTosRGB(this.b) + ); + } + cap() { if (this.r < 0) { this.r = 0; @@ -321,7 +357,7 @@ let palette256 = [ 0xf6e46f, 0xfffa84, 0xffff99, -].map((hex) => RGB.fromHex(hex)); +].map((hex) => RGB.fromHex(hex).sRGBToLinear()); function decimate(input, palette, n) { // to brute-force, the possible palettes are: @@ -499,7 +535,7 @@ function convert(source, sink) { (line[x * 4 + 0] + line[x * 4 + 4]) / 2, (line[x * 4 + 1] + line[x * 4 + 5]) / 2, (line[x * 4 + 2] + line[x * 4 + 6]) / 2 - ); + ).sRGBToLinear(); if (nextError) { rgb.r += nextError.red[i]; rgb.g += nextError.green[i]; @@ -513,7 +549,7 @@ function convert(source, sink) { nextError = error; for (let x = 0; x < width; x++) { - let rgb = palette[output[x >> 1]]; + let rgb = palette[output[x >> 1]].linearTosRGB(); line[x * 4 + 0] = rgb.r; line[x * 4 + 1] = rgb.g; line[x * 4 + 2] = rgb.b;