diff --git a/index.html b/index.html
index 81f32b6..16a14e3 100644
--- a/index.html
+++ b/index.html
@@ -53,7 +53,7 @@ const palette = [
}
})();
-function distance(r1,g1,b1,r2,b2,g2) {
+function distance(r1,g1,b1,r2,g2,b2) {
const dr = r1 - r2;
const dg = g1 - g2;
const db = b1 - b2;
@@ -68,31 +68,63 @@ function quantize(imagedata) {
hash[rgb] = i;
}
-// Nearest Neighbor
- for (let i = 0; i < imagedata.data.length; i += 4) {
- const r = imagedata.data[i];
- const g = imagedata.data[i+1];
- const b = imagedata.data[i+2];
+ // Floyd-Steinberg
+ function offset(x, y) {
+ return 4 * (x + y * imagedata.width);
+ }
- const rgb = (r << 16) | (g << 8) | b;
- let index = hash[rgb];
- if (index === undefined) {
- let dist;
- for (let p = 0; p < palette.length; ++p) {
- const entry = palette[p];
- const d = distance(r,g,b, entry[0], entry[1], entry[2]);
- if (dist === undefined || d < dist) {
- dist = d;
- index = p;
+ function err(x, y, er, eg, eb) {
+ if (x < 0 || x >= imagedata.width || y < 0 || y >= imagedata.height)
+ return;
+ const i = offset(x, y);
+ const data = imagedata.data;
+ data[i + 0] += er;
+ data[i + 1] += eg;
+ data[i + 2] += eb;
+ }
+
+ const data = imagedata.data;
+ for (let y = 0; y < imagedata.height; ++y) {
+ for (let x = 0; x < imagedata.width; ++x) {
+ const i = offset(x, y);
+
+ const r = data[i];
+ const g = data[i+1];
+ const b = data[i+2];
+
+ // Find closest in palette.
+ const rgb = (r << 16) | (g << 8) | b;
+ let index = hash[rgb];
+ if (index === undefined) {
+ let dist;
+ for (let p = 0; p < palette.length; ++p) {
+ const entry = palette[p];
+ const d = distance(r,g,b, entry[0], entry[1], entry[2]);
+ if (dist === undefined || d < dist) {
+ dist = d;
+ index = p;
+ }
}
+ hash[rgb] = index;
}
- hash[rgb] = index;
- }
+ const pi = palette[index];
- const pi = palette[index];
- imagedata.data[i] = pi[0];
- imagedata.data[i+1] = pi[1];
- imagedata.data[i+2] = pi[2];
+ // Calculate error
+ const err_r = data[i] - pi[0];
+ const err_g = data[i+1] - pi[1];
+ const err_b = data[i+2] - pi[2];
+
+ // Update pixel
+ data[i] = pi[0];
+ data[i+1] = pi[1];
+ data[i+2] = pi[2];
+
+ // Distribute error
+ err(x + 1, y, err_r * 7/16, err_g * 7/16, err_b * 7/16);
+ err(x - 1, y + 1, err_r * 3/16, err_g * 3/16, err_b * 3/16);
+ err(x, y + 1, err_r * 5/16, err_g * 5/16, err_b * 5/16);
+ err(x + 1, y + 1, err_r * 1/16, err_g * 1/16, err_b * 1/16);
+ }
}
}