@thi.ng/pixel-dither 1.1.88 → 1.1.90
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/CHANGELOG.md +1 -1
- package/api.js +0 -1
- package/atkinson.js +8 -13
- package/burkes.js +8 -12
- package/diffusion.js +21 -25
- package/dither.js +36 -43
- package/floyd-steinberg.js +8 -13
- package/jarvis.js +8 -13
- package/ordered.js +43 -81
- package/package.json +9 -7
- package/sierra2.js +8 -12
- package/stucki.js +8 -12
- package/threshold.js +8 -8
package/CHANGELOG.md
CHANGED
package/api.js
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/atkinson.js
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export const ATKINSON = {
|
|
10
|
-
ox: [1, 2, -1, 0, 1, 0],
|
|
11
|
-
oy: [0, 0, 1, 1, 1, 2],
|
|
12
|
-
weights: [1, 1, 1, 1, 1, 1],
|
|
13
|
-
shift: 3,
|
|
1
|
+
const ATKINSON = {
|
|
2
|
+
ox: [1, 2, -1, 0, 1, 0],
|
|
3
|
+
oy: [0, 0, 1, 1, 1, 2],
|
|
4
|
+
weights: [1, 1, 1, 1, 1, 1],
|
|
5
|
+
shift: 3
|
|
6
|
+
};
|
|
7
|
+
export {
|
|
8
|
+
ATKINSON
|
|
14
9
|
};
|
package/burkes.js
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
ox: [1, 2, -2, -1, 0, 1, 2],
|
|
10
|
-
oy: [0, 0, 1, 1, 1, 1, 1],
|
|
11
|
-
weights: [8, 4, 2, 4, 8, 4, 2],
|
|
12
|
-
shift: 5,
|
|
1
|
+
const BURKES = {
|
|
2
|
+
ox: [1, 2, -2, -1, 0, 1, 2],
|
|
3
|
+
oy: [0, 0, 1, 1, 1, 1, 1],
|
|
4
|
+
weights: [8, 4, 2, 4, 8, 4, 2],
|
|
5
|
+
shift: 5
|
|
6
|
+
};
|
|
7
|
+
export {
|
|
8
|
+
BURKES
|
|
13
9
|
};
|
package/diffusion.js
CHANGED
|
@@ -1,28 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
weights: [1],
|
|
8
|
-
shift: 0,
|
|
9
|
-
// x2: width - 1,
|
|
1
|
+
const DIFFUSION_ROW = {
|
|
2
|
+
ox: [1],
|
|
3
|
+
oy: [0],
|
|
4
|
+
weights: [1],
|
|
5
|
+
shift: 0
|
|
6
|
+
// x2: width - 1,
|
|
10
7
|
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
oy: [1],
|
|
17
|
-
weights: [1],
|
|
18
|
-
shift: 0,
|
|
8
|
+
const DIFFUSION_COLUMN = {
|
|
9
|
+
ox: [0],
|
|
10
|
+
oy: [1],
|
|
11
|
+
weights: [1],
|
|
12
|
+
shift: 0
|
|
19
13
|
};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
14
|
+
const DIFFUSION_2D = {
|
|
15
|
+
ox: [1, 0],
|
|
16
|
+
oy: [0, 1],
|
|
17
|
+
weights: [1, 1],
|
|
18
|
+
shift: 1
|
|
19
|
+
};
|
|
20
|
+
export {
|
|
21
|
+
DIFFUSION_2D,
|
|
22
|
+
DIFFUSION_COLUMN,
|
|
23
|
+
DIFFUSION_ROW
|
|
28
24
|
};
|
package/dither.js
CHANGED
|
@@ -1,47 +1,40 @@
|
|
|
1
1
|
import { range } from "@thi.ng/pixel/range";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
err = (data[i] - p) * bleed;
|
|
31
|
-
data[i] = p;
|
|
32
|
-
if (!err)
|
|
33
|
-
continue;
|
|
34
|
-
for (let j = ox.length; j-- > 0;) {
|
|
35
|
-
const xx = x + ox[j];
|
|
36
|
-
const yy = y + oy[j];
|
|
37
|
-
if (yy >= 0 && yy < height && xx >= 0 && xx < width) {
|
|
38
|
-
data[yy * width + xx] += (err * weights[j]) >> shift;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
2
|
+
const ditherWith = (kernel, img, opts) => {
|
|
3
|
+
const { channels, bleed, threshold } = {
|
|
4
|
+
bleed: 1,
|
|
5
|
+
threshold: 0.5,
|
|
6
|
+
...opts
|
|
7
|
+
};
|
|
8
|
+
const { format, width, height } = img;
|
|
9
|
+
const { ox, oy, weights, shift } = kernel;
|
|
10
|
+
let p, err;
|
|
11
|
+
for (let cid of channels || range(format.channels.length)) {
|
|
12
|
+
const cimg = img.getChannel(cid);
|
|
13
|
+
const chan = format.channels[cid];
|
|
14
|
+
const $thresh = chan.num * threshold;
|
|
15
|
+
const $max = chan.mask0;
|
|
16
|
+
const data = new Int32Array(cimg.data);
|
|
17
|
+
for (let y = 0; y < height; y++) {
|
|
18
|
+
for (let x = 0, i = x + y * width; x < width; x++, i++) {
|
|
19
|
+
p = data[i] < $thresh ? 0 : $max;
|
|
20
|
+
err = (data[i] - p) * bleed;
|
|
21
|
+
data[i] = p;
|
|
22
|
+
if (!err)
|
|
23
|
+
continue;
|
|
24
|
+
for (let j = ox.length; j-- > 0; ) {
|
|
25
|
+
const xx = x + ox[j];
|
|
26
|
+
const yy = y + oy[j];
|
|
27
|
+
if (yy >= 0 && yy < height && xx >= 0 && xx < width) {
|
|
28
|
+
data[yy * width + xx] += err * weights[j] >> shift;
|
|
29
|
+
}
|
|
42
30
|
}
|
|
43
|
-
|
|
44
|
-
img.setChannel(cid, cimg);
|
|
31
|
+
}
|
|
45
32
|
}
|
|
46
|
-
|
|
33
|
+
cimg.data.set(data);
|
|
34
|
+
img.setChannel(cid, cimg);
|
|
35
|
+
}
|
|
36
|
+
return img;
|
|
37
|
+
};
|
|
38
|
+
export {
|
|
39
|
+
ditherWith
|
|
47
40
|
};
|
package/floyd-steinberg.js
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export const FLOYD_STEINBERG = {
|
|
10
|
-
ox: [1, -1, 0, 1],
|
|
11
|
-
oy: [0, 1, 1, 1],
|
|
12
|
-
weights: [7, 3, 5, 1],
|
|
13
|
-
shift: 4,
|
|
1
|
+
const FLOYD_STEINBERG = {
|
|
2
|
+
ox: [1, -1, 0, 1],
|
|
3
|
+
oy: [0, 1, 1, 1],
|
|
4
|
+
weights: [7, 3, 5, 1],
|
|
5
|
+
shift: 4
|
|
6
|
+
};
|
|
7
|
+
export {
|
|
8
|
+
FLOYD_STEINBERG
|
|
14
9
|
};
|
package/jarvis.js
CHANGED
|
@@ -2,17 +2,12 @@ const A = 64 / 48;
|
|
|
2
2
|
const B = 3 * A;
|
|
3
3
|
const C = 5 * A;
|
|
4
4
|
const D = 7 * A;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export const JARVIS_JUDICE_NINKE = {
|
|
14
|
-
ox: [1, 2, -2, -1, 0, 1, 2, -2, -1, 0, 1, 2],
|
|
15
|
-
oy: [0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2],
|
|
16
|
-
weights: [D, C, B, C, D, C, B, A, B, C, B, A],
|
|
17
|
-
shift: 6,
|
|
5
|
+
const JARVIS_JUDICE_NINKE = {
|
|
6
|
+
ox: [1, 2, -2, -1, 0, 1, 2, -2, -1, 0, 1, 2],
|
|
7
|
+
oy: [0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2],
|
|
8
|
+
weights: [D, C, B, C, D, C, B, A, B, C, B, A],
|
|
9
|
+
shift: 6
|
|
10
|
+
};
|
|
11
|
+
export {
|
|
12
|
+
JARVIS_JUDICE_NINKE
|
|
18
13
|
};
|
package/ordered.js
CHANGED
|
@@ -1,91 +1,53 @@
|
|
|
1
1
|
import { isNumber } from "@thi.ng/checks/is-number";
|
|
2
2
|
import { clamp } from "@thi.ng/math/interval";
|
|
3
3
|
const init = (x, y, size, val, step, mat) => {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return mat;
|
|
8
|
-
}
|
|
9
|
-
size >>= 1;
|
|
10
|
-
const step4 = step << 2;
|
|
11
|
-
init(x, y, size, val, step4, mat);
|
|
12
|
-
init(x + size, y + size, size, val + step, step4, mat);
|
|
13
|
-
init(x + size, y, size, val + step * 2, step4, mat);
|
|
14
|
-
init(x, y + size, size, val + step * 3, step4, mat);
|
|
4
|
+
if (size === 1) {
|
|
5
|
+
!mat[y] && (mat[y] = []);
|
|
6
|
+
mat[y][x] = val;
|
|
15
7
|
return mat;
|
|
8
|
+
}
|
|
9
|
+
size >>= 1;
|
|
10
|
+
const step4 = step << 2;
|
|
11
|
+
init(x, y, size, val, step4, mat);
|
|
12
|
+
init(x + size, y + size, size, val + step, step4, mat);
|
|
13
|
+
init(x + size, y, size, val + step * 2, step4, mat);
|
|
14
|
+
init(x, y + size, size, val + step * 3, step4, mat);
|
|
15
|
+
return mat;
|
|
16
16
|
};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
* @remarks
|
|
22
|
-
* Reference:
|
|
23
|
-
* - https://en.wikipedia.org/wiki/Ordered_dithering
|
|
24
|
-
*
|
|
25
|
-
* @param size -
|
|
26
|
-
*/
|
|
27
|
-
export const defBayer = (size) => ({
|
|
28
|
-
mat: init(0, 0, size, 0, 1, []),
|
|
29
|
-
invSize: 1 / (size * size),
|
|
30
|
-
mask: size - 1,
|
|
17
|
+
const defBayer = (size) => ({
|
|
18
|
+
mat: init(0, 0, size, 0, 1, []),
|
|
19
|
+
invSize: 1 / (size * size),
|
|
20
|
+
mask: size - 1
|
|
31
21
|
});
|
|
32
|
-
/**
|
|
33
|
-
* Single-channel/value ordered dither using provided Bayer matrix.
|
|
34
|
-
*
|
|
35
|
-
* @param mat - matrix
|
|
36
|
-
* @param dsteps - number of dest colors
|
|
37
|
-
* @param drange - dest color range
|
|
38
|
-
* @param srange - src color range
|
|
39
|
-
* @param x - x pos
|
|
40
|
-
* @param y - y pos
|
|
41
|
-
* @param val - src value
|
|
42
|
-
*
|
|
43
|
-
* @internal
|
|
44
|
-
*/
|
|
45
22
|
const orderedDither1 = ({ mat, mask, invSize }, dsteps, drange, srange, x, y, val) => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return clamp(val, 0, dsteps) * ((drange - 1) / dsteps);
|
|
23
|
+
val = dsteps * (val / srange) + mat[y & mask][x & mask] * invSize - 0.5 | 0;
|
|
24
|
+
dsteps--;
|
|
25
|
+
return clamp(val, 0, dsteps) * ((drange - 1) / dsteps);
|
|
50
26
|
};
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const { data, format, width } = img;
|
|
71
|
-
const steps = isNumber(numColors)
|
|
72
|
-
? new Array(format.channels.length).fill(numColors)
|
|
73
|
-
: numColors;
|
|
74
|
-
const mat = isNumber(size) ? defBayer(size) : size;
|
|
75
|
-
for (let i = 0, n = data.length, nc = format.channels.length, x = 0, y = 0; i < n; i++) {
|
|
76
|
-
let col = data[i];
|
|
77
|
-
for (let j = 0; j < nc; j++) {
|
|
78
|
-
const ch = format.channels[j];
|
|
79
|
-
const num = ch.num;
|
|
80
|
-
const cs = steps[j];
|
|
81
|
-
cs > 0 &&
|
|
82
|
-
(col = ch.setInt(col, orderedDither1(mat, cs, num, num, x, y, ch.int(col))));
|
|
83
|
-
}
|
|
84
|
-
data[i] = col;
|
|
85
|
-
if (++x === width) {
|
|
86
|
-
x = 0;
|
|
87
|
-
y++;
|
|
88
|
-
}
|
|
27
|
+
const orderedDither = (img, size, numColors) => {
|
|
28
|
+
const { data, format, width } = img;
|
|
29
|
+
const steps = isNumber(numColors) ? new Array(format.channels.length).fill(numColors) : numColors;
|
|
30
|
+
const mat = isNumber(size) ? defBayer(size) : size;
|
|
31
|
+
for (let i = 0, n = data.length, nc = format.channels.length, x = 0, y = 0; i < n; i++) {
|
|
32
|
+
let col = data[i];
|
|
33
|
+
for (let j = 0; j < nc; j++) {
|
|
34
|
+
const ch = format.channels[j];
|
|
35
|
+
const num = ch.num;
|
|
36
|
+
const cs = steps[j];
|
|
37
|
+
cs > 0 && (col = ch.setInt(
|
|
38
|
+
col,
|
|
39
|
+
orderedDither1(mat, cs, num, num, x, y, ch.int(col))
|
|
40
|
+
));
|
|
41
|
+
}
|
|
42
|
+
data[i] = col;
|
|
43
|
+
if (++x === width) {
|
|
44
|
+
x = 0;
|
|
45
|
+
y++;
|
|
89
46
|
}
|
|
90
|
-
|
|
47
|
+
}
|
|
48
|
+
return img;
|
|
49
|
+
};
|
|
50
|
+
export {
|
|
51
|
+
defBayer,
|
|
52
|
+
orderedDither
|
|
91
53
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/pixel-dither",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.90",
|
|
4
4
|
"description": "Extensible image dithering w/ various algorithm presets",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
"author": "Karsten Schmidt (https://thi.ng)",
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"scripts": {
|
|
27
|
-
"build": "yarn
|
|
27
|
+
"build": "yarn build:esbuild && yarn build:decl",
|
|
28
|
+
"build:decl": "tsc --declaration --emitDeclarationOnly",
|
|
29
|
+
"build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
|
|
28
30
|
"clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc",
|
|
29
31
|
"doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
|
|
30
32
|
"doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
|
|
@@ -33,13 +35,13 @@
|
|
|
33
35
|
"test": "bun test"
|
|
34
36
|
},
|
|
35
37
|
"dependencies": {
|
|
36
|
-
"@thi.ng/checks": "^3.4.
|
|
37
|
-
"@thi.ng/math": "^5.7.
|
|
38
|
-
"@thi.ng/pixel": "^5.0.
|
|
38
|
+
"@thi.ng/checks": "^3.4.12",
|
|
39
|
+
"@thi.ng/math": "^5.7.7",
|
|
40
|
+
"@thi.ng/pixel": "^5.0.4"
|
|
39
41
|
},
|
|
40
42
|
"devDependencies": {
|
|
41
43
|
"@microsoft/api-extractor": "^7.38.3",
|
|
42
|
-
"
|
|
44
|
+
"esbuild": "^0.19.8",
|
|
43
45
|
"rimraf": "^5.0.5",
|
|
44
46
|
"tools": "^0.0.1",
|
|
45
47
|
"typedoc": "^0.25.4",
|
|
@@ -104,5 +106,5 @@
|
|
|
104
106
|
"parent": "@thi.ng/pixel",
|
|
105
107
|
"year": 2021
|
|
106
108
|
},
|
|
107
|
-
"gitHead": "
|
|
109
|
+
"gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
|
|
108
110
|
}
|
package/sierra2.js
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
ox: [1, 2, -2, -1, 0, 1, 2],
|
|
10
|
-
oy: [0, 0, 1, 1, 1, 1, 1],
|
|
11
|
-
weights: [4, 3, 1, 2, 3, 2, 1],
|
|
12
|
-
shift: 4,
|
|
1
|
+
const SIERRA2 = {
|
|
2
|
+
ox: [1, 2, -2, -1, 0, 1, 2],
|
|
3
|
+
oy: [0, 0, 1, 1, 1, 1, 1],
|
|
4
|
+
weights: [4, 3, 1, 2, 3, 2, 1],
|
|
5
|
+
shift: 4
|
|
6
|
+
};
|
|
7
|
+
export {
|
|
8
|
+
SIERRA2
|
|
13
9
|
};
|
package/stucki.js
CHANGED
|
@@ -2,16 +2,12 @@ const A = 64 / 42;
|
|
|
2
2
|
const B = 2 * A;
|
|
3
3
|
const C = 4 * A;
|
|
4
4
|
const D = 8 * A;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
ox: [1, 2, -2, -1, 0, 1, 2, -2, -1, 0, 1, 2],
|
|
14
|
-
oy: [0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2],
|
|
15
|
-
weights: [D, C, B, C, D, C, B, A, B, C, B, A],
|
|
16
|
-
shift: C,
|
|
5
|
+
const STUCKI = {
|
|
6
|
+
ox: [1, 2, -2, -1, 0, 1, 2, -2, -1, 0, 1, 2],
|
|
7
|
+
oy: [0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2],
|
|
8
|
+
weights: [D, C, B, C, D, C, B, A, B, C, B, A],
|
|
9
|
+
shift: C
|
|
10
|
+
};
|
|
11
|
+
export {
|
|
12
|
+
STUCKI
|
|
17
13
|
};
|
package/threshold.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
const THRESHOLD = {
|
|
2
|
+
ox: [],
|
|
3
|
+
oy: [],
|
|
4
|
+
weights: [],
|
|
5
|
+
shift: 0
|
|
6
|
+
};
|
|
7
|
+
export {
|
|
8
|
+
THRESHOLD
|
|
9
9
|
};
|