supervision 0.2.9 → 0.2.10
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/dist/{depth-preview-probe-DG7McQEN.js → depth-preview-probe-BG0k-33U.js} +3 -3
- package/dist/{depth-preview-probe-DG7McQEN.js.map → depth-preview-probe-BG0k-33U.js.map} +1 -1
- package/dist/{depth-preview-track-BvgwNEE9.js → depth-preview-track-BEag1ZWF.js} +2 -2
- package/dist/{depth-preview-track-BvgwNEE9.js.map → depth-preview-track-BEag1ZWF.js.map} +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2041 -478
- package/dist/index.js.map +1 -1
- package/dist/mask-preparation.worker.js +166 -45
- package/dist/mask-preparation.worker.js.map +1 -1
- package/dist/media/display-pixel-ratio.d.ts +3 -8
- package/dist/media/display-pixel-ratio.d.ts.map +1 -1
- package/dist/media/video-engine-media-source.js +1 -1
- package/dist/render-preparation/mask-frame-artifact.d.ts +7 -1
- package/dist/render-preparation/mask-frame-artifact.d.ts.map +1 -1
- package/dist/render-preparation/mask-frame-compositor.d.ts +1 -1
- package/dist/render-preparation/mask-frame-compositor.d.ts.map +1 -1
- package/dist/render-preparation/mask-preparation-worker-protocol.d.ts +3 -0
- package/dist/render-preparation/mask-preparation-worker-protocol.d.ts.map +1 -1
- package/dist/render-preparation/prepared-render-window.d.ts +2 -0
- package/dist/render-preparation/prepared-render-window.d.ts.map +1 -1
- package/dist/renderers/annotation-fxaa.d.ts +37 -0
- package/dist/renderers/annotation-fxaa.d.ts.map +1 -0
- package/dist/renderers/id-mask-stroke-coverage.d.ts +2 -2
- package/dist/renderers/id-mask-stroke-coverage.d.ts.map +1 -1
- package/dist/renderers/mask-palette.d.ts +4 -8
- package/dist/renderers/mask-palette.d.ts.map +1 -1
- package/dist/renderers/media-renderer-core.d.ts.map +1 -1
- package/dist/renderers/media-renderer-scene.d.ts +1 -0
- package/dist/renderers/media-renderer-scene.d.ts.map +1 -1
- package/dist/renderers/pixi-annotation-antialias-layer.d.ts +15 -0
- package/dist/renderers/pixi-annotation-antialias-layer.d.ts.map +1 -0
- package/dist/renderers/pixi-annotation-antialias.d.ts +14 -0
- package/dist/renderers/pixi-annotation-antialias.d.ts.map +1 -0
- package/dist/renderers/pixi-annotation-overlay-layer.d.ts +6 -1
- package/dist/renderers/pixi-annotation-overlay-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-box-layer.d.ts +6 -1
- package/dist/renderers/pixi-box-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-filter-bindings.d.ts +6 -0
- package/dist/renderers/pixi-filter-bindings.d.ts.map +1 -0
- package/dist/renderers/pixi-focus-heatmap-cutout.d.ts +17 -0
- package/dist/renderers/pixi-focus-heatmap-cutout.d.ts.map +1 -0
- package/dist/renderers/pixi-focus-layer.d.ts +18 -1
- package/dist/renderers/pixi-focus-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-heatmap-layer.d.ts +4 -1
- package/dist/renderers/pixi-heatmap-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-id-mask-shader.d.ts +2 -1
- package/dist/renderers/pixi-id-mask-shader.d.ts.map +1 -1
- package/dist/renderers/pixi-interaction-presentation-layer.d.ts +5 -1
- package/dist/renderers/pixi-interaction-presentation-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-label-layer.d.ts +8 -4
- package/dist/renderers/pixi-label-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-mask-layer.d.ts +2 -1
- package/dist/renderers/pixi-mask-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-mask-screen-stroke.d.ts +1 -1
- package/dist/renderers/pixi-mask-screen-stroke.d.ts.map +1 -1
- package/dist/renderers/pixi-mask-viewport.d.ts +21 -0
- package/dist/renderers/pixi-mask-viewport.d.ts.map +1 -0
- package/dist/renderers/pixi-media-scene.d.ts.map +1 -1
- package/dist/renderers/pixi-polygon-layer.d.ts +3 -0
- package/dist/renderers/pixi-polygon-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-region-coverage-mask.d.ts +14 -3
- package/dist/renderers/pixi-region-coverage-mask.d.ts.map +1 -1
- package/dist/renderers/pixi-region-layer.d.ts +7 -1
- package/dist/renderers/pixi-region-layer.d.ts.map +1 -1
- package/dist/sessions/media-session.d.ts.map +1 -1
- package/dist/tracking.worker.js.map +1 -1
- package/dist/types/media-renderer.d.ts +1 -1
- package/dist/types/media-renderer.d.ts.map +1 -1
- package/dist/{video-engine-media-source-DOFsuGS4.js → video-engine-media-source-MFthAkbe.js} +4 -9
- package/dist/video-engine-media-source-MFthAkbe.js.map +1 -0
- package/node_modules/supervision-js-core/dist/index.d.ts +2 -2
- package/node_modules/supervision-js-core/dist/index.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/index.js +57 -25
- package/node_modules/supervision-js-core/dist/index.js.map +1 -1
- package/node_modules/supervision-js-core/dist/styles/default-annotation-presentation.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/styles/focus-style.d.ts +4 -3
- package/node_modules/supervision-js-core/dist/styles/focus-style.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/styles/mask-style.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/focus-style.d.ts +2 -1
- package/node_modules/supervision-js-core/dist/types/focus-style.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/label-style.d.ts +4 -1
- package/node_modules/supervision-js-core/dist/types/label-style.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/mask-style.d.ts +8 -1
- package/node_modules/supervision-js-core/dist/types/mask-style.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts +14 -0
- package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/utils/detection-conversions.d.ts +1 -0
- package/node_modules/supervision-js-core/dist/utils/detection-conversions.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/utils/id-mask-frame.d.ts +21 -7
- package/node_modules/supervision-js-core/dist/utils/id-mask-frame.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/video-engine-media-source-DOFsuGS4.js.map +0 -1
|
@@ -508,8 +508,15 @@
|
|
|
508
508
|
new Set(Object.values(DepthSampling));
|
|
509
509
|
function rasterizePolygonToMask(points, dimensions) {
|
|
510
510
|
const data = createEmptyMask(dimensions);
|
|
511
|
+
forEachRasterPolygonSpan(points, dimensions, (y, left, right) => {
|
|
512
|
+
data.fill(1, y * dimensions.width + left, y * dimensions.width + right);
|
|
513
|
+
});
|
|
514
|
+
return data;
|
|
515
|
+
}
|
|
516
|
+
function forEachRasterPolygonSpan(points, dimensions, writeSpan) {
|
|
517
|
+
assertMediaDimensions(dimensions);
|
|
511
518
|
if (points.length < 3) {
|
|
512
|
-
return
|
|
519
|
+
return;
|
|
513
520
|
}
|
|
514
521
|
const bounds = centerRectToTopLeftRect(getPointsRect(points));
|
|
515
522
|
const startY = Math.max(0, Math.floor(bounds.y));
|
|
@@ -530,27 +537,29 @@
|
|
|
530
537
|
for (let index = 0; index < intersections.length - 1; index += 2) {
|
|
531
538
|
const left = Math.max(0, Math.round(intersections[index]));
|
|
532
539
|
const right = Math.min(dimensions.width, Math.round(intersections[index + 1]));
|
|
533
|
-
|
|
534
|
-
|
|
540
|
+
if (left < right) {
|
|
541
|
+
writeSpan(y, left, right);
|
|
535
542
|
}
|
|
536
543
|
}
|
|
537
544
|
}
|
|
538
|
-
return data;
|
|
539
545
|
}
|
|
540
546
|
function createEmptyMask(dimensions) {
|
|
547
|
+
assertMediaDimensions(dimensions);
|
|
548
|
+
return new Uint8Array(dimensions.width * dimensions.height);
|
|
549
|
+
}
|
|
550
|
+
function assertMediaDimensions(dimensions) {
|
|
541
551
|
if (!Number.isInteger(dimensions.width) ||
|
|
542
552
|
dimensions.width <= 0 ||
|
|
543
553
|
!Number.isInteger(dimensions.height) ||
|
|
544
554
|
dimensions.height <= 0) {
|
|
545
555
|
throw new Error("Media dimensions must be positive integers.");
|
|
546
556
|
}
|
|
547
|
-
return new Uint8Array(dimensions.width * dimensions.height);
|
|
548
557
|
}
|
|
549
558
|
|
|
550
559
|
/**
|
|
551
560
|
* Palette slots an id raster can name, one of them the background. A GLSL
|
|
552
561
|
* fragment stage is guaranteed only 224 uniform vectors, and a palette entry
|
|
553
|
-
* costs 2.
|
|
562
|
+
* costs 2.5 of them, so this stays a multiple of four and well inside that
|
|
554
563
|
* budget; past it a frame draws from the RGBA composite instead.
|
|
555
564
|
*/
|
|
556
565
|
const MAX_ID_MASK_PALETTE_ENTRIES = 80;
|
|
@@ -561,8 +570,8 @@
|
|
|
561
570
|
}
|
|
562
571
|
/* Pixel-grid dimensions use integer parts throughout allocation and RLE
|
|
563
572
|
traversal. */
|
|
564
|
-
const maskWidth = Math.floor(Math.max(...instructions.map((
|
|
565
|
-
const maskHeight = Math.floor(Math.max(...instructions.map((
|
|
573
|
+
const maskWidth = Math.floor(Math.max(...instructions.map((instruction) => (instruction.mask ?? instruction.polygon).width)));
|
|
574
|
+
const maskHeight = Math.floor(Math.max(...instructions.map((instruction) => (instruction.mask ?? instruction.polygon).height)));
|
|
566
575
|
const width = resolveRasterWidth(maskWidth, options.maxWidth);
|
|
567
576
|
const height = width === maskWidth
|
|
568
577
|
? maskHeight
|
|
@@ -574,6 +583,7 @@
|
|
|
574
583
|
const fillPalette = new Float32Array(new ArrayBuffer(MAX_ID_MASK_PALETTE_ENTRIES * 4 * 4));
|
|
575
584
|
const strokePalette = new Float32Array(new ArrayBuffer(MAX_ID_MASK_PALETTE_ENTRIES * 4 * 4));
|
|
576
585
|
const strokeWidths = new Float32Array(new ArrayBuffer(MAX_ID_MASK_PALETTE_ENTRIES * 4));
|
|
586
|
+
const strokeAlignments = new Float32Array(new ArrayBuffer(MAX_ID_MASK_PALETTE_ENTRIES * 4));
|
|
577
587
|
let hasStroke = false;
|
|
578
588
|
let maxStrokeWidth = 0;
|
|
579
589
|
for (const instruction of instructions) {
|
|
@@ -583,13 +593,22 @@
|
|
|
583
593
|
}
|
|
584
594
|
writeIdMaskPaletteEntry(fillPalette, detectionMaskId, instruction.color, instruction.alpha);
|
|
585
595
|
if (instruction.stroke && instruction.stroke.width > 0) {
|
|
586
|
-
const strokeWidth =
|
|
596
|
+
const strokeWidth = instruction.stroke.width;
|
|
597
|
+
const alignment = instruction.stroke.alignment === StrokeAlignment.Inside
|
|
598
|
+
? 1
|
|
599
|
+
: instruction.stroke.alignment === StrokeAlignment.Center
|
|
600
|
+
? 0.5
|
|
601
|
+
: 0;
|
|
587
602
|
hasStroke = true;
|
|
588
603
|
strokeWidths[detectionMaskId] = strokeWidth;
|
|
589
|
-
|
|
604
|
+
strokeAlignments[detectionMaskId] = alignment;
|
|
605
|
+
maxStrokeWidth = Math.max(maxStrokeWidth, strokeWidth * (1 - alignment));
|
|
590
606
|
writeIdMaskPaletteEntry(strokePalette, detectionMaskId, instruction.stroke.color, instruction.stroke.alpha);
|
|
591
607
|
}
|
|
592
|
-
if (
|
|
608
|
+
if (instruction.polygon) {
|
|
609
|
+
writePolygonSpans(data, width, instruction.polygon, detectionMaskId, scaledAxes);
|
|
610
|
+
}
|
|
611
|
+
else if (scaledAxes) {
|
|
593
612
|
writeScaledMaskRuns(data, scaledAxes, instruction.mask, detectionMaskId);
|
|
594
613
|
}
|
|
595
614
|
else {
|
|
@@ -604,10 +623,19 @@
|
|
|
604
623
|
maxStrokeWidth,
|
|
605
624
|
sourceWidth: maskWidth,
|
|
606
625
|
strokePalette,
|
|
626
|
+
strokeAlignments,
|
|
607
627
|
strokeWidths,
|
|
608
628
|
width,
|
|
609
629
|
};
|
|
610
630
|
}
|
|
631
|
+
function writePolygonSpans(data, frameWidth, polygon, detectionMaskId, axes) {
|
|
632
|
+
forEachRasterPolygonSpan(polygon.points, polygon, (y, left, right) => {
|
|
633
|
+
const row = axes ? axes.rows[y] : y * frameWidth;
|
|
634
|
+
const first = axes ? axes.columns[left] : left;
|
|
635
|
+
const last = axes ? axes.columns[right - 1] + 1 : right;
|
|
636
|
+
data.fill(detectionMaskId, row + first, row + last);
|
|
637
|
+
});
|
|
638
|
+
}
|
|
611
639
|
/**
|
|
612
640
|
* Compressed RLE counts runs down each column in turn, so a foreground run is
|
|
613
641
|
* a contiguous walk down one column that wraps into the next.
|
|
@@ -696,13 +724,13 @@
|
|
|
696
724
|
: maskWidth;
|
|
697
725
|
}
|
|
698
726
|
/**
|
|
699
|
-
* Converts
|
|
700
|
-
*
|
|
701
|
-
* neighbourhood the shaders scan.
|
|
727
|
+
* Converts total CSS outline width into raster texels. Each side is bounded
|
|
728
|
+
* independently so centered outlines can use the full supported radius.
|
|
702
729
|
*/
|
|
703
|
-
function resolveIdMaskStrokeTexels(strokeWidth,
|
|
704
|
-
const scale =
|
|
705
|
-
|
|
730
|
+
function resolveIdMaskStrokeTexels(strokeWidth, displayWidth, rasterWidth, alignment = StrokeAlignment.Outside) {
|
|
731
|
+
const scale = displayWidth > 0 ? rasterWidth / displayWidth : 1;
|
|
732
|
+
const sideFraction = alignment === StrokeAlignment.Center ? 0.5 : 1;
|
|
733
|
+
return Math.min(strokeWidth * scale, MAX_ID_MASK_STROKE_WIDTH / sideFraction);
|
|
706
734
|
}
|
|
707
735
|
function createScaledMaskAxes(frame) {
|
|
708
736
|
return {
|
|
@@ -948,7 +976,7 @@
|
|
|
948
976
|
}
|
|
949
977
|
return { data, detectionIndex, height, width, x: minX, y: minY };
|
|
950
978
|
}
|
|
951
|
-
function compositeMaskFrame(instructions) {
|
|
979
|
+
function compositeMaskFrame(instructions, displayWidth) {
|
|
952
980
|
const maskInstructions = materializeMaskInstructions(instructions);
|
|
953
981
|
if (maskInstructions.length === 0) {
|
|
954
982
|
return undefined;
|
|
@@ -956,18 +984,22 @@
|
|
|
956
984
|
const width = Math.max(...maskInstructions.map(({ mask }) => mask.width));
|
|
957
985
|
const height = Math.max(...maskInstructions.map(({ mask }) => mask.height));
|
|
958
986
|
const data = new Uint8ClampedArray(new ArrayBuffer(width * height * 4));
|
|
987
|
+
const strokeCoverage = maskInstructions.some(({ stroke }) => stroke && stroke.width > 0 && stroke.alpha > 0)
|
|
988
|
+
? new Uint8Array(width * height)
|
|
989
|
+
: undefined;
|
|
990
|
+
const strokeKernels = new Map();
|
|
959
991
|
for (const instruction of maskInstructions) {
|
|
960
|
-
compositeInstruction(data, width, instruction);
|
|
992
|
+
compositeInstruction(data, width, instruction, strokeCoverage, strokeKernels, displayWidth);
|
|
961
993
|
}
|
|
962
994
|
return { data, height, width };
|
|
963
995
|
}
|
|
964
996
|
function createIdMaskRasterFrame(instructions, maxRasterWidth) {
|
|
965
997
|
try {
|
|
966
|
-
return createIdMaskFrame(
|
|
998
|
+
return createIdMaskFrame(instructions
|
|
999
|
+
.filter((instruction) => instruction.visible !== false)
|
|
1000
|
+
.map((instruction) => instruction.stroke?.widthUnit
|
|
967
1001
|
? { ...instruction, stroke: undefined }
|
|
968
|
-
: instruction), {
|
|
969
|
-
maxWidth: maxRasterWidth,
|
|
970
|
-
});
|
|
1002
|
+
: instruction), { maxWidth: maxRasterWidth });
|
|
971
1003
|
}
|
|
972
1004
|
catch {
|
|
973
1005
|
// The id raster is the fast path, not the only one: answering with nothing
|
|
@@ -997,25 +1029,29 @@
|
|
|
997
1029
|
? { data: frame.data, height: frame.height, width: frame.width }
|
|
998
1030
|
: undefined;
|
|
999
1031
|
}
|
|
1000
|
-
function compositeInstruction(rgba, canvasWidth, instruction) {
|
|
1032
|
+
function compositeInstruction(rgba, canvasWidth, instruction, strokeCoverage, strokeKernels, displayWidth) {
|
|
1001
1033
|
const fill = resolveRgbaColor(instruction.color, instruction.alpha);
|
|
1002
1034
|
const bounds = compositeMaskFill(rgba, canvasWidth, instruction.mask, fill);
|
|
1003
1035
|
if (instruction.stroke &&
|
|
1004
|
-
instruction.stroke.widthUnit
|
|
1005
|
-
bounds
|
|
1006
|
-
|
|
1036
|
+
instruction.stroke.widthUnit === undefined &&
|
|
1037
|
+
bounds &&
|
|
1038
|
+
strokeCoverage) {
|
|
1039
|
+
compositeMaskStroke(rgba, canvasWidth, decodeCompressedRleMask(instruction.mask), bounds, {
|
|
1040
|
+
...instruction.stroke,
|
|
1041
|
+
width: resolveIdMaskStrokeTexels(instruction.stroke.width, displayWidth ?? canvasWidth, canvasWidth, instruction.stroke.alignment),
|
|
1042
|
+
}, strokeCoverage, strokeKernels);
|
|
1007
1043
|
}
|
|
1008
1044
|
}
|
|
1009
1045
|
function createMaskScreenStrokes(instructions, width, height, preparedRaster) {
|
|
1010
1046
|
const outlined = instructions.filter(({ visible, stroke }) => visible !== false &&
|
|
1011
|
-
stroke?.widthUnit
|
|
1047
|
+
stroke?.widthUnit !== undefined &&
|
|
1012
1048
|
stroke.width > 0 &&
|
|
1013
1049
|
stroke.alpha > 0);
|
|
1014
1050
|
if (outlined.length === 0)
|
|
1015
1051
|
return undefined;
|
|
1016
|
-
const masks = materializeMaskInstructions(instructions);
|
|
1017
1052
|
const raster = preparedRaster ?? new Uint32Array(width * height);
|
|
1018
1053
|
if (!preparedRaster) {
|
|
1054
|
+
const masks = materializeMaskInstructions(instructions);
|
|
1019
1055
|
for (const { detectionIndex, mask } of masks) {
|
|
1020
1056
|
const counts = decodeCompressedRleCounts(mask.counts);
|
|
1021
1057
|
let offset = 0;
|
|
@@ -1038,6 +1074,8 @@
|
|
|
1038
1074
|
alpha: stroke.alpha,
|
|
1039
1075
|
color: stroke.color,
|
|
1040
1076
|
width: stroke.width,
|
|
1077
|
+
widthUnit: stroke.widthUnit,
|
|
1078
|
+
alignment: stroke.alignment,
|
|
1041
1079
|
paths: paths.get(detectionIndex + 1) ?? [],
|
|
1042
1080
|
}));
|
|
1043
1081
|
}
|
|
@@ -1103,31 +1141,110 @@
|
|
|
1103
1141
|
}
|
|
1104
1142
|
return maxX < minX || maxY < minY ? undefined : { maxX, maxY, minX, minY };
|
|
1105
1143
|
}
|
|
1106
|
-
function
|
|
1107
|
-
const
|
|
1108
|
-
|
|
1109
|
-
|
|
1144
|
+
function createStrokeKernel(width, alignment) {
|
|
1145
|
+
const innerFraction = alignment === StrokeAlignment.Inside
|
|
1146
|
+
? 1
|
|
1147
|
+
: alignment === StrokeAlignment.Center
|
|
1148
|
+
? 0.5
|
|
1149
|
+
: 0;
|
|
1150
|
+
const inside = [];
|
|
1151
|
+
const outside = [];
|
|
1152
|
+
const radius = Math.min(MAX_ID_MASK_STROKE_WIDTH + 1, Math.ceil(width * Math.max(innerFraction, 1 - innerFraction) + 0.5));
|
|
1153
|
+
const clamp = (value) => Math.max(0, Math.min(1, value));
|
|
1154
|
+
const coverage = (signed) => Math.round(255 *
|
|
1155
|
+
(clamp(0.5 + width * (1 - innerFraction) - signed) -
|
|
1156
|
+
clamp(0.5 - width * innerFraction - signed)));
|
|
1157
|
+
for (let y = -radius; y <= radius; y += 1) {
|
|
1158
|
+
for (let x = -radius; x <= radius; x += 1) {
|
|
1159
|
+
if (x === 0 && y === 0)
|
|
1160
|
+
continue;
|
|
1161
|
+
const distance = Math.max(Math.abs(x) - 0.5, Math.abs(y) - 0.5, 0);
|
|
1162
|
+
const innerCoverage = coverage(-distance);
|
|
1163
|
+
const outerCoverage = coverage(distance);
|
|
1164
|
+
if (innerCoverage > 0)
|
|
1165
|
+
inside.push([x, y, innerCoverage]);
|
|
1166
|
+
if (outerCoverage > 0)
|
|
1167
|
+
outside.push([x, y, outerCoverage]);
|
|
1168
|
+
}
|
|
1110
1169
|
}
|
|
1111
|
-
|
|
1170
|
+
return { inside, outside, radius };
|
|
1171
|
+
}
|
|
1172
|
+
function compositeMaskStroke(rgba, canvasWidth, decodedMask, bounds, stroke, coverage, kernels) {
|
|
1173
|
+
if (stroke.width <= 0 || stroke.alpha <= 0)
|
|
1174
|
+
return;
|
|
1175
|
+
const key = `${stroke.width}:${stroke.alignment}`;
|
|
1176
|
+
let kernel = kernels.get(key);
|
|
1177
|
+
if (!kernel) {
|
|
1178
|
+
kernel = createStrokeKernel(stroke.width, stroke.alignment);
|
|
1179
|
+
kernels.set(key, kernel);
|
|
1180
|
+
}
|
|
1181
|
+
const visitedAir = kernel.inside.length > 0 ? new Set() : undefined;
|
|
1182
|
+
const mark = (sourceX, sourceY, offsets, foreground) => {
|
|
1183
|
+
for (const [dx, dy, value] of offsets) {
|
|
1184
|
+
const x = sourceX + dx;
|
|
1185
|
+
const y = sourceY + dy;
|
|
1186
|
+
if (isOutsideMaskBounds(decodedMask, x, y) ||
|
|
1187
|
+
isMaskPixel(decodedMask, x, y) !== foreground)
|
|
1188
|
+
continue;
|
|
1189
|
+
const index = y * canvasWidth + x;
|
|
1190
|
+
coverage[index] = Math.max(coverage[index], value);
|
|
1191
|
+
}
|
|
1192
|
+
};
|
|
1112
1193
|
for (let y = bounds.minY; y <= bounds.maxY; y += 1) {
|
|
1113
1194
|
for (let x = bounds.minX; x <= bounds.maxX; x += 1) {
|
|
1114
1195
|
if (!isMaskPixel(decodedMask, x, y) ||
|
|
1115
|
-
!isBoundaryPixel(decodedMask, x, y))
|
|
1196
|
+
!isBoundaryPixel(decodedMask, x, y))
|
|
1116
1197
|
continue;
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1198
|
+
mark(x, y, kernel.outside, false);
|
|
1199
|
+
if (!visitedAir)
|
|
1200
|
+
continue;
|
|
1201
|
+
for (let dy = -1; dy <= 1; dy += 1) {
|
|
1202
|
+
for (let dx = -1; dx <= 1; dx += 1) {
|
|
1203
|
+
const airX = x + dx;
|
|
1204
|
+
const airY = y + dy;
|
|
1205
|
+
if (isOutsideMaskBounds(decodedMask, airX, airY) ||
|
|
1206
|
+
isMaskPixel(decodedMask, airX, airY))
|
|
1124
1207
|
continue;
|
|
1125
|
-
|
|
1126
|
-
|
|
1208
|
+
const airIndex = airY * decodedMask.width + airX;
|
|
1209
|
+
if (visitedAir.has(airIndex))
|
|
1210
|
+
continue;
|
|
1211
|
+
visitedAir.add(airIndex);
|
|
1212
|
+
mark(airX, airY, kernel.inside, true);
|
|
1127
1213
|
}
|
|
1128
1214
|
}
|
|
1129
1215
|
}
|
|
1130
1216
|
}
|
|
1217
|
+
const color = resolveRgbaColor(stroke.color, stroke.alpha);
|
|
1218
|
+
for (let y = Math.max(0, bounds.minY - kernel.radius); y <= Math.min(decodedMask.height - 1, bounds.maxY + kernel.radius); y += 1) {
|
|
1219
|
+
for (let x = Math.max(0, bounds.minX - kernel.radius); x <= Math.min(decodedMask.width - 1, bounds.maxX + kernel.radius); x += 1) {
|
|
1220
|
+
const index = y * canvasWidth + x;
|
|
1221
|
+
const amount = coverage[index] / 255;
|
|
1222
|
+
if (amount <= 0)
|
|
1223
|
+
continue;
|
|
1224
|
+
coverage[index] = 0;
|
|
1225
|
+
const offset = index * 4;
|
|
1226
|
+
const alpha = rgba[offset + 3] * (1 - amount) + color.alpha * amount;
|
|
1227
|
+
rgba[offset] =
|
|
1228
|
+
alpha > 0
|
|
1229
|
+
? (rgba[offset] * rgba[offset + 3] * (1 - amount) +
|
|
1230
|
+
color.red * color.alpha * amount) /
|
|
1231
|
+
alpha
|
|
1232
|
+
: 0;
|
|
1233
|
+
rgba[offset + 1] =
|
|
1234
|
+
alpha > 0
|
|
1235
|
+
? (rgba[offset + 1] * rgba[offset + 3] * (1 - amount) +
|
|
1236
|
+
color.green * color.alpha * amount) /
|
|
1237
|
+
alpha
|
|
1238
|
+
: 0;
|
|
1239
|
+
rgba[offset + 2] =
|
|
1240
|
+
alpha > 0
|
|
1241
|
+
? (rgba[offset + 2] * rgba[offset + 3] * (1 - amount) +
|
|
1242
|
+
color.blue * color.alpha * amount) /
|
|
1243
|
+
alpha
|
|
1244
|
+
: 0;
|
|
1245
|
+
rgba[offset + 3] = alpha;
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1131
1248
|
}
|
|
1132
1249
|
function isBoundaryPixel(mask, x, y) {
|
|
1133
1250
|
for (let offsetY = -1; offsetY <= 1; offsetY += 1) {
|
|
@@ -1910,6 +2027,7 @@
|
|
|
1910
2027
|
requestId: message.requestId,
|
|
1911
2028
|
sourceWidth: idMaskFrame.sourceWidth,
|
|
1912
2029
|
strokePalette: idMaskFrame.strokePalette,
|
|
2030
|
+
strokeAlignments: idMaskFrame.strokeAlignments,
|
|
1913
2031
|
strokeWidths: idMaskFrame.strokeWidths,
|
|
1914
2032
|
type: MaskPreparationWorkerMessageType.Complete,
|
|
1915
2033
|
width: idMaskFrame.width,
|
|
@@ -1917,13 +2035,16 @@
|
|
|
1917
2035
|
idMaskFrame.data.buffer,
|
|
1918
2036
|
idMaskFrame.fillPalette.buffer,
|
|
1919
2037
|
idMaskFrame.strokePalette.buffer,
|
|
2038
|
+
...(idMaskFrame.strokeAlignments
|
|
2039
|
+
? [idMaskFrame.strokeAlignments.buffer]
|
|
2040
|
+
: []),
|
|
1920
2041
|
idMaskFrame.strokeWidths.buffer,
|
|
1921
2042
|
...strokeTransfers,
|
|
1922
2043
|
...coverageTransfers,
|
|
1923
2044
|
]);
|
|
1924
2045
|
return;
|
|
1925
2046
|
}
|
|
1926
|
-
const compositedFrame = compositeMaskFrame(message.job.instructions);
|
|
2047
|
+
const compositedFrame = compositeMaskFrame(message.job.instructions, message.job.displayWidth);
|
|
1927
2048
|
if (!compositedFrame && !regionMaskCoverage) {
|
|
1928
2049
|
workerScope.postMessage({
|
|
1929
2050
|
key: message.job.key,
|