supervision 0.1.7 → 0.2.0-next.0
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/detections/chunked-detection-frame-source.d.ts.map +1 -1
- package/dist/index.d.ts +11 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8856 -4559
- package/dist/index.js.map +1 -1
- package/dist/mask-preparation.worker.js +310 -164
- package/dist/mask-preparation.worker.js.map +1 -1
- package/dist/media/display-pixel-ratio.d.ts +15 -0
- package/dist/media/display-pixel-ratio.d.ts.map +1 -0
- package/dist/media/engine-import-failure.d.ts +16 -0
- package/dist/media/engine-import-failure.d.ts.map +1 -0
- package/dist/media/media-condition-probe.d.ts +51 -0
- package/dist/media/media-condition-probe.d.ts.map +1 -0
- package/dist/media/media-conditions.d.ts +28 -0
- package/dist/media/media-conditions.d.ts.map +1 -0
- package/dist/media/media-errors.d.ts +6 -0
- package/dist/media/media-errors.d.ts.map +1 -1
- package/dist/media/media-normalization.d.ts.map +1 -1
- package/dist/media/media-probe.d.ts.map +1 -1
- package/dist/media/media-source.d.ts +17 -0
- package/dist/media/media-source.d.ts.map +1 -1
- package/dist/media/mediabunny-media-source.d.ts.map +1 -1
- package/dist/media/video-engine-media-source.d.ts +44 -0
- package/dist/media/video-engine-media-source.d.ts.map +1 -0
- package/dist/render-preparation/mask-frame-artifact.d.ts +46 -12
- package/dist/render-preparation/mask-frame-artifact.d.ts.map +1 -1
- package/dist/render-preparation/mask-frame-compositor.d.ts +5 -5
- package/dist/render-preparation/mask-frame-compositor.d.ts.map +1 -1
- package/dist/render-preparation/mask-frame-preparer.d.ts.map +1 -1
- package/dist/render-preparation/mask-preparation-worker-count.d.ts +4 -4
- package/dist/render-preparation/mask-preparation-worker-protocol.d.ts +12 -3
- package/dist/render-preparation/mask-preparation-worker-protocol.d.ts.map +1 -1
- package/dist/render-preparation/prepared-render-window.d.ts +23 -0
- package/dist/render-preparation/prepared-render-window.d.ts.map +1 -1
- package/dist/render-preparation/prepared-window-timeline.d.ts +2 -6
- package/dist/render-preparation/prepared-window-timeline.d.ts.map +1 -1
- package/dist/renderers/mask-palette.d.ts +14 -0
- package/dist/renderers/mask-palette.d.ts.map +1 -0
- package/dist/renderers/mask-vertex.d.ts +14 -0
- package/dist/renderers/mask-vertex.d.ts.map +1 -0
- package/dist/renderers/media-renderer-core.d.ts.map +1 -1
- package/dist/renderers/media-renderer-scene.d.ts +37 -0
- package/dist/renderers/media-renderer-scene.d.ts.map +1 -1
- package/dist/renderers/media-renderer-state.d.ts +11 -1
- package/dist/renderers/media-renderer-state.d.ts.map +1 -1
- package/dist/renderers/media-renderer-transport.d.ts +59 -0
- package/dist/renderers/media-renderer-transport.d.ts.map +1 -0
- package/dist/renderers/pixi-box-layer.d.ts +5 -0
- package/dist/renderers/pixi-box-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-focus-layer.d.ts +18 -2
- package/dist/renderers/pixi-focus-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-frame-present.d.ts +77 -0
- package/dist/renderers/pixi-frame-present.d.ts.map +1 -0
- package/dist/renderers/pixi-id-mask-shader.d.ts +12 -2
- package/dist/renderers/pixi-id-mask-shader.d.ts.map +1 -1
- package/dist/renderers/pixi-interaction-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-interaction-presentation-layer.d.ts +13 -2
- package/dist/renderers/pixi-interaction-presentation-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-mask-halo.d.ts +24 -0
- package/dist/renderers/pixi-mask-halo.d.ts.map +1 -1
- package/dist/renderers/pixi-mask-layer.d.ts +71 -8
- package/dist/renderers/pixi-mask-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-media-scene.d.ts +67 -0
- package/dist/renderers/pixi-media-scene.d.ts.map +1 -1
- package/dist/renderers/pixi-polygon-layer.d.ts +7 -2
- package/dist/renderers/pixi-polygon-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-region-coverage-mask.d.ts +91 -0
- package/dist/renderers/pixi-region-coverage-mask.d.ts.map +1 -0
- package/dist/renderers/pixi-region-effect.d.ts +42 -0
- package/dist/renderers/pixi-region-effect.d.ts.map +1 -0
- package/dist/renderers/pixi-region-layer.d.ts +68 -2
- package/dist/renderers/pixi-region-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-vector-layer.d.ts.map +1 -1
- package/dist/renderers/prepared-annotation-window.d.ts +47 -0
- package/dist/renderers/prepared-annotation-window.d.ts.map +1 -0
- package/dist/renderers/presented-frame-channel.d.ts +107 -0
- package/dist/renderers/presented-frame-channel.d.ts.map +1 -0
- package/dist/renderers/scene-render-scheduler.d.ts +20 -0
- package/dist/renderers/scene-render-scheduler.d.ts.map +1 -0
- package/dist/sessions/media-session-defaults.d.ts +10 -6
- package/dist/sessions/media-session-defaults.d.ts.map +1 -1
- package/dist/sessions/media-session-media.d.ts.map +1 -1
- package/dist/sessions/media-session-state.d.ts.map +1 -1
- package/dist/sessions/media-session.d.ts.map +1 -1
- package/dist/tracking.worker.js +58 -3
- package/dist/tracking.worker.js.map +1 -1
- package/dist/types/media-conditions.d.ts +149 -0
- package/dist/types/media-conditions.d.ts.map +1 -0
- package/dist/types/media-normalization.d.ts +34 -0
- package/dist/types/media-normalization.d.ts.map +1 -1
- package/dist/types/media-renderer.d.ts +31 -1
- package/dist/types/media-renderer.d.ts.map +1 -1
- package/dist/types/media-session.d.ts +89 -8
- package/dist/types/media-session.d.ts.map +1 -1
- package/dist/types/render-preparation.d.ts +101 -4
- package/dist/types/render-preparation.d.ts.map +1 -1
- package/dist/web-video-engine/analysis-session.d.ts +76 -0
- package/dist/web-video-engine/analysis-session.d.ts.map +1 -0
- package/dist/web-video-engine/analysis.d.ts +7 -0
- package/dist/web-video-engine/analysis.d.ts.map +1 -0
- package/dist/web-video-engine/analysis.js +1678 -0
- package/dist/web-video-engine/analysis.js.map +1 -0
- package/dist/web-video-engine/cache-budget.d.ts +19 -0
- package/dist/web-video-engine/cache-budget.d.ts.map +1 -0
- package/dist/web-video-engine/canvas-sink-scrub-cursor.d.ts +90 -0
- package/dist/web-video-engine/canvas-sink-scrub-cursor.d.ts.map +1 -0
- package/dist/web-video-engine/clock.d.ts +83 -0
- package/dist/web-video-engine/clock.d.ts.map +1 -0
- package/dist/web-video-engine/constants.d.ts +253 -0
- package/dist/web-video-engine/constants.d.ts.map +1 -0
- package/dist/web-video-engine/create-scrub-cursor.d.ts +62 -0
- package/dist/web-video-engine/create-scrub-cursor.d.ts.map +1 -0
- package/dist/web-video-engine/decode-resolution.d.ts +115 -0
- package/dist/web-video-engine/decode-resolution.d.ts.map +1 -0
- package/dist/web-video-engine/decode-scheduler.d.ts +356 -0
- package/dist/web-video-engine/decode-scheduler.d.ts.map +1 -0
- package/dist/web-video-engine/decode-session.d.ts +276 -0
- package/dist/web-video-engine/decode-session.d.ts.map +1 -0
- package/dist/web-video-engine/decode-source.d.ts +250 -0
- package/dist/web-video-engine/decode-source.d.ts.map +1 -0
- package/dist/web-video-engine/diagnostics-store.d.ts +18 -0
- package/dist/web-video-engine/diagnostics-store.d.ts.map +1 -0
- package/dist/web-video-engine/diagnostics.d.ts +316 -0
- package/dist/web-video-engine/diagnostics.d.ts.map +1 -0
- package/dist/web-video-engine/embedded-engine-worker.d.ts +2 -0
- package/dist/web-video-engine/embedded-engine-worker.d.ts.map +1 -0
- package/dist/web-video-engine/engine-core.d.ts +258 -0
- package/dist/web-video-engine/engine-core.d.ts.map +1 -0
- package/dist/web-video-engine/engine.d.ts +26 -0
- package/dist/web-video-engine/engine.d.ts.map +1 -0
- package/dist/web-video-engine/engine.js +873 -0
- package/dist/web-video-engine/engine.js.map +1 -0
- package/dist/web-video-engine/engine.worker.d.ts +2 -0
- package/dist/web-video-engine/engine.worker.d.ts.map +1 -0
- package/dist/web-video-engine/engine.worker.js +31320 -0
- package/dist/web-video-engine/engine.worker.js.map +1 -0
- package/dist/web-video-engine/frame-cache.d.ts +129 -0
- package/dist/web-video-engine/frame-cache.d.ts.map +1 -0
- package/dist/web-video-engine/frame-extractor.d.ts +28 -0
- package/dist/web-video-engine/frame-extractor.d.ts.map +1 -0
- package/dist/web-video-engine/frame-timeline-GINI8Gum.js +560 -0
- package/dist/web-video-engine/frame-timeline-GINI8Gum.js.map +1 -0
- package/dist/web-video-engine/frame-timeline.d.ts +71 -0
- package/dist/web-video-engine/frame-timeline.d.ts.map +1 -0
- package/dist/web-video-engine/frame-walker.d.ts +74 -0
- package/dist/web-video-engine/frame-walker.d.ts.map +1 -0
- package/dist/web-video-engine/index.d.ts +3 -0
- package/dist/web-video-engine/index.d.ts.map +1 -0
- package/dist/web-video-engine/index.js +3 -0
- package/dist/web-video-engine/index.js.map +1 -0
- package/dist/web-video-engine/key-packet.d.ts +65 -0
- package/dist/web-video-engine/key-packet.d.ts.map +1 -0
- package/dist/web-video-engine/keyframe-index.d.ts +91 -0
- package/dist/web-video-engine/keyframe-index.d.ts.map +1 -0
- package/dist/web-video-engine/mirror-store.d.ts +56 -0
- package/dist/web-video-engine/mirror-store.d.ts.map +1 -0
- package/dist/web-video-engine/renderer.d.ts +94 -0
- package/dist/web-video-engine/renderer.d.ts.map +1 -0
- package/dist/web-video-engine/rotation.d.ts +35 -0
- package/dist/web-video-engine/rotation.d.ts.map +1 -0
- package/dist/web-video-engine/scrub-controller.d.ts +376 -0
- package/dist/web-video-engine/scrub-controller.d.ts.map +1 -0
- package/dist/web-video-engine/scrub-cursor.d.ts +342 -0
- package/dist/web-video-engine/scrub-cursor.d.ts.map +1 -0
- package/dist/web-video-engine/scrub-trajectory.d.ts +30 -0
- package/dist/web-video-engine/scrub-trajectory.d.ts.map +1 -0
- package/dist/web-video-engine/source-residency.d.ts +57 -0
- package/dist/web-video-engine/source-residency.d.ts.map +1 -0
- package/dist/web-video-engine/trace-recorder.d.ts +164 -0
- package/dist/web-video-engine/trace-recorder.d.ts.map +1 -0
- package/dist/web-video-engine/types.d.ts +204 -0
- package/dist/web-video-engine/types.d.ts.map +1 -0
- package/dist/web-video-engine/video-engine.d.ts +426 -0
- package/dist/web-video-engine/video-engine.d.ts.map +1 -0
- package/dist/web-video-engine/webgpu-renderer.d.ts +62 -0
- package/dist/web-video-engine/webgpu-renderer.d.ts.map +1 -0
- package/dist/web-video-engine/worker-bridge.d.ts +12 -0
- package/dist/web-video-engine/worker-bridge.d.ts.map +1 -0
- package/dist/web-video-engine/worker-dispatch.d.ts +17 -0
- package/dist/web-video-engine/worker-dispatch.d.ts.map +1 -0
- package/dist/web-video-engine/worker-protocol.d.ts +267 -0
- package/dist/web-video-engine/worker-protocol.d.ts.map +1 -0
- package/node_modules/supervision-js-core/dist/detections/buffered-detection-timeline.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/detections/composite-detection-frame-source.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/index.d.ts +4 -3
- package/node_modules/supervision-js-core/dist/index.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/index.js +1075 -266
- 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/interaction-style.d.ts +4 -36
- package/node_modules/supervision-js-core/dist/styles/interaction-style.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/styles/polyline-style.d.ts +5 -0
- package/node_modules/supervision-js-core/dist/styles/polyline-style.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/annotation-renderer.d.ts +92 -7
- package/node_modules/supervision-js-core/dist/types/annotation-renderer.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/detection-timeline.d.ts +179 -17
- package/node_modules/supervision-js-core/dist/types/detection-timeline.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts +78 -2
- package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/polyline-style.d.ts +2 -0
- package/node_modules/supervision-js-core/dist/types/polyline-style.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/session-lifecycle.d.ts +16 -0
- package/node_modules/supervision-js-core/dist/types/session-lifecycle.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/utils/detection-conversions.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/utils/detection-frames.d.ts +5 -0
- package/node_modules/supervision-js-core/dist/utils/detection-frames.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/utils/detection-masks.d.ts +9 -0
- package/node_modules/supervision-js-core/dist/utils/detection-masks.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/utils/detection-ranges.d.ts +18 -0
- package/node_modules/supervision-js-core/dist/utils/detection-ranges.d.ts.map +1 -0
- package/node_modules/supervision-js-core/dist/utils/id-mask-frame.d.ts +15 -2
- package/node_modules/supervision-js-core/dist/utils/id-mask-frame.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/utils/wait-bound.d.ts +7 -0
- package/node_modules/supervision-js-core/dist/utils/wait-bound.d.ts.map +1 -0
- package/package.json +22 -1
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
(function (DetectionBufferStatus) {
|
|
18
18
|
DetectionBufferStatus["Idle"] = "idle";
|
|
19
19
|
DetectionBufferStatus["Loading"] = "loading";
|
|
20
|
+
/**
|
|
21
|
+
* The playback gate is holding playback until the source produces detections
|
|
22
|
+
* for the requested range. {@link DetectionBufferStatus.Loading} fetches
|
|
23
|
+
* detections the source already has; this one waits on a producer.
|
|
24
|
+
*/
|
|
25
|
+
DetectionBufferStatus["AwaitingCoverage"] = "awaitingCoverage";
|
|
20
26
|
DetectionBufferStatus["Ready"] = "ready";
|
|
21
27
|
DetectionBufferStatus["Error"] = "error";
|
|
22
28
|
DetectionBufferStatus["Destroyed"] = "destroyed";
|
|
@@ -29,12 +35,30 @@
|
|
|
29
35
|
/**
|
|
30
36
|
* Select the frame whose `[mediaTime, endTime)` interval contains the media
|
|
31
37
|
* time. This is the default for interval annotations and timestamped sources.
|
|
38
|
+
*
|
|
39
|
+
* Frame times must come from the media itself. Times reconstructed from a
|
|
40
|
+
* nominal frame rate drift against a clip whose real rate differs, and once
|
|
41
|
+
* that drift passes the half-millisecond selection tolerance a playhead
|
|
42
|
+
* landing on a frame boundary selects the previous frame's detections.
|
|
43
|
+
*
|
|
44
|
+
* A frame that carries no `endTime` stays active until the next frame starts,
|
|
45
|
+
* which bridges an index the source never produced.
|
|
46
|
+
* {@link DetectionFrameSelectionMode.NearestFrameIndex} bounds each frame to
|
|
47
|
+
* one grid step instead, so a missing index reads as no detections.
|
|
32
48
|
*/
|
|
33
49
|
DetectionFrameSelectionMode["Interval"] = "interval";
|
|
34
50
|
/**
|
|
35
|
-
* Select from a known inference frame grid using `frameIndex
|
|
36
|
-
*
|
|
37
|
-
*
|
|
51
|
+
* Select from a known inference frame grid using `frameIndex` and
|
|
52
|
+
* `frameRate`. This is useful when inference was run on normalized frames and
|
|
53
|
+
* playback should snap detections to that grid.
|
|
54
|
+
*
|
|
55
|
+
* A frame speaks for one grid step starting at its own media time, and an
|
|
56
|
+
* index the source never produced selects nothing rather than a neighbour.
|
|
57
|
+
*
|
|
58
|
+
* A frame carrying no `frameIndex` speaks for its step on the same terms,
|
|
59
|
+
* reached by the time it starts at rather than by an index that names it. Its
|
|
60
|
+
* `endTime` does not widen it past that step, so a source that labels only
|
|
61
|
+
* part of what it writes cannot bridge the indexes it never produced.
|
|
38
62
|
*/
|
|
39
63
|
DetectionFrameSelectionMode["NearestFrameIndex"] = "nearestFrameIndex";
|
|
40
64
|
})(DetectionFrameSelectionMode || (DetectionFrameSelectionMode = {}));
|
|
@@ -177,13 +201,16 @@
|
|
|
177
201
|
width,
|
|
178
202
|
};
|
|
179
203
|
}
|
|
180
|
-
function
|
|
204
|
+
function assertMaskSize(width, height) {
|
|
181
205
|
if (!Number.isInteger(width) || width <= 0) {
|
|
182
206
|
throw new Error("Mask width must be a positive integer.");
|
|
183
207
|
}
|
|
184
208
|
if (!Number.isInteger(height) || height <= 0) {
|
|
185
209
|
throw new Error("Mask height must be a positive integer.");
|
|
186
210
|
}
|
|
211
|
+
}
|
|
212
|
+
function assertMaskDimensions(data, width, height) {
|
|
213
|
+
assertMaskSize(width, height);
|
|
187
214
|
if (data.length !== width * height) {
|
|
188
215
|
throw new Error("Mask data length must equal width * height.");
|
|
189
216
|
}
|
|
@@ -362,9 +389,9 @@
|
|
|
362
389
|
}
|
|
363
390
|
intersections.sort((left, right) => left - right);
|
|
364
391
|
for (let index = 0; index < intersections.length - 1; index += 2) {
|
|
365
|
-
const left = Math.max(0, Math.
|
|
366
|
-
const right = Math.min(dimensions.width
|
|
367
|
-
for (let x = left; x
|
|
392
|
+
const left = Math.max(0, Math.round(intersections[index]));
|
|
393
|
+
const right = Math.min(dimensions.width, Math.round(intersections[index + 1]));
|
|
394
|
+
for (let x = left; x < right; x += 1) {
|
|
368
395
|
data[y * dimensions.width + x] = 1;
|
|
369
396
|
}
|
|
370
397
|
}
|
|
@@ -381,14 +408,28 @@
|
|
|
381
408
|
return new Uint8Array(dimensions.width * dimensions.height);
|
|
382
409
|
}
|
|
383
410
|
|
|
384
|
-
|
|
411
|
+
/**
|
|
412
|
+
* Palette slots an id raster can name, one of them the background. A GLSL
|
|
413
|
+
* fragment stage is guaranteed only 224 uniform vectors, and a palette entry
|
|
414
|
+
* costs 2.25 of them, so this stays a multiple of four and well inside that
|
|
415
|
+
* budget; past it a frame draws from the RGBA composite instead.
|
|
416
|
+
*/
|
|
417
|
+
const MAX_ID_MASK_PALETTE_ENTRIES = 80;
|
|
385
418
|
const MAX_ID_MASK_STROKE_WIDTH = 16;
|
|
386
|
-
function createIdMaskFrame(instructions) {
|
|
419
|
+
function createIdMaskFrame(instructions, options = {}) {
|
|
387
420
|
if (instructions.length === 0) {
|
|
388
421
|
return undefined;
|
|
389
422
|
}
|
|
390
|
-
const
|
|
391
|
-
const
|
|
423
|
+
const maskWidth = Math.max(...instructions.map(({ mask }) => mask.width));
|
|
424
|
+
const maskHeight = Math.max(...instructions.map(({ mask }) => mask.height));
|
|
425
|
+
const width = resolveRasterWidth(maskWidth, options.maxWidth);
|
|
426
|
+
const height = width === maskWidth
|
|
427
|
+
? maskHeight
|
|
428
|
+
: Math.max(1, Math.round((width * maskHeight) / maskWidth));
|
|
429
|
+
const strokeScale = width / maskWidth;
|
|
430
|
+
const scaledAxes = width === maskWidth
|
|
431
|
+
? undefined
|
|
432
|
+
: createScaledMaskAxes({ height, maskHeight, maskWidth, width });
|
|
392
433
|
const data = new Uint8Array(new ArrayBuffer(width * height));
|
|
393
434
|
const fillPalette = new Float32Array(new ArrayBuffer(MAX_ID_MASK_PALETTE_ENTRIES * 4 * 4));
|
|
394
435
|
const strokePalette = new Float32Array(new ArrayBuffer(MAX_ID_MASK_PALETTE_ENTRIES * 4 * 4));
|
|
@@ -403,20 +444,17 @@
|
|
|
403
444
|
}
|
|
404
445
|
writePaletteEntry(fillPalette, detectionMaskId, instruction.color, instruction.alpha);
|
|
405
446
|
if (instruction.stroke && instruction.stroke.width > 0) {
|
|
406
|
-
const strokeWidth = Math.min(
|
|
447
|
+
const strokeWidth = Math.min(resolveStrokeTexels(instruction.stroke.width, strokeScale), MAX_ID_MASK_STROKE_WIDTH);
|
|
407
448
|
hasStroke = true;
|
|
408
449
|
strokeWidths[detectionMaskId] = strokeWidth;
|
|
409
450
|
maxStrokeWidth = Math.max(maxStrokeWidth, strokeWidth);
|
|
410
451
|
writePaletteEntry(strokePalette, detectionMaskId, instruction.stroke.color, instruction.stroke.alpha);
|
|
411
452
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
data[y * width + x] = detectionMaskId;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
453
|
+
if (scaledAxes) {
|
|
454
|
+
writeScaledMaskRuns(data, scaledAxes, instruction.mask, detectionMaskId);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
writeMaskRuns(data, width, instruction.mask, detectionMaskId);
|
|
420
458
|
}
|
|
421
459
|
}
|
|
422
460
|
return {
|
|
@@ -430,6 +468,103 @@
|
|
|
430
468
|
width,
|
|
431
469
|
};
|
|
432
470
|
}
|
|
471
|
+
/**
|
|
472
|
+
* Compressed RLE counts runs down each column in turn, so a foreground run is
|
|
473
|
+
* a contiguous walk down one column that wraps into the next.
|
|
474
|
+
*/
|
|
475
|
+
function writeMaskRuns(data, frameWidth, mask, detectionMaskId) {
|
|
476
|
+
const counts = decodeCompressedRleCounts(mask.counts);
|
|
477
|
+
const maskWidth = mask.width;
|
|
478
|
+
const maskHeight = mask.height;
|
|
479
|
+
let maskOffset = 0;
|
|
480
|
+
for (let index = 0; index < counts.length; index += 1) {
|
|
481
|
+
const runLength = counts[index] ?? 0;
|
|
482
|
+
if (index % 2 === 0 || runLength <= 0) {
|
|
483
|
+
maskOffset += runLength;
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
let x = Math.floor(maskOffset / maskHeight);
|
|
487
|
+
let y = maskOffset - x * maskHeight;
|
|
488
|
+
let frameOffset = y * frameWidth + x;
|
|
489
|
+
for (let step = 0; step < runLength; step += 1) {
|
|
490
|
+
if (x >= maskWidth) {
|
|
491
|
+
break;
|
|
492
|
+
}
|
|
493
|
+
data[frameOffset] = detectionMaskId;
|
|
494
|
+
y += 1;
|
|
495
|
+
frameOffset += frameWidth;
|
|
496
|
+
if (y === maskHeight) {
|
|
497
|
+
y = 0;
|
|
498
|
+
x += 1;
|
|
499
|
+
frameOffset = x;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
maskOffset += runLength;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* The scaled twin of writeMaskRuns. Every source pixel marks the texel it lands
|
|
507
|
+
* in, so a mask keeps every part of itself the smaller raster can hold and
|
|
508
|
+
* gains up to a texel at its edges.
|
|
509
|
+
*/
|
|
510
|
+
function writeScaledMaskRuns(data, axes, mask, detectionMaskId) {
|
|
511
|
+
const counts = decodeCompressedRleCounts(mask.counts);
|
|
512
|
+
const maskWidth = mask.width;
|
|
513
|
+
const maskHeight = mask.height;
|
|
514
|
+
let maskOffset = 0;
|
|
515
|
+
for (let index = 0; index < counts.length; index += 1) {
|
|
516
|
+
const runLength = counts[index] ?? 0;
|
|
517
|
+
if (index % 2 === 0 || runLength <= 0) {
|
|
518
|
+
maskOffset += runLength;
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
let x = Math.floor(maskOffset / maskHeight);
|
|
522
|
+
let y = maskOffset - x * maskHeight;
|
|
523
|
+
let column = axes.columns[x];
|
|
524
|
+
for (let step = 0; step < runLength; step += 1) {
|
|
525
|
+
if (x >= maskWidth) {
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
528
|
+
data[axes.rows[y] + column] = detectionMaskId;
|
|
529
|
+
y += 1;
|
|
530
|
+
if (y === maskHeight) {
|
|
531
|
+
y = 0;
|
|
532
|
+
x += 1;
|
|
533
|
+
column = axes.columns[x];
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
maskOffset += runLength;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
function resolveRasterWidth(maskWidth, maxWidth) {
|
|
540
|
+
return maxWidth !== undefined && maxWidth > 0 && maxWidth < maskWidth
|
|
541
|
+
? Math.max(1, Math.floor(maxWidth))
|
|
542
|
+
: maskWidth;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* A stroke is measured in texels of the raster it is drawn on, so a coarser
|
|
546
|
+
* raster measures it in coarser texels. A stroke of a texel or more keeps at
|
|
547
|
+
* least one, the thinnest line the shader can draw; a narrower one keeps its
|
|
548
|
+
* own width, which the shader draws as an inner boundary at any scale.
|
|
549
|
+
*/
|
|
550
|
+
function resolveStrokeTexels(strokeWidth, scale) {
|
|
551
|
+
return Math.max(strokeWidth * scale, Math.min(strokeWidth, 1));
|
|
552
|
+
}
|
|
553
|
+
function createScaledMaskAxes(frame) {
|
|
554
|
+
return {
|
|
555
|
+
columns: createMaskAxisMap(frame.maskWidth, frame.width, 1),
|
|
556
|
+
rows: createMaskAxisMap(frame.maskHeight, frame.height, frame.width),
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
/** Rows hold their destination offset, so writing a run is two lookups. */
|
|
560
|
+
function createMaskAxisMap(sourceLength, targetLength, stride) {
|
|
561
|
+
const map = new Int32Array(sourceLength);
|
|
562
|
+
const scale = targetLength / sourceLength;
|
|
563
|
+
for (let index = 0; index < sourceLength; index += 1) {
|
|
564
|
+
map[index] = Math.min(targetLength - 1, Math.floor(index * scale)) * stride;
|
|
565
|
+
}
|
|
566
|
+
return map;
|
|
567
|
+
}
|
|
433
568
|
function writePaletteEntry(palette, id, color, alpha) {
|
|
434
569
|
const offset = id * 4;
|
|
435
570
|
palette[offset] = ((color >> 16) & 0xff) / 255;
|
|
@@ -496,6 +631,30 @@
|
|
|
496
631
|
MediaSourceStatus["Error"] = "error";
|
|
497
632
|
MediaSourceStatus["Destroyed"] = "destroyed";
|
|
498
633
|
})(MediaSourceStatus || (MediaSourceStatus = {}));
|
|
634
|
+
/**
|
|
635
|
+
* Current renderer state.
|
|
636
|
+
*/
|
|
637
|
+
/**
|
|
638
|
+
* What a playback gate holds back.
|
|
639
|
+
*
|
|
640
|
+
* A source the renderer pulls samples from can be held between any two frames,
|
|
641
|
+
* because the renderer decides when each one is drawn. A source that presents
|
|
642
|
+
* its own frames owns the playhead, so holding it means stopping the producer
|
|
643
|
+
* and starting it again, which the detection gate does and the
|
|
644
|
+
* render-preparation gate does not.
|
|
645
|
+
*/
|
|
646
|
+
var PlaybackGateReach;
|
|
647
|
+
(function (PlaybackGateReach) {
|
|
648
|
+
/** No gate: the picture moves and unprepared layers are absent from it. */
|
|
649
|
+
PlaybackGateReach["Off"] = "off";
|
|
650
|
+
/**
|
|
651
|
+
* Playback waits to begin, and stops again at any frame whose artifacts are
|
|
652
|
+
* missing, until they arrive or the gate's own wait bound gives up on them.
|
|
653
|
+
*/
|
|
654
|
+
PlaybackGateReach["EveryFrame"] = "everyFrame";
|
|
655
|
+
/** Playback waits to begin; frames after that are not held. */
|
|
656
|
+
PlaybackGateReach["StartOfPlayback"] = "startOfPlayback";
|
|
657
|
+
})(PlaybackGateReach || (PlaybackGateReach = {}));
|
|
499
658
|
|
|
500
659
|
/**
|
|
501
660
|
* Media-session operating mode.
|
|
@@ -536,6 +695,13 @@
|
|
|
536
695
|
*/
|
|
537
696
|
var MediaSessionActivityKind;
|
|
538
697
|
(function (MediaSessionActivityKind) {
|
|
698
|
+
/**
|
|
699
|
+
* Playback is held waiting for a producer to emit detections for the frame
|
|
700
|
+
* about to be shown, which is a wait on inference rather than on transfer or
|
|
701
|
+
* decode. `DetectionsBuffering` is the transfer of detections that already
|
|
702
|
+
* exist, and `PlaybackBuffering` is the wait for media bytes.
|
|
703
|
+
*/
|
|
704
|
+
MediaSessionActivityKind["DetectionsAwaitingCoverage"] = "detectionsAwaitingCoverage";
|
|
539
705
|
MediaSessionActivityKind["DetectionsBuffering"] = "detectionsBuffering";
|
|
540
706
|
MediaSessionActivityKind["DetectionsLoading"] = "detectionsLoading";
|
|
541
707
|
MediaSessionActivityKind["Error"] = "error";
|
|
@@ -554,9 +720,52 @@
|
|
|
554
720
|
MediaSessionActivityStatus["Waiting"] = "waiting";
|
|
555
721
|
})(MediaSessionActivityStatus || (MediaSessionActivityStatus = {}));
|
|
556
722
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
723
|
+
/** Builds compact, independent alpha crops so overlapping masks stay exact. */
|
|
724
|
+
function createRegionMaskCoverageFrame(instructions) {
|
|
725
|
+
const entries = [];
|
|
726
|
+
for (const instruction of instructions) {
|
|
727
|
+
if (!instruction.regionCoverageMask) {
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
const entry = cropCoverageMask(instruction.detectionIndex, decodeCompressedRleMask(instruction.regionCoverageMask));
|
|
731
|
+
if (entry) {
|
|
732
|
+
entries.push(entry);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return entries.length > 0 ? { entries } : undefined;
|
|
736
|
+
}
|
|
737
|
+
function cropCoverageMask(detectionIndex, decodedMask) {
|
|
738
|
+
let minX = decodedMask.width;
|
|
739
|
+
let minY = decodedMask.height;
|
|
740
|
+
let maxX = -1;
|
|
741
|
+
let maxY = -1;
|
|
742
|
+
for (let y = 0; y < decodedMask.height; y += 1) {
|
|
743
|
+
for (let x = 0; x < decodedMask.width; x += 1) {
|
|
744
|
+
if (!decodedMask.data[y * decodedMask.width + x]) {
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
minX = Math.min(minX, x);
|
|
748
|
+
minY = Math.min(minY, y);
|
|
749
|
+
maxX = Math.max(maxX, x);
|
|
750
|
+
maxY = Math.max(maxY, y);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
if (maxX < minX || maxY < minY) {
|
|
754
|
+
return undefined;
|
|
755
|
+
}
|
|
756
|
+
const width = maxX - minX + 1;
|
|
757
|
+
const height = maxY - minY + 1;
|
|
758
|
+
const data = new Uint8Array(new ArrayBuffer(width * height));
|
|
759
|
+
for (let y = minY; y <= maxY; y += 1) {
|
|
760
|
+
for (let x = minX; x <= maxX; x += 1) {
|
|
761
|
+
if (!decodedMask.data[y * decodedMask.width + x]) {
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
data[(y - minY) * width + (x - minX)] = 255;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return { data, detectionIndex, height, width, x: minX, y: minY };
|
|
768
|
+
}
|
|
560
769
|
function compositeMaskFrame(instructions) {
|
|
561
770
|
const maskInstructions = materializeMaskInstructions(instructions);
|
|
562
771
|
if (maskInstructions.length === 0) {
|
|
@@ -570,22 +779,23 @@
|
|
|
570
779
|
}
|
|
571
780
|
return { data, height, width };
|
|
572
781
|
}
|
|
573
|
-
function
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
782
|
+
function createIdMaskRasterFrame(instructions, maxRasterWidth) {
|
|
783
|
+
try {
|
|
784
|
+
return createIdMaskFrame(materializeMaskInstructions(instructions), {
|
|
785
|
+
maxWidth: maxRasterWidth,
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
catch {
|
|
789
|
+
// The id raster is the fast path, not the only one: answering with nothing
|
|
790
|
+
// puts the caller on the RGBA composite, which draws the same picture.
|
|
579
791
|
return undefined;
|
|
580
792
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
}),
|
|
588
|
-
};
|
|
793
|
+
}
|
|
794
|
+
function createIdMaskPlane(instructions, maxRasterWidth) {
|
|
795
|
+
const frame = createIdMaskRasterFrame(instructions, maxRasterWidth);
|
|
796
|
+
return frame
|
|
797
|
+
? { data: frame.data, height: frame.height, width: frame.width }
|
|
798
|
+
: undefined;
|
|
589
799
|
}
|
|
590
800
|
function compositeInstruction(rgba, canvasWidth, instruction) {
|
|
591
801
|
const decodedMask = decodeCompressedRleMask(instruction.mask);
|
|
@@ -596,7 +806,9 @@
|
|
|
596
806
|
}
|
|
597
807
|
}
|
|
598
808
|
function materializeMaskInstructions(instructions) {
|
|
599
|
-
return instructions
|
|
809
|
+
return instructions
|
|
810
|
+
.filter((instruction) => instruction.visible !== false)
|
|
811
|
+
.map((instruction) => {
|
|
600
812
|
if (instruction.mask) {
|
|
601
813
|
return instruction;
|
|
602
814
|
}
|
|
@@ -684,86 +896,23 @@
|
|
|
684
896
|
rgba[rgbaOffset + 2] = color.blue;
|
|
685
897
|
rgba[rgbaOffset + 3] = color.alpha;
|
|
686
898
|
}
|
|
687
|
-
async function encodeGrayscalePng(options) {
|
|
688
|
-
if (typeof CompressionStream === "undefined") {
|
|
689
|
-
throw new Error("CompressionStream is required to encode PNG ID masks.");
|
|
690
|
-
}
|
|
691
|
-
const rawScanlines = createFilterlessPngScanlines(options);
|
|
692
|
-
const ihdr = new Uint8Array(13);
|
|
693
|
-
const ihdrView = new DataView(ihdr.buffer);
|
|
694
|
-
ihdrView.setUint32(0, options.width);
|
|
695
|
-
ihdrView.setUint32(4, options.height);
|
|
696
|
-
ihdr[8] = 8;
|
|
697
|
-
ihdr[9] = 0;
|
|
698
|
-
ihdr[10] = 0;
|
|
699
|
-
ihdr[11] = 0;
|
|
700
|
-
ihdr[12] = 0;
|
|
701
|
-
const compressed = new Uint8Array(await new Response(new Blob([rawScanlines])
|
|
702
|
-
.stream()
|
|
703
|
-
.pipeThrough(new CompressionStream("deflate"))).arrayBuffer());
|
|
704
|
-
return concatUint8Arrays([
|
|
705
|
-
PNG_SIGNATURE,
|
|
706
|
-
createPngChunk("IHDR", ihdr),
|
|
707
|
-
createPngChunk("IDAT", compressed),
|
|
708
|
-
createPngChunk("IEND", new Uint8Array(0)),
|
|
709
|
-
]);
|
|
710
|
-
}
|
|
711
|
-
function createFilterlessPngScanlines(options) {
|
|
712
|
-
const rowStride = options.width + 1;
|
|
713
|
-
const scanlines = new Uint8Array(rowStride * options.height);
|
|
714
|
-
for (let y = 0; y < options.height; y += 1) {
|
|
715
|
-
const sourceOffset = y * options.width;
|
|
716
|
-
const targetOffset = y * rowStride;
|
|
717
|
-
scanlines[targetOffset] = 0;
|
|
718
|
-
scanlines.set(options.pixels.subarray(sourceOffset, sourceOffset + options.width), targetOffset + 1);
|
|
719
|
-
}
|
|
720
|
-
return scanlines;
|
|
721
|
-
}
|
|
722
|
-
function createPngChunk(type, data) {
|
|
723
|
-
const typeBytes = new TextEncoder().encode(type);
|
|
724
|
-
const chunk = new Uint8Array(12 + data.length);
|
|
725
|
-
const view = new DataView(chunk.buffer);
|
|
726
|
-
view.setUint32(0, data.length);
|
|
727
|
-
chunk.set(typeBytes, 4);
|
|
728
|
-
chunk.set(data, 8);
|
|
729
|
-
view.setUint32(8 + data.length, crc32(concatUint8Arrays([typeBytes, data])));
|
|
730
|
-
return chunk;
|
|
731
|
-
}
|
|
732
|
-
const crc32Table = createCrc32Table();
|
|
733
|
-
function createCrc32Table() {
|
|
734
|
-
const table = new Uint32Array(256);
|
|
735
|
-
for (let index = 0; index < table.length; index += 1) {
|
|
736
|
-
let value = index;
|
|
737
|
-
for (let bit = 0; bit < 8; bit += 1) {
|
|
738
|
-
value = value & 1 ? 0xedb88320 ^ (value >>> 1) : value >>> 1;
|
|
739
|
-
}
|
|
740
|
-
table[index] = value >>> 0;
|
|
741
|
-
}
|
|
742
|
-
return table;
|
|
743
|
-
}
|
|
744
|
-
function crc32(bytes) {
|
|
745
|
-
let crc = 0xffffffff;
|
|
746
|
-
for (const byte of bytes) {
|
|
747
|
-
crc = crc32Table[(crc ^ byte) & 0xff] ^ (crc >>> 8);
|
|
748
|
-
}
|
|
749
|
-
return (crc ^ 0xffffffff) >>> 0;
|
|
750
|
-
}
|
|
751
|
-
function concatUint8Arrays(chunks) {
|
|
752
|
-
const totalLength = chunks.reduce((total, chunk) => total + chunk.length, 0);
|
|
753
|
-
const result = new Uint8Array(totalLength);
|
|
754
|
-
let offset = 0;
|
|
755
|
-
for (const chunk of chunks) {
|
|
756
|
-
result.set(chunk, offset);
|
|
757
|
-
offset += chunk.length;
|
|
758
|
-
}
|
|
759
|
-
return result;
|
|
760
|
-
}
|
|
761
899
|
|
|
762
900
|
var PreparedMaskFrameKind;
|
|
763
901
|
(function (PreparedMaskFrameKind) {
|
|
764
|
-
PreparedMaskFrameKind["
|
|
902
|
+
PreparedMaskFrameKind["IdMask"] = "idMask";
|
|
765
903
|
PreparedMaskFrameKind["RgbaImage"] = "rgbaImage";
|
|
766
904
|
})(PreparedMaskFrameKind || (PreparedMaskFrameKind = {}));
|
|
905
|
+
/**
|
|
906
|
+
* How an id raster is laid out for the texture it is uploaded into. WebGPU
|
|
907
|
+
* takes any bytesPerRow, so ids go up one byte per pixel. WebGL aligns every
|
|
908
|
+
* uploaded row to four bytes and rejects a single-channel upload whose width
|
|
909
|
+
* is not a multiple of four, which is what the four-channel layout is for.
|
|
910
|
+
*/
|
|
911
|
+
var IdMaskTextureFormat;
|
|
912
|
+
(function (IdMaskTextureFormat) {
|
|
913
|
+
IdMaskTextureFormat["R8"] = "r8unorm";
|
|
914
|
+
IdMaskTextureFormat["Rgba8"] = "rgba8unorm";
|
|
915
|
+
})(IdMaskTextureFormat || (IdMaskTextureFormat = {}));
|
|
767
916
|
|
|
768
917
|
var MaskPreparationWorkerMessageType;
|
|
769
918
|
(function (MaskPreparationWorkerMessageType) {
|
|
@@ -779,23 +928,39 @@
|
|
|
779
928
|
if (message.type !== MaskPreparationWorkerMessageType.Prepare) {
|
|
780
929
|
return;
|
|
781
930
|
}
|
|
782
|
-
|
|
931
|
+
prepareMaskFrame(message);
|
|
783
932
|
});
|
|
784
|
-
|
|
933
|
+
function prepareMaskFrame(message) {
|
|
785
934
|
try {
|
|
786
|
-
const
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
935
|
+
const regionMaskCoverage = createRegionMaskCoverageFrame(message.job.instructions);
|
|
936
|
+
const coverageTransfers = getRegionMaskCoverageTransfers(regionMaskCoverage);
|
|
937
|
+
const idMaskFrame = createIdMaskRasterFrame(message.job.instructions, message.job.maxRasterWidth);
|
|
938
|
+
if (idMaskFrame) {
|
|
939
|
+
workerScope.postMessage({
|
|
940
|
+
artifactKind: PreparedMaskFrameKind.IdMask,
|
|
941
|
+
fillPalette: idMaskFrame.fillPalette,
|
|
942
|
+
hasStroke: idMaskFrame.hasStroke,
|
|
943
|
+
height: idMaskFrame.height,
|
|
944
|
+
key: message.job.key,
|
|
945
|
+
maxStrokeWidth: idMaskFrame.maxStrokeWidth,
|
|
946
|
+
raster: idMaskFrame.data,
|
|
947
|
+
regionMaskCoverage,
|
|
948
|
+
requestId: message.requestId,
|
|
949
|
+
strokePalette: idMaskFrame.strokePalette,
|
|
950
|
+
strokeWidths: idMaskFrame.strokeWidths,
|
|
951
|
+
type: MaskPreparationWorkerMessageType.Complete,
|
|
952
|
+
width: idMaskFrame.width,
|
|
953
|
+
}, [
|
|
954
|
+
idMaskFrame.data.buffer,
|
|
955
|
+
idMaskFrame.fillPalette.buffer,
|
|
956
|
+
idMaskFrame.strokePalette.buffer,
|
|
957
|
+
idMaskFrame.strokeWidths.buffer,
|
|
958
|
+
...coverageTransfers,
|
|
794
959
|
]);
|
|
795
960
|
return;
|
|
796
961
|
}
|
|
797
962
|
const compositedFrame = compositeMaskFrame(message.job.instructions);
|
|
798
|
-
if (!compositedFrame) {
|
|
963
|
+
if (!compositedFrame && !regionMaskCoverage) {
|
|
799
964
|
workerScope.postMessage({
|
|
800
965
|
key: message.job.key,
|
|
801
966
|
requestId: message.requestId,
|
|
@@ -803,26 +968,30 @@
|
|
|
803
968
|
});
|
|
804
969
|
return;
|
|
805
970
|
}
|
|
806
|
-
const
|
|
971
|
+
const preparedPixels = compositedFrame ?? createTransparentCoverageCarrier();
|
|
972
|
+
const imageData = new ImageData(preparedPixels.data, preparedPixels.width, preparedPixels.height);
|
|
807
973
|
const imageBitmap = createImageBitmapFromImageData(imageData);
|
|
808
|
-
const
|
|
974
|
+
const idMaskPlane = createIdMaskPlane(message.job.instructions, message.job.maxRasterWidth);
|
|
975
|
+
const idMaskTransfers = idMaskPlane ? [idMaskPlane.data.buffer] : [];
|
|
809
976
|
if (imageBitmap) {
|
|
810
977
|
workerScope.postMessage({
|
|
978
|
+
idMaskPlane,
|
|
811
979
|
imageBitmap,
|
|
812
|
-
idMaskData,
|
|
813
980
|
key: message.job.key,
|
|
981
|
+
regionMaskCoverage,
|
|
814
982
|
requestId: message.requestId,
|
|
815
983
|
type: MaskPreparationWorkerMessageType.Complete,
|
|
816
|
-
}, [...
|
|
984
|
+
}, [imageBitmap, ...idMaskTransfers, ...coverageTransfers]);
|
|
817
985
|
return;
|
|
818
986
|
}
|
|
819
987
|
workerScope.postMessage({
|
|
820
|
-
|
|
988
|
+
idMaskPlane,
|
|
821
989
|
imageData,
|
|
822
990
|
key: message.job.key,
|
|
991
|
+
regionMaskCoverage,
|
|
823
992
|
requestId: message.requestId,
|
|
824
993
|
type: MaskPreparationWorkerMessageType.Complete,
|
|
825
|
-
}, [
|
|
994
|
+
}, [imageData.data.buffer, ...idMaskTransfers, ...coverageTransfers]);
|
|
826
995
|
}
|
|
827
996
|
catch (error) {
|
|
828
997
|
workerScope.postMessage({
|
|
@@ -835,43 +1004,20 @@
|
|
|
835
1004
|
});
|
|
836
1005
|
}
|
|
837
1006
|
}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
}
|
|
844
|
-
let frame;
|
|
845
|
-
try {
|
|
846
|
-
frame = await createPngIdMaskFrame(message.job.instructions);
|
|
847
|
-
}
|
|
848
|
-
catch {
|
|
849
|
-
return undefined;
|
|
850
|
-
}
|
|
851
|
-
if (!frame) {
|
|
852
|
-
return undefined;
|
|
853
|
-
}
|
|
854
|
-
let imageBitmap;
|
|
855
|
-
try {
|
|
856
|
-
imageBitmap = await createImageBitmap(new Blob([frame.png], { type: "image/png" }));
|
|
857
|
-
}
|
|
858
|
-
catch {
|
|
859
|
-
return undefined;
|
|
860
|
-
}
|
|
1007
|
+
/**
|
|
1008
|
+
* A frame that carries only region coverage has nothing to composite, and the
|
|
1009
|
+
* RGBA branch still has to produce an artifact for the coverage to ride on.
|
|
1010
|
+
*/
|
|
1011
|
+
function createTransparentCoverageCarrier() {
|
|
861
1012
|
return {
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
imageBitmap,
|
|
866
|
-
key: message.job.key,
|
|
867
|
-
maxStrokeWidth: frame.maxStrokeWidth,
|
|
868
|
-
png: frame.png,
|
|
869
|
-
requestId: message.requestId,
|
|
870
|
-
strokePalette: frame.strokePalette,
|
|
871
|
-
strokeWidths: frame.strokeWidths,
|
|
872
|
-
type: MaskPreparationWorkerMessageType.Complete,
|
|
1013
|
+
data: new Uint8ClampedArray(new ArrayBuffer(4)),
|
|
1014
|
+
height: 1,
|
|
1015
|
+
width: 1,
|
|
873
1016
|
};
|
|
874
1017
|
}
|
|
1018
|
+
function getRegionMaskCoverageTransfers(coverage) {
|
|
1019
|
+
return coverage?.entries.map(({ data }) => data.buffer) ?? [];
|
|
1020
|
+
}
|
|
875
1021
|
function createImageBitmapFromImageData(imageData) {
|
|
876
1022
|
if (typeof OffscreenCanvas === "undefined") {
|
|
877
1023
|
return null;
|