@opendisplay/epaper-dithering 2.1.4 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -126
- package/dist/index.cjs +406 -128
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -16
- package/dist/index.d.ts +24 -16
- package/dist/index.js +400 -129
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -12,6 +12,159 @@ var DitherMode = /* @__PURE__ */ ((DitherMode2) => {
|
|
|
12
12
|
return DitherMode2;
|
|
13
13
|
})(DitherMode || {});
|
|
14
14
|
|
|
15
|
+
// src/color_space.ts
|
|
16
|
+
var SRGB_TO_LINEAR_LUT = (() => {
|
|
17
|
+
const lut = new Float64Array(256);
|
|
18
|
+
for (let i = 0; i < 256; i++) {
|
|
19
|
+
const s = i / 255;
|
|
20
|
+
lut[i] = s <= 0.04045 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
|
|
21
|
+
}
|
|
22
|
+
return lut;
|
|
23
|
+
})();
|
|
24
|
+
|
|
25
|
+
// src/color_space_lab.ts
|
|
26
|
+
var M00 = 0.4124564;
|
|
27
|
+
var M01 = 0.3575761;
|
|
28
|
+
var M02 = 0.1804375;
|
|
29
|
+
var M10 = 0.2126729;
|
|
30
|
+
var M11 = 0.7151522;
|
|
31
|
+
var M12 = 0.072175;
|
|
32
|
+
var M20 = 0.0193339;
|
|
33
|
+
var M21 = 0.119192;
|
|
34
|
+
var M22 = 0.9503041;
|
|
35
|
+
var XN = 0.95047;
|
|
36
|
+
var YN = 1;
|
|
37
|
+
var ZN = 1.08883;
|
|
38
|
+
var EPSILON = 216 / 24389;
|
|
39
|
+
var KAPPA = 24389 / 27;
|
|
40
|
+
var WL2 = 0.25;
|
|
41
|
+
var WC2 = 1;
|
|
42
|
+
var WH2 = 4;
|
|
43
|
+
function labF(t) {
|
|
44
|
+
return t > EPSILON ? Math.cbrt(t) : (KAPPA * t + 16) / 116;
|
|
45
|
+
}
|
|
46
|
+
function rgbToLabScalar(r, g, b) {
|
|
47
|
+
const x = M00 * r + M01 * g + M02 * b;
|
|
48
|
+
const y = M10 * r + M11 * g + M12 * b;
|
|
49
|
+
const z = M20 * r + M21 * g + M22 * b;
|
|
50
|
+
const fx = labF(x / XN);
|
|
51
|
+
const fy = labF(y / YN);
|
|
52
|
+
const fz = labF(z / ZN);
|
|
53
|
+
return [116 * fy - 16, 500 * (fx - fy), 200 * (fy - fz)];
|
|
54
|
+
}
|
|
55
|
+
function precomputePaletteLab(paletteLinear) {
|
|
56
|
+
const n = paletteLinear.length;
|
|
57
|
+
const L = new Float64Array(n);
|
|
58
|
+
const a = new Float64Array(n);
|
|
59
|
+
const b = new Float64Array(n);
|
|
60
|
+
const C = new Float64Array(n);
|
|
61
|
+
for (let i = 0; i < n; i++) {
|
|
62
|
+
const [pl, pa, pb] = rgbToLabScalar(paletteLinear[i][0], paletteLinear[i][1], paletteLinear[i][2]);
|
|
63
|
+
L[i] = pl;
|
|
64
|
+
a[i] = pa;
|
|
65
|
+
b[i] = pb;
|
|
66
|
+
C[i] = Math.sqrt(pa * pa + pb * pb);
|
|
67
|
+
}
|
|
68
|
+
return { L, a, b, C };
|
|
69
|
+
}
|
|
70
|
+
function matchPixelLch(r, g, b, paletteL, paletteA, paletteB, paletteC) {
|
|
71
|
+
const [pL, pa, pb] = rgbToLabScalar(r, g, b);
|
|
72
|
+
const pC = Math.sqrt(pa * pa + pb * pb);
|
|
73
|
+
let bestIdx = 0;
|
|
74
|
+
let bestDist = Infinity;
|
|
75
|
+
const n = paletteL.length;
|
|
76
|
+
for (let i = 0; i < n; i++) {
|
|
77
|
+
const dL = pL - paletteL[i];
|
|
78
|
+
const da = pa - paletteA[i];
|
|
79
|
+
const db = pb - paletteB[i];
|
|
80
|
+
const dC = pC - paletteC[i];
|
|
81
|
+
let dHsq = da * da + db * db - dC * dC;
|
|
82
|
+
if (dHsq < 0) dHsq = 0;
|
|
83
|
+
const dist = WL2 * dL * dL + WC2 * dC * dC + WH2 * dHsq;
|
|
84
|
+
if (dist < bestDist) {
|
|
85
|
+
bestDist = dist;
|
|
86
|
+
bestIdx = i;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return bestIdx;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/tone_map.ts
|
|
93
|
+
var LUM_R = 0.2126729;
|
|
94
|
+
var LUM_G = 0.7151522;
|
|
95
|
+
var LUM_B = 0.072175;
|
|
96
|
+
function compressDynamicRange(pixels, width, height, paletteLinear, strength = 1) {
|
|
97
|
+
if (strength <= 0) return;
|
|
98
|
+
const [br, bg, bb] = paletteLinear[0];
|
|
99
|
+
const [wr, wg, wb] = paletteLinear[1];
|
|
100
|
+
const blackY = LUM_R * br + LUM_G * bg + LUM_B * bb;
|
|
101
|
+
const whiteY = LUM_R * wr + LUM_G * wg + LUM_B * wb;
|
|
102
|
+
const displayRange = whiteY - blackY;
|
|
103
|
+
if (displayRange <= 0) return;
|
|
104
|
+
const n = width * height;
|
|
105
|
+
const blackLevel = blackY * strength;
|
|
106
|
+
for (let i = 0; i < n; i++) {
|
|
107
|
+
const base = i * 3;
|
|
108
|
+
const r = pixels[base];
|
|
109
|
+
const g = pixels[base + 1];
|
|
110
|
+
const b = pixels[base + 2];
|
|
111
|
+
const Y = LUM_R * r + LUM_G * g + LUM_B * b;
|
|
112
|
+
if (Y > 1e-6) {
|
|
113
|
+
const compressedY = blackY + Y * displayRange;
|
|
114
|
+
const targetY = strength < 1 ? Y + strength * (compressedY - Y) : compressedY;
|
|
115
|
+
const scale = targetY / Y;
|
|
116
|
+
pixels[base] = Math.max(0, Math.min(1, r * scale));
|
|
117
|
+
pixels[base + 1] = Math.max(0, Math.min(1, g * scale));
|
|
118
|
+
pixels[base + 2] = Math.max(0, Math.min(1, b * scale));
|
|
119
|
+
} else {
|
|
120
|
+
pixels[base] = blackLevel;
|
|
121
|
+
pixels[base + 1] = blackLevel;
|
|
122
|
+
pixels[base + 2] = blackLevel;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function autoCompressDynamicRange(pixels, width, height, paletteLinear) {
|
|
127
|
+
const [br, bg, bb] = paletteLinear[0];
|
|
128
|
+
const [wr, wg, wb] = paletteLinear[1];
|
|
129
|
+
const blackY = LUM_R * br + LUM_G * bg + LUM_B * bb;
|
|
130
|
+
const whiteY = LUM_R * wr + LUM_G * wg + LUM_B * wb;
|
|
131
|
+
const displayRange = whiteY - blackY;
|
|
132
|
+
if (displayRange <= 0) return;
|
|
133
|
+
const n = width * height;
|
|
134
|
+
const luminances = new Float32Array(n);
|
|
135
|
+
for (let i = 0; i < n; i++) {
|
|
136
|
+
const base = i * 3;
|
|
137
|
+
luminances[i] = LUM_R * pixels[base] + LUM_G * pixels[base + 1] + LUM_B * pixels[base + 2];
|
|
138
|
+
}
|
|
139
|
+
luminances.sort();
|
|
140
|
+
const pLow = luminances[Math.floor(n * 0.02)];
|
|
141
|
+
const pHigh = luminances[Math.floor(n * 0.98)];
|
|
142
|
+
const imageRange = pHigh - pLow;
|
|
143
|
+
if (imageRange < 1e-6) {
|
|
144
|
+
compressDynamicRange(pixels, width, height, paletteLinear, 1);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
for (let i = 0; i < n; i++) {
|
|
148
|
+
const base = i * 3;
|
|
149
|
+
const r = pixels[base];
|
|
150
|
+
const g = pixels[base + 1];
|
|
151
|
+
const b = pixels[base + 2];
|
|
152
|
+
const Y = LUM_R * r + LUM_G * g + LUM_B * b;
|
|
153
|
+
if (Y > 1e-6) {
|
|
154
|
+
const normalizedY = (Y - pLow) / imageRange;
|
|
155
|
+
const targetY = blackY + normalizedY * displayRange;
|
|
156
|
+
const scale = targetY / Y;
|
|
157
|
+
pixels[base] = Math.max(0, Math.min(1, r * scale));
|
|
158
|
+
pixels[base + 1] = Math.max(0, Math.min(1, g * scale));
|
|
159
|
+
pixels[base + 2] = Math.max(0, Math.min(1, b * scale));
|
|
160
|
+
} else {
|
|
161
|
+
pixels[base] = blackY;
|
|
162
|
+
pixels[base + 1] = blackY;
|
|
163
|
+
pixels[base + 2] = blackY;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
15
168
|
// src/palettes.ts
|
|
16
169
|
var ColorScheme = /* @__PURE__ */ ((ColorScheme3) => {
|
|
17
170
|
ColorScheme3[ColorScheme3["MONO"] = 0] = "MONO";
|
|
@@ -20,6 +173,8 @@ var ColorScheme = /* @__PURE__ */ ((ColorScheme3) => {
|
|
|
20
173
|
ColorScheme3[ColorScheme3["BWRY"] = 3] = "BWRY";
|
|
21
174
|
ColorScheme3[ColorScheme3["BWGBRY"] = 4] = "BWGBRY";
|
|
22
175
|
ColorScheme3[ColorScheme3["GRAYSCALE_4"] = 5] = "GRAYSCALE_4";
|
|
176
|
+
ColorScheme3[ColorScheme3["GRAYSCALE_8"] = 6] = "GRAYSCALE_8";
|
|
177
|
+
ColorScheme3[ColorScheme3["GRAYSCALE_16"] = 7] = "GRAYSCALE_16";
|
|
23
178
|
return ColorScheme3;
|
|
24
179
|
})(ColorScheme || {});
|
|
25
180
|
var PALETTES = {
|
|
@@ -74,6 +229,40 @@ var PALETTES = {
|
|
|
74
229
|
white: { r: 255, g: 255, b: 255 }
|
|
75
230
|
},
|
|
76
231
|
accent: "black"
|
|
232
|
+
},
|
|
233
|
+
[6 /* GRAYSCALE_8 */]: {
|
|
234
|
+
colors: {
|
|
235
|
+
black: { r: 0, g: 0, b: 0 },
|
|
236
|
+
gray1: { r: 36, g: 36, b: 36 },
|
|
237
|
+
gray2: { r: 73, g: 73, b: 73 },
|
|
238
|
+
gray3: { r: 109, g: 109, b: 109 },
|
|
239
|
+
gray4: { r: 146, g: 146, b: 146 },
|
|
240
|
+
gray5: { r: 182, g: 182, b: 182 },
|
|
241
|
+
gray6: { r: 219, g: 219, b: 219 },
|
|
242
|
+
white: { r: 255, g: 255, b: 255 }
|
|
243
|
+
},
|
|
244
|
+
accent: "black"
|
|
245
|
+
},
|
|
246
|
+
[7 /* GRAYSCALE_16 */]: {
|
|
247
|
+
colors: {
|
|
248
|
+
black: { r: 0, g: 0, b: 0 },
|
|
249
|
+
gray1: { r: 17, g: 17, b: 17 },
|
|
250
|
+
gray2: { r: 34, g: 34, b: 34 },
|
|
251
|
+
gray3: { r: 51, g: 51, b: 51 },
|
|
252
|
+
gray4: { r: 68, g: 68, b: 68 },
|
|
253
|
+
gray5: { r: 85, g: 85, b: 85 },
|
|
254
|
+
gray6: { r: 102, g: 102, b: 102 },
|
|
255
|
+
gray7: { r: 119, g: 119, b: 119 },
|
|
256
|
+
gray8: { r: 136, g: 136, b: 136 },
|
|
257
|
+
gray9: { r: 153, g: 153, b: 153 },
|
|
258
|
+
gray10: { r: 170, g: 170, b: 170 },
|
|
259
|
+
gray11: { r: 187, g: 187, b: 187 },
|
|
260
|
+
gray12: { r: 204, g: 204, b: 204 },
|
|
261
|
+
gray13: { r: 221, g: 221, b: 221 },
|
|
262
|
+
gray14: { r: 238, g: 238, b: 238 },
|
|
263
|
+
white: { r: 255, g: 255, b: 255 }
|
|
264
|
+
},
|
|
265
|
+
accent: "black"
|
|
77
266
|
}
|
|
78
267
|
};
|
|
79
268
|
function getPalette(scheme) {
|
|
@@ -83,143 +272,230 @@ function getColorCount(scheme) {
|
|
|
83
272
|
return Object.keys(PALETTES[scheme].colors).length;
|
|
84
273
|
}
|
|
85
274
|
function fromValue(value) {
|
|
86
|
-
if (value < 0 || value >
|
|
275
|
+
if (value < 0 || value > 7) {
|
|
87
276
|
throw new Error(`Invalid color scheme value: ${value}`);
|
|
88
277
|
}
|
|
89
278
|
return value;
|
|
90
279
|
}
|
|
280
|
+
var SPECTRA_7_3_6COLOR = {
|
|
281
|
+
colors: {
|
|
282
|
+
black: { r: 26, g: 13, b: 35 },
|
|
283
|
+
white: { r: 185, g: 202, b: 205 },
|
|
284
|
+
yellow: { r: 202, g: 184, b: 0 },
|
|
285
|
+
red: { r: 121, g: 9, b: 0 },
|
|
286
|
+
blue: { r: 0, g: 69, b: 139 },
|
|
287
|
+
green: { r: 40, g: 82, b: 57 }
|
|
288
|
+
},
|
|
289
|
+
accent: "red"
|
|
290
|
+
};
|
|
291
|
+
var MONO_4_26 = {
|
|
292
|
+
colors: {
|
|
293
|
+
black: { r: 5, g: 5, b: 5 },
|
|
294
|
+
white: { r: 220, g: 220, b: 220 }
|
|
295
|
+
},
|
|
296
|
+
accent: "black"
|
|
297
|
+
};
|
|
298
|
+
var BWRY_4_2 = {
|
|
299
|
+
colors: {
|
|
300
|
+
black: { r: 5, g: 5, b: 5 },
|
|
301
|
+
white: { r: 200, g: 200, b: 200 },
|
|
302
|
+
yellow: { r: 200, g: 180, b: 0 },
|
|
303
|
+
red: { r: 120, g: 15, b: 5 }
|
|
304
|
+
},
|
|
305
|
+
accent: "red"
|
|
306
|
+
};
|
|
307
|
+
var SOLUM_BWR = {
|
|
308
|
+
colors: {
|
|
309
|
+
black: { r: 5, g: 5, b: 5 },
|
|
310
|
+
white: { r: 200, g: 200, b: 200 },
|
|
311
|
+
red: { r: 120, g: 15, b: 5 }
|
|
312
|
+
},
|
|
313
|
+
accent: "red"
|
|
314
|
+
};
|
|
315
|
+
var HANSHOW_BWR = {
|
|
316
|
+
colors: {
|
|
317
|
+
black: { r: 5, g: 5, b: 5 },
|
|
318
|
+
white: { r: 200, g: 200, b: 200 },
|
|
319
|
+
red: { r: 120, g: 15, b: 5 }
|
|
320
|
+
},
|
|
321
|
+
accent: "red"
|
|
322
|
+
};
|
|
323
|
+
var HANSHOW_BWY = {
|
|
324
|
+
colors: {
|
|
325
|
+
black: { r: 5, g: 5, b: 5 },
|
|
326
|
+
white: { r: 200, g: 200, b: 200 },
|
|
327
|
+
yellow: { r: 200, g: 180, b: 0 }
|
|
328
|
+
},
|
|
329
|
+
accent: "yellow"
|
|
330
|
+
};
|
|
331
|
+
var BWRY_3_97 = {
|
|
332
|
+
colors: {
|
|
333
|
+
black: { r: 10, g: 7, b: 14 },
|
|
334
|
+
white: { r: 173, g: 178, b: 174 },
|
|
335
|
+
yellow: { r: 172, g: 128, b: 0 },
|
|
336
|
+
red: { r: 85, g: 24, b: 14 }
|
|
337
|
+
},
|
|
338
|
+
accent: "red"
|
|
339
|
+
};
|
|
91
340
|
|
|
92
341
|
// src/algorithms.ts
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
342
|
+
var BAYER_4X4 = new Float32Array([
|
|
343
|
+
0,
|
|
344
|
+
8,
|
|
345
|
+
2,
|
|
346
|
+
10,
|
|
347
|
+
12,
|
|
348
|
+
4,
|
|
349
|
+
14,
|
|
350
|
+
6,
|
|
351
|
+
3,
|
|
352
|
+
11,
|
|
353
|
+
1,
|
|
354
|
+
9,
|
|
355
|
+
15,
|
|
356
|
+
7,
|
|
357
|
+
13,
|
|
358
|
+
5
|
|
359
|
+
].map((v) => v / 16 - 0.5));
|
|
360
|
+
function resolvePalette(scheme) {
|
|
361
|
+
return typeof scheme === "number" ? getPalette(scheme) : scheme;
|
|
96
362
|
}
|
|
97
|
-
function
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
363
|
+
function paletteToLinear(palette) {
|
|
364
|
+
const paletteRgb = Object.values(palette.colors);
|
|
365
|
+
const paletteLinear = paletteRgb.map((c) => [
|
|
366
|
+
SRGB_TO_LINEAR_LUT[c.r],
|
|
367
|
+
SRGB_TO_LINEAR_LUT[c.g],
|
|
368
|
+
SRGB_TO_LINEAR_LUT[c.b]
|
|
369
|
+
]);
|
|
370
|
+
return { paletteRgb, paletteLinear };
|
|
371
|
+
}
|
|
372
|
+
function buildLinearBuffer(image) {
|
|
373
|
+
const n = image.width * image.height;
|
|
374
|
+
const pixels = new Float32Array(n * 3);
|
|
375
|
+
const { data } = image;
|
|
376
|
+
for (let i = 0; i < n; i++) {
|
|
377
|
+
const base4 = i * 4;
|
|
378
|
+
const base3 = i * 3;
|
|
379
|
+
const alpha = data[base4 + 3] / 255;
|
|
380
|
+
const inv = 1 - alpha;
|
|
381
|
+
pixels[base3] = SRGB_TO_LINEAR_LUT[data[base4]] * alpha + inv;
|
|
382
|
+
pixels[base3 + 1] = SRGB_TO_LINEAR_LUT[data[base4 + 1]] * alpha + inv;
|
|
383
|
+
pixels[base3 + 2] = SRGB_TO_LINEAR_LUT[data[base4 + 2]] * alpha + inv;
|
|
107
384
|
}
|
|
108
|
-
return
|
|
385
|
+
return pixels;
|
|
109
386
|
}
|
|
110
|
-
function errorDiffusionDither(image,
|
|
387
|
+
function errorDiffusionDither(image, scheme, kernel, serpentine) {
|
|
111
388
|
const { width, height } = image;
|
|
112
|
-
const palette =
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
pixels
|
|
389
|
+
const palette = resolvePalette(scheme);
|
|
390
|
+
const { paletteRgb, paletteLinear } = paletteToLinear(palette);
|
|
391
|
+
const numColors = paletteRgb.length;
|
|
392
|
+
const pixels = buildLinearBuffer(image);
|
|
393
|
+
if (typeof scheme !== "number") {
|
|
394
|
+
autoCompressDynamicRange(pixels, width, height, paletteLinear);
|
|
395
|
+
}
|
|
396
|
+
const { L: palL, a: palA, b: palB, C: palC } = precomputePaletteLab(paletteLinear);
|
|
397
|
+
const palR = new Float64Array(numColors);
|
|
398
|
+
const palG = new Float64Array(numColors);
|
|
399
|
+
const palBl = new Float64Array(numColors);
|
|
400
|
+
for (let i = 0; i < numColors; i++) {
|
|
401
|
+
palR[i] = paletteLinear[i][0];
|
|
402
|
+
palG[i] = paletteLinear[i][1];
|
|
403
|
+
palBl[i] = paletteLinear[i][2];
|
|
118
404
|
}
|
|
119
405
|
const indices = new Uint8Array(width * height);
|
|
120
406
|
for (let y = 0; y < height; y++) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
const
|
|
131
|
-
indices[
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
for (
|
|
136
|
-
const
|
|
137
|
-
const
|
|
407
|
+
const leftToRight = !serpentine || y % 2 === 0;
|
|
408
|
+
const xStart = leftToRight ? 0 : width - 1;
|
|
409
|
+
const xEnd = leftToRight ? width : -1;
|
|
410
|
+
const xStep = leftToRight ? 1 : -1;
|
|
411
|
+
for (let x = xStart; x !== xEnd; x += xStep) {
|
|
412
|
+
const pixIdx = (y * width + x) * 3;
|
|
413
|
+
const r = Math.max(0, Math.min(1, pixels[pixIdx]));
|
|
414
|
+
const g = Math.max(0, Math.min(1, pixels[pixIdx + 1]));
|
|
415
|
+
const b = Math.max(0, Math.min(1, pixels[pixIdx + 2]));
|
|
416
|
+
const newIdx = matchPixelLch(r, g, b, palL, palA, palB, palC);
|
|
417
|
+
indices[y * width + x] = newIdx;
|
|
418
|
+
const errR = r - palR[newIdx];
|
|
419
|
+
const errG = g - palG[newIdx];
|
|
420
|
+
const errB = b - palBl[newIdx];
|
|
421
|
+
for (let k = 0; k < kernel.length; k++) {
|
|
422
|
+
const kEntry = kernel[k];
|
|
423
|
+
const nx = x + (leftToRight ? kEntry.dx : -kEntry.dx);
|
|
424
|
+
const ny = y + kEntry.dy;
|
|
138
425
|
if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
|
|
139
|
-
const
|
|
140
|
-
pixels[
|
|
141
|
-
pixels[
|
|
142
|
-
pixels[
|
|
426
|
+
const nIdx = (ny * width + nx) * 3;
|
|
427
|
+
pixels[nIdx] += errR * kEntry.weight;
|
|
428
|
+
pixels[nIdx + 1] += errG * kEntry.weight;
|
|
429
|
+
pixels[nIdx + 2] += errB * kEntry.weight;
|
|
143
430
|
}
|
|
144
431
|
}
|
|
145
432
|
}
|
|
146
433
|
}
|
|
147
|
-
return { width, height, indices, palette };
|
|
434
|
+
return { width, height, indices, palette: paletteRgb };
|
|
148
435
|
}
|
|
149
|
-
function directPaletteMap(image,
|
|
436
|
+
function directPaletteMap(image, scheme) {
|
|
150
437
|
const { width, height } = image;
|
|
151
|
-
const palette =
|
|
438
|
+
const palette = resolvePalette(scheme);
|
|
439
|
+
const { paletteRgb, paletteLinear } = paletteToLinear(palette);
|
|
440
|
+
const pixels = buildLinearBuffer(image);
|
|
441
|
+
if (typeof scheme !== "number") {
|
|
442
|
+
autoCompressDynamicRange(pixels, width, height, paletteLinear);
|
|
443
|
+
}
|
|
444
|
+
const { L: palL, a: palA, b: palB, C: palC } = precomputePaletteLab(paletteLinear);
|
|
152
445
|
const indices = new Uint8Array(width * height);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
indices[i] =
|
|
446
|
+
const n = width * height;
|
|
447
|
+
for (let i = 0; i < n; i++) {
|
|
448
|
+
const base = i * 3;
|
|
449
|
+
const r = Math.max(0, Math.min(1, pixels[base]));
|
|
450
|
+
const g = Math.max(0, Math.min(1, pixels[base + 1]));
|
|
451
|
+
const b = Math.max(0, Math.min(1, pixels[base + 2]));
|
|
452
|
+
indices[i] = matchPixelLch(r, g, b, palL, palA, palB, palC);
|
|
160
453
|
}
|
|
161
|
-
return { width, height, indices, palette };
|
|
454
|
+
return { width, height, indices, palette: paletteRgb };
|
|
162
455
|
}
|
|
163
|
-
function orderedDither(image,
|
|
164
|
-
const bayerMatrix = new Uint8Array([
|
|
165
|
-
0,
|
|
166
|
-
8,
|
|
167
|
-
2,
|
|
168
|
-
10,
|
|
169
|
-
12,
|
|
170
|
-
4,
|
|
171
|
-
14,
|
|
172
|
-
6,
|
|
173
|
-
3,
|
|
174
|
-
11,
|
|
175
|
-
1,
|
|
176
|
-
9,
|
|
177
|
-
15,
|
|
178
|
-
7,
|
|
179
|
-
13,
|
|
180
|
-
5
|
|
181
|
-
]).map((v) => v * 16);
|
|
456
|
+
function orderedDither(image, scheme) {
|
|
182
457
|
const { width, height } = image;
|
|
183
|
-
const palette =
|
|
458
|
+
const palette = resolvePalette(scheme);
|
|
459
|
+
const { paletteRgb, paletteLinear } = paletteToLinear(palette);
|
|
460
|
+
const pixels = buildLinearBuffer(image);
|
|
461
|
+
if (typeof scheme !== "number") {
|
|
462
|
+
autoCompressDynamicRange(pixels, width, height, paletteLinear);
|
|
463
|
+
}
|
|
464
|
+
const { L: palL, a: palA, b: palB, C: palC } = precomputePaletteLab(paletteLinear);
|
|
184
465
|
const indices = new Uint8Array(width * height);
|
|
185
466
|
for (let y = 0; y < height; y++) {
|
|
186
467
|
for (let x = 0; x < width; x++) {
|
|
187
|
-
const
|
|
188
|
-
const
|
|
189
|
-
const
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
b: Math.max(0, Math.min(255, Math.trunc(image.data[dataIdx + 2] + threshold)))
|
|
194
|
-
};
|
|
195
|
-
indices[idx] = findClosestPaletteColor(rgb, palette);
|
|
468
|
+
const base = (y * width + x) * 3;
|
|
469
|
+
const threshold = BAYER_4X4[y % 4 * 4 + x % 4];
|
|
470
|
+
const r = Math.max(0, Math.min(1, pixels[base] + threshold));
|
|
471
|
+
const g = Math.max(0, Math.min(1, pixels[base + 1] + threshold));
|
|
472
|
+
const b = Math.max(0, Math.min(1, pixels[base + 2] + threshold));
|
|
473
|
+
indices[y * width + x] = matchPixelLch(r, g, b, palL, palA, palB, palC);
|
|
196
474
|
}
|
|
197
475
|
}
|
|
198
|
-
return { width, height, indices, palette };
|
|
476
|
+
return { width, height, indices, palette: paletteRgb };
|
|
199
477
|
}
|
|
200
|
-
function
|
|
201
|
-
|
|
202
|
-
{ dx: 1, dy: 0, weight: 32 / 200 },
|
|
203
|
-
{ dx: 2, dy: 0, weight: 12 / 200 },
|
|
204
|
-
{ dx: -2, dy: 1, weight: 5 / 200 },
|
|
205
|
-
{ dx: -1, dy: 1, weight: 12 / 200 },
|
|
206
|
-
{ dx: 0, dy: 1, weight: 26 / 200 },
|
|
207
|
-
{ dx: 1, dy: 1, weight: 12 / 200 },
|
|
208
|
-
{ dx: 2, dy: 1, weight: 5 / 200 }
|
|
209
|
-
];
|
|
210
|
-
return errorDiffusionDither(image, colorScheme, kernel);
|
|
211
|
-
}
|
|
212
|
-
function floydSteinbergDither(image, colorScheme) {
|
|
213
|
-
const kernel = [
|
|
478
|
+
function floydSteinbergDither(image, scheme, serpentine = true) {
|
|
479
|
+
return errorDiffusionDither(image, scheme, [
|
|
214
480
|
{ dx: 1, dy: 0, weight: 7 / 16 },
|
|
215
481
|
{ dx: -1, dy: 1, weight: 3 / 16 },
|
|
216
482
|
{ dx: 0, dy: 1, weight: 5 / 16 },
|
|
217
483
|
{ dx: 1, dy: 1, weight: 1 / 16 }
|
|
218
|
-
];
|
|
219
|
-
|
|
484
|
+
], serpentine);
|
|
485
|
+
}
|
|
486
|
+
function burkesDither(image, scheme, serpentine = true) {
|
|
487
|
+
return errorDiffusionDither(image, scheme, [
|
|
488
|
+
{ dx: 1, dy: 0, weight: 8 / 32 },
|
|
489
|
+
{ dx: 2, dy: 0, weight: 4 / 32 },
|
|
490
|
+
{ dx: -2, dy: 1, weight: 2 / 32 },
|
|
491
|
+
{ dx: -1, dy: 1, weight: 4 / 32 },
|
|
492
|
+
{ dx: 0, dy: 1, weight: 8 / 32 },
|
|
493
|
+
{ dx: 1, dy: 1, weight: 4 / 32 },
|
|
494
|
+
{ dx: 2, dy: 1, weight: 2 / 32 }
|
|
495
|
+
], serpentine);
|
|
220
496
|
}
|
|
221
|
-
function sierraDither(image,
|
|
222
|
-
|
|
497
|
+
function sierraDither(image, scheme, serpentine = true) {
|
|
498
|
+
return errorDiffusionDither(image, scheme, [
|
|
223
499
|
{ dx: 1, dy: 0, weight: 5 / 32 },
|
|
224
500
|
{ dx: 2, dy: 0, weight: 3 / 32 },
|
|
225
501
|
{ dx: -2, dy: 1, weight: 2 / 32 },
|
|
@@ -230,30 +506,27 @@ function sierraDither(image, colorScheme) {
|
|
|
230
506
|
{ dx: -1, dy: 2, weight: 2 / 32 },
|
|
231
507
|
{ dx: 0, dy: 2, weight: 3 / 32 },
|
|
232
508
|
{ dx: 1, dy: 2, weight: 2 / 32 }
|
|
233
|
-
];
|
|
234
|
-
return errorDiffusionDither(image, colorScheme, kernel);
|
|
509
|
+
], serpentine);
|
|
235
510
|
}
|
|
236
|
-
function sierraLiteDither(image,
|
|
237
|
-
|
|
511
|
+
function sierraLiteDither(image, scheme, serpentine = true) {
|
|
512
|
+
return errorDiffusionDither(image, scheme, [
|
|
238
513
|
{ dx: 1, dy: 0, weight: 2 / 4 },
|
|
239
514
|
{ dx: -1, dy: 1, weight: 1 / 4 },
|
|
240
515
|
{ dx: 0, dy: 1, weight: 1 / 4 }
|
|
241
|
-
];
|
|
242
|
-
return errorDiffusionDither(image, colorScheme, kernel);
|
|
516
|
+
], serpentine);
|
|
243
517
|
}
|
|
244
|
-
function atkinsonDither(image,
|
|
245
|
-
|
|
518
|
+
function atkinsonDither(image, scheme, serpentine = true) {
|
|
519
|
+
return errorDiffusionDither(image, scheme, [
|
|
246
520
|
{ dx: 1, dy: 0, weight: 1 / 8 },
|
|
247
521
|
{ dx: 2, dy: 0, weight: 1 / 8 },
|
|
248
522
|
{ dx: -1, dy: 1, weight: 1 / 8 },
|
|
249
523
|
{ dx: 0, dy: 1, weight: 1 / 8 },
|
|
250
524
|
{ dx: 1, dy: 1, weight: 1 / 8 },
|
|
251
525
|
{ dx: 0, dy: 2, weight: 1 / 8 }
|
|
252
|
-
];
|
|
253
|
-
return errorDiffusionDither(image, colorScheme, kernel);
|
|
526
|
+
], serpentine);
|
|
254
527
|
}
|
|
255
|
-
function stuckiDither(image,
|
|
256
|
-
|
|
528
|
+
function stuckiDither(image, scheme, serpentine = true) {
|
|
529
|
+
return errorDiffusionDither(image, scheme, [
|
|
257
530
|
{ dx: 1, dy: 0, weight: 8 / 42 },
|
|
258
531
|
{ dx: 2, dy: 0, weight: 4 / 42 },
|
|
259
532
|
{ dx: -2, dy: 1, weight: 2 / 42 },
|
|
@@ -266,11 +539,10 @@ function stuckiDither(image, colorScheme) {
|
|
|
266
539
|
{ dx: 0, dy: 2, weight: 4 / 42 },
|
|
267
540
|
{ dx: 1, dy: 2, weight: 2 / 42 },
|
|
268
541
|
{ dx: 2, dy: 2, weight: 1 / 42 }
|
|
269
|
-
];
|
|
270
|
-
return errorDiffusionDither(image, colorScheme, kernel);
|
|
542
|
+
], serpentine);
|
|
271
543
|
}
|
|
272
|
-
function jarvisJudiceNinkeDither(image,
|
|
273
|
-
|
|
544
|
+
function jarvisJudiceNinkeDither(image, scheme, serpentine = true) {
|
|
545
|
+
return errorDiffusionDither(image, scheme, [
|
|
274
546
|
{ dx: 1, dy: 0, weight: 7 / 48 },
|
|
275
547
|
{ dx: 2, dy: 0, weight: 5 / 48 },
|
|
276
548
|
{ dx: -2, dy: 1, weight: 3 / 48 },
|
|
@@ -283,38 +555,37 @@ function jarvisJudiceNinkeDither(image, colorScheme) {
|
|
|
283
555
|
{ dx: 0, dy: 2, weight: 5 / 48 },
|
|
284
556
|
{ dx: 1, dy: 2, weight: 3 / 48 },
|
|
285
557
|
{ dx: 2, dy: 2, weight: 1 / 48 }
|
|
286
|
-
];
|
|
287
|
-
return errorDiffusionDither(image, colorScheme, kernel);
|
|
558
|
+
], serpentine);
|
|
288
559
|
}
|
|
289
560
|
|
|
290
561
|
// src/core.ts
|
|
291
|
-
function ditherImage(image, colorScheme, mode = 1 /* BURKES
|
|
562
|
+
function ditherImage(image, colorScheme, mode = 1 /* BURKES */, serpentine = true) {
|
|
292
563
|
switch (mode) {
|
|
293
564
|
case 0 /* NONE */:
|
|
294
565
|
return directPaletteMap(image, colorScheme);
|
|
295
566
|
case 2 /* ORDERED */:
|
|
296
567
|
return orderedDither(image, colorScheme);
|
|
297
568
|
case 3 /* FLOYD_STEINBERG */:
|
|
298
|
-
return floydSteinbergDither(image, colorScheme);
|
|
569
|
+
return floydSteinbergDither(image, colorScheme, serpentine);
|
|
299
570
|
case 4 /* ATKINSON */:
|
|
300
|
-
return atkinsonDither(image, colorScheme);
|
|
571
|
+
return atkinsonDither(image, colorScheme, serpentine);
|
|
301
572
|
case 5 /* STUCKI */:
|
|
302
|
-
return stuckiDither(image, colorScheme);
|
|
573
|
+
return stuckiDither(image, colorScheme, serpentine);
|
|
303
574
|
case 6 /* SIERRA */:
|
|
304
|
-
return sierraDither(image, colorScheme);
|
|
575
|
+
return sierraDither(image, colorScheme, serpentine);
|
|
305
576
|
case 7 /* SIERRA_LITE */:
|
|
306
|
-
return sierraLiteDither(image, colorScheme);
|
|
577
|
+
return sierraLiteDither(image, colorScheme, serpentine);
|
|
307
578
|
case 8 /* JARVIS_JUDICE_NINKE */:
|
|
308
|
-
return jarvisJudiceNinkeDither(image, colorScheme);
|
|
579
|
+
return jarvisJudiceNinkeDither(image, colorScheme, serpentine);
|
|
309
580
|
case 1 /* BURKES */:
|
|
310
581
|
default:
|
|
311
|
-
return burkesDither(image, colorScheme);
|
|
582
|
+
return burkesDither(image, colorScheme, serpentine);
|
|
312
583
|
}
|
|
313
584
|
}
|
|
314
585
|
|
|
315
586
|
// src/index.ts
|
|
316
587
|
var VERSION = "0.1.0";
|
|
317
588
|
|
|
318
|
-
export { ColorScheme, DitherMode, VERSION, ditherImage, fromValue, getColorCount, getPalette };
|
|
589
|
+
export { BWRY_3_97, BWRY_4_2, ColorScheme, DitherMode, HANSHOW_BWR, HANSHOW_BWY, MONO_4_26, SOLUM_BWR, SPECTRA_7_3_6COLOR, VERSION, ditherImage, fromValue, getColorCount, getPalette };
|
|
319
590
|
//# sourceMappingURL=index.js.map
|
|
320
591
|
//# sourceMappingURL=index.js.map
|