-
Notifications
You must be signed in to change notification settings - Fork 103
/
colorworker.js
110 lines (103 loc) · 3.33 KB
/
colorworker.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import potrace from 'esm-potrace-wasm';
const extractColors = (imageData) => {
const colors = {};
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];
const a = imageData.data[i + 3];
if (a === 0) {
continue;
}
const rgba = `${r},${g},${b},${a}`;
if (!colors[rgba]) {
colors[rgba] = [i];
} else {
colors[rgba].push(i);
}
}
return colors;
};
const convertToColorSVG = async (imageData, params, progressPort) => {
const colors = extractColors(imageData);
let prefix = '';
let suffix = '';
let svgString = '';
const promises = [];
let processed = 0;
for (const [color, occurrences] of Object.entries(colors)) {
promises.push(() => {
let newImageData = new ImageData(imageData.width, imageData.height);
newImageData.data.fill(255);
const len = occurrences.length;
for (let i = 0; i < len; i++) {
const location = occurrences[i];
newImageData.data[location] = 0;
newImageData.data[location + 1] = 0;
newImageData.data[location + 2] = 0;
newImageData.data[location + 3] = 255;
}
return new Promise(async (resolve) => {
let svg = await potrace(newImageData, params);
newImageData = null;
svg = svg.replace(
'fill="#000000" stroke="none"',
`fill="rgba(${color})" stroke="rgba(${color})" stroke-width="${params.strokeWidth}px"`,
);
const pathRegEx = /<path\s*d="([^"]+)"\/>/g;
let matches;
const shortPaths = [];
while ((matches = pathRegEx.exec(svg)) !== null) {
const path = matches[1];
if (path.split(' ').length < params.minPathSegments) {
shortPaths.push(matches[0]);
}
}
shortPaths.forEach((path) => {
svg = svg.replace(path, '');
});
processed++;
if (!/<path/.test(svg)) {
if (total === processed) {
console.log(`Potraced 100% %c■■`, `color: rgba(${color})`);
}
progressPort.postMessage({ processed, total });
resolve('');
return;
}
console.log(
`Potraced ${String(((processed / total) * 100).toFixed())}% %c■■`,
`color: rgba(${color})`,
);
progressPort.postMessage({ processed, total, svg });
resolve(svg);
});
});
}
const total = promises.length;
const promiseChunks = [];
// @ToDo: What is the problem?
const chunkSize = 1; // 2 * navigator.hardwareConcurrency || 16;
while (promises.length > 0) {
promiseChunks.push(promises.splice(0, chunkSize));
}
const svgs = [];
for (const chunk of promiseChunks) {
svgs.push(await Promise.all(chunk.map((f) => f())));
}
for (const svg of svgs.flat()) {
if (!prefix) {
prefix = svg.replace(/(.*?<svg[^>]+>)(.*?)(<\/svg>)/, '$1');
suffix = svg.replace(/(.*?<svg[^>]+>)(.*?)(<\/svg>)/, '$3');
svgString = prefix;
}
svgString += svg.replace(/(.*?<svg[^>]+>)(.*?)(<\/svg>)/, '$2');
}
svgString += suffix;
return svgString;
};
self.addEventListener('message', async (e) => {
const { imageData, params } = e.data;
const svg = await convertToColorSVG(imageData, params, e.ports[1]);
e.ports[0].postMessage({ result: svg });
});