depixel 1.0.4 → 1.0.5
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 +19 -2
- package/lib.js +345 -177
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,8 @@ Improvements upon original GPU version:
|
|
|
11
11
|
* Add a threshold parameter to adjust how close two colors need to be to be consider similar
|
|
12
12
|
* Better handle alpha channels (treat as dissimilar from non-transparent pixels)
|
|
13
13
|
* Fix stretching/scaling due to texel misalignment
|
|
14
|
+
* Add option to pass in a similarity map
|
|
15
|
+
* Relatedly, renderMode:similarityMask outputs a scaled up similarity map (for use in post-processing, etc)
|
|
14
16
|
|
|
15
17
|
Notes
|
|
16
18
|
* Original GPU code is MIT licensed, the same license may apply here, all original code in this project is additionally released under the MIT License
|
|
@@ -25,12 +27,27 @@ type Image = {
|
|
|
25
27
|
data: Buffer; // Or Uint8Array - pixels in RGBA byte order
|
|
26
28
|
width: number;
|
|
27
29
|
height: number;
|
|
30
|
+
similarityData?: Buffer;
|
|
28
31
|
};
|
|
29
32
|
|
|
30
33
|
type Opts = {
|
|
31
34
|
height: number;
|
|
32
|
-
threshold?: number; // 0..255, lower = fewer similarity edges
|
|
33
|
-
borderPx
|
|
35
|
+
threshold?: number; // 0..255, lower = fewer similarity edges; default 255
|
|
36
|
+
// borderPx - pad input with this many pixels (1-2) - useful with
|
|
37
|
+
// `threshold=0` for complete hard edges
|
|
38
|
+
borderPx?: number;
|
|
39
|
+
// if `similarity` specified, uses values here instead or RGBA values to
|
|
40
|
+
// determine similarity. Note: `threshold` (default 3) is used against sum
|
|
41
|
+
// of RGB differences in this buffer instead the default of using a
|
|
42
|
+
// perceptual threshold based on color
|
|
43
|
+
// note: color still impacts shape of splines
|
|
44
|
+
similarity?: Buffer;
|
|
45
|
+
// if `outputSimilarityMask` and `similarity` is specified,
|
|
46
|
+
// then `similarityData` in the return will contain a scaled up version of
|
|
47
|
+
// the `similarity` mask (for use in post-processing, etc
|
|
48
|
+
outputSimilarityMask?: boolean; // default false
|
|
49
|
+
renderMode?: 'splines' | 'default'; // default default
|
|
50
|
+
doOpt?: boolean; // default true
|
|
34
51
|
}
|
|
35
52
|
|
|
36
53
|
function scaleImage(src: Image, opts: Opts): Image;
|
package/lib.js
CHANGED
|
@@ -3,12 +3,17 @@ type Image = {
|
|
|
3
3
|
data: Buffer; // Or Uint8Array - pixels in RGBA byte order
|
|
4
4
|
width: number;
|
|
5
5
|
height: number;
|
|
6
|
+
similarityData?: Buffer;
|
|
6
7
|
};
|
|
7
8
|
|
|
8
9
|
type Opts = {
|
|
9
10
|
height: number;
|
|
10
11
|
threshold?: number; // 0..255, lower = fewer similarity edges
|
|
11
12
|
borderPx?: number; // pad input with this many pixels (1-2)
|
|
13
|
+
similarity?: Buffer; // if specified uses values here instead or RGBA values to determine similarity - threshold (default 3) is used against sum of RGB differences
|
|
14
|
+
outputSimilarityMask?: boolean; // if this and similarity are specified, return similarityData
|
|
15
|
+
renderMode?: 'splines' | 'default';
|
|
16
|
+
doOpt?: boolean; // default true
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
function scaleImage(src: Image, opts: Opts): Image;
|
|
@@ -149,7 +154,7 @@ function isContour(src, pL, pR) {
|
|
|
149
154
|
return dist_sq > THRESHOLD_CONTOUR_SQ;
|
|
150
155
|
}
|
|
151
156
|
|
|
152
|
-
function buildSimilarityGraph(src, similarityThreshold) {
|
|
157
|
+
function buildSimilarityGraph(src, similarityThreshold, similarity) {
|
|
153
158
|
const w = src.width;
|
|
154
159
|
const h = src.height;
|
|
155
160
|
const sgW = 2 * w + 1;
|
|
@@ -167,6 +172,24 @@ function buildSimilarityGraph(src, similarityThreshold) {
|
|
|
167
172
|
return [floor((gx - 1) / 2), floor((gy - 1) / 2)];
|
|
168
173
|
}
|
|
169
174
|
|
|
175
|
+
let isSimilar2;
|
|
176
|
+
if (similarity) {
|
|
177
|
+
let simImage = {
|
|
178
|
+
...src,
|
|
179
|
+
data: similarity,
|
|
180
|
+
};
|
|
181
|
+
isSimilar2 = function(p1, p2) {
|
|
182
|
+
let v1 = fetchPixelRGBA8(simImage, p1[0], p1[1]);
|
|
183
|
+
let v2 = fetchPixelRGBA8(simImage, p2[0], p2[1]);
|
|
184
|
+
let sum = abs(v1[0] - v2[0]) + abs(v1[1] - v2[1]) + abs(v1[2] - v2[2]);
|
|
185
|
+
return sum <= similarityThreshold;
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
isSimilar2 = function(p1, p2) {
|
|
189
|
+
return isSimilar(fetchPixelRGBA(src, p1[0], p1[1]), fetchPixelRGBA(src, p2[0], p2[1]), similarityThreshold);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
170
193
|
for (let y = 0; y < sgH; y++) {
|
|
171
194
|
for (let x = 0; x < sgW; x++) {
|
|
172
195
|
if (x === 0 || x === sgW - 1 || y === 0 || y === sgH - 1) {
|
|
@@ -181,19 +204,19 @@ function buildSimilarityGraph(src, similarityThreshold) {
|
|
|
181
204
|
let diagonal = 0;
|
|
182
205
|
let pA = getPixelCoords(x - 1, y + 1);
|
|
183
206
|
let pB = getPixelCoords(x + 1, y - 1);
|
|
184
|
-
if (
|
|
207
|
+
if (isSimilar2(pA, pB)) {
|
|
185
208
|
diagonal = EDGE_DIAGONAL_ULLR;
|
|
186
209
|
}
|
|
187
210
|
pA = getPixelCoords(x - 1, y - 1);
|
|
188
211
|
pB = getPixelCoords(x + 1, y + 1);
|
|
189
|
-
if (
|
|
212
|
+
if (isSimilar2(pA, pB)) {
|
|
190
213
|
diagonal |= EDGE_DIAGONAL_LLUR;
|
|
191
214
|
}
|
|
192
215
|
setRG(x, y, diagonal, 0);
|
|
193
216
|
} else if (evalX === 0 && evalY === 1) {
|
|
194
217
|
const pA = getPixelCoords(x - 1, y);
|
|
195
218
|
const pB = getPixelCoords(x + 1, y);
|
|
196
|
-
if (
|
|
219
|
+
if (isSimilar2(pA, pB)) {
|
|
197
220
|
setRG(x, y, EDGE_HORVERT, 0);
|
|
198
221
|
} else {
|
|
199
222
|
setRG(x, y, 0, 0);
|
|
@@ -201,7 +224,7 @@ function buildSimilarityGraph(src, similarityThreshold) {
|
|
|
201
224
|
} else if (evalX === 1 && evalY === 0) {
|
|
202
225
|
const pA = getPixelCoords(x, y - 1);
|
|
203
226
|
const pB = getPixelCoords(x, y + 1);
|
|
204
|
-
if (
|
|
227
|
+
if (isSimilar2(pA, pB)) {
|
|
205
228
|
setRG(x, y, EDGE_HORVERT, 0);
|
|
206
229
|
} else {
|
|
207
230
|
setRG(x, y, 0, 0);
|
|
@@ -386,20 +409,20 @@ function eliminateCrossings(sim) {
|
|
|
386
409
|
nNE = 8 * (2 - level) + (4 - level);
|
|
387
410
|
nE = 8 * (3 - level) + (4 - level);
|
|
388
411
|
if (currentComponent === 0) {
|
|
389
|
-
if ((
|
|
390
|
-
else if ((
|
|
391
|
-
else if ((
|
|
392
|
-
else if ((
|
|
393
|
-
else if ((
|
|
394
|
-
else if ((
|
|
395
|
-
else if ((
|
|
412
|
+
if ((nhood & SOUTH) && (lArray[nS] !== 0)) { currentComponent = lArray[nS]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
413
|
+
else if ((nhood & SOUTHWEST) && (lArray[nSW] !== 0)) { currentComponent = lArray[nSW]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
414
|
+
else if ((nhood & WEST) && (lArray[nW] !== 0)) { currentComponent = lArray[nW]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
415
|
+
else if ((nhood & NORTHWEST) && (lArray[nNW] !== 0)) { currentComponent = lArray[nNW]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
416
|
+
else if ((nhood & NORTH) && (lArray[nN] !== 0)) { currentComponent = lArray[nN]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
417
|
+
else if ((nhood & NORTHEAST) && (lArray[nNE] !== 0)) { currentComponent = lArray[nNE]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
418
|
+
else if ((nhood & EAST) && (lArray[nE] !== 0)) { currentComponent = lArray[nE]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
396
419
|
}
|
|
397
420
|
if (currentComponent !== 0) {
|
|
398
|
-
if (
|
|
399
|
-
if (
|
|
400
|
-
if (
|
|
401
|
-
if (
|
|
402
|
-
if (
|
|
421
|
+
if (nhood & SOUTHWEST) { if (lArray[nSW] === 0) { lArray[nSW] = currentComponent; countForComponent(currentComponent); } }
|
|
422
|
+
if (nhood & WEST) { if (lArray[nW] === 0) { lArray[nW] = currentComponent; countForComponent(currentComponent); } }
|
|
423
|
+
if (nhood & NORTHWEST) { if (lArray[nNW] === 0) { lArray[nNW] = currentComponent; countForComponent(currentComponent); } }
|
|
424
|
+
if (nhood & NORTH) { if (lArray[nN] === 0) { lArray[nN] = currentComponent; countForComponent(currentComponent); } }
|
|
425
|
+
if (nhood & NORTHEAST) { if (lArray[nNE] === 0) { lArray[nNE] = currentComponent; countForComponent(currentComponent); } }
|
|
403
426
|
}
|
|
404
427
|
if (level > 0) {
|
|
405
428
|
for (let i = 0; i < level * 2; i++) {
|
|
@@ -414,16 +437,16 @@ function eliminateCrossings(sim) {
|
|
|
414
437
|
nNE = 8 * (2 - level) + (i + 5 - level);
|
|
415
438
|
nE = 8 * (3 - level) + (i + 5 - level);
|
|
416
439
|
if (currentComponent === 0) {
|
|
417
|
-
if ((
|
|
418
|
-
else if ((
|
|
419
|
-
else if ((
|
|
420
|
-
else if ((
|
|
421
|
-
else if ((
|
|
440
|
+
if ((nhood & WEST) && (lArray[nW] !== 0)) { currentComponent = lArray[nW]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
441
|
+
else if ((nhood & NORTHWEST) && (lArray[nNW] !== 0)) { currentComponent = lArray[nNW]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
442
|
+
else if ((nhood & NORTH) && (lArray[nN] !== 0)) { currentComponent = lArray[nN]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
443
|
+
else if ((nhood & NORTHEAST) && (lArray[nNE] !== 0)) { currentComponent = lArray[nNE]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
444
|
+
else if ((nhood & EAST) && (lArray[nE] !== 0)) { currentComponent = lArray[nE]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
422
445
|
}
|
|
423
446
|
if (currentComponent !== 0) {
|
|
424
|
-
if (
|
|
425
|
-
if (
|
|
426
|
-
if (
|
|
447
|
+
if (nhood & NORTHWEST) { if (lArray[nNW] === 0) { lArray[nNW] = currentComponent; countForComponent(currentComponent); } }
|
|
448
|
+
if (nhood & NORTH) { if (lArray[nN] === 0) { lArray[nN] = currentComponent; countForComponent(currentComponent); } }
|
|
449
|
+
if (nhood & NORTHEAST) { if (lArray[nNE] === 0) { lArray[nNE] = currentComponent; countForComponent(currentComponent); } }
|
|
427
450
|
}
|
|
428
451
|
}
|
|
429
452
|
}
|
|
@@ -441,20 +464,20 @@ function eliminateCrossings(sim) {
|
|
|
441
464
|
nSE = 8 * (4 - level) + (5 + level);
|
|
442
465
|
nS = 8 * (4 - level) + (4 + level);
|
|
443
466
|
if (currentComponent === 0) {
|
|
444
|
-
if ((
|
|
445
|
-
else if ((
|
|
446
|
-
else if ((
|
|
447
|
-
else if ((
|
|
448
|
-
else if ((
|
|
449
|
-
else if ((
|
|
450
|
-
else if ((
|
|
467
|
+
if ((nhood & WEST) && (lArray[nNW] !== 0)) { currentComponent = lArray[nW]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
468
|
+
else if ((nhood & NORTHWEST) && (lArray[nNW] !== 0)) { currentComponent = lArray[nNW]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
469
|
+
else if ((nhood & NORTH) && (lArray[nN] !== 0)) { currentComponent = lArray[nN]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
470
|
+
else if ((nhood & NORTHEAST) && (lArray[nNE] !== 0)) { currentComponent = lArray[nNE]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
471
|
+
else if ((nhood & EAST) && (lArray[nE] !== 0)) { currentComponent = lArray[nE]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
472
|
+
else if ((nhood & SOUTHEAST) && (lArray[nSE] !== 0)) { currentComponent = lArray[nSE]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
473
|
+
else if ((nhood & SOUTH) && (lArray[nS] !== 0)) { currentComponent = lArray[nS]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
451
474
|
}
|
|
452
475
|
if (currentComponent !== 0) {
|
|
453
|
-
if (
|
|
454
|
-
if (
|
|
455
|
-
if (
|
|
456
|
-
if (
|
|
457
|
-
if (
|
|
476
|
+
if (nhood & NORTHWEST) { if (lArray[nNW] === 0) { lArray[nNW] = currentComponent; countForComponent(currentComponent); } }
|
|
477
|
+
if (nhood & NORTH) { if (lArray[nN] === 0) { lArray[nN] = currentComponent; countForComponent(currentComponent); } }
|
|
478
|
+
if (nhood & NORTHEAST) { if (lArray[nNE] === 0) { lArray[nNE] = currentComponent; countForComponent(currentComponent); } }
|
|
479
|
+
if (nhood & EAST) { if (lArray[nE] === 0) { lArray[nE] = currentComponent; countForComponent(currentComponent); } }
|
|
480
|
+
if (nhood & SOUTHEAST) { if (lArray[nSE] === 0) { lArray[nSE] = currentComponent; countForComponent(currentComponent); } }
|
|
458
481
|
}
|
|
459
482
|
if (level > 0) {
|
|
460
483
|
for (let i = 0; i < level * 2; i++) {
|
|
@@ -469,16 +492,16 @@ function eliminateCrossings(sim) {
|
|
|
469
492
|
nSE = 8 * (i + 5 - level) + (5 + level);
|
|
470
493
|
nS = 8 * (i + 5 - level) + (4 + level);
|
|
471
494
|
if (currentComponent === 0) {
|
|
472
|
-
if ((
|
|
473
|
-
else if ((
|
|
474
|
-
else if ((
|
|
475
|
-
else if ((
|
|
476
|
-
else if ((
|
|
495
|
+
if ((nhood & NORTH) && (lArray[nN] !== 0)) { currentComponent = lArray[nN]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
496
|
+
else if ((nhood & NORTHEAST) && (lArray[nNE] !== 0)) { currentComponent = lArray[nNE]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
497
|
+
else if ((nhood & EAST) && (lArray[nE] !== 0)) { currentComponent = lArray[nE]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
498
|
+
else if ((nhood & SOUTHEAST) && (lArray[nSE] !== 0)) { currentComponent = lArray[nSE]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
499
|
+
else if ((nhood & SOUTH) && (lArray[nS] !== 0)) { currentComponent = lArray[nS]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
477
500
|
}
|
|
478
501
|
if (currentComponent !== 0) {
|
|
479
|
-
if (
|
|
480
|
-
if (
|
|
481
|
-
if (
|
|
502
|
+
if (nhood & NORTHEAST) { if (lArray[nNE] === 0) { lArray[nNE] = currentComponent; countForComponent(currentComponent); } }
|
|
503
|
+
if (nhood & EAST) { if (lArray[nE] === 0) { lArray[nE] = currentComponent; countForComponent(currentComponent); } }
|
|
504
|
+
if (nhood & SOUTHEAST) { if (lArray[nSE] === 0) { lArray[nSE] = currentComponent; countForComponent(currentComponent); } }
|
|
482
505
|
}
|
|
483
506
|
}
|
|
484
507
|
}
|
|
@@ -496,20 +519,20 @@ function eliminateCrossings(sim) {
|
|
|
496
519
|
nSW = 8 * (5 + level) + (3 + level);
|
|
497
520
|
nW = 8 * (4 + level) + (3 + level);
|
|
498
521
|
if (currentComponent === 0) {
|
|
499
|
-
if ((
|
|
500
|
-
else if ((
|
|
501
|
-
else if ((
|
|
502
|
-
else if ((
|
|
503
|
-
else if ((
|
|
504
|
-
else if ((
|
|
505
|
-
else if ((
|
|
522
|
+
if ((nhood & NORTH) && (lArray[nN] !== 0)) { currentComponent = lArray[nN]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
523
|
+
else if ((nhood & NORTHEAST) && (lArray[nNE] !== 0)) { currentComponent = lArray[nNE]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
524
|
+
else if ((nhood & EAST) && (lArray[nE] !== 0)) { currentComponent = lArray[nE]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
525
|
+
else if ((nhood & SOUTHEAST) && (lArray[nSE] !== 0)) { currentComponent = lArray[nSE]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
526
|
+
else if ((nhood & SOUTH) && (lArray[nS] !== 0)) { currentComponent = lArray[nS]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
527
|
+
else if ((nhood & SOUTHWEST) && (lArray[nSW] !== 0)) { currentComponent = lArray[nSW]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
528
|
+
else if ((nhood & WEST) && (lArray[nW] !== 0)) { currentComponent = lArray[nW]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
506
529
|
}
|
|
507
530
|
if (currentComponent !== 0) {
|
|
508
|
-
if (
|
|
509
|
-
if (
|
|
510
|
-
if (
|
|
511
|
-
if (
|
|
512
|
-
if (
|
|
531
|
+
if (nhood & NORTHEAST) { if (lArray[nNE] === 0) { lArray[nNE] = currentComponent; countForComponent(currentComponent); } }
|
|
532
|
+
if (nhood & EAST) { if (lArray[nE] === 0) { lArray[nE] = currentComponent; countForComponent(currentComponent); } }
|
|
533
|
+
if (nhood & SOUTHEAST) { if (lArray[nSE] === 0) { lArray[nSE] = currentComponent; countForComponent(currentComponent); } }
|
|
534
|
+
if (nhood & SOUTH) { if (lArray[nS] === 0) { lArray[nS] = currentComponent; countForComponent(currentComponent); } }
|
|
535
|
+
if (nhood & SOUTHWEST) { if (lArray[nSW] === 0) { lArray[nSW] = currentComponent; countForComponent(currentComponent); } }
|
|
513
536
|
}
|
|
514
537
|
|
|
515
538
|
if (level > 0) {
|
|
@@ -525,18 +548,18 @@ function eliminateCrossings(sim) {
|
|
|
525
548
|
nSW = 8 * (5 + level) + (i + 3 - level);
|
|
526
549
|
nW = 8 * (4 + level) + (i + 3 - level);
|
|
527
550
|
if (currentComponent === 0) {
|
|
528
|
-
if ((
|
|
529
|
-
else if ((
|
|
530
|
-
else if ((
|
|
531
|
-
else if ((
|
|
532
|
-
else if ((
|
|
551
|
+
if ((nhood & EAST) && (lArray[nE] !== 0)) { currentComponent = lArray[nE]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
552
|
+
else if ((nhood & SOUTHEAST) && (lArray[nSE] !== 0)) { currentComponent = lArray[nSE]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
553
|
+
else if ((nhood & SOUTH) && (lArray[nS] !== 0)) { currentComponent = lArray[nS]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
554
|
+
else if ((nhood & SOUTHWEST) && (lArray[nSW] !== 0)) { currentComponent = lArray[nSW]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
555
|
+
else if ((nhood & WEST) && (lArray[nW] !== 0)) { currentComponent = lArray[nW]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
533
556
|
}
|
|
534
557
|
if (currentComponent !== 0) {
|
|
535
|
-
if (
|
|
536
|
-
if (
|
|
537
|
-
if (
|
|
538
|
-
if (
|
|
539
|
-
if (
|
|
558
|
+
if (nhood & EAST) { if (lArray[nE] === 0) { lArray[nE] = currentComponent; countForComponent(currentComponent); } }
|
|
559
|
+
if (nhood & SOUTHEAST) { if (lArray[nSE] === 0) { lArray[nSE] = currentComponent; countForComponent(currentComponent); } }
|
|
560
|
+
if (nhood & SOUTH) { if (lArray[nS] === 0) { lArray[nS] = currentComponent; countForComponent(currentComponent); } }
|
|
561
|
+
if (nhood & SOUTHWEST) { if (lArray[nSW] === 0) { lArray[nSW] = currentComponent; countForComponent(currentComponent); } }
|
|
562
|
+
if (nhood & WEST) { if (lArray[nW] === 0) { lArray[nW] = currentComponent; countForComponent(currentComponent); } }
|
|
540
563
|
}
|
|
541
564
|
}
|
|
542
565
|
}
|
|
@@ -554,20 +577,20 @@ function eliminateCrossings(sim) {
|
|
|
554
577
|
nSE = 8 * (5 + level) + (4 - level);
|
|
555
578
|
nE = 8 * (4 + level) + (4 - level);
|
|
556
579
|
if (currentComponent === 0) {
|
|
557
|
-
if ((
|
|
558
|
-
else if ((
|
|
559
|
-
else if ((
|
|
560
|
-
else if ((
|
|
561
|
-
else if ((
|
|
562
|
-
else if ((
|
|
563
|
-
else if ((
|
|
580
|
+
if ((nhood & NORTH) && (lArray[nN] !== 0)) { currentComponent = lArray[nN]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
581
|
+
else if ((nhood & NORTHWEST) && (lArray[nNW] !== 0)) { currentComponent = lArray[nNW]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
582
|
+
else if ((nhood & WEST) && (lArray[nW] !== 0)) { currentComponent = lArray[nW]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
583
|
+
else if ((nhood & SOUTHWEST) && (lArray[nSW] !== 0)) { currentComponent = lArray[nSW]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
584
|
+
else if ((nhood & SOUTH) && (lArray[nS] !== 0)) { currentComponent = lArray[nS]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
585
|
+
else if ((nhood & SOUTHEAST) && (lArray[nSE] !== 0)) { currentComponent = lArray[nSE]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
586
|
+
else if ((nhood & EAST) && (lArray[nE] !== 0)) { currentComponent = lArray[nE]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
564
587
|
}
|
|
565
588
|
if (currentComponent !== 0) {
|
|
566
|
-
if (
|
|
567
|
-
if (
|
|
568
|
-
if (
|
|
569
|
-
if (
|
|
570
|
-
if (
|
|
589
|
+
if (nhood & NORTH) { if (lArray[nN] === 0) { lArray[nN] = currentComponent; countForComponent(currentComponent); } }
|
|
590
|
+
if (nhood & NORTHWEST) { if (lArray[nNW] === 0) { lArray[nNW] = currentComponent; countForComponent(currentComponent); } }
|
|
591
|
+
if (nhood & WEST) { if (lArray[nW] === 0) { lArray[nW] = currentComponent; countForComponent(currentComponent); } }
|
|
592
|
+
if (nhood & SOUTHWEST) { if (lArray[nSW] === 0) { lArray[nSW] = currentComponent; countForComponent(currentComponent); } }
|
|
593
|
+
if (nhood & SOUTH) { if (lArray[nS] === 0) { lArray[nS] = currentComponent; countForComponent(currentComponent); } }
|
|
571
594
|
}
|
|
572
595
|
|
|
573
596
|
if (level > 0) {
|
|
@@ -583,18 +606,18 @@ function eliminateCrossings(sim) {
|
|
|
583
606
|
nSW = 8 * (i + 5 - level) + (2 - level);
|
|
584
607
|
nS = 8 * (i + 5 - level) + (3 - level);
|
|
585
608
|
if (currentComponent === 0) {
|
|
586
|
-
if ((
|
|
587
|
-
else if ((
|
|
588
|
-
else if ((
|
|
589
|
-
else if ((
|
|
590
|
-
else if ((
|
|
609
|
+
if ((nhood & NORTH) && (lArray[nN] !== 0)) { currentComponent = lArray[nN]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
610
|
+
else if ((nhood & NORTHWEST) && (lArray[nNW] !== 0)) { currentComponent = lArray[nNW]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
611
|
+
else if ((nhood & WEST) && (lArray[nW] !== 0)) { currentComponent = lArray[nW]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
612
|
+
else if ((nhood & SOUTHWEST) && (lArray[nSW] !== 0)) { currentComponent = lArray[nSW]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
613
|
+
else if ((nhood & SOUTH) && (lArray[nS] !== 0)) { currentComponent = lArray[nS]; lArray[currentComponentIndex] = currentComponent; countForComponent(currentComponent); }
|
|
591
614
|
}
|
|
592
615
|
if (currentComponent !== 0) {
|
|
593
|
-
if (
|
|
594
|
-
if (
|
|
595
|
-
if (
|
|
596
|
-
if (
|
|
597
|
-
if (
|
|
616
|
+
if (nhood & NORTH) { if (lArray[nN] === 0) { lArray[nN] = currentComponent; countForComponent(currentComponent); } }
|
|
617
|
+
if (nhood & NORTHWEST) { if (lArray[nNW] === 0) { lArray[nNW] = currentComponent; countForComponent(currentComponent); } }
|
|
618
|
+
if (nhood & WEST) { if (lArray[nW] === 0) { lArray[nW] = currentComponent; countForComponent(currentComponent); } }
|
|
619
|
+
if (nhood & SOUTHWEST) { if (lArray[nSW] === 0) { lArray[nSW] = currentComponent; countForComponent(currentComponent); } }
|
|
620
|
+
if (nhood & SOUTH) { if (lArray[nS] === 0) { lArray[nS] = currentComponent; countForComponent(currentComponent); } }
|
|
598
621
|
}
|
|
599
622
|
}
|
|
600
623
|
}
|
|
@@ -1192,7 +1215,7 @@ function optimizeCellGraph(cell, width, height) {
|
|
|
1192
1215
|
|
|
1193
1216
|
for (let i = 0; i < count; i++) {
|
|
1194
1217
|
const flags = cell.flags[i];
|
|
1195
|
-
if (flags
|
|
1218
|
+
if (flags & (HAS_NORTHERN_SPLINE|HAS_EASTERN_SPLINE|HAS_SOUTHERN_SPLINE|HAS_WESTERN_SPLINE)) {
|
|
1196
1219
|
const base = i * 4;
|
|
1197
1220
|
const neighbors = [cell.neighbors[base], cell.neighbors[base + 1], cell.neighbors[base + 2], cell.neighbors[base + 3]];
|
|
1198
1221
|
const pos = getPosOpt(i);
|
|
@@ -1201,18 +1224,18 @@ function optimizeCellGraph(cell, width, height) {
|
|
|
1201
1224
|
let splineCount = 0;
|
|
1202
1225
|
let splineNoOpt = false;
|
|
1203
1226
|
|
|
1204
|
-
const hasN = (flags & HAS_NORTHERN_SPLINE)
|
|
1205
|
-
const hasE = (flags & HAS_EASTERN_SPLINE)
|
|
1206
|
-
const hasS = (flags & HAS_SOUTHERN_SPLINE)
|
|
1207
|
-
const hasW = (flags & HAS_WESTERN_SPLINE)
|
|
1227
|
+
const hasN = Boolean(flags & HAS_NORTHERN_SPLINE);
|
|
1228
|
+
const hasE = Boolean(flags & HAS_EASTERN_SPLINE);
|
|
1229
|
+
const hasS = Boolean(flags & HAS_SOUTHERN_SPLINE);
|
|
1230
|
+
const hasW = Boolean(flags & HAS_WESTERN_SPLINE);
|
|
1208
1231
|
|
|
1209
1232
|
if (hasN) {
|
|
1210
1233
|
const neighborflags = cell.flags[neighbors[0]] | 0;
|
|
1211
|
-
if ((
|
|
1234
|
+
if ((flags & DONT_OPTIMIZE_N) || (neighborflags & DONT_OPTIMIZE_S)) {
|
|
1212
1235
|
splineNoOpt = true;
|
|
1213
1236
|
}
|
|
1214
1237
|
if (!splineNoOpt) {
|
|
1215
|
-
if ((
|
|
1238
|
+
if ((neighborflags & HAS_CORRECTED_POSITION) && !(neighborflags & HAS_SOUTHERN_SPLINE)) {
|
|
1216
1239
|
splineNeighbors[splineCount++] = getPos(neighbors[0] + 1);
|
|
1217
1240
|
} else {
|
|
1218
1241
|
splineNeighbors[splineCount++] = getPos(neighbors[0]);
|
|
@@ -1221,11 +1244,11 @@ function optimizeCellGraph(cell, width, height) {
|
|
|
1221
1244
|
}
|
|
1222
1245
|
if (hasE) {
|
|
1223
1246
|
const neighborflags = cell.flags[neighbors[1]] | 0;
|
|
1224
|
-
if ((
|
|
1247
|
+
if ((flags & DONT_OPTIMIZE_E) || (neighborflags & DONT_OPTIMIZE_W)) {
|
|
1225
1248
|
splineNoOpt = true;
|
|
1226
1249
|
}
|
|
1227
1250
|
if (!splineNoOpt) {
|
|
1228
|
-
if ((
|
|
1251
|
+
if ((neighborflags & HAS_CORRECTED_POSITION) && !(neighborflags & HAS_WESTERN_SPLINE)) {
|
|
1229
1252
|
splineNeighbors[splineCount++] = getPos(neighbors[1] + 1);
|
|
1230
1253
|
} else {
|
|
1231
1254
|
splineNeighbors[splineCount++] = getPos(neighbors[1]);
|
|
@@ -1234,11 +1257,11 @@ function optimizeCellGraph(cell, width, height) {
|
|
|
1234
1257
|
}
|
|
1235
1258
|
if (hasS) {
|
|
1236
1259
|
const neighborflags = cell.flags[neighbors[2]] | 0;
|
|
1237
|
-
if ((
|
|
1260
|
+
if ((flags & DONT_OPTIMIZE_S) || (neighborflags & DONT_OPTIMIZE_N)) {
|
|
1238
1261
|
splineNoOpt = true;
|
|
1239
1262
|
}
|
|
1240
1263
|
if (!splineNoOpt) {
|
|
1241
|
-
if ((
|
|
1264
|
+
if ((neighborflags & HAS_CORRECTED_POSITION) && !(neighborflags & HAS_NORTHERN_SPLINE)) {
|
|
1242
1265
|
splineNeighbors[splineCount++] = getPos(neighbors[2] + 1);
|
|
1243
1266
|
} else {
|
|
1244
1267
|
splineNeighbors[splineCount++] = getPos(neighbors[2]);
|
|
@@ -1247,11 +1270,11 @@ function optimizeCellGraph(cell, width, height) {
|
|
|
1247
1270
|
}
|
|
1248
1271
|
if (hasW) {
|
|
1249
1272
|
const neighborflags = cell.flags[neighbors[3]] | 0;
|
|
1250
|
-
if ((
|
|
1273
|
+
if ((flags & DONT_OPTIMIZE_W) || (neighborflags & DONT_OPTIMIZE_E)) {
|
|
1251
1274
|
splineNoOpt = true;
|
|
1252
1275
|
}
|
|
1253
1276
|
if (!splineNoOpt) {
|
|
1254
|
-
if ((
|
|
1277
|
+
if ((neighborflags & HAS_CORRECTED_POSITION) && !(neighborflags & HAS_EASTERN_SPLINE)) {
|
|
1255
1278
|
splineNeighbors[splineCount++] = getPos(neighbors[3] + 1);
|
|
1256
1279
|
} else {
|
|
1257
1280
|
splineNeighbors[splineCount++] = getPos(neighbors[3]);
|
|
@@ -1276,7 +1299,7 @@ function computeCorrectedPositions(cell, optimized) {
|
|
|
1276
1299
|
corrected.set(optimized);
|
|
1277
1300
|
|
|
1278
1301
|
function getPos(idx) {
|
|
1279
|
-
return [
|
|
1302
|
+
return [optimized[idx * 2], optimized[idx * 2 + 1]];
|
|
1280
1303
|
}
|
|
1281
1304
|
|
|
1282
1305
|
function calcAdjustedPoint(p0, p1, p2) {
|
|
@@ -1293,16 +1316,16 @@ function computeCorrectedPositions(cell, optimized) {
|
|
|
1293
1316
|
const parentNeighborIndices = [cell.neighbors[base], cell.neighbors[base + 1], cell.neighbors[base + 2], cell.neighbors[base + 3]];
|
|
1294
1317
|
const splinePoints = [null, null];
|
|
1295
1318
|
let countSp = 0;
|
|
1296
|
-
if (
|
|
1319
|
+
if (parentFlags & HAS_NORTHERN_SPLINE) {
|
|
1297
1320
|
splinePoints[countSp++] = getPos(parentNeighborIndices[0]);
|
|
1298
1321
|
}
|
|
1299
|
-
if (
|
|
1322
|
+
if (parentFlags & HAS_EASTERN_SPLINE) {
|
|
1300
1323
|
splinePoints[countSp++] = getPos(parentNeighborIndices[1]);
|
|
1301
1324
|
}
|
|
1302
|
-
if (
|
|
1325
|
+
if (parentFlags & HAS_SOUTHERN_SPLINE) {
|
|
1303
1326
|
splinePoints[countSp++] = getPos(parentNeighborIndices[2]);
|
|
1304
1327
|
}
|
|
1305
|
-
if (
|
|
1328
|
+
if (parentFlags & HAS_WESTERN_SPLINE) {
|
|
1306
1329
|
splinePoints[countSp++] = getPos(parentNeighborIndices[3]);
|
|
1307
1330
|
}
|
|
1308
1331
|
if (countSp === 2) {
|
|
@@ -1319,10 +1342,10 @@ function computeCorrectedPositions(cell, optimized) {
|
|
|
1319
1342
|
return corrected;
|
|
1320
1343
|
}
|
|
1321
1344
|
|
|
1322
|
-
function gaussRasterize(src, sim, cell, positions, outW, outH) {
|
|
1345
|
+
function gaussRasterize(src, sim, cell, positions, outW, outH, needSplines, outputSimilarityMask, similarity) {
|
|
1323
1346
|
const w = src.width;
|
|
1324
1347
|
const h = src.height;
|
|
1325
|
-
const out =
|
|
1348
|
+
const out = Buffer.alloc(outW * outH * 4);
|
|
1326
1349
|
|
|
1327
1350
|
function getG(x, y) {
|
|
1328
1351
|
if (x < 0 || y < 0 || x >= sim.w || y >= sim.h) {
|
|
@@ -1380,16 +1403,16 @@ function gaussRasterize(src, sim, cell, positions, outW, outH) {
|
|
|
1380
1403
|
|
|
1381
1404
|
function computeValence(flags) {
|
|
1382
1405
|
let v = 0;
|
|
1383
|
-
if (
|
|
1406
|
+
if (flags & HAS_NORTHERN_NEIGHBOR) {
|
|
1384
1407
|
v++;
|
|
1385
1408
|
}
|
|
1386
|
-
if (
|
|
1409
|
+
if (flags & HAS_EASTERN_NEIGHBOR) {
|
|
1387
1410
|
v++;
|
|
1388
1411
|
}
|
|
1389
|
-
if (
|
|
1412
|
+
if (flags & HAS_SOUTHERN_NEIGHBOR) {
|
|
1390
1413
|
v++;
|
|
1391
1414
|
}
|
|
1392
|
-
if (
|
|
1415
|
+
if (flags & HAS_WESTERN_NEIGHBOR) {
|
|
1393
1416
|
v++;
|
|
1394
1417
|
}
|
|
1395
1418
|
return v;
|
|
@@ -1422,7 +1445,7 @@ function gaussRasterize(src, sim, cell, positions, outW, outH) {
|
|
|
1422
1445
|
if ((node0neighborFlags & checkFwd[0]) === checkFwd[0]) {
|
|
1423
1446
|
const neighborsNeighborIndex = getNeighborIndex(node0neighborIndex, dir);
|
|
1424
1447
|
const neighborsNeighborflags = cell.flags[neighborsNeighborIndex] | 0;
|
|
1425
|
-
if (
|
|
1448
|
+
if (neighborsNeighborflags & HAS_CORRECTED_POSITION) {
|
|
1426
1449
|
cpArray[1] = neighborsNeighborIndex + 1;
|
|
1427
1450
|
} else {
|
|
1428
1451
|
cpArray[1] = neighborsNeighborIndex;
|
|
@@ -1430,7 +1453,7 @@ function gaussRasterize(src, sim, cell, positions, outW, outH) {
|
|
|
1430
1453
|
} else if ((node0neighborFlags & checkFwd[1]) === checkFwd[1]) {
|
|
1431
1454
|
const neighborsNeighborIndex = getNeighborIndex(node0neighborIndex, chkdirs[0]);
|
|
1432
1455
|
const neighborsNeighborflags = cell.flags[neighborsNeighborIndex] | 0;
|
|
1433
|
-
if (
|
|
1456
|
+
if (neighborsNeighborflags & HAS_CORRECTED_POSITION) {
|
|
1434
1457
|
cpArray[1] = neighborsNeighborIndex + 1;
|
|
1435
1458
|
} else {
|
|
1436
1459
|
cpArray[1] = neighborsNeighborIndex;
|
|
@@ -1438,20 +1461,32 @@ function gaussRasterize(src, sim, cell, positions, outW, outH) {
|
|
|
1438
1461
|
} else if ((node0neighborFlags & checkFwd[2]) === checkFwd[2]) {
|
|
1439
1462
|
const neighborsNeighborIndex = getNeighborIndex(node0neighborIndex, chkdirs[1]);
|
|
1440
1463
|
const neighborsNeighborflags = cell.flags[neighborsNeighborIndex] | 0;
|
|
1441
|
-
if (
|
|
1464
|
+
if (neighborsNeighborflags & HAS_CORRECTED_POSITION) {
|
|
1442
1465
|
cpArray[1] = neighborsNeighborIndex + 1;
|
|
1443
1466
|
} else {
|
|
1444
1467
|
cpArray[1] = neighborsNeighborIndex;
|
|
1445
1468
|
}
|
|
1446
1469
|
}
|
|
1447
1470
|
} else {
|
|
1448
|
-
if (
|
|
1471
|
+
if (node0neighborFlags & HAS_CORRECTED_POSITION) {
|
|
1449
1472
|
cpArray[0]++;
|
|
1450
1473
|
}
|
|
1451
1474
|
}
|
|
1452
1475
|
return cpArray;
|
|
1453
1476
|
}
|
|
1454
1477
|
|
|
1478
|
+
const allSplines = needSplines ? [] : null;
|
|
1479
|
+
const allSplinesDone = {};
|
|
1480
|
+
let similarityData;
|
|
1481
|
+
let simImage;
|
|
1482
|
+
if (outputSimilarityMask) {
|
|
1483
|
+
similarityData = Buffer.alloc(outW * outH * 4);
|
|
1484
|
+
simImage = {
|
|
1485
|
+
...src,
|
|
1486
|
+
data: similarity,
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1455
1490
|
for (let oy = 0; oy < outH; oy++) {
|
|
1456
1491
|
for (let ox = 0; ox < outW; ox++) {
|
|
1457
1492
|
let influencingPixels = [true, true, true, true];
|
|
@@ -1469,6 +1504,13 @@ function gaussRasterize(src, sim, cell, positions, outW, outH) {
|
|
|
1469
1504
|
const LRCoords = [ceil(cellSpaceCoords[0]), floor(cellSpaceCoords[1])];
|
|
1470
1505
|
|
|
1471
1506
|
function findSegmentIntersections(p0, p1, p2) {
|
|
1507
|
+
if (allSplines) {
|
|
1508
|
+
const key = [p0.join(), p1.join(), p2.join()].join();
|
|
1509
|
+
if (!allSplinesDone[key]) {
|
|
1510
|
+
allSplinesDone[key] = true;
|
|
1511
|
+
allSplines.push([p0, p1, p2]);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1472
1514
|
let pointA = calcSplinePoint(p0, p1, p2, 0.0);
|
|
1473
1515
|
for (let t = STEP; t < (1.0 + STEP); t += STEP) {
|
|
1474
1516
|
const pointB = calcSplinePoint(p0, p1, p2, t);
|
|
@@ -1499,13 +1541,13 @@ function gaussRasterize(src, sim, cell, positions, outW, outH) {
|
|
|
1499
1541
|
let node0pos = getPos(fragmentBaseKnotIndex);
|
|
1500
1542
|
if (node0valence === 1) {
|
|
1501
1543
|
let cpArray = [-1, -1];
|
|
1502
|
-
if (
|
|
1544
|
+
if (node0flags & HAS_NORTHERN_NEIGHBOR) {
|
|
1503
1545
|
cpArray = getCPs(node0neighbors[0], NORTH);
|
|
1504
|
-
} else if (
|
|
1546
|
+
} else if (node0flags & HAS_EASTERN_NEIGHBOR) {
|
|
1505
1547
|
cpArray = getCPs(node0neighbors[1], EAST);
|
|
1506
|
-
} else if (
|
|
1548
|
+
} else if (node0flags & HAS_SOUTHERN_NEIGHBOR) {
|
|
1507
1549
|
cpArray = getCPs(node0neighbors[2], SOUTH);
|
|
1508
|
-
} else if (
|
|
1550
|
+
} else if (node0flags & HAS_WESTERN_NEIGHBOR) {
|
|
1509
1551
|
cpArray = getCPs(node0neighbors[3], WEST);
|
|
1510
1552
|
}
|
|
1511
1553
|
const p1pos = getPos(cpArray[0]);
|
|
@@ -1519,18 +1561,18 @@ function gaussRasterize(src, sim, cell, positions, outW, outH) {
|
|
|
1519
1561
|
} else if (node0valence === 2) {
|
|
1520
1562
|
let cpArray = [-1, -1, -1, -1];
|
|
1521
1563
|
let foundFirst = false;
|
|
1522
|
-
if (
|
|
1523
|
-
if (
|
|
1564
|
+
if (node0flags & HAS_NORTHERN_NEIGHBOR) { cpArray[0] = getCPs(node0neighbors[0], NORTH)[0]; cpArray[1] = getCPs(node0neighbors[0], NORTH)[1]; foundFirst = true; }
|
|
1565
|
+
if (node0flags & HAS_EASTERN_NEIGHBOR) {
|
|
1524
1566
|
const tmp = getCPs(node0neighbors[1], EAST);
|
|
1525
1567
|
if (foundFirst) { cpArray[2] = tmp[0]; cpArray[3] = tmp[1]; }
|
|
1526
1568
|
else { cpArray[0] = tmp[0]; cpArray[1] = tmp[1]; foundFirst = true; }
|
|
1527
1569
|
}
|
|
1528
|
-
if (
|
|
1570
|
+
if (node0flags & HAS_SOUTHERN_NEIGHBOR) {
|
|
1529
1571
|
const tmp = getCPs(node0neighbors[2], SOUTH);
|
|
1530
1572
|
if (foundFirst) { cpArray[2] = tmp[0]; cpArray[3] = tmp[1]; }
|
|
1531
1573
|
else { cpArray[0] = tmp[0]; cpArray[1] = tmp[1]; foundFirst = true; }
|
|
1532
1574
|
}
|
|
1533
|
-
if (
|
|
1575
|
+
if (node0flags & HAS_WESTERN_NEIGHBOR) {
|
|
1534
1576
|
const tmp = getCPs(node0neighbors[3], WEST);
|
|
1535
1577
|
cpArray[2] = tmp[0]; cpArray[3] = tmp[1];
|
|
1536
1578
|
}
|
|
@@ -1555,24 +1597,24 @@ function gaussRasterize(src, sim, cell, positions, outW, outH) {
|
|
|
1555
1597
|
let foundFirst = false;
|
|
1556
1598
|
let tBaseDir = 0;
|
|
1557
1599
|
let tBaseNeighborIndex = -1;
|
|
1558
|
-
if (
|
|
1559
|
-
if (
|
|
1600
|
+
if (node0flags & HAS_NORTHERN_NEIGHBOR) {
|
|
1601
|
+
if (node0flags & HAS_NORTHERN_SPLINE) { const tmp = getCPs(node0neighbors[0], NORTH); cpArray[0] = tmp[0]; cpArray[1] = tmp[1]; foundFirst = true; }
|
|
1560
1602
|
else { tBaseDir = NORTH; tBaseNeighborIndex = node0neighbors[0]; }
|
|
1561
1603
|
}
|
|
1562
|
-
if (
|
|
1563
|
-
if (
|
|
1604
|
+
if (node0flags & HAS_EASTERN_NEIGHBOR) {
|
|
1605
|
+
if (node0flags & HAS_EASTERN_SPLINE) {
|
|
1564
1606
|
const tmp = getCPs(node0neighbors[1], EAST);
|
|
1565
1607
|
if (foundFirst) { cpArray[2] = tmp[0]; cpArray[3] = tmp[1]; } else { cpArray[0] = tmp[0]; cpArray[1] = tmp[1]; foundFirst = true; }
|
|
1566
1608
|
} else { tBaseDir = EAST; tBaseNeighborIndex = node0neighbors[1]; }
|
|
1567
1609
|
}
|
|
1568
|
-
if (
|
|
1569
|
-
if (
|
|
1610
|
+
if (node0flags & HAS_SOUTHERN_NEIGHBOR) {
|
|
1611
|
+
if (node0flags & HAS_SOUTHERN_SPLINE) {
|
|
1570
1612
|
const tmp = getCPs(node0neighbors[2], SOUTH);
|
|
1571
1613
|
if (foundFirst) { cpArray[2] = tmp[0]; cpArray[3] = tmp[1]; } else { cpArray[0] = tmp[0]; cpArray[1] = tmp[1]; foundFirst = true; }
|
|
1572
1614
|
} else { tBaseDir = SOUTH; tBaseNeighborIndex = node0neighbors[2]; }
|
|
1573
1615
|
}
|
|
1574
|
-
if (
|
|
1575
|
-
if (
|
|
1616
|
+
if (node0flags & HAS_WESTERN_NEIGHBOR) {
|
|
1617
|
+
if (node0flags & HAS_WESTERN_SPLINE) {
|
|
1576
1618
|
const tmp = getCPs(node0neighbors[3], WEST);
|
|
1577
1619
|
cpArray[2] = tmp[0]; cpArray[3] = tmp[1];
|
|
1578
1620
|
} else { tBaseDir = WEST; tBaseNeighborIndex = node0neighbors[3]; }
|
|
@@ -1655,13 +1697,13 @@ function gaussRasterize(src, sim, cell, positions, outW, outH) {
|
|
|
1655
1697
|
const node1pos = getPos(fragmentBaseKnotIndex + 1);
|
|
1656
1698
|
if (node1valence === 1) {
|
|
1657
1699
|
let cpArray = [-1, -1];
|
|
1658
|
-
if (
|
|
1700
|
+
if (node1flags & HAS_NORTHERN_NEIGHBOR) {
|
|
1659
1701
|
cpArray = getCPs(node1neighbors[0], NORTH);
|
|
1660
|
-
} else if (
|
|
1702
|
+
} else if (node1flags & HAS_EASTERN_NEIGHBOR) {
|
|
1661
1703
|
cpArray = getCPs(node1neighbors[1], EAST);
|
|
1662
|
-
} else if (
|
|
1704
|
+
} else if (node1flags & HAS_SOUTHERN_NEIGHBOR) {
|
|
1663
1705
|
cpArray = getCPs(node1neighbors[2], SOUTH);
|
|
1664
|
-
} else if (
|
|
1706
|
+
} else if (node1flags & HAS_WESTERN_NEIGHBOR) {
|
|
1665
1707
|
cpArray = getCPs(node1neighbors[3], WEST);
|
|
1666
1708
|
}
|
|
1667
1709
|
const p1pos = getPos(cpArray[0]);
|
|
@@ -1675,16 +1717,16 @@ function gaussRasterize(src, sim, cell, positions, outW, outH) {
|
|
|
1675
1717
|
} else if (node1valence === 2) {
|
|
1676
1718
|
let cpArray = [-1, -1, -1, -1];
|
|
1677
1719
|
let foundFirst = false;
|
|
1678
|
-
if (
|
|
1679
|
-
if (
|
|
1720
|
+
if (node1flags & HAS_NORTHERN_NEIGHBOR) { const tmp = getCPs(node1neighbors[0], NORTH); cpArray[0] = tmp[0]; cpArray[1] = tmp[1]; foundFirst = true; }
|
|
1721
|
+
if (node1flags & HAS_EASTERN_NEIGHBOR) {
|
|
1680
1722
|
const tmp = getCPs(node1neighbors[1], EAST);
|
|
1681
1723
|
if (foundFirst) { cpArray[2] = tmp[0]; cpArray[3] = tmp[1]; } else { cpArray[0] = tmp[0]; cpArray[1] = tmp[1]; foundFirst = true; }
|
|
1682
1724
|
}
|
|
1683
|
-
if (
|
|
1725
|
+
if (node1flags & HAS_SOUTHERN_NEIGHBOR) {
|
|
1684
1726
|
const tmp = getCPs(node1neighbors[2], SOUTH);
|
|
1685
1727
|
if (foundFirst) { cpArray[2] = tmp[0]; cpArray[3] = tmp[1]; } else { cpArray[0] = tmp[0]; cpArray[1] = tmp[1]; foundFirst = true; }
|
|
1686
1728
|
}
|
|
1687
|
-
if (
|
|
1729
|
+
if (node1flags & HAS_WESTERN_NEIGHBOR) {
|
|
1688
1730
|
const tmp = getCPs(node1neighbors[3], WEST);
|
|
1689
1731
|
cpArray[2] = tmp[0]; cpArray[3] = tmp[1];
|
|
1690
1732
|
}
|
|
@@ -1709,6 +1751,8 @@ function gaussRasterize(src, sim, cell, positions, outW, outH) {
|
|
|
1709
1751
|
|
|
1710
1752
|
let colorSum = [0, 0, 0, 0];
|
|
1711
1753
|
let weightSum = 0.0;
|
|
1754
|
+
let maxWeight = 0;
|
|
1755
|
+
let maxSimilarity = null;
|
|
1712
1756
|
|
|
1713
1757
|
function addWeightedColor(px, py) {
|
|
1714
1758
|
const col = fetchPixelRGBA8(src, px, py);
|
|
@@ -1721,75 +1765,80 @@ function gaussRasterize(src, sim, cell, positions, outW, outH) {
|
|
|
1721
1765
|
colorSum[2] += col[2] * weight;
|
|
1722
1766
|
colorSum[3] += col[3] * weight;
|
|
1723
1767
|
weightSum += weight;
|
|
1768
|
+
if (outputSimilarityMask && weight >= maxWeight) {
|
|
1769
|
+
// note: would also be reasonable to blend this like we blend the colors
|
|
1770
|
+
maxWeight = weight;
|
|
1771
|
+
maxSimilarity = fetchPixelRGBA8(simImage, px, py);
|
|
1772
|
+
}
|
|
1724
1773
|
}
|
|
1725
1774
|
|
|
1726
1775
|
if (influencingPixels[0]) {
|
|
1727
1776
|
addWeightedColor(ULCoords[0], ULCoords[1]);
|
|
1728
1777
|
const edges = getG(2 * ULCoords[0] + 1, 2 * ULCoords[1] + 1);
|
|
1729
|
-
if (
|
|
1778
|
+
if (edges & SOUTHWEST) {
|
|
1730
1779
|
addWeightedColor(ULCoords[0] - 1, ULCoords[1] - 1);
|
|
1731
1780
|
}
|
|
1732
|
-
if (
|
|
1781
|
+
if (edges & WEST) {
|
|
1733
1782
|
addWeightedColor(ULCoords[0] - 1, ULCoords[1]);
|
|
1734
1783
|
}
|
|
1735
|
-
if (
|
|
1784
|
+
if (edges & NORTHWEST) {
|
|
1736
1785
|
addWeightedColor(ULCoords[0] - 1, ULCoords[1] + 1);
|
|
1737
1786
|
}
|
|
1738
|
-
if (
|
|
1787
|
+
if (edges & NORTH) {
|
|
1739
1788
|
addWeightedColor(ULCoords[0], ULCoords[1] + 1);
|
|
1740
1789
|
}
|
|
1741
|
-
if (
|
|
1790
|
+
if (edges & NORTHEAST) {
|
|
1742
1791
|
addWeightedColor(ULCoords[0] + 1, ULCoords[1] + 1);
|
|
1743
1792
|
}
|
|
1744
1793
|
}
|
|
1745
1794
|
if (influencingPixels[1]) {
|
|
1746
1795
|
addWeightedColor(URCoords[0], URCoords[1]);
|
|
1747
1796
|
const edges = getG(2 * URCoords[0] + 1, 2 * URCoords[1] + 1);
|
|
1748
|
-
if (
|
|
1797
|
+
if (edges & NORTH) {
|
|
1749
1798
|
addWeightedColor(URCoords[0], URCoords[1] + 1);
|
|
1750
1799
|
}
|
|
1751
|
-
if (
|
|
1800
|
+
if (edges & NORTHEAST) {
|
|
1752
1801
|
addWeightedColor(URCoords[0] + 1, URCoords[1] + 1);
|
|
1753
1802
|
}
|
|
1754
|
-
if (
|
|
1803
|
+
if (edges & EAST) {
|
|
1755
1804
|
addWeightedColor(URCoords[0] + 1, URCoords[1]);
|
|
1756
1805
|
}
|
|
1757
|
-
if (
|
|
1806
|
+
if (edges & SOUTHEAST) {
|
|
1758
1807
|
addWeightedColor(URCoords[0] + 1, URCoords[1] - 1);
|
|
1759
1808
|
}
|
|
1760
1809
|
}
|
|
1761
1810
|
if (influencingPixels[2]) {
|
|
1762
1811
|
addWeightedColor(LLCoords[0], LLCoords[1]);
|
|
1763
1812
|
const edges = getG(2 * LLCoords[0] + 1, 2 * LLCoords[1] + 1);
|
|
1764
|
-
if (
|
|
1813
|
+
if (edges & WEST) {
|
|
1765
1814
|
addWeightedColor(LLCoords[0] - 1, LLCoords[1]);
|
|
1766
1815
|
}
|
|
1767
|
-
if (
|
|
1816
|
+
if (edges & SOUTHWEST) {
|
|
1768
1817
|
addWeightedColor(LLCoords[0] - 1, LLCoords[1] - 1);
|
|
1769
1818
|
}
|
|
1770
|
-
if (
|
|
1819
|
+
if (edges & SOUTH) {
|
|
1771
1820
|
addWeightedColor(LLCoords[0], LLCoords[1] - 1);
|
|
1772
1821
|
}
|
|
1773
|
-
if (
|
|
1822
|
+
if (edges & SOUTHEAST) {
|
|
1774
1823
|
addWeightedColor(LLCoords[0] + 1, LLCoords[1] - 1);
|
|
1775
1824
|
}
|
|
1776
1825
|
}
|
|
1777
1826
|
if (influencingPixels[3]) {
|
|
1778
1827
|
addWeightedColor(LRCoords[0], LRCoords[1]);
|
|
1779
1828
|
const edges = getG(2 * LRCoords[0] + 1, 2 * LRCoords[1] + 1);
|
|
1780
|
-
if (
|
|
1829
|
+
if (edges & NORTHEAST) {
|
|
1781
1830
|
addWeightedColor(LRCoords[0] + 1, LRCoords[1] + 1);
|
|
1782
1831
|
}
|
|
1783
|
-
if (
|
|
1832
|
+
if (edges & EAST) {
|
|
1784
1833
|
addWeightedColor(LRCoords[0] + 1, LRCoords[1]);
|
|
1785
1834
|
}
|
|
1786
|
-
if (
|
|
1835
|
+
if (edges & SOUTHWEST) {
|
|
1787
1836
|
addWeightedColor(LRCoords[0] - 1, LRCoords[1] - 1);
|
|
1788
1837
|
}
|
|
1789
|
-
if (
|
|
1838
|
+
if (edges & SOUTH) {
|
|
1790
1839
|
addWeightedColor(LRCoords[0], LRCoords[1] - 1);
|
|
1791
1840
|
}
|
|
1792
|
-
if (
|
|
1841
|
+
if (edges & SOUTHEAST) {
|
|
1793
1842
|
addWeightedColor(LRCoords[0] + 1, LRCoords[1] - 1);
|
|
1794
1843
|
}
|
|
1795
1844
|
}
|
|
@@ -1803,38 +1852,127 @@ function gaussRasterize(src, sim, cell, positions, outW, outH) {
|
|
|
1803
1852
|
out[outIdx + 1] = col[1];
|
|
1804
1853
|
out[outIdx + 2] = col[2];
|
|
1805
1854
|
out[outIdx + 3] = col[3];
|
|
1855
|
+
if (outputSimilarityMask) {
|
|
1856
|
+
maxSimilarity = fetchPixelRGBA8(simImage, nx, ny);
|
|
1857
|
+
}
|
|
1806
1858
|
} else {
|
|
1807
1859
|
out[outIdx] = clampInt(round((colorSum[0] / weightSum)), 0, 255);
|
|
1808
1860
|
out[outIdx + 1] = clampInt(round((colorSum[1] / weightSum)), 0, 255);
|
|
1809
1861
|
out[outIdx + 2] = clampInt(round((colorSum[2] / weightSum)), 0, 255);
|
|
1810
1862
|
out[outIdx + 3] = clampInt(round((colorSum[3] / weightSum)), 0, 255);
|
|
1811
1863
|
}
|
|
1864
|
+
if (outputSimilarityMask) {
|
|
1865
|
+
similarityData[outIdx] = maxSimilarity[0];
|
|
1866
|
+
similarityData[outIdx + 1] = maxSimilarity[1];
|
|
1867
|
+
similarityData[outIdx + 2] = maxSimilarity[2];
|
|
1868
|
+
similarityData[outIdx + 3] = maxSimilarity[3];
|
|
1869
|
+
}
|
|
1812
1870
|
}
|
|
1813
1871
|
}
|
|
1814
1872
|
|
|
1815
|
-
return
|
|
1873
|
+
return {
|
|
1874
|
+
data: out,
|
|
1875
|
+
width: outW,
|
|
1876
|
+
height: outH,
|
|
1877
|
+
allSplines,
|
|
1878
|
+
similarityData,
|
|
1879
|
+
};
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
function renderSplines(out, outW, outH, inW, inH, allSplines) {
|
|
1883
|
+
for (let ii = 0; ii < out.length; ++ii) {
|
|
1884
|
+
out[ii] = 0;
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
function mapToOutput(p) {
|
|
1888
|
+
const x = (p[0] / (inW - 1)) * (outW - 1);
|
|
1889
|
+
const y = (p[1] / (inH - 1)) * (outH - 1);
|
|
1890
|
+
return [x, y];
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
function drawLine(p0, p1) {
|
|
1894
|
+
let x0 = round(p0[0]);
|
|
1895
|
+
let y0 = round(p0[1]);
|
|
1896
|
+
let x1 = round(p1[0]);
|
|
1897
|
+
let y1 = round(p1[1]);
|
|
1898
|
+
const dx = abs(x1 - x0);
|
|
1899
|
+
const dy = abs(y1 - y0);
|
|
1900
|
+
const sx = x0 < x1 ? 1 : -1;
|
|
1901
|
+
const sy = y0 < y1 ? 1 : -1;
|
|
1902
|
+
let err = dx - dy;
|
|
1903
|
+
while (true) {
|
|
1904
|
+
if (x0 >= 0 && x0 < outW && y0 >= 0 && y0 < outH) {
|
|
1905
|
+
const idx = (y0 * outW + x0) * 4;
|
|
1906
|
+
out[idx] = 0;
|
|
1907
|
+
out[idx + 1] = 0;
|
|
1908
|
+
out[idx + 2] = 0;
|
|
1909
|
+
out[idx + 3] = 255;
|
|
1910
|
+
}
|
|
1911
|
+
if (x0 === x1 && y0 === y1) {
|
|
1912
|
+
break;
|
|
1913
|
+
}
|
|
1914
|
+
const e2 = 2 * err;
|
|
1915
|
+
if (e2 > -dy) { err -= dy; x0 += sx; }
|
|
1916
|
+
if (e2 < dx) { err += dx; y0 += sy; }
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
function calcSplinePoint(p0, p1, p2, t) {
|
|
1921
|
+
const t2 = 0.5 * t * t;
|
|
1922
|
+
const a = t2 - t + 0.5;
|
|
1923
|
+
const b = -2.0 * t2 + t + 0.5;
|
|
1924
|
+
return [a * p0[0] + b * p1[0] + t2 * p2[0], a * p0[1] + b * p1[1] + t2 * p2[1]];
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
for (let i = 0; i < allSplines.length; i++) {
|
|
1928
|
+
const spline = allSplines[i];
|
|
1929
|
+
const p0 = spline[0];
|
|
1930
|
+
const p1 = spline[1];
|
|
1931
|
+
const p2 = spline[2];
|
|
1932
|
+
let prev = null;
|
|
1933
|
+
for (let t = 0.0; t < (1.0 + STEP); t += STEP) {
|
|
1934
|
+
const p = calcSplinePoint(p0, p1, p2, t);
|
|
1935
|
+
const outP = mapToOutput(p);
|
|
1936
|
+
if (prev) {
|
|
1937
|
+
drawLine(prev, outP);
|
|
1938
|
+
}
|
|
1939
|
+
prev = outP;
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1816
1942
|
}
|
|
1817
1943
|
|
|
1818
|
-
function runPipeline(src, outH, threshold) {
|
|
1944
|
+
function runPipeline(src, outH, threshold, similarity, renderMode, doOpt, outputSimilarityMask) {
|
|
1819
1945
|
const inW = src.width | 0;
|
|
1820
1946
|
const inH = src.height | 0;
|
|
1821
1947
|
const outHeight = outH | 0;
|
|
1822
1948
|
const outWidth = max(1, round((inW / inH) * outHeight));
|
|
1823
1949
|
|
|
1824
|
-
const similarityThreshold = ((typeof threshold === "number") ? threshold : 255) / 255;
|
|
1950
|
+
const similarityThreshold = similarity ? threshold : ((typeof threshold === "number") ? threshold : 255) / 255;
|
|
1825
1951
|
|
|
1826
|
-
const sim0 = buildSimilarityGraph(src, similarityThreshold);
|
|
1952
|
+
const sim0 = buildSimilarityGraph(src, similarityThreshold, similarity);
|
|
1827
1953
|
const sim1 = valenceUpdate(sim0);
|
|
1828
1954
|
const sim2 = eliminateCrossings(sim1);
|
|
1829
1955
|
const sim3 = valenceUpdate(sim2);
|
|
1830
1956
|
|
|
1831
1957
|
const cell = computeCellGraph(src, sim3);
|
|
1832
|
-
|
|
1833
|
-
|
|
1958
|
+
let corrected;
|
|
1959
|
+
if (doOpt) {
|
|
1960
|
+
const optimized = optimizeCellGraph(cell, inW, inH);
|
|
1961
|
+
corrected = computeCorrectedPositions(cell, optimized);
|
|
1962
|
+
} else {
|
|
1963
|
+
corrected = computeCorrectedPositions(cell, cell.pos);
|
|
1964
|
+
}
|
|
1834
1965
|
|
|
1835
|
-
const
|
|
1966
|
+
const out = gaussRasterize(
|
|
1967
|
+
src,
|
|
1968
|
+
sim3, cell, corrected, outWidth, outHeight,
|
|
1969
|
+
renderMode === 'splines',
|
|
1970
|
+
outputSimilarityMask, similarity);
|
|
1971
|
+
if (renderMode === 'splines') {
|
|
1972
|
+
renderSplines(out.data, outWidth, outHeight, inW, inH, out.allSplines);
|
|
1973
|
+
}
|
|
1836
1974
|
|
|
1837
|
-
return
|
|
1975
|
+
return out;
|
|
1838
1976
|
}
|
|
1839
1977
|
|
|
1840
1978
|
function scaleImage(src, opts) {
|
|
@@ -1847,30 +1985,47 @@ function scaleImage(src, opts) {
|
|
|
1847
1985
|
const inW = src.width | 0;
|
|
1848
1986
|
const inH = src.height | 0;
|
|
1849
1987
|
const outH = opts.height | 0;
|
|
1850
|
-
const
|
|
1988
|
+
const similarity = opts.similarity;
|
|
1989
|
+
const threshold = typeof opts.threshold === 'number' ? opts.threshold : (similarity ? 3 : 255);
|
|
1990
|
+
const renderMode = opts.renderMode || 'default';
|
|
1991
|
+
const outputSimilarityMask = opts.outputSimilarityMask || false;
|
|
1992
|
+
const doOpt = opts.doOpt ?? true;
|
|
1993
|
+
if (outputSimilarityMask && !similarity) {
|
|
1994
|
+
throw new Error('outputSimilarityMask requires a similarity mask input');
|
|
1995
|
+
}
|
|
1851
1996
|
|
|
1852
1997
|
const borderPx = max(0, round(opts.borderPx || 0));
|
|
1853
1998
|
if (borderPx > 0) {
|
|
1854
1999
|
const padW = inW + 2 * borderPx;
|
|
1855
2000
|
const padH = inH + 2 * borderPx;
|
|
1856
|
-
const padData =
|
|
2001
|
+
const padData = Buffer.alloc(padW * padH * 4);
|
|
2002
|
+
const padSimilarity = similarity && Buffer.alloc(padW * padH * 4);
|
|
1857
2003
|
// copy src into center, expand into borders
|
|
1858
2004
|
for (let y = 0; y < padH; y++) {
|
|
1859
2005
|
const srcRow = min(inH - 1, max(0, y - borderPx)) * inW * 4;
|
|
1860
2006
|
const dstRow = y * padW * 4 + borderPx * 4;
|
|
1861
|
-
|
|
2007
|
+
src.data.copy(padData, dstRow, srcRow, srcRow + inW * 4);
|
|
2008
|
+
if (padSimilarity) {
|
|
2009
|
+
similarity.copy(padSimilarity, dstRow, srcRow, srcRow + inW * 4);
|
|
2010
|
+
}
|
|
1862
2011
|
for (let ii = 0; ii < borderPx * 4; ++ii) {
|
|
1863
2012
|
padData[dstRow - borderPx * 4 + ii] = src.data[srcRow + ii % 4];
|
|
1864
|
-
padData[dstRow + inW * 4 + ii] = src.data[srcRow + ii % 4];
|
|
2013
|
+
padData[dstRow + inW * 4 + ii] = src.data[srcRow + (inW - 1) * 4 + ii % 4];
|
|
2014
|
+
if (padSimilarity) {
|
|
2015
|
+
padSimilarity[dstRow - borderPx * 4 + ii] = similarity[srcRow + ii % 4];
|
|
2016
|
+
padSimilarity[dstRow + inW * 4 + ii] = similarity[srcRow + (inW - 1) * 4 + ii % 4];
|
|
2017
|
+
}
|
|
1865
2018
|
}
|
|
1866
2019
|
}
|
|
1867
2020
|
const scale = outH / inH;
|
|
1868
2021
|
const outHpad = max(1, round(padH * scale));
|
|
1869
|
-
const padded = runPipeline({ data: padData, width: padW, height: padH }, outHpad, threshold);
|
|
2022
|
+
const padded = runPipeline({ data: padData, width: padW, height: padH }, outHpad, threshold, padSimilarity, renderMode, doOpt, outputSimilarityMask);
|
|
1870
2023
|
|
|
1871
2024
|
const padOut = max(0, round(borderPx * scale));
|
|
1872
2025
|
const outW = max(1, round((inW / inH) * outH));
|
|
1873
|
-
const cropped =
|
|
2026
|
+
const cropped = Buffer.alloc(outW * outH * 4);
|
|
2027
|
+
const croppedSimilarity = padded.similarityData && Buffer.alloc(outW * outH * 4);
|
|
2028
|
+
const srcData = padded.data;
|
|
1874
2029
|
|
|
1875
2030
|
for (let y = 0; y < outH; y++) {
|
|
1876
2031
|
const srcY = y + padOut;
|
|
@@ -1880,14 +2035,27 @@ function scaleImage(src, opts) {
|
|
|
1880
2035
|
const srcRow = (srcY * padded.width + padOut) * 4;
|
|
1881
2036
|
const dstRow = y * outW * 4;
|
|
1882
2037
|
const len = min(outW, max(0, padded.width - padOut)) * 4;
|
|
1883
|
-
|
|
2038
|
+
srcData.copy(cropped, dstRow, srcRow, srcRow + len);
|
|
2039
|
+
if (padded.similarityData) {
|
|
2040
|
+
padded.similarityData.copy(croppedSimilarity, dstRow, srcRow, srcRow + len);
|
|
2041
|
+
}
|
|
1884
2042
|
}
|
|
1885
2043
|
|
|
1886
|
-
return {
|
|
2044
|
+
return {
|
|
2045
|
+
data: cropped,
|
|
2046
|
+
width: outW,
|
|
2047
|
+
height: outH,
|
|
2048
|
+
similarityData: croppedSimilarity,
|
|
2049
|
+
};
|
|
1887
2050
|
}
|
|
1888
2051
|
|
|
1889
|
-
const result = runPipeline(src, outH, threshold);
|
|
1890
|
-
return {
|
|
2052
|
+
const result = runPipeline(src, outH, threshold, similarity, renderMode, doOpt, outputSimilarityMask);
|
|
2053
|
+
return {
|
|
2054
|
+
data: result.data,
|
|
2055
|
+
width: result.width,
|
|
2056
|
+
height: result.height,
|
|
2057
|
+
similarityData: result.similarityData,
|
|
2058
|
+
};
|
|
1891
2059
|
}
|
|
1892
2060
|
|
|
1893
2061
|
exports.scaleImage = scaleImage;
|