@jax-data-science/sci-image-visualizer 0.1.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/LICENSE +21 -0
- package/README.md +404 -0
- package/esm2022/index.mjs +52 -0
- package/esm2022/jax-data-science-sci-image-visualizer.mjs +5 -0
- package/esm2022/lib/channel-histogram/channel-histogram.component.mjs +398 -0
- package/esm2022/lib/contracts/capabilities.contract.mjs +32 -0
- package/esm2022/lib/contracts/cell-segmenter.contract.mjs +12 -0
- package/esm2022/lib/contracts/channel-histogram-api.contract.mjs +14 -0
- package/esm2022/lib/contracts/colormap-lut.mjs +95 -0
- package/esm2022/lib/contracts/coordinate-transform.contract.mjs +2 -0
- package/esm2022/lib/contracts/display-types.mjs +8 -0
- package/esm2022/lib/contracts/image.contract.mjs +11 -0
- package/esm2022/lib/contracts/intensity.mjs +28 -0
- package/esm2022/lib/contracts/plot-type.mjs +112 -0
- package/esm2022/lib/contracts/ports/image-state.port.mjs +3 -0
- package/esm2022/lib/contracts/ports/preferences.port.mjs +3 -0
- package/esm2022/lib/contracts/ports/region-io.port.mjs +3 -0
- package/esm2022/lib/contracts/ports/tile-access.port.mjs +3 -0
- package/esm2022/lib/contracts/region-editor-api.contract.mjs +4 -0
- package/esm2022/lib/contracts/region-overlay.contract.mjs +2 -0
- package/esm2022/lib/contracts/region-store.contract.mjs +2 -0
- package/esm2022/lib/contracts/sam.contract.mjs +12 -0
- package/esm2022/lib/contracts/toolbar-config.mjs +8 -0
- package/esm2022/lib/contracts/visualizer.contract.mjs +10 -0
- package/esm2022/lib/contracts/viz-config.mjs +3 -0
- package/esm2022/lib/hex-color-picker/hex-color-picker.component.mjs +260 -0
- package/esm2022/lib/implementations/base-store-visualizer.mjs +76 -0
- package/esm2022/lib/implementations/napari-js/napari-axes-labels.mjs +106 -0
- package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +847 -0
- package/esm2022/lib/implementations/napari-js/napari-scale-bar.mjs +92 -0
- package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +2283 -0
- package/esm2022/lib/implementations/napari-js/napari-volume-z-handle.mjs +126 -0
- package/esm2022/lib/implementations/osd/display-pipeline.mjs +143 -0
- package/esm2022/lib/implementations/osd/histogram-sampler.mjs +217 -0
- package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +1780 -0
- package/esm2022/lib/implementations/osd/osd-coordinate-transform.mjs +25 -0
- package/esm2022/lib/implementations/osd/osd-coords.mjs +53 -0
- package/esm2022/lib/implementations/osd/osd-lib.mjs +20 -0
- package/esm2022/lib/implementations/osd/osd-region-overlay.mjs +1237 -0
- package/esm2022/lib/implementations/osd/osd-scale-bar.mjs +88 -0
- package/esm2022/lib/implementations/osd/slice-cache.mjs +488 -0
- package/esm2022/lib/implementations/osd/tile-client.mjs +29 -0
- package/esm2022/lib/implementations/plotly/plotly-coordinate-transform.mjs +37 -0
- package/esm2022/lib/implementations/plotly/plotly-region-overlay.mjs +31 -0
- package/esm2022/lib/implementations/plotly/plotly-trace-builders.mjs +183 -0
- package/esm2022/lib/implementations/plotly/plotly.service.mjs +2137 -0
- package/esm2022/lib/implementations/simple-slice-access.service.mjs +99 -0
- package/esm2022/lib/models/bezier.mjs +120 -0
- package/esm2022/lib/models/class-preset.mjs +46 -0
- package/esm2022/lib/models/geometry.mjs +198 -0
- package/esm2022/lib/models/region.mjs +166 -0
- package/esm2022/lib/models/shape.mjs +53 -0
- package/esm2022/lib/plot.utilities.mjs +693 -0
- package/esm2022/lib/processing/image-converter.service.mjs +90 -0
- package/esm2022/lib/processing/image-crop.mjs +62 -0
- package/esm2022/lib/processing/processing-image.mjs +50 -0
- package/esm2022/lib/provide-visualization.mjs +63 -0
- package/esm2022/lib/region-editor/mask-raster.mjs +95 -0
- package/esm2022/lib/region-editor/mask-worker.mjs +10 -0
- package/esm2022/lib/region-editor/region-editor.component.mjs +1190 -0
- package/esm2022/lib/region-ops.service.mjs +301 -0
- package/esm2022/lib/render-orchestrator.mjs +87 -0
- package/esm2022/lib/routing-visualizer.service.mjs +533 -0
- package/esm2022/lib/store/class-color.util.mjs +52 -0
- package/esm2022/lib/store/region-store.service.mjs +1029 -0
- package/esm2022/lib/store/visualizer-store.service.mjs +352 -0
- package/esm2022/lib/toolbar/brush/brush-tool.service.mjs +546 -0
- package/esm2022/lib/toolbar/crop/slide-crop.mjs +57 -0
- package/esm2022/lib/toolbar/segmentation/cell-segment-tool.service.mjs +115 -0
- package/esm2022/lib/toolbar/segmentation/cellpose-segmenter.service.mjs +84 -0
- package/esm2022/lib/toolbar/segmentation/onnx-sam-session.mjs +133 -0
- package/esm2022/lib/toolbar/segmentation/ort-runtime-config.mjs +31 -0
- package/esm2022/lib/toolbar/segmentation/sam-model-registry.mjs +108 -0
- package/esm2022/lib/toolbar/segmentation/sam-onnx-core.mjs +159 -0
- package/esm2022/lib/toolbar/segmentation/sam-point-tool.service.mjs +255 -0
- package/esm2022/lib/toolbar/segmentation/sam-prompt.mjs +94 -0
- package/esm2022/lib/toolbar/segmentation/sam-tool.service.mjs +188 -0
- package/esm2022/lib/toolbar/toolbar.component.mjs +311 -0
- package/esm2022/lib/toolbar/vertex-eraser/vertex-eraser-tool.service.mjs +234 -0
- package/esm2022/lib/toolbar/wand/wand-tool.service.mjs +499 -0
- package/esm2022/lib/toolbar/wand/wand.service.mjs +921 -0
- package/esm2022/lib/toolbar/zoom-to-box/zoom-to-box-tool.service.mjs +157 -0
- package/esm2022/lib/visualization.module.mjs +162 -0
- package/esm2022/lib/visualizer.component.mjs +1597 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-mask-worker-uhepXUhM.mjs +12 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-mask-worker-uhepXUhM.mjs.map +1 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs +327 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs.map +1 -0
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs +21314 -0
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -0
- package/fesm2022/mask.worker.js +989 -0
- package/fesm2022/onnx-sam.worker.js +202 -0
- package/index.d.ts +39 -0
- package/lib/channel-histogram/channel-histogram.component.d.ts +112 -0
- package/lib/contracts/capabilities.contract.d.ts +30 -0
- package/lib/contracts/cell-segmenter.contract.d.ts +40 -0
- package/lib/contracts/channel-histogram-api.contract.d.ts +98 -0
- package/lib/contracts/colormap-lut.d.ts +21 -0
- package/lib/contracts/coordinate-transform.contract.d.ts +43 -0
- package/lib/contracts/display-types.d.ts +41 -0
- package/lib/contracts/image.contract.d.ts +72 -0
- package/lib/contracts/intensity.d.ts +18 -0
- package/lib/contracts/plot-type.d.ts +108 -0
- package/lib/contracts/ports/image-state.port.d.ts +29 -0
- package/lib/contracts/ports/preferences.port.d.ts +19 -0
- package/lib/contracts/ports/region-io.port.d.ts +29 -0
- package/lib/contracts/ports/tile-access.port.d.ts +27 -0
- package/lib/contracts/region-editor-api.contract.d.ts +83 -0
- package/lib/contracts/region-overlay.contract.d.ts +47 -0
- package/lib/contracts/region-store.contract.d.ts +74 -0
- package/lib/contracts/sam.contract.d.ts +98 -0
- package/lib/contracts/toolbar-config.d.ts +24 -0
- package/lib/contracts/visualizer.contract.d.ts +346 -0
- package/lib/contracts/viz-config.d.ts +25 -0
- package/lib/hex-color-picker/hex-color-picker.component.d.ts +47 -0
- package/lib/implementations/base-store-visualizer.d.ts +71 -0
- package/lib/implementations/napari-js/napari-axes-labels.d.ts +47 -0
- package/lib/implementations/napari-js/napari-region-overlay.d.ts +115 -0
- package/lib/implementations/napari-js/napari-scale-bar.d.ts +31 -0
- package/lib/implementations/napari-js/napari-visualizer.service.d.ts +455 -0
- package/lib/implementations/napari-js/napari-volume-z-handle.d.ts +46 -0
- package/lib/implementations/osd/display-pipeline.d.ts +47 -0
- package/lib/implementations/osd/histogram-sampler.d.ts +85 -0
- package/lib/implementations/osd/openseadragon-visualizer.service.d.ts +476 -0
- package/lib/implementations/osd/osd-coordinate-transform.d.ts +17 -0
- package/lib/implementations/osd/osd-coords.d.ts +16 -0
- package/lib/implementations/osd/osd-lib.d.ts +19 -0
- package/lib/implementations/osd/osd-region-overlay.d.ts +191 -0
- package/lib/implementations/osd/osd-scale-bar.d.ts +17 -0
- package/lib/implementations/osd/slice-cache.d.ts +161 -0
- package/lib/implementations/osd/tile-client.d.ts +32 -0
- package/lib/implementations/plotly/plotly-coordinate-transform.d.ts +19 -0
- package/lib/implementations/plotly/plotly-region-overlay.d.ts +23 -0
- package/lib/implementations/plotly/plotly-trace-builders.d.ts +60 -0
- package/lib/implementations/plotly/plotly.service.d.ts +594 -0
- package/lib/implementations/simple-slice-access.service.d.ts +64 -0
- package/lib/models/bezier.d.ts +59 -0
- package/lib/models/class-preset.d.ts +39 -0
- package/lib/models/geometry.d.ts +66 -0
- package/lib/models/region.d.ts +132 -0
- package/lib/models/shape.d.ts +44 -0
- package/lib/plot.utilities.d.ts +107 -0
- package/lib/processing/image-converter.service.d.ts +28 -0
- package/lib/processing/image-crop.d.ts +18 -0
- package/lib/processing/processing-image.d.ts +17 -0
- package/lib/provide-visualization.d.ts +34 -0
- package/lib/region-editor/mask-raster.d.ts +61 -0
- package/lib/region-editor/mask-worker.d.ts +7 -0
- package/lib/region-editor/region-editor.component.d.ts +305 -0
- package/lib/region-ops.service.d.ts +92 -0
- package/lib/render-orchestrator.d.ts +59 -0
- package/lib/routing-visualizer.service.d.ts +227 -0
- package/lib/store/class-color.util.d.ts +26 -0
- package/lib/store/region-store.service.d.ts +315 -0
- package/lib/store/visualizer-store.service.d.ts +123 -0
- package/lib/toolbar/brush/brush-tool.service.d.ts +143 -0
- package/lib/toolbar/crop/slide-crop.d.ts +30 -0
- package/lib/toolbar/segmentation/cell-segment-tool.service.d.ts +32 -0
- package/lib/toolbar/segmentation/cellpose-segmenter.service.d.ts +39 -0
- package/lib/toolbar/segmentation/onnx-sam-session.d.ts +25 -0
- package/lib/toolbar/segmentation/ort-runtime-config.d.ts +4 -0
- package/lib/toolbar/segmentation/sam-model-registry.d.ts +36 -0
- package/lib/toolbar/segmentation/sam-onnx-core.d.ts +48 -0
- package/lib/toolbar/segmentation/sam-point-tool.service.d.ts +59 -0
- package/lib/toolbar/segmentation/sam-prompt.d.ts +34 -0
- package/lib/toolbar/segmentation/sam-tool.service.d.ts +50 -0
- package/lib/toolbar/toolbar.component.d.ts +167 -0
- package/lib/toolbar/vertex-eraser/vertex-eraser-tool.service.d.ts +64 -0
- package/lib/toolbar/wand/wand-tool.service.d.ts +149 -0
- package/lib/toolbar/wand/wand.service.d.ts +133 -0
- package/lib/toolbar/zoom-to-box/zoom-to-box-tool.service.d.ts +49 -0
- package/lib/visualization.module.d.ts +45 -0
- package/lib/visualizer.component.d.ts +378 -0
- package/package.json +73 -0
- package/src/lib/assets/3d-coordinates.svg +49 -0
- package/src/lib/assets/cells.svg +1 -0
- package/src/lib/assets/click.svg +1 -0
- package/src/lib/assets/colormap-luts.json +1 -0
- package/src/lib/assets/contour.svg +48 -0
- package/src/lib/assets/cube-3d.svg +1 -0
- package/src/lib/assets/edit-selection-white.svg +47 -0
- package/src/lib/assets/heatmap.svg +1 -0
- package/src/lib/assets/icons/colormap-Pastel1.png +0 -0
- package/src/lib/assets/icons/colormap-accent.png +0 -0
- package/src/lib/assets/icons/colormap-afmhot.png +0 -0
- package/src/lib/assets/icons/colormap-autumn.png +0 -0
- package/src/lib/assets/icons/colormap-berlin.png +0 -0
- package/src/lib/assets/icons/colormap-binary.png +0 -0
- package/src/lib/assets/icons/colormap-blackbody.png +0 -0
- package/src/lib/assets/icons/colormap-bluered.png +0 -0
- package/src/lib/assets/icons/colormap-blues.png +0 -0
- package/src/lib/assets/icons/colormap-bone.png +0 -0
- package/src/lib/assets/icons/colormap-brbg.png +0 -0
- package/src/lib/assets/icons/colormap-brg.png +0 -0
- package/src/lib/assets/icons/colormap-bugn.png +0 -0
- package/src/lib/assets/icons/colormap-bupu.png +0 -0
- package/src/lib/assets/icons/colormap-bwr.png +0 -0
- package/src/lib/assets/icons/colormap-cividis.png +0 -0
- package/src/lib/assets/icons/colormap-cmrmap.png +0 -0
- package/src/lib/assets/icons/colormap-cool.png +0 -0
- package/src/lib/assets/icons/colormap-coolwarm.png +0 -0
- package/src/lib/assets/icons/colormap-copper.png +0 -0
- package/src/lib/assets/icons/colormap-cubehelix.png +0 -0
- package/src/lib/assets/icons/colormap-dark2.png +0 -0
- package/src/lib/assets/icons/colormap-earth.png +0 -0
- package/src/lib/assets/icons/colormap-electric.png +0 -0
- package/src/lib/assets/icons/colormap-flag.png +0 -0
- package/src/lib/assets/icons/colormap-gist_earth.png +0 -0
- package/src/lib/assets/icons/colormap-gist_gray.png +0 -0
- package/src/lib/assets/icons/colormap-gist_heat.png +0 -0
- package/src/lib/assets/icons/colormap-gist_ncar.png +0 -0
- package/src/lib/assets/icons/colormap-gist_rainbow.png +0 -0
- package/src/lib/assets/icons/colormap-gist_stern.png +0 -0
- package/src/lib/assets/icons/colormap-gist_yarg.png +0 -0
- package/src/lib/assets/icons/colormap-gnbu.png +0 -0
- package/src/lib/assets/icons/colormap-gnuplot.png +0 -0
- package/src/lib/assets/icons/colormap-gnuplot2.png +0 -0
- package/src/lib/assets/icons/colormap-gray.png +0 -0
- package/src/lib/assets/icons/colormap-greens-inv.png +0 -0
- package/src/lib/assets/icons/colormap-greens.png +0 -0
- package/src/lib/assets/icons/colormap-greys-inv.png +0 -0
- package/src/lib/assets/icons/colormap-greys.png +0 -0
- package/src/lib/assets/icons/colormap-hot.png +0 -0
- package/src/lib/assets/icons/colormap-hsv.png +0 -0
- package/src/lib/assets/icons/colormap-inferno.png +0 -0
- package/src/lib/assets/icons/colormap-jet.png +0 -0
- package/src/lib/assets/icons/colormap-magma.png +0 -0
- package/src/lib/assets/icons/colormap-managua.png +0 -0
- package/src/lib/assets/icons/colormap-nipy_spectral.png +0 -0
- package/src/lib/assets/icons/colormap-ocean.png +0 -0
- package/src/lib/assets/icons/colormap-oranges.png +0 -0
- package/src/lib/assets/icons/colormap-orrd.png +0 -0
- package/src/lib/assets/icons/colormap-pastel2.png +0 -0
- package/src/lib/assets/icons/colormap-picnic.png +0 -0
- package/src/lib/assets/icons/colormap-pink.png +0 -0
- package/src/lib/assets/icons/colormap-piyg.png +0 -0
- package/src/lib/assets/icons/colormap-placeholder.png +0 -0
- package/src/lib/assets/icons/colormap-plasma.png +0 -0
- package/src/lib/assets/icons/colormap-portland.png +0 -0
- package/src/lib/assets/icons/colormap-prgn.png +0 -0
- package/src/lib/assets/icons/colormap-prism.png +0 -0
- package/src/lib/assets/icons/colormap-pubu.png +0 -0
- package/src/lib/assets/icons/colormap-pubugn.png +0 -0
- package/src/lib/assets/icons/colormap-puor.png +0 -0
- package/src/lib/assets/icons/colormap-purd.png +0 -0
- package/src/lib/assets/icons/colormap-purples.png +0 -0
- package/src/lib/assets/icons/colormap-rainbow.png +0 -0
- package/src/lib/assets/icons/colormap-rdbu.png +0 -0
- package/src/lib/assets/icons/colormap-rdgy.png +0 -0
- package/src/lib/assets/icons/colormap-rdpu.png +0 -0
- package/src/lib/assets/icons/colormap-rdylbu.png +0 -0
- package/src/lib/assets/icons/colormap-rdylgn.png +0 -0
- package/src/lib/assets/icons/colormap-reds.png +0 -0
- package/src/lib/assets/icons/colormap-seismic.png +0 -0
- package/src/lib/assets/icons/colormap-set1.png +0 -0
- package/src/lib/assets/icons/colormap-set2.png +0 -0
- package/src/lib/assets/icons/colormap-set3.png +0 -0
- package/src/lib/assets/icons/colormap-spectral.png +0 -0
- package/src/lib/assets/icons/colormap-spring.png +0 -0
- package/src/lib/assets/icons/colormap-summer.png +0 -0
- package/src/lib/assets/icons/colormap-tab10.png +0 -0
- package/src/lib/assets/icons/colormap-tab20.png +0 -0
- package/src/lib/assets/icons/colormap-tab20b.png +0 -0
- package/src/lib/assets/icons/colormap-tab20c.png +0 -0
- package/src/lib/assets/icons/colormap-terrain.png +0 -0
- package/src/lib/assets/icons/colormap-turbo.png +0 -0
- package/src/lib/assets/icons/colormap-twilight.png +0 -0
- package/src/lib/assets/icons/colormap-twilight_shifted.png +0 -0
- package/src/lib/assets/icons/colormap-vanimo.png +0 -0
- package/src/lib/assets/icons/colormap-viridis.png +0 -0
- package/src/lib/assets/icons/colormap-winter.png +0 -0
- package/src/lib/assets/icons/colormap-wistia.png +0 -0
- package/src/lib/assets/icons/colormap-ylgn.png +0 -0
- package/src/lib/assets/icons/colormap-ylgnbu-inv.png +0 -0
- package/src/lib/assets/icons/colormap-ylgnbu.png +0 -0
- package/src/lib/assets/icons/colormap-ylorbr.png +0 -0
- package/src/lib/assets/icons/colormap-ylorrd-inv.png +0 -0
- package/src/lib/assets/icons/colormap-ylorrd.png +0 -0
- package/src/lib/assets/interactive-segmentation-cursor.svg +1 -0
- package/src/lib/assets/invert.svg +1 -0
- package/src/lib/assets/isosurface.svg +7 -0
- package/src/lib/assets/mask.svg +1 -0
- package/src/lib/assets/paintbrush.svg +1 -0
- package/src/lib/assets/picture-segmentation.svg +43 -0
- package/src/lib/assets/polygon-vertices.svg +13 -0
- package/src/lib/assets/polyline.svg +3 -0
- package/src/lib/assets/redo.svg +39 -0
- package/src/lib/assets/region-editor.svg +37 -0
- package/src/lib/assets/region-move.svg +7 -0
- package/src/lib/assets/smooth.svg +56 -0
- package/src/lib/assets/spline-chart.svg +7 -0
- package/src/lib/assets/surface.svg +37 -0
- package/src/lib/assets/test/download-379.svg +1 -0
- package/src/lib/assets/test/download-98.svg +1 -0
- package/src/lib/assets/test/json-1.svg +1 -0
- package/src/lib/assets/test/load-53.svg +1 -0
- package/src/lib/assets/test/mask-44.svg +1 -0
- package/src/lib/assets/test/mask-download-44.svg +15 -0
- package/src/lib/assets/test/mask-save-44.svg +11 -0
- package/src/lib/assets/test/overlay-1.svg +1 -0
- package/src/lib/assets/test/overlay-4.svg +1 -0
- package/src/lib/assets/test/region-26.svg +1 -0
- package/src/lib/assets/test/save-56.svg +1 -0
- package/src/lib/assets/test/upload-43.svg +1 -0
- package/src/lib/assets/to-bezier.svg +15 -0
- package/src/lib/assets/to-polygon.svg +11 -0
- package/src/lib/assets/undo.svg +38 -0
- package/src/lib/assets/vertex-add.svg +12 -0
- package/src/lib/assets/vertex-delete.svg +12 -0
- package/src/lib/assets/wand.svg +54 -0
- package/src/lib/assets/zoom-box.svg +10 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { ProcessingImage } from './processing-image';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* Converts between various image representations. Bridges image-js, browser
|
|
6
|
+
* canvas/image elements, and the neutral {@link ProcessingImage}. Generic image
|
|
7
|
+
* plumbing — no app/pipeline coupling.
|
|
8
|
+
*/
|
|
9
|
+
export class ImageConverterService {
|
|
10
|
+
/**
|
|
11
|
+
* Convert an image-js Image instance to ProcessingImage.
|
|
12
|
+
* image-js stores pixel data in a typed array with shape (width*height*channels).
|
|
13
|
+
*/
|
|
14
|
+
fromImageJs(image) {
|
|
15
|
+
const width = image.width;
|
|
16
|
+
const height = image.height;
|
|
17
|
+
const channels = image.channels;
|
|
18
|
+
// image-js data is Uint8Array or Uint16Array; normalize to Uint8ClampedArray
|
|
19
|
+
let data;
|
|
20
|
+
if (image.bitDepth > 8) {
|
|
21
|
+
// Normalize 16-bit to 8-bit
|
|
22
|
+
const src = image.data;
|
|
23
|
+
const max = (1 << image.bitDepth) - 1;
|
|
24
|
+
data = new Uint8ClampedArray(src.length);
|
|
25
|
+
for (let i = 0; i < src.length; i++) {
|
|
26
|
+
data[i] = Math.round((src[i] / max) * 255);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
data = new Uint8ClampedArray(image.data);
|
|
31
|
+
}
|
|
32
|
+
return new ProcessingImage(width, height, channels, data);
|
|
33
|
+
}
|
|
34
|
+
/** Convert from an HTMLCanvasElement. */
|
|
35
|
+
fromCanvas(canvas) {
|
|
36
|
+
const ctx = canvas.getContext('2d');
|
|
37
|
+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
38
|
+
return ProcessingImage.fromImageData(imageData);
|
|
39
|
+
}
|
|
40
|
+
/** Convert from an HTMLImageElement. */
|
|
41
|
+
fromHtmlImage(img) {
|
|
42
|
+
const canvas = document.createElement('canvas');
|
|
43
|
+
canvas.width = img.naturalWidth;
|
|
44
|
+
canvas.height = img.naturalHeight;
|
|
45
|
+
const ctx = canvas.getContext('2d');
|
|
46
|
+
ctx.drawImage(img, 0, 0);
|
|
47
|
+
return this.fromCanvas(canvas);
|
|
48
|
+
}
|
|
49
|
+
/** Convert ProcessingImage to a 2D array suitable for Plotly heatmap (grayscale). */
|
|
50
|
+
toPlotlyHeatmapZ(image) {
|
|
51
|
+
const z = [];
|
|
52
|
+
for (let y = 0; y < image.height; y++) {
|
|
53
|
+
const row = [];
|
|
54
|
+
for (let x = 0; x < image.width; x++) {
|
|
55
|
+
const idx = (y * image.width + x) * image.channels;
|
|
56
|
+
row.push(image.data[idx]);
|
|
57
|
+
}
|
|
58
|
+
z.push(row);
|
|
59
|
+
}
|
|
60
|
+
return z;
|
|
61
|
+
}
|
|
62
|
+
/** Convert ProcessingImage to Plotly RGB arrays [r[][], g[][], b[][]]. */
|
|
63
|
+
toPlotlyRgbArrays(image) {
|
|
64
|
+
const r = [];
|
|
65
|
+
const g = [];
|
|
66
|
+
const b = [];
|
|
67
|
+
for (let y = 0; y < image.height; y++) {
|
|
68
|
+
const rRow = [];
|
|
69
|
+
const gRow = [];
|
|
70
|
+
const bRow = [];
|
|
71
|
+
for (let x = 0; x < image.width; x++) {
|
|
72
|
+
const idx = (y * image.width + x) * image.channels;
|
|
73
|
+
rRow.push(image.data[idx]);
|
|
74
|
+
gRow.push(image.data[idx + 1]);
|
|
75
|
+
bRow.push(image.data[idx + 2]);
|
|
76
|
+
}
|
|
77
|
+
r.push(rRow);
|
|
78
|
+
g.push(gRow);
|
|
79
|
+
b.push(bRow);
|
|
80
|
+
}
|
|
81
|
+
return { r, g, b };
|
|
82
|
+
}
|
|
83
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: ImageConverterService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
84
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: ImageConverterService, providedIn: 'root' }); }
|
|
85
|
+
}
|
|
86
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: ImageConverterService, decorators: [{
|
|
87
|
+
type: Injectable,
|
|
88
|
+
args: [{ providedIn: 'root' }]
|
|
89
|
+
}] });
|
|
90
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW1hZ2UtY29udmVydGVyLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3Byb2Nlc3NpbmcvaW1hZ2UtY29udmVydGVyLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMzQyxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7O0FBRXJEOzs7O0dBSUc7QUFFSCxNQUFNLE9BQU8scUJBQXFCO0lBQ2hDOzs7T0FHRztJQUNILFdBQVcsQ0FBQyxLQUFVO1FBQ3BCLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxLQUFlLENBQUM7UUFDcEMsTUFBTSxNQUFNLEdBQUcsS0FBSyxDQUFDLE1BQWdCLENBQUM7UUFDdEMsTUFBTSxRQUFRLEdBQUcsS0FBSyxDQUFDLFFBQWtCLENBQUM7UUFFMUMsNkVBQTZFO1FBQzdFLElBQUksSUFBdUIsQ0FBQztRQUM1QixJQUFJLEtBQUssQ0FBQyxRQUFRLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDdkIsNEJBQTRCO1lBQzVCLE1BQU0sR0FBRyxHQUFHLEtBQUssQ0FBQyxJQUFtQixDQUFDO1lBQ3RDLE1BQU0sR0FBRyxHQUFHLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDdEMsSUFBSSxHQUFHLElBQUksaUJBQWlCLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQ3pDLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxHQUFHLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7Z0JBQ3BDLElBQUksQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFD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bkQsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQzNCLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDL0IsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ2pDLENBQUM7WUFDRCxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ2IsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUNiLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDZixDQUFDO1FBQ0QsT0FBTyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUM7SUFDckIsQ0FBQzs4R0E5RVUscUJBQXFCO2tIQUFyQixxQkFBcUIsY0FEUixNQUFNOzsyRkFDbkIscUJBQXFCO2tCQURqQyxVQUFVO21CQUFDLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IFByb2Nlc3NpbmdJbWFnZSB9IGZyb20gJy4vcHJvY2Vzc2luZy1pbWFnZSc7XG5cbi8qKlxuICogQ29udmVydHMgYmV0d2VlbiB2YXJpb3VzIGltYWdlIHJlcHJlc2VudGF0aW9ucy4gQnJpZGdlcyBpbWFnZS1qcywgYnJvd3NlclxuICogY2FudmFzL2ltYWdlIGVsZW1lbnRzLCBhbmQgdGhlIG5ldXRyYWwge0BsaW5rIFByb2Nlc3NpbmdJbWFnZX0uIEdlbmVyaWMgaW1hZ2VcbiAqIHBsdW1iaW5nIOKAlCBubyBhcHAvcGlwZWxpbmUgY291cGxpbmcuXG4gKi9cbkBJbmplY3RhYmxlKHsgcHJvdmlkZWRJbjogJ3Jvb3QnIH0pXG5leHBvcnQgY2xhc3MgSW1hZ2VDb252ZXJ0ZXJTZXJ2aWNlIHtcbiAgLyoqXG4gICAqIENvbnZlcnQgYW4gaW1hZ2UtanMgSW1hZ2UgaW5zdGFuY2UgdG8gUHJvY2Vzc2luZ0ltYWdlLlxuICAgKiBpbWFnZS1qcyBzdG9yZXMgcGl4ZWwgZGF0YSBpbiBhIHR5cGVkIGFycmF5IHdpdGggc2hhcGUgKHdpZHRoKmhlaWdodCpjaGFubmVscykuXG4gICAqL1xuICBmcm9tSW1hZ2VKcyhpbWFnZTogYW55KTogUHJvY2Vzc2luZ0ltYWdlIHtcbiAgICBjb25zdCB3aWR0aCA9IGltYWdlLndpZHRoIGFzIG51bWJlcjtcbiAgICBjb25zdCBoZWlnaHQgPSBpbWFnZS5oZWlnaHQgYXMgbnVtYmVyO1xuICAgIGNvbnN0IGNoYW5uZWxzID0gaW1hZ2UuY2hhbm5lbHMgYXMgbnVtYmVyO1xuXG4gICAgLy8gaW1hZ2UtanMgZGF0YSBpcyBVaW50OEFycmF5IG9yIFVpbnQxNkFycmF5OyBub3JtYWxpemUgdG8gVWludDhDbGFtcGVkQXJyYXlcbiAgICBsZXQgZGF0YTogVWludDhDbGFtcGVkQXJyYXk7XG4gICAgaWYgKGltYWdlLmJpdERlcHRoID4gOCkge1xuICAgICAgLy8gTm9ybWFsaXplIDE2LWJpdCB0byA4LWJpdFxuICAgICAgY29uc3Qgc3JjID0gaW1hZ2UuZGF0YSBhcyBVaW50MTZBcnJheTtcbiAgICAgIGNvbnN0IG1heCA9ICgxIDw8IGltYWdlLmJpdERlcHRoKSAtIDE7XG4gICAgICBkYXRhID0gbmV3IFVpbnQ4Q2xhbXBlZEFycmF5KHNyYy5sZW5ndGgpO1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzcmMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgZGF0YVtpXSA9IE1hdGgucm91bmQoKHNyY1tpXSAvIG1heCkgKiAyNTUpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBkYXRhID0gbmV3IFVpbnQ4Q2xhbXBlZEFycmF5KGltYWdlLmRhdGEpO1xuICAgIH1cblxuICAgIHJldHVybiBuZXcgUHJvY2Vzc2luZ0ltYWdlKHdpZHRoLCBoZWlnaHQsIGNoYW5uZWxzLCBkYXRhKTtcbiAgfVxuXG4gIC8qKiBDb252ZXJ0IGZyb20gYW4gSFRNTENhbnZhc0VsZW1lbnQuICovXG4gIGZyb21DYW52YXMoY2FudmFzOiBIVE1MQ2FudmFzRWxlbWVudCk6IFByb2Nlc3NpbmdJbWFnZSB7XG4gICAgY29uc3QgY3R4ID0gY2FudmFzLmdldENvbnRleHQoJzJkJykhO1xuICAgIGNvbnN0IGltYWdlRGF0YSA9IGN0eC5nZXRJbWFnZURhdGEoMCwgMCwgY2FudmFzLndpZHRoLCBjYW52YXMuaGVpZ2h0KTtcbiAgICByZXR1cm4gUHJvY2Vzc2luZ0ltYWdlLmZyb21JbWFnZURhdGEoaW1hZ2VEYXRhKTtcbiAgfVxuXG4gIC8qKiBDb252ZXJ0IGZyb20gYW4gSFRNTEltYWdlRWxlbWVudC4gKi9cbiAgZnJvbUh0bWxJbWFnZShpbWc6IEhUTUxJbWFnZUVsZW1lbnQpOiBQcm9jZXNzaW5nSW1hZ2Uge1xuICAgIGNvbnN0IGNhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuICAgIGNhbnZhcy53aWR0aCA9IGltZy5uYXR1cmFsV2lkdGg7XG4gICAgY2FudmFzLmhlaWdodCA9IGltZy5uYXR1cmFsSGVpZ2h0O1xuICAgIGNvbnN0IGN0eCA9IGNhbnZhcy5nZXRDb250ZXh0KCcyZCcpITtcbiAgICBjdHguZHJhd0ltYWdlKGltZywgMCwgMCk7XG4gICAgcmV0dXJuIHRoaXMuZnJvbUNhbnZhcyhjYW52YXMpO1xuICB9XG5cbiAgLyoqIENvbnZlcnQgUHJvY2Vzc2luZ0ltYWdlIHRvIGEgMkQgYXJyYXkgc3VpdGFibGUgZm9yIFBsb3RseSBoZWF0bWFwIChncmF5c2NhbGUpLiAqL1xuICB0b1Bsb3RseUhlYXRtYXBaKGltYWdlOiBQcm9jZXNzaW5nSW1hZ2UpOiBudW1iZXJbXVtdIHtcbiAgICBjb25zdCB6OiBudW1iZXJbXVtdID0gW107XG4gICAgZm9yIChsZXQgeSA9IDA7IHkgPCBpbWFnZS5oZWlnaHQ7IHkrKykge1xuICAgICAgY29uc3Qgcm93OiBudW1iZXJbXSA9IFtdO1xuICAgICAgZm9yIChsZXQgeCA9IDA7IHggPCBpbWFnZS53aWR0aDsgeCsrKSB7XG4gICAgICAgIGNvbnN0IGlkeCA9ICh5ICogaW1hZ2Uud2lkdGggKyB4KSAqIGltYWdlLmNoYW5uZWxzO1xuICAgICAgICByb3cucHVzaChpbWFnZS5kYXRhW2lkeF0pO1xuICAgICAgfVxuICAgICAgei5wdXNoKHJvdyk7XG4gICAgfVxuICAgIHJldHVybiB6O1xuICB9XG5cbiAgLyoqIENvbnZlcnQgUHJvY2Vzc2luZ0ltYWdlIHRvIFBsb3RseSBSR0IgYXJyYXlzIFtyW11bXSwgZ1tdW10sIGJbXVtdXS4gKi9cbiAgdG9QbG90bHlSZ2JBcnJheXMoaW1hZ2U6IFByb2Nlc3NpbmdJbWFnZSk6IHsgcjogbnVtYmVyW11bXTsgZzogbnVtYmVyW11bXTsgYjogbnVtYmVyW11bXSB9IHtcbiAgICBjb25zdCByOiBudW1iZXJbXVtdID0gW107XG4gICAgY29uc3QgZzogbnVtYmVyW11bXSA9IFtdO1xuICAgIGNvbnN0IGI6IG51bWJlcltdW10gPSBbXTtcbiAgICBmb3IgKGxldCB5ID0gMDsgeSA8IGltYWdlLmhlaWdodDsgeSsrKSB7XG4gICAgICBjb25zdCByUm93OiBudW1iZXJbXSA9IFtdO1xuICAgICAgY29uc3QgZ1JvdzogbnVtYmVyW10gPSBbXTtcbiAgICAgIGNvbnN0IGJSb3c6IG51bWJlcltdID0gW107XG4gICAgICBmb3IgKGxldCB4ID0gMDsgeCA8IGltYWdlLndpZHRoOyB4KyspIHtcbiAgICAgICAgY29uc3QgaWR4ID0gKHkgKiBpbWFnZS53aWR0aCArIHgpICogaW1hZ2UuY2hhbm5lbHM7XG4gICAgICAgIHJSb3cucHVzaChpbWFnZS5kYXRhW2lkeF0pO1xuICAgICAgICBnUm93LnB1c2goaW1hZ2UuZGF0YVtpZHggKyAxXSk7XG4gICAgICAgIGJSb3cucHVzaChpbWFnZS5kYXRhW2lkeCArIDJdKTtcbiAgICAgIH1cbiAgICAgIHIucHVzaChyUm93KTtcbiAgICAgIGcucHVzaChnUm93KTtcbiAgICAgIGIucHVzaChiUm93KTtcbiAgICB9XG4gICAgcmV0dXJuIHsgciwgZywgYiB9O1xuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ProcessingImage } from './processing-image';
|
|
2
|
+
/** Copy a clamped rectangle of an RGBA source into a fresh, tightly-packed buffer. */
|
|
3
|
+
function cropRgba(src, srcWidth, srcHeight, x, y, w, h) {
|
|
4
|
+
// Clamp the rectangle to the image so bad params snap to the valid region
|
|
5
|
+
// instead of reading out of bounds.
|
|
6
|
+
const x0 = Math.max(0, Math.min(srcWidth - 1, Math.floor(x)));
|
|
7
|
+
const y0 = Math.max(0, Math.min(srcHeight - 1, Math.floor(y)));
|
|
8
|
+
const width = Math.max(1, Math.min(srcWidth - x0, Math.floor(w)));
|
|
9
|
+
const height = Math.max(1, Math.min(srcHeight - y0, Math.floor(h)));
|
|
10
|
+
const out = new Uint8ClampedArray(width * height * 4);
|
|
11
|
+
for (let row = 0; row < height; row++) {
|
|
12
|
+
const srcRow = (y0 + row) * srcWidth + x0;
|
|
13
|
+
const dstRow = row * width;
|
|
14
|
+
for (let col = 0; col < width; col++) {
|
|
15
|
+
const si = (srcRow + col) * 4;
|
|
16
|
+
const di = (dstRow + col) * 4;
|
|
17
|
+
out[di] = src[si];
|
|
18
|
+
out[di + 1] = src[si + 1];
|
|
19
|
+
out[di + 2] = src[si + 2];
|
|
20
|
+
out[di + 3] = src[si + 3];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return { data: out, width, height };
|
|
24
|
+
}
|
|
25
|
+
/** Nearest-neighbour downsample by an integer factor (≥1). factor=1 is a no-op. */
|
|
26
|
+
function downsampleRgba(src, srcWidth, srcHeight, factor) {
|
|
27
|
+
if (factor <= 1)
|
|
28
|
+
return { data: src, width: srcWidth, height: srcHeight };
|
|
29
|
+
const width = Math.max(1, Math.floor(srcWidth / factor));
|
|
30
|
+
const height = Math.max(1, Math.floor(srcHeight / factor));
|
|
31
|
+
const out = new Uint8ClampedArray(width * height * 4);
|
|
32
|
+
for (let row = 0; row < height; row++) {
|
|
33
|
+
const sy = Math.min(srcHeight - 1, row * factor);
|
|
34
|
+
for (let col = 0; col < width; col++) {
|
|
35
|
+
const sx = Math.min(srcWidth - 1, col * factor);
|
|
36
|
+
const si = (sy * srcWidth + sx) * 4;
|
|
37
|
+
const di = (row * width + col) * 4;
|
|
38
|
+
out[di] = src[si];
|
|
39
|
+
out[di + 1] = src[si + 1];
|
|
40
|
+
out[di + 2] = src[si + 2];
|
|
41
|
+
out[di + 3] = src[si + 3];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { data: out, width, height };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Crop a rectangular region out of a {@link ProcessingImage} (browser-side, the
|
|
48
|
+
* counterpart of the JIT server's Slide Crop), optionally downsampling the crop
|
|
49
|
+
* by `2^level`. The rectangle is in source pixels and clamped to the image, so
|
|
50
|
+
* out-of-range values snap to the valid region rather than throwing. Always
|
|
51
|
+
* returns a 4-channel (RGBA) image.
|
|
52
|
+
*/
|
|
53
|
+
export function cropImage(image, opts = {}) {
|
|
54
|
+
// Work on RGBA so the crop is channel-count agnostic.
|
|
55
|
+
const src = image.channels === 4 ? image.data : image.toImageData().data;
|
|
56
|
+
const cropped = cropRgba(src, image.width, image.height, Number(opts.x ?? 0), Number(opts.y ?? 0), Number(opts.width ?? image.width), Number(opts.height ?? image.height));
|
|
57
|
+
const level = Math.max(0, Math.floor(Number(opts.level ?? 0)));
|
|
58
|
+
const factor = level > 0 ? 2 ** level : 1;
|
|
59
|
+
const final = downsampleRgba(cropped.data, cropped.width, cropped.height, factor);
|
|
60
|
+
return new ProcessingImage(final.width, final.height, 4, final.data);
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW1hZ2UtY3JvcC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvcHJvY2Vzc2luZy9pbWFnZS1jcm9wLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQVlyRCxzRkFBc0Y7QUFDdEYsU0FBUyxRQUFRLENBQ2YsR0FBc0IsRUFBRSxRQUFnQixFQUFFLFNBQWlCLEVBQzNELENBQVMsRUFBRSxDQUFTLEVBQUUsQ0FBUyxFQUFFLENBQVM7SUFFMUMsMEVBQTBFO0lBQzFFLG9DQUFvQztJQUNwQyxNQUFNLEVBQUUsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsR0FBRyxDQUFDLFFBQVEsR0FBRyxDQUFDLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDOUQsTUFBTSxFQUFFLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxTQUFTLEdBQUcsQ0FBQyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQy9ELE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsUUFBUSxHQUFHLEVBQUUsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNsRSxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsR0FBRyxDQUFDLFNBQVMsR0FBRyxFQUFFLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDcEUsTUFBTSxHQUFHLEdBQUcsSUFBSSxpQkFBaUIsQ0FBQyxLQUFLLEdBQUcsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQ3RELEtBQUssSUFBSSxHQUFHLEdBQUcsQ0FBQyxFQUFFLEdBQUcsR0FBRyxNQUFNLEVBQUUsR0FBRyxFQUFFLEVBQUUsQ0FBQztRQUN0QyxNQUFNLE1BQU0sR0FBRyxDQUFDLEVBQUUsR0FBRyxHQUFHLENBQUMsR0FBRyxRQUFRLEdBQUcsRUFBRSxDQUFDO1FBQzFDLE1BQU0sTUFBTSxHQUFHLEdBQUcsR0FBRyxLQUFLLENBQUM7UUFDM0IsS0FBSyxJQUFJLEdBQUcsR0FBRyxDQUFDLEVBQUUsR0FBRyxHQUFHLEtBQUssRUFBRSxHQUFHLEVBQUUsRUFBRSxDQUFDO1lBQ3JDLE1BQU0sRUFBRSxHQUFHLENBQUMsTUFBTSxHQUFHLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUM5QixNQUFNLEVBQUUsR0FBRyxDQUFDLE1BQU0sR0FBRyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDOUIsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFPLEdBQUcsQ0FBQyxFQUFFLENBQWUsQ0FBQztZQUNwQyxHQUFHLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFXLENBQUM7WUFDcEMsR0FBRyxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUMsR0FBRyxHQUFHLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBVyxDQUFDO1lBQ3BDLEdBQUcsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDLEdBQUcsR0FBRyxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQVcsQ0FBQztRQUN0QyxDQUFDO0lBQ0gsQ0FBQztJQUNELE9BQU8sRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsQ0FBQztBQUN0QyxDQUFDO0FBRUQsbUZBQW1GO0FBQ25GLFNBQVMsY0FBYyxDQUNyQixHQUFzQixFQUFFLFFBQWdCLEVBQUUsU0FBaUIsRUFBRSxNQUFjO0lBRTNFLElBQUksTUFBTSxJQUFJLENBQUM7UUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsQ0FBQztJQUMxRSxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLFFBQVEsR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDO0lBQ3pELE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsU0FBUyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDM0QsTUFBTSxHQUFHLEdBQUcsSUFBSSxpQkFBaUIsQ0FBQyxLQUFLLEdBQUcsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQ3RELEtBQUssSUFBSSxHQUFHLEdBQUcsQ0FBQyxFQUFFLEdBQUcsR0FBRyxNQUFNLEVBQUUsR0FBRyxFQUFFLEVBQUUsQ0FBQztRQUN0QyxNQUFNLEVBQUUsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLFNBQVMsR0FBRyxDQUFDLEVBQUUsR0FBRyxHQUFHLE1BQU0sQ0FBQyxDQUFDO1FBQ2pELEtBQUssSUFBSSxHQUFHLEdBQUcsQ0FBQyxFQUFFLEdBQUcsR0FBRyxLQUFLLEVBQUUsR0FBRyxFQUFFLEVBQUUsQ0FBQztZQUNyQyxNQUFNLEVBQUUsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLFFBQVEsR0FBRyxDQUFDLEVBQUUsR0FBRyxHQUFHLE1BQU0sQ0FBQyxDQUFDO1lBQ2hELE1BQU0sRUFBRSxHQUFHLENBQUMsRUFBRSxHQUFHLFFBQVEsR0FBRyxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDcEMsTUFBTSxFQUFFLEdBQUcsQ0FBQyxHQUFHLEdBQUcsS0FBSyxHQUFHLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUNuQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQU8sR0FBRyxDQUFDLEVBQUUsQ0FBZSxDQUFDO1lBQ3BDLEdBQUcsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDLEdBQUcsR0FBRyxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQVcsQ0FBQztZQUNwQyxHQUFHLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFXLENBQUM7WUFDcEMsR0FBRyxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUMsR0FBRyxHQUFHLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBVyxDQUFDO1FBQ3RDLENBQUM7SUFDSCxDQUFDO0lBQ0QsT0FBTyxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxDQUFDO0FBQ3RDLENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxNQUFNLFVBQVUsU0FBUyxDQUFDLEtBQXNCLEVBQUUsT0FBeUIsRUFBRTtJQUMzRSxzREFBc0Q7SUFDdEQsTUFBTSxHQUFHLEdBQUcsS0FBSyxDQUFDLFFBQVEsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxXQUFXLEVBQUUsQ0FBQyxJQUFJLENBQUM7SUFDekUsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUN0QixHQUFHLEVBQUUsS0FBSyxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUMsTUFBTSxFQUM5QixNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsRUFDeEMsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLElBQUksS0FBSyxDQUFDLEtBQUssQ0FBQyxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxJQUFJLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FDdkUsQ0FBQztJQUNGLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQy9ELE1BQU0sTUFBTSxHQUFHLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMxQyxNQUFNLEtBQUssR0FBRyxjQUFjLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDbEYsT0FBTyxJQUFJLGVBQWUsQ0FBQyxLQUFLLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUN2RSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgUHJvY2Vzc2luZ0ltYWdlIH0gZnJvbSAnLi9wcm9jZXNzaW5nLWltYWdlJztcblxuLyoqIENyb3Agb3B0aW9ucyBpbiBzb3VyY2UtaW1hZ2UgcGl4ZWxzLiBPdXQtb2YtcmFuZ2UgdmFsdWVzIGFyZSBjbGFtcGVkLiAqL1xuZXhwb3J0IGludGVyZmFjZSBDcm9wSW1hZ2VPcHRpb25zIHtcbiAgeD86IG51bWJlcjtcbiAgeT86IG51bWJlcjtcbiAgd2lkdGg/OiBudW1iZXI7XG4gIGhlaWdodD86IG51bWJlcjtcbiAgLyoqIFBvd2VyLW9mLXR3byBkb3duc2FtcGxlIGxldmVsIGFwcGxpZWQgdG8gdGhlIGNyb3AgKDAgPSBmdWxsIHJlc29sdXRpb24pLiAqL1xuICBsZXZlbD86IG51bWJlcjtcbn1cblxuLyoqIENvcHkgYSBjbGFtcGVkIHJlY3RhbmdsZSBvZiBhbiBSR0JBIHNvdXJjZSBpbnRvIGEgZnJlc2gsIHRpZ2h0bHktcGFja2VkIGJ1ZmZlci4gKi9cbmZ1bmN0aW9uIGNyb3BSZ2JhKFxuICBzcmM6IFVpbnQ4Q2xhbXBlZEFycmF5LCBzcmNXaWR0aDogbnVtYmVyLCBzcmNIZWlnaHQ6IG51bWJlcixcbiAgeDogbnVtYmVyLCB5OiBudW1iZXIsIHc6IG51bWJlciwgaDogbnVtYmVyLFxuKTogeyBkYXRhOiBVaW50OENsYW1wZWRBcnJheTsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfSB7XG4gIC8vIENsYW1wIHRoZSByZWN0YW5nbGUgdG8gdGhlIGltYWdlIHNvIGJhZCBwYXJhbXMgc25hcCB0byB0aGUgdmFsaWQgcmVnaW9uXG4gIC8vIGluc3RlYWQgb2YgcmVhZGluZyBvdXQgb2YgYm91bmRzLlxuICBjb25zdCB4MCA9IE1hdGgubWF4KDAsIE1hdGgubWluKHNyY1dpZHRoIC0gMSwgTWF0aC5mbG9vcih4KSkpO1xuICBjb25zdCB5MCA9IE1hdGgubWF4KDAsIE1hdGgubWluKHNyY0hlaWdodCAtIDEsIE1hdGguZmxvb3IoeSkpKTtcbiAgY29uc3Qgd2lkdGggPSBNYXRoLm1heCgxLCBNYXRoLm1pbihzcmNXaWR0aCAtIHgwLCBNYXRoLmZsb29yKHcpKSk7XG4gIGNvbnN0IGhlaWdodCA9IE1hdGgubWF4KDEsIE1hdGgubWluKHNyY0hlaWdodCAtIHkwLCBNYXRoLmZsb29yKGgpKSk7XG4gIGNvbnN0IG91dCA9IG5ldyBVaW50OENsYW1wZWRBcnJheSh3aWR0aCAqIGhlaWdodCAqIDQpO1xuICBmb3IgKGxldCByb3cgPSAwOyByb3cgPCBoZWlnaHQ7IHJvdysrKSB7XG4gICAgY29uc3Qgc3JjUm93ID0gKHkwICsgcm93KSAqIHNyY1dpZHRoICsgeDA7XG4gICAgY29uc3QgZHN0Um93ID0gcm93ICogd2lkdGg7XG4gICAgZm9yIChsZXQgY29sID0gMDsgY29sIDwgd2lkdGg7IGNvbCsrKSB7XG4gICAgICBjb25zdCBzaSA9IChzcmNSb3cgKyBjb2wpICogNDtcbiAgICAgIGNvbnN0IGRpID0gKGRzdFJvdyArIGNvbCkgKiA0O1xuICAgICAgb3V0W2RpXSAgICAgPSBzcmNbc2ldICAgICBhcyBudW1iZXI7XG4gICAgICBvdXRbZGkgKyAxXSA9IHNyY1tzaSArIDFdIGFzIG51bWJlcjtcbiAgICAgIG91dFtkaSArIDJdID0gc3JjW3NpICsgMl0gYXMgbnVtYmVyO1xuICAgICAgb3V0W2RpICsgM10gPSBzcmNbc2kgKyAzXSBhcyBudW1iZXI7XG4gICAgfVxuICB9XG4gIHJldHVybiB7IGRhdGE6IG91dCwgd2lkdGgsIGhlaWdodCB9O1xufVxuXG4vKiogTmVhcmVzdC1uZWlnaGJvdXIgZG93bnNhbXBsZSBieSBhbiBpbnRlZ2VyIGZhY3RvciAo4omlMSkuIGZhY3Rvcj0xIGlzIGEgbm8tb3AuICovXG5mdW5jdGlvbiBkb3duc2FtcGxlUmdiYShcbiAgc3JjOiBVaW50OENsYW1wZWRBcnJheSwgc3JjV2lkdGg6IG51bWJlciwgc3JjSGVpZ2h0OiBudW1iZXIsIGZhY3RvcjogbnVtYmVyLFxuKTogeyBkYXRhOiBVaW50OENsYW1wZWRBcnJheTsgd2lkdGg6IG51bWJlcjsgaGVpZ2h0OiBudW1iZXIgfSB7XG4gIGlmIChmYWN0b3IgPD0gMSkgcmV0dXJuIHsgZGF0YTogc3JjLCB3aWR0aDogc3JjV2lkdGgsIGhlaWdodDogc3JjSGVpZ2h0IH07XG4gIGNvbnN0IHdpZHRoID0gTWF0aC5tYXgoMSwgTWF0aC5mbG9vcihzcmNXaWR0aCAvIGZhY3RvcikpO1xuICBjb25zdCBoZWlnaHQgPSBNYXRoLm1heCgxLCBNYXRoLmZsb29yKHNyY0hlaWdodCAvIGZhY3RvcikpO1xuICBjb25zdCBvdXQgPSBuZXcgVWludDhDbGFtcGVkQXJyYXkod2lkdGggKiBoZWlnaHQgKiA0KTtcbiAgZm9yIChsZXQgcm93ID0gMDsgcm93IDwgaGVpZ2h0OyByb3crKykge1xuICAgIGNvbnN0IHN5ID0gTWF0aC5taW4oc3JjSGVpZ2h0IC0gMSwgcm93ICogZmFjdG9yKTtcbiAgICBmb3IgKGxldCBjb2wgPSAwOyBjb2wgPCB3aWR0aDsgY29sKyspIHtcbiAgICAgIGNvbnN0IHN4ID0gTWF0aC5taW4oc3JjV2lkdGggLSAxLCBjb2wgKiBmYWN0b3IpO1xuICAgICAgY29uc3Qgc2kgPSAoc3kgKiBzcmNXaWR0aCArIHN4KSAqIDQ7XG4gICAgICBjb25zdCBkaSA9IChyb3cgKiB3aWR0aCArIGNvbCkgKiA0O1xuICAgICAgb3V0W2RpXSAgICAgPSBzcmNbc2ldICAgICBhcyBudW1iZXI7XG4gICAgICBvdXRbZGkgKyAxXSA9IHNyY1tzaSArIDFdIGFzIG51bWJlcjtcbiAgICAgIG91dFtkaSArIDJdID0gc3JjW3NpICsgMl0gYXMgbnVtYmVyO1xuICAgICAgb3V0W2RpICsgM10gPSBzcmNbc2kgKyAzXSBhcyBudW1iZXI7XG4gICAgfVxuICB9XG4gIHJldHVybiB7IGRhdGE6IG91dCwgd2lkdGgsIGhlaWdodCB9O1xufVxuXG4vKipcbiAqIENyb3AgYSByZWN0YW5ndWxhciByZWdpb24gb3V0IG9mIGEge0BsaW5rIFByb2Nlc3NpbmdJbWFnZX0gKGJyb3dzZXItc2lkZSwgdGhlXG4gKiBjb3VudGVycGFydCBvZiB0aGUgSklUIHNlcnZlcidzIFNsaWRlIENyb3ApLCBvcHRpb25hbGx5IGRvd25zYW1wbGluZyB0aGUgY3JvcFxuICogYnkgYDJebGV2ZWxgLiBUaGUgcmVjdGFuZ2xlIGlzIGluIHNvdXJjZSBwaXhlbHMgYW5kIGNsYW1wZWQgdG8gdGhlIGltYWdlLCBzb1xuICogb3V0LW9mLXJhbmdlIHZhbHVlcyBzbmFwIHRvIHRoZSB2YWxpZCByZWdpb24gcmF0aGVyIHRoYW4gdGhyb3dpbmcuIEFsd2F5c1xuICogcmV0dXJucyBhIDQtY2hhbm5lbCAoUkdCQSkgaW1hZ2UuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcm9wSW1hZ2UoaW1hZ2U6IFByb2Nlc3NpbmdJbWFnZSwgb3B0czogQ3JvcEltYWdlT3B0aW9ucyA9IHt9KTogUHJvY2Vzc2luZ0ltYWdlIHtcbiAgLy8gV29yayBvbiBSR0JBIHNvIHRoZSBjcm9wIGlzIGNoYW5uZWwtY291bnQgYWdub3N0aWMuXG4gIGNvbnN0IHNyYyA9IGltYWdlLmNoYW5uZWxzID09PSA0ID8gaW1hZ2UuZGF0YSA6IGltYWdlLnRvSW1hZ2VEYXRhKCkuZGF0YTtcbiAgY29uc3QgY3JvcHBlZCA9IGNyb3BSZ2JhKFxuICAgIHNyYywgaW1hZ2Uud2lkdGgsIGltYWdlLmhlaWdodCxcbiAgICBOdW1iZXIob3B0cy54ID8/IDApLCBOdW1iZXIob3B0cy55ID8/IDApLFxuICAgIE51bWJlcihvcHRzLndpZHRoID8/IGltYWdlLndpZHRoKSwgTnVtYmVyKG9wdHMuaGVpZ2h0ID8/IGltYWdlLmhlaWdodCksXG4gICk7XG4gIGNvbnN0IGxldmVsID0gTWF0aC5tYXgoMCwgTWF0aC5mbG9vcihOdW1iZXIob3B0cy5sZXZlbCA/PyAwKSkpO1xuICBjb25zdCBmYWN0b3IgPSBsZXZlbCA+IDAgPyAyICoqIGxldmVsIDogMTtcbiAgY29uc3QgZmluYWwgPSBkb3duc2FtcGxlUmdiYShjcm9wcGVkLmRhdGEsIGNyb3BwZWQud2lkdGgsIGNyb3BwZWQuaGVpZ2h0LCBmYWN0b3IpO1xuICByZXR1cm4gbmV3IFByb2Nlc3NpbmdJbWFnZShmaW5hbC53aWR0aCwgZmluYWwuaGVpZ2h0LCA0LCBmaW5hbC5kYXRhKTtcbn1cbiJdfQ==
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified image container used throughout the processing pipeline.
|
|
3
|
+
* Decouples pipeline logic from any specific library's image format.
|
|
4
|
+
*/
|
|
5
|
+
export class ProcessingImage {
|
|
6
|
+
/** Create from browser ImageData. */
|
|
7
|
+
static fromImageData(imageData) {
|
|
8
|
+
return new ProcessingImage(imageData.width, imageData.height, 4, new Uint8ClampedArray(imageData.data));
|
|
9
|
+
}
|
|
10
|
+
constructor(width, height, channels, data) {
|
|
11
|
+
this.width = width;
|
|
12
|
+
this.height = height;
|
|
13
|
+
this.channels = channels;
|
|
14
|
+
this.data = data;
|
|
15
|
+
}
|
|
16
|
+
/** Convert to browser-native ImageData (RGBA). */
|
|
17
|
+
toImageData() {
|
|
18
|
+
if (this.channels === 4) {
|
|
19
|
+
return new ImageData(this.data, this.width, this.height);
|
|
20
|
+
}
|
|
21
|
+
// Expand grayscale or RGB to RGBA
|
|
22
|
+
const rgba = new Uint8ClampedArray(this.width * this.height * 4);
|
|
23
|
+
for (let i = 0; i < this.width * this.height; i++) {
|
|
24
|
+
if (this.channels === 1) {
|
|
25
|
+
rgba[i * 4] = this.data[i];
|
|
26
|
+
rgba[i * 4 + 1] = this.data[i];
|
|
27
|
+
rgba[i * 4 + 2] = this.data[i];
|
|
28
|
+
}
|
|
29
|
+
else if (this.channels === 3) {
|
|
30
|
+
rgba[i * 4] = this.data[i * 3];
|
|
31
|
+
rgba[i * 4 + 1] = this.data[i * 3 + 1];
|
|
32
|
+
rgba[i * 4 + 2] = this.data[i * 3 + 2];
|
|
33
|
+
}
|
|
34
|
+
rgba[i * 4 + 3] = 255;
|
|
35
|
+
}
|
|
36
|
+
return new ImageData(rgba, this.width, this.height);
|
|
37
|
+
}
|
|
38
|
+
/** Convert to Blob for saving/downloading. */
|
|
39
|
+
async toBlob(mime = 'image/png') {
|
|
40
|
+
const canvas = document.createElement('canvas');
|
|
41
|
+
canvas.width = this.width;
|
|
42
|
+
canvas.height = this.height;
|
|
43
|
+
const ctx = canvas.getContext('2d');
|
|
44
|
+
ctx.putImageData(this.toImageData(), 0, 0);
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
canvas.toBlob((blob) => resolve(blob), mime);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvY2Vzc2luZy1pbWFnZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvcHJvY2Vzc2luZy9wcm9jZXNzaW5nLWltYWdlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUNILE1BQU0sT0FBTyxlQUFlO0lBTTFCLHFDQUFxQztJQUNyQyxNQUFNLENBQUMsYUFBYSxDQUFDLFNBQW9CO1FBQ3ZDLE9BQU8sSUFBSSxlQUFlLENBQUMsU0FBUyxDQUFDLEtBQUssRUFBRSxTQUFTLENBQUMsTUFBTSxFQUFFLENBQUMsRUFDN0QsSUFBSSxpQkFBaUIsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztJQUMzQyxDQUFDO0lBRUQsWUFBWSxLQUFhLEVBQUUsTUFBYyxFQUFFLFFBQWdCLEVBQUUsSUFBdUI7UUFDbEYsSUFBSSxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUM7UUFDbkIsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsSUFBSSxDQUFDLFFBQVEsR0FBRyxRQUFRLENBQUM7UUFDekIsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUM7SUFDbkIsQ0FBQztJQUVELGtEQUFrRDtJQUNsRCxXQUFXO1FBQ1QsSUFBSSxJQUFJLENBQUMsUUFBUSxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ3hCLE9BQU8sSUFBSSxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMzRCxDQUFDO1FBQ0Qsa0NBQWtDO1FBQ2xDLE1BQU0sSUFBSSxHQUFHLElBQUksaUJBQWlCLENBQUMsSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ2pFLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUNsRCxJQUFJLElBQUksQ0FBQyxRQUFRLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0JBQ3hCLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDM0IsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDL0IsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNqQyxDQUFDO2lCQUFNLElBQUksSUFBSSxDQUFDLFFBQVEsS0FBSyxDQUFDLEVBQUUsQ0FBQztnQkFDL0IsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztnQkFDL0IsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUN2QyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7WUFDekMsQ0FBQztZQUNELElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLEdBQUcsQ0FBQztRQUN4QixDQUFDO1FBQ0QsT0FBTyxJQUFJLFNBQVMsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDdEQsQ0FBQztJQUVELDhDQUE4QztJQUM5QyxLQUFLLENBQUMsTUFBTSxDQUFDLE9BQWUsV0FBVztRQUNyQyxNQUFNLE1BQU0sR0FBRyxRQUFRLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ2hELE1BQU0sQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQztRQUMxQixNQUFNLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUM7UUFDNUIsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUUsQ0FBQztRQUNyQyxHQUFHLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDM0MsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1lBQzdCLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFLLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztRQUNoRCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogVW5pZmllZCBpbWFnZSBjb250YWluZXIgdXNlZCB0aHJvdWdob3V0IHRoZSBwcm9jZXNzaW5nIHBpcGVsaW5lLlxuICogRGVjb3VwbGVzIHBpcGVsaW5lIGxvZ2ljIGZyb20gYW55IHNwZWNpZmljIGxpYnJhcnkncyBpbWFnZSBmb3JtYXQuXG4gKi9cbmV4cG9ydCBjbGFzcyBQcm9jZXNzaW5nSW1hZ2Uge1xuICByZWFkb25seSB3aWR0aDogbnVtYmVyO1xuICByZWFkb25seSBoZWlnaHQ6IG51bWJlcjtcbiAgcmVhZG9ubHkgY2hhbm5lbHM6IG51bWJlcjtcbiAgcmVhZG9ubHkgZGF0YTogVWludDhDbGFtcGVkQXJyYXk7XG5cbiAgLyoqIENyZWF0ZSBmcm9tIGJyb3dzZXIgSW1hZ2VEYXRhLiAqL1xuICBzdGF0aWMgZnJvbUltYWdlRGF0YShpbWFnZURhdGE6IEltYWdlRGF0YSk6IFByb2Nlc3NpbmdJbWFnZSB7XG4gICAgcmV0dXJuIG5ldyBQcm9jZXNzaW5nSW1hZ2UoaW1hZ2VEYXRhLndpZHRoLCBpbWFnZURhdGEuaGVpZ2h0LCA0LFxuICAgICAgbmV3IFVpbnQ4Q2xhbXBlZEFycmF5KGltYWdlRGF0YS5kYXRhKSk7XG4gIH1cblxuICBjb25zdHJ1Y3Rvcih3aWR0aDogbnVtYmVyLCBoZWlnaHQ6IG51bWJlciwgY2hhbm5lbHM6IG51bWJlciwgZGF0YTogVWludDhDbGFtcGVkQXJyYXkpIHtcbiAgICB0aGlzLndpZHRoID0gd2lkdGg7XG4gICAgdGhpcy5oZWlnaHQgPSBoZWlnaHQ7XG4gICAgdGhpcy5jaGFubmVscyA9IGNoYW5uZWxzO1xuICAgIHRoaXMuZGF0YSA9IGRhdGE7XG4gIH1cblxuICAvKiogQ29udmVydCB0byBicm93c2VyLW5hdGl2ZSBJbWFnZURhdGEgKFJHQkEpLiAqL1xuICB0b0ltYWdlRGF0YSgpOiBJbWFnZURhdGEge1xuICAgIGlmICh0aGlzLmNoYW5uZWxzID09PSA0KSB7XG4gICAgICByZXR1cm4gbmV3IEltYWdlRGF0YSh0aGlzLmRhdGEsIHRoaXMud2lkdGgsIHRoaXMuaGVpZ2h0KTtcbiAgICB9XG4gICAgLy8gRXhwYW5kIGdyYXlzY2FsZSBvciBSR0IgdG8gUkdCQVxuICAgIGNvbnN0IHJnYmEgPSBuZXcgVWludDhDbGFtcGVkQXJyYXkodGhpcy53aWR0aCAqIHRoaXMuaGVpZ2h0ICogNCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLndpZHRoICogdGhpcy5oZWlnaHQ7IGkrKykge1xuICAgICAgaWYgKHRoaXMuY2hhbm5lbHMgPT09IDEpIHtcbiAgICAgICAgcmdiYVtpICogNF0gPSB0aGlzLmRhdGFbaV07XG4gICAgICAgIHJnYmFbaSAqIDQgKyAxXSA9IHRoaXMuZGF0YVtpXTtcbiAgICAgICAgcmdiYVtpICogNCArIDJdID0gdGhpcy5kYXRhW2ldO1xuICAgICAgfSBlbHNlIGlmICh0aGlzLmNoYW5uZWxzID09PSAzKSB7XG4gICAgICAgIHJnYmFbaSAqIDRdID0gdGhpcy5kYXRhW2kgKiAzXTtcbiAgICAgICAgcmdiYVtpICogNCArIDFdID0gdGhpcy5kYXRhW2kgKiAzICsgMV07XG4gICAgICAgIHJnYmFbaSAqIDQgKyAyXSA9IHRoaXMuZGF0YVtpICogMyArIDJdO1xuICAgICAgfVxuICAgICAgcmdiYVtpICogNCArIDNdID0gMjU1O1xuICAgIH1cbiAgICByZXR1cm4gbmV3IEltYWdlRGF0YShyZ2JhLCB0aGlzLndpZHRoLCB0aGlzLmhlaWdodCk7XG4gIH1cblxuICAvKiogQ29udmVydCB0byBCbG9iIGZvciBzYXZpbmcvZG93bmxvYWRpbmcuICovXG4gIGFzeW5jIHRvQmxvYihtaW1lOiBzdHJpbmcgPSAnaW1hZ2UvcG5nJyk6IFByb21pc2U8QmxvYj4ge1xuICAgIGNvbnN0IGNhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuICAgIGNhbnZhcy53aWR0aCA9IHRoaXMud2lkdGg7XG4gICAgY2FudmFzLmhlaWdodCA9IHRoaXMuaGVpZ2h0O1xuICAgIGNvbnN0IGN0eCA9IGNhbnZhcy5nZXRDb250ZXh0KCcyZCcpITtcbiAgICBjdHgucHV0SW1hZ2VEYXRhKHRoaXMudG9JbWFnZURhdGEoKSwgMCwgMCk7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICBjYW52YXMudG9CbG9iKChibG9iKSA9PiByZXNvbHZlKGJsb2IhKSwgbWltZSk7XG4gICAgfSk7XG4gIH1cbn1cbiJdfQ==
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { RoutingVisualizerService } from './routing-visualizer.service';
|
|
2
|
+
import { PlotlyService } from './implementations/plotly/plotly.service';
|
|
3
|
+
import { OpenSeadragonVisualizerService } from './implementations/osd/openseadragon-visualizer.service';
|
|
4
|
+
import { NapariVisualizerService } from './implementations/napari-js/napari-visualizer.service';
|
|
5
|
+
import { VisualizerStore } from './store/visualizer-store.service';
|
|
6
|
+
import { RegionStore } from './store/region-store.service';
|
|
7
|
+
import { WandToolService } from './toolbar/wand/wand-tool.service';
|
|
8
|
+
import { VertexEraserToolService } from './toolbar/vertex-eraser/vertex-eraser-tool.service';
|
|
9
|
+
import { ZoomToBoxToolService } from './toolbar/zoom-to-box/zoom-to-box-tool.service';
|
|
10
|
+
import { VISUALIZER } from './contracts/visualizer.contract';
|
|
11
|
+
import { REGION_EDITOR_API } from './contracts/region-editor-api.contract';
|
|
12
|
+
import { CHANNEL_HISTOGRAM_API } from './contracts/channel-histogram-api.contract';
|
|
13
|
+
/**
|
|
14
|
+
* A self-contained, isolated visualization backend chain for a component subtree.
|
|
15
|
+
*
|
|
16
|
+
* The chain services are `providedIn: 'root'` singletons, so by default the whole
|
|
17
|
+
* app shares one viewer's state (regions, image, channels, render handles) — which
|
|
18
|
+
* is correct for the single main viewer. A consumer that needs a SECOND, independent
|
|
19
|
+
* viewer (e.g. a modal that mounts `<visualizer>` over the live main view
|
|
20
|
+
* and must not clobber its regions/image) drops this into its component `providers`:
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* @Component({
|
|
24
|
+
* ...,
|
|
25
|
+
* providers: [
|
|
26
|
+
* ...provideVisualization(), // isolated chain instance
|
|
27
|
+
* // plus the host ports + config for THIS viewer:
|
|
28
|
+
* { provide: IMAGE_STATE_PORT, useClass: MyImageStateAdapter },
|
|
29
|
+
* { provide: TILE_ACCESS_PORT, useClass: MyTileAccessAdapter },
|
|
30
|
+
* { provide: REGION_IO_PORT, useClass: MyRegionIoAdapter },
|
|
31
|
+
* { provide: VIZ_CONFIG, useValue: { slideCropServer: '' } },
|
|
32
|
+
* ],
|
|
33
|
+
* })
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* Component-scoped providers shadow the root singletons for that subtree, so the
|
|
37
|
+
* embedded viewer gets its own router/Plotly/OSD/stores/tools while the rest of the
|
|
38
|
+
* app keeps the default root instance.
|
|
39
|
+
*
|
|
40
|
+
* Lists EVERY stateful service in the chain. Stateless collaborators (HttpClient,
|
|
41
|
+
* MessageService, WandService) deliberately resolve to root — they hold no
|
|
42
|
+
* per-viewer state, so sharing them is correct and keeps this list minimal. When a
|
|
43
|
+
* new stateful service joins the rendering chain, add it here too.
|
|
44
|
+
*/
|
|
45
|
+
export function provideVisualization() {
|
|
46
|
+
return [
|
|
47
|
+
RoutingVisualizerService,
|
|
48
|
+
PlotlyService,
|
|
49
|
+
OpenSeadragonVisualizerService,
|
|
50
|
+
NapariVisualizerService,
|
|
51
|
+
VisualizerStore,
|
|
52
|
+
RegionStore,
|
|
53
|
+
WandToolService,
|
|
54
|
+
VertexEraserToolService,
|
|
55
|
+
ZoomToBoxToolService,
|
|
56
|
+
// The three host-facing contracts are all served by the router — bound here at
|
|
57
|
+
// the SAME (component) scope so they resolve to the isolated instance, not root.
|
|
58
|
+
{ provide: VISUALIZER, useExisting: RoutingVisualizerService },
|
|
59
|
+
{ provide: REGION_EDITOR_API, useExisting: RoutingVisualizerService },
|
|
60
|
+
{ provide: CHANNEL_HISTOGRAM_API, useExisting: RoutingVisualizerService },
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvdmlkZS12aXN1YWxpemF0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2xpYi9wcm92aWRlLXZpc3VhbGl6YXRpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUEsT0FBTyxFQUFFLHdCQUF3QixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDeEUsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLHlDQUF5QyxDQUFDO0FBQ3hFLE9BQU8sRUFBRSw4QkFBOEIsRUFBRSxNQUFNLHdEQUF3RCxDQUFDO0FBQ3hHLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLHVEQUF1RCxDQUFDO0FBQ2hHLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUNuRSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDM0QsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBQ25FLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLG9EQUFvRCxDQUFDO0FBQzdGLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGdEQUFnRCxDQUFDO0FBQ3RGLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUM3RCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx3Q0FBd0MsQ0FBQztBQUMzRSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQUVuRjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQStCRztBQUNILE1BQU0sVUFBVSxvQkFBb0I7SUFDbEMsT0FBTztRQUNMLHdCQUF3QjtRQUN4QixhQUFhO1FBQ2IsOEJBQThCO1FBQzlCLHVCQUF1QjtRQUN2QixlQUFlO1FBQ2YsV0FBVztRQUNYLGVBQWU7UUFDZix1QkFBdUI7UUFDdkIsb0JBQW9CO1FBQ3BCLCtFQUErRTtRQUMvRSxpRkFBaUY7UUFDakYsRUFBRSxPQUFPLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSx3QkFBd0IsRUFBRTtRQUM5RCxFQUFFLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxXQUFXLEVBQUUsd0JBQXdCLEVBQUU7UUFDckUsRUFBRSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsV0FBVyxFQUFFLHdCQUF3QixFQUFFO0tBQzFFLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgUHJvdmlkZXIgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuaW1wb3J0IHsgUm91dGluZ1Zpc3VhbGl6ZXJTZXJ2aWNlIH0gZnJvbSAnLi9yb3V0aW5nLXZpc3VhbGl6ZXIuc2VydmljZSc7XG5pbXBvcnQgeyBQbG90bHlTZXJ2aWNlIH0gZnJvbSAnLi9pbXBsZW1lbnRhdGlvbnMvcGxvdGx5L3Bsb3RseS5zZXJ2aWNlJztcbmltcG9ydCB7IE9wZW5TZWFkcmFnb25WaXN1YWxpemVyU2VydmljZSB9IGZyb20gJy4vaW1wbGVtZW50YXRpb25zL29zZC9vcGVuc2VhZHJhZ29uLXZpc3VhbGl6ZXIuc2VydmljZSc7XG5pbXBvcnQgeyBOYXBhcmlWaXN1YWxpemVyU2VydmljZSB9IGZyb20gJy4vaW1wbGVtZW50YXRpb25zL25hcGFyaS1qcy9uYXBhcmktdmlzdWFsaXplci5zZXJ2aWNlJztcbmltcG9ydCB7IFZpc3VhbGl6ZXJTdG9yZSB9IGZyb20gJy4vc3RvcmUvdmlzdWFsaXplci1zdG9yZS5zZXJ2aWNlJztcbmltcG9ydCB7IFJlZ2lvblN0b3JlIH0gZnJvbSAnLi9zdG9yZS9yZWdpb24tc3RvcmUuc2VydmljZSc7XG5pbXBvcnQgeyBXYW5kVG9vbFNlcnZpY2UgfSBmcm9tICcuL3Rvb2xiYXIvd2FuZC93YW5kLXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBWZXJ0ZXhFcmFzZXJUb29sU2VydmljZSB9IGZyb20gJy4vdG9vbGJhci92ZXJ0ZXgtZXJhc2VyL3ZlcnRleC1lcmFzZXItdG9vbC5zZXJ2aWNlJztcbmltcG9ydCB7IFpvb21Ub0JveFRvb2xTZXJ2aWNlIH0gZnJvbSAnLi90b29sYmFyL3pvb20tdG8tYm94L3pvb20tdG8tYm94LXRvb2wuc2VydmljZSc7XG5pbXBvcnQgeyBWSVNVQUxJWkVSIH0gZnJvbSAnLi9jb250cmFjdHMvdmlzdWFsaXplci5jb250cmFjdCc7XG5pbXBvcnQgeyBSRUdJT05fRURJVE9SX0FQSSB9IGZyb20gJy4vY29udHJhY3RzL3JlZ2lvbi1lZGl0b3ItYXBpLmNvbnRyYWN0JztcbmltcG9ydCB7IENIQU5ORUxfSElTVE9HUkFNX0FQSSB9IGZyb20gJy4vY29udHJhY3RzL2NoYW5uZWwtaGlzdG9ncmFtLWFwaS5jb250cmFjdCc7XG5cbi8qKlxuICogQSBzZWxmLWNvbnRhaW5lZCwgaXNvbGF0ZWQgdmlzdWFsaXphdGlvbiBiYWNrZW5kIGNoYWluIGZvciBhIGNvbXBvbmVudCBzdWJ0cmVlLlxuICpcbiAqIFRoZSBjaGFpbiBzZXJ2aWNlcyBhcmUgYHByb3ZpZGVkSW46ICdyb290J2Agc2luZ2xldG9ucywgc28gYnkgZGVmYXVsdCB0aGUgd2hvbGVcbiAqIGFwcCBzaGFyZXMgb25lIHZpZXdlcidzIHN0YXRlIChyZWdpb25zLCBpbWFnZSwgY2hhbm5lbHMsIHJlbmRlciBoYW5kbGVzKSDigJQgd2hpY2hcbiAqIGlzIGNvcnJlY3QgZm9yIHRoZSBzaW5nbGUgbWFpbiB2aWV3ZXIuIEEgY29uc3VtZXIgdGhhdCBuZWVkcyBhIFNFQ09ORCwgaW5kZXBlbmRlbnRcbiAqIHZpZXdlciAoZS5nLiBhIG1vZGFsIHRoYXQgbW91bnRzIGA8dmlzdWFsaXplcj5gIG92ZXIgdGhlIGxpdmUgbWFpbiB2aWV3XG4gKiBhbmQgbXVzdCBub3QgY2xvYmJlciBpdHMgcmVnaW9ucy9pbWFnZSkgZHJvcHMgdGhpcyBpbnRvIGl0cyBjb21wb25lbnQgYHByb3ZpZGVyc2A6XG4gKlxuICogYGBgdHNcbiAqIEBDb21wb25lbnQoe1xuICogICAuLi4sXG4gKiAgIHByb3ZpZGVyczogW1xuICogICAgIC4uLnByb3ZpZGVWaXN1YWxpemF0aW9uKCksICAgICAgICAgICAgICAgICAvLyBpc29sYXRlZCBjaGFpbiBpbnN0YW5jZVxuICogICAgIC8vIHBsdXMgdGhlIGhvc3QgcG9ydHMgKyBjb25maWcgZm9yIFRISVMgdmlld2VyOlxuICogICAgIHsgcHJvdmlkZTogSU1BR0VfU1RBVEVfUE9SVCwgdXNlQ2xhc3M6IE15SW1hZ2VTdGF0ZUFkYXB0ZXIgfSxcbiAqICAgICB7IHByb3ZpZGU6IFRJTEVfQUNDRVNTX1BPUlQsIHVzZUNsYXNzOiBNeVRpbGVBY2Nlc3NBZGFwdGVyIH0sXG4gKiAgICAgeyBwcm92aWRlOiBSRUdJT05fSU9fUE9SVCwgICB1c2VDbGFzczogTXlSZWdpb25Jb0FkYXB0ZXIgfSxcbiAqICAgICB7IHByb3ZpZGU6IFZJWl9DT05GSUcsICAgICAgIHVzZVZhbHVlOiB7IHNsaWRlQ3JvcFNlcnZlcjogJycgfSB9LFxuICogICBdLFxuICogfSlcbiAqIGBgYFxuICpcbiAqIENvbXBvbmVudC1zY29wZWQgcHJvdmlkZXJzIHNoYWRvdyB0aGUgcm9vdCBzaW5nbGV0b25zIGZvciB0aGF0IHN1YnRyZWUsIHNvIHRoZVxuICogZW1iZWRkZWQgdmlld2VyIGdldHMgaXRzIG93biByb3V0ZXIvUGxvdGx5L09TRC9zdG9yZXMvdG9vbHMgd2hpbGUgdGhlIHJlc3Qgb2YgdGhlXG4gKiBhcHAga2VlcHMgdGhlIGRlZmF1bHQgcm9vdCBpbnN0YW5jZS5cbiAqXG4gKiBMaXN0cyBFVkVSWSBzdGF0ZWZ1bCBzZXJ2aWNlIGluIHRoZSBjaGFpbi4gU3RhdGVsZXNzIGNvbGxhYm9yYXRvcnMgKEh0dHBDbGllbnQsXG4gKiBNZXNzYWdlU2VydmljZSwgV2FuZFNlcnZpY2UpIGRlbGliZXJhdGVseSByZXNvbHZlIHRvIHJvb3Qg4oCUIHRoZXkgaG9sZCBub1xuICogcGVyLXZpZXdlciBzdGF0ZSwgc28gc2hhcmluZyB0aGVtIGlzIGNvcnJlY3QgYW5kIGtlZXBzIHRoaXMgbGlzdCBtaW5pbWFsLiBXaGVuIGFcbiAqIG5ldyBzdGF0ZWZ1bCBzZXJ2aWNlIGpvaW5zIHRoZSByZW5kZXJpbmcgY2hhaW4sIGFkZCBpdCBoZXJlIHRvby5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHByb3ZpZGVWaXN1YWxpemF0aW9uKCk6IFByb3ZpZGVyW10ge1xuICByZXR1cm4gW1xuICAgIFJvdXRpbmdWaXN1YWxpemVyU2VydmljZSxcbiAgICBQbG90bHlTZXJ2aWNlLFxuICAgIE9wZW5TZWFkcmFnb25WaXN1YWxpemVyU2VydmljZSxcbiAgICBOYXBhcmlWaXN1YWxpemVyU2VydmljZSxcbiAgICBWaXN1YWxpemVyU3RvcmUsXG4gICAgUmVnaW9uU3RvcmUsXG4gICAgV2FuZFRvb2xTZXJ2aWNlLFxuICAgIFZlcnRleEVyYXNlclRvb2xTZXJ2aWNlLFxuICAgIFpvb21Ub0JveFRvb2xTZXJ2aWNlLFxuICAgIC8vIFRoZSB0aHJlZSBob3N0LWZhY2luZyBjb250cmFjdHMgYXJlIGFsbCBzZXJ2ZWQgYnkgdGhlIHJvdXRlciDigJQgYm91bmQgaGVyZSBhdFxuICAgIC8vIHRoZSBTQU1FIChjb21wb25lbnQpIHNjb3BlIHNvIHRoZXkgcmVzb2x2ZSB0byB0aGUgaXNvbGF0ZWQgaW5zdGFuY2UsIG5vdCByb290LlxuICAgIHsgcHJvdmlkZTogVklTVUFMSVpFUiwgdXNlRXhpc3Rpbmc6IFJvdXRpbmdWaXN1YWxpemVyU2VydmljZSB9LFxuICAgIHsgcHJvdmlkZTogUkVHSU9OX0VESVRPUl9BUEksIHVzZUV4aXN0aW5nOiBSb3V0aW5nVmlzdWFsaXplclNlcnZpY2UgfSxcbiAgICB7IHByb3ZpZGU6IENIQU5ORUxfSElTVE9HUkFNX0FQSSwgdXNlRXhpc3Rpbmc6IFJvdXRpbmdWaXN1YWxpemVyU2VydmljZSB9LFxuICBdO1xufVxuIl19
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Rectangle, Polygon, MultiPolygon } from '../models/region';
|
|
2
|
+
/**
|
|
3
|
+
* The largest mask we will allocate, in pixels. A single-channel 8-bit mask is
|
|
4
|
+
* one byte per pixel, so this is also its byte size. Whole-slide level-0 images
|
|
5
|
+
* (e.g. 119040×90112 ≈ 1.07e10 px) blow past both the JS typed-array length cap
|
|
6
|
+
* (~4.29e9) and available memory, so masks for images larger than this are
|
|
7
|
+
* rasterized at a proportionally downscaled resolution. ~100 MP ≈ 100 MB.
|
|
8
|
+
*/
|
|
9
|
+
export const MAX_MASK_PIXELS = 100_000_000;
|
|
10
|
+
/** Downscale factor (≤ 1) to keep `width * height` within {@link MAX_MASK_PIXELS}.
|
|
11
|
+
* 1 when the image already fits. */
|
|
12
|
+
export function maskScaleFor(width, height) {
|
|
13
|
+
const total = width * height;
|
|
14
|
+
if (!Number.isFinite(total) || total <= MAX_MASK_PIXELS)
|
|
15
|
+
return 1;
|
|
16
|
+
return Math.sqrt(MAX_MASK_PIXELS / total);
|
|
17
|
+
}
|
|
18
|
+
/** Multiply every coordinate (and hole coordinate) of a region's parts by
|
|
19
|
+
* `scale`, for rasterizing into a downscaled mask. Returns the parts unchanged
|
|
20
|
+
* when `scale` is 1. */
|
|
21
|
+
export function scaleParts(parts, scale) {
|
|
22
|
+
if (scale === 1)
|
|
23
|
+
return parts;
|
|
24
|
+
return parts.map((p) => ({
|
|
25
|
+
xpoints: p.xpoints.map((x) => x * scale),
|
|
26
|
+
ypoints: p.ypoints.map((y) => y * scale),
|
|
27
|
+
holes: p.holes?.map((ring) => ring.map(([x, y]) => [x * scale, y * scale])),
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
/** Flatten a region's geometry to plain rings. Rectangles become a 4-point ring;
|
|
31
|
+
* polygons keep their points + holes; multi-polygons expand to one part each. */
|
|
32
|
+
export function regionToParts(region) {
|
|
33
|
+
const b = region.bounds;
|
|
34
|
+
if (b instanceof Rectangle) {
|
|
35
|
+
return [{
|
|
36
|
+
xpoints: [b.x, b.x + b.width, b.x + b.width, b.x],
|
|
37
|
+
ypoints: [b.y, b.y, b.y + b.height, b.y + b.height],
|
|
38
|
+
}];
|
|
39
|
+
}
|
|
40
|
+
if (b instanceof Polygon) {
|
|
41
|
+
return [{ xpoints: b.xpoints, ypoints: b.ypoints, holes: b.holes }];
|
|
42
|
+
}
|
|
43
|
+
if (b instanceof MultiPolygon) {
|
|
44
|
+
return b.polygons.map((p) => ({ xpoints: p.xpoints, ypoints: p.ypoints, holes: p.holes }));
|
|
45
|
+
}
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Rasterize regions (as plain parts) to a full image-size label mask (row-major,
|
|
50
|
+
* `W * H` samples; pixel `(x, y)` at `y * W + x`). Background is 0.
|
|
51
|
+
*
|
|
52
|
+
* - `'binary'` — every region pixel is 255 (standard 8-bit black/white mask:
|
|
53
|
+
* `> 0` = foreground; also directly viewable).
|
|
54
|
+
* - `'multiclass'` — each region gets a distinct 1-based id (its index + 1).
|
|
55
|
+
* Stays 8-bit for ≤255 regions; promotes to 16-bit beyond that. Ids are
|
|
56
|
+
* distinct up to the 16-bit ceiling (65535 regions); a selection larger than
|
|
57
|
+
* that exceeds the PNG's representable range and ids wrap (not a realistic
|
|
58
|
+
* hand-annotation case, but called out so the limit is explicit).
|
|
59
|
+
*
|
|
60
|
+
* Later regions paint over earlier ones where they overlap. `onProgress` is
|
|
61
|
+
* called after each region (1-based) so a caller can drive a progress bar.
|
|
62
|
+
* Returns null when nothing rasterises (no regions, zero/insane size).
|
|
63
|
+
*/
|
|
64
|
+
export function regionsToMask(regions, imageWidth, imageHeight, mode, rasterize, onProgress) {
|
|
65
|
+
const W = Math.round(imageWidth);
|
|
66
|
+
const H = Math.round(imageHeight);
|
|
67
|
+
if (!Number.isFinite(W) || !Number.isFinite(H) || W <= 0 || H <= 0 || !regions?.length) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const multiclass = mode === 'multiclass';
|
|
71
|
+
const bitDepth = multiclass && regions.length > 255 ? 16 : 8;
|
|
72
|
+
const out = bitDepth === 16 ? new Uint16Array(W * H) : new Uint8Array(W * H);
|
|
73
|
+
let painted = false;
|
|
74
|
+
for (let i = 0; i < regions.length; i++) {
|
|
75
|
+
// Binary → 255 (viewable, standard). Multi-class → 1-based class id.
|
|
76
|
+
const value = multiclass ? i + 1 : 255;
|
|
77
|
+
for (const part of regions[i]) {
|
|
78
|
+
const m = rasterize(part.xpoints, part.ypoints, W, H, part.holes);
|
|
79
|
+
if (!m)
|
|
80
|
+
continue;
|
|
81
|
+
painted = true;
|
|
82
|
+
for (let y = 0; y < m.bh; y++) {
|
|
83
|
+
const srcRow = y * m.bw;
|
|
84
|
+
const dstRow = (m.by + y) * W + m.bx;
|
|
85
|
+
for (let x = 0; x < m.bw; x++) {
|
|
86
|
+
if (m.mask[srcRow + x])
|
|
87
|
+
out[dstRow + x] = value;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
onProgress?.(i + 1, regions.length);
|
|
92
|
+
}
|
|
93
|
+
return painted ? { data: out, bitDepth } : null;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFzay1yYXN0ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3JlZ2lvbi1lZGl0b3IvbWFzay1yYXN0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFVLFNBQVMsRUFBRSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUE4QjVFOzs7Ozs7R0FNRztBQUNILE1BQU0sQ0FBQyxNQUFNLGVBQWUsR0FBRyxXQUFXLENBQUM7QUFFM0M7cUNBQ3FDO0FBQ3JDLE1BQU0sVUFBVSxZQUFZLENBQUMsS0FBYSxFQUFFLE1BQWM7SUFDeEQsTUFBTSxLQUFLLEdBQUcsS0FBSyxHQUFHLE1BQU0sQ0FBQztJQUM3QixJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsSUFBSSxLQUFLLElBQUksZUFBZTtRQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ2xFLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxlQUFlLEdBQUcsS0FBSyxDQUFDLENBQUM7QUFDNUMsQ0FBQztBQUVEOzt5QkFFeUI7QUFDekIsTUFBTSxVQUFVLFVBQVUsQ0FBQyxLQUFpQixFQUFFLEtBQWE7SUFDekQsSUFBSSxLQUFLLEtBQUssQ0FBQztRQUFFLE9BQU8sS0FBSyxDQUFDO0lBQzlCLE9BQU8sS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUN2QixPQUFPLEVBQUUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsR0FBRyxLQUFLLENBQUM7UUFDeEMsT0FBTyxFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEdBQUcsS0FBSyxDQUFDO1FBQ3hDLEtBQUssRUFBRSxDQUFDLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxDQUFD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SUFDdEMsQ0FBQztJQUNELE9BQU8sT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztBQUNsRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgUmVnaW9uLCBSZWN0YW5nbGUsIFBvbHlnb24sIE11bHRpUG9seWdvbiB9IGZyb20gJy4uL21vZGVscy9yZWdpb24nO1xuXG4vKiogQSBzaW5nbGUgY2xvc2VkIHJpbmcgKCsgb3B0aW9uYWwgaG9sZXMpIGluIGltYWdlLXBpeGVsIGNvb3JkaW5hdGVzLiBBIHJlZ2lvblxuICogIHJhc3Rlcml6ZXMgYXMgdGhlIHVuaW9uIG9mIGl0cyBwYXJ0cy4gUGxhaW4gZGF0YSBzbyBpdCBzdXJ2aXZlcyBhXG4gKiAgYHBvc3RNZXNzYWdlYCBzdHJ1Y3R1cmVkIGNsb25lIGludG8gdGhlIG1hc2sgd29ya2VyLiAqL1xuZXhwb3J0IGludGVyZmFjZSBNYXNrUGFydCB7XG4gIHhwb2ludHM6IG51bWJlcltdO1xuICB5cG9pbnRzOiBudW1iZXJbXTtcbiAgaG9sZXM/OiBudW1iZXJbXVtdW107XG59XG5cbi8qKiBBIGJib3gtcmVsYXRpdmUgYmluYXJ5IG1hc2ssIGFzIHJldHVybmVkIGJ5IGBXYW5kU2VydmljZS5yYXN0ZXJpemVQb2x5Z29uYC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQkJveE1hc2sge1xuICBieDogbnVtYmVyO1xuICBieTogbnVtYmVyO1xuICBidzogbnVtYmVyO1xuICBiaDogbnVtYmVyO1xuICBtYXNrOiBVaW50OEFycmF5O1xufVxuXG4vKiogUG9seWdvbi1maWxsIGZ1bmN0aW9uICh0aGUgd2FuZCdzIHNjYW5saW5lIHJhc3Rlcml6ZXIpLCBpbmplY3RlZCBzbyB0aGlzXG4gKiAgbW9kdWxlIHN0YXlzIGZyZWUgb2YgQW5ndWxhci9ET00gYW5kIGNhbiBydW4gaW5zaWRlIGEgd29ya2VyLiAqL1xuZXhwb3J0IHR5cGUgUmFzdGVyaXplRm4gPSAoXG4gIHhwb2ludHM6IG51bWJlcltdLFxuICB5cG9pbnRzOiBudW1iZXJbXSxcbiAgaW1hZ2VXaWR0aDogbnVtYmVyLFxuICBpbWFnZUhlaWdodDogbnVtYmVyLFxuICBob2xlcz86IG51bWJlcltdW11bXSxcbikgPT4gQkJveE1hc2sgfCBudWxsO1xuXG4vKipcbiAqIFRoZSBsYXJnZXN0IG1hc2sgd2Ugd2lsbCBhbGxvY2F0ZSwgaW4gcGl4ZWxzLiBBIHNpbmdsZS1jaGFubmVsIDgtYml0IG1hc2sgaXNcbiAqIG9uZSBieXRlIHBlciBwaXhlbCwgc28gdGhpcyBpcyBhbHNvIGl0cyBieXRlIHNpemUuIFdob2xlLXNsaWRlIGxldmVsLTAgaW1hZ2VzXG4gKiAoZS5nLiAxMTkwNDDDlzkwMTEyIOKJiCAxLjA3ZTEwIHB4KSBibG93IHBhc3QgYm90aCB0aGUgSlMgdHlwZWQtYXJyYXkgbGVuZ3RoIGNhcFxuICogKH40LjI5ZTkpIGFuZCBhdmFpbGFibGUgbWVtb3J5LCBzbyBtYXNrcyBmb3IgaW1hZ2VzIGxhcmdlciB0aGFuIHRoaXMgYXJlXG4gKiByYXN0ZXJpemVkIGF0IGEgcHJvcG9ydGlvbmFsbHkgZG93bnNjYWxlZCByZXNvbHV0aW9uLiB+MTAwIE1QIOKJiCAxMDAgTUIuXG4gKi9cbmV4cG9ydCBjb25zdCBNQVhfTUFTS19QSVhFTFMgPSAxMDBfMDAwXzAwMDtcblxuLyoqIERvd25zY2FsZSBmYWN0b3IgKOKJpCAxKSB0byBrZWVwIGB3aWR0aCAqIGhlaWdodGAgd2l0aGluIHtAbGluayBNQVhfTUFTS19QSVhFTFN9LlxuICogIDEgd2hlbiB0aGUgaW1hZ2UgYWxyZWFkeSBmaXRzLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG1hc2tTY2FsZUZvcih3aWR0aDogbnVtYmVyLCBoZWlnaHQ6IG51bWJlcik6IG51bWJlciB7XG4gIGNvbnN0IHRvdGFsID0gd2lkdGggKiBoZWlnaHQ7XG4gIGlmICghTnVtYmVyLmlzRmluaXRlKHRvdGFsKSB8fCB0b3RhbCA8PSBNQVhfTUFTS19QSVhFTFMpIHJldHVybiAxO1xuICByZXR1cm4gTWF0aC5zcXJ0KE1BWF9NQVNLX1BJWEVMUyAvIHRvdGFsKTtcbn1cblxuLyoqIE11bHRpcGx5IGV2ZXJ5IGNvb3JkaW5hdGUgKGFuZCBob2xlIGNvb3JkaW5hdGUpIG9mIGEgcmVnaW9uJ3MgcGFydHMgYnlcbiAqICBgc2NhbGVgLCBmb3IgcmFzdGVyaXppbmcgaW50byBhIGRvd25zY2FsZWQgbWFzay4gUmV0dXJucyB0aGUgcGFydHMgdW5jaGFuZ2VkXG4gKiAgd2hlbiBgc2NhbGVgIGlzIDEuICovXG5leHBvcnQgZnVuY3Rpb24gc2NhbGVQYXJ0cyhwYXJ0czogTWFza1BhcnRbXSwgc2NhbGU6IG51bWJlcik6IE1hc2tQYXJ0W10ge1xuICBpZiAoc2NhbGUgPT09IDEpIHJldHVybiBwYXJ0cztcbiAgcmV0dXJuIHBhcnRzLm1hcCgocCkgPT4gKHtcbiAgICB4cG9pbnRzOiBwLnhwb2ludHMubWFwKCh4KSA9PiB4ICogc2NhbGUpLFxuICAgIHlwb2ludHM6IHAueXBvaW50cy5tYXAoKHkpID0+IHkgKiBzY2FsZSksXG4gICAgaG9sZXM6IHAuaG9sZXM/Lm1hcCgocmluZykgPT4gcmluZy5tYXAoKFt4LCB5XSkgPT4gW3ggKiBzY2FsZSwgeSAqIHNjYWxlXSkpLFxuICB9KSk7XG59XG5cbi8qKiBGbGF0dGVuIGEgcmVnaW9uJ3MgZ2VvbWV0cnkgdG8gcGxhaW4gcmluZ3MuIFJlY3RhbmdsZXMgYmVjb21lIGEgNC1wb2ludCByaW5nO1xuICogIHBvbHlnb25zIGtlZXAgdGhlaXIgcG9pbnRzICsgaG9sZXM7IG11bHRpLXBvbHlnb25zIGV4cGFuZCB0byBvbmUgcGFydCBlYWNoLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlZ2lvblRvUGFydHMocmVnaW9uOiBSZWdpb24pOiBNYXNrUGFydFtdIHtcbiAgY29uc3QgYiA9IHJlZ2lvbi5ib3VuZHM7XG4gIGlmIChiIGluc3RhbmNlb2YgUmVjdGFuZ2xlKSB7XG4gICAgcmV0dXJuIFt7XG4gICAgICB4cG9pbnRzOiBbYi54LCBiLnggKyBiLndpZHRoLCBiLnggKyBiLndpZHRoLCBiLnhdLFxuICAgICAgeXBvaW50czogW2IueSwgYi55LCBiLnkgKyBiLmhlaWdodCwgYi55ICsgYi5oZWlnaHRdLFxuICAgIH1dO1xuICB9XG4gIGlmIChiIGluc3RhbmNlb2YgUG9seWdvbikge1xuICAgIHJldHVybiBbeyB4cG9pbnRzOiBiLnhwb2ludHMsIHlwb2ludHM6IGIueXBvaW50cywgaG9sZXM6IGIuaG9sZXMgfV07XG4gIH1cbiAgaWYgKGIgaW5zdGFuY2VvZiBNdWx0aVBvbHlnb24pIHtcbiAgICByZXR1cm4gYi5wb2x5Z29ucy5tYXAoKHApID0+ICh7IHhwb2ludHM6IHAueHBvaW50cywgeXBvaW50czogcC55cG9pbnRzLCBob2xlczogcC5ob2xlcyB9KSk7XG4gIH1cbiAgcmV0dXJuIFtdO1xufVxuXG4vKiogQSByYXN0ZXJpemVkIGxhYmVsIG1hc2sgcGx1cyB0aGUgUE5HIGJpdCBkZXB0aCBpdCBzaG91bGQgYmUgZW5jb2RlZCBhdC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTGFiZWxNYXNrIHtcbiAgZGF0YTogVWludDhBcnJheSB8IFVpbnQxNkFycmF5O1xuICAvKiogOCBmb3Ig4omkMjU1IGRpc3RpbmN0IHZhbHVlcywgMTYgZm9yIGEgbXVsdGktY2xhc3MgbWFzayB3aXRoID4yNTUgcmVnaW9ucy4gKi9cbiAgYml0RGVwdGg6IDggfCAxNjtcbn1cblxuLyoqXG4gKiBSYXN0ZXJpemUgcmVnaW9ucyAoYXMgcGxhaW4gcGFydHMpIHRvIGEgZnVsbCBpbWFnZS1zaXplIGxhYmVsIG1hc2sgKHJvdy1tYWpvcixcbiAqIGBXICogSGAgc2FtcGxlczsgcGl4ZWwgYCh4LCB5KWAgYXQgYHkgKiBXICsgeGApLiBCYWNrZ3JvdW5kIGlzIDAuXG4gKlxuICogLSBgJ2JpbmFyeSdgIOKAlCBldmVyeSByZWdpb24gcGl4ZWwgaXMgMjU1IChzdGFuZGFyZCA4LWJpdCBibGFjay93aGl0ZSBtYXNrOlxuICogICBgPiAwYCA9IGZvcmVncm91bmQ7IGFsc28gZGlyZWN0bHkgdmlld2FibGUpLlxuICogLSBgJ211bHRpY2xhc3MnYCDigJQgZWFjaCByZWdpb24gZ2V0cyBhIGRpc3RpbmN0IDEtYmFzZWQgaWQgKGl0cyBpbmRleCArIDEpLlxuICogICBTdGF5cyA4LWJpdCBmb3Ig4omkMjU1IHJlZ2lvbnM7IHByb21vdGVzIHRvIDE2LWJpdCBiZXlvbmQgdGhhdC4gSWRzIGFyZVxuICogICBkaXN0aW5jdCB1cCB0byB0aGUgMTYtYml0IGNlaWxpbmcgKDY1NTM1IHJlZ2lvbnMpOyBhIHNlbGVjdGlvbiBsYXJnZXIgdGhhblxuICogICB0aGF0IGV4Y2VlZHMgdGhlIFBORydzIHJlcHJlc2VudGFibGUgcmFuZ2UgYW5kIGlkcyB3cmFwIChub3QgYSByZWFsaXN0aWNcbiAqICAgaGFuZC1hbm5vdGF0aW9uIGNhc2UsIGJ1dCBjYWxsZWQgb3V0IHNvIHRoZSBsaW1pdCBpcyBleHBsaWNpdCkuXG4gKlxuICogTGF0ZXIgcmVnaW9ucyBwYWludCBvdmVyIGVhcmxpZXIgb25lcyB3aGVyZSB0aGV5IG92ZXJsYXAuIGBvblByb2dyZXNzYCBpc1xuICogY2FsbGVkIGFmdGVyIGVhY2ggcmVnaW9uICgxLWJhc2VkKSBzbyBhIGNhbGxlciBjYW4gZHJpdmUgYSBwcm9ncmVzcyBiYXIuXG4gKiBSZXR1cm5zIG51bGwgd2hlbiBub3RoaW5nIHJhc3RlcmlzZXMgKG5vIHJlZ2lvbnMsIHplcm8vaW5zYW5lIHNpemUpLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVnaW9uc1RvTWFzayhcbiAgcmVnaW9uczogTWFza1BhcnRbXVtdLFxuICBpbWFnZVdpZHRoOiBudW1iZXIsXG4gIGltYWdlSGVpZ2h0OiBudW1iZXIsXG4gIG1vZGU6ICdiaW5hcnknIHwgJ211bHRpY2xhc3MnLFxuICByYXN0ZXJpemU6IFJhc3Rlcml6ZUZuLFxuICBvblByb2dyZXNzPzogKGRvbmU6IG51bWJlciwgdG90YWw6IG51bWJlcikgPT4gdm9pZCxcbik6IExhYmVsTWFzayB8IG51bGwge1xuICBjb25zdCBXID0gTWF0aC5yb3VuZChpbWFnZVdpZHRoKTtcbiAgY29uc3QgSCA9IE1hdGgucm91bmQoaW1hZ2VIZWlnaHQpO1xuICBpZiAoIU51bWJlci5pc0Zpbml0ZShXKSB8fCAhTnVtYmVyLmlzRmluaXRlKEgpIHx8IFcgPD0gMCB8fCBIIDw9IDAgfHwgIXJlZ2lvbnM/Lmxlbmd0aCkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgY29uc3QgbXVsdGljbGFzcyA9IG1vZGUgPT09ICdtdWx0aWNsYXNzJztcbiAgY29uc3QgYml0RGVwdGg6IDggfCAxNiA9IG11bHRpY2xhc3MgJiYgcmVnaW9ucy5sZW5ndGggPiAyNTUgPyAxNiA6IDg7XG4gIGNvbnN0IG91dDogVWludDhBcnJheSB8IFVpbnQxNkFycmF5ID1cbiAgICBiaXREZXB0aCA9PT0gMTYgPyBuZXcgVWludDE2QXJyYXkoVyAqIEgpIDogbmV3IFVpbnQ4QXJyYXkoVyAqIEgpO1xuICBsZXQgcGFpbnRlZCA9IGZhbHNlO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IHJlZ2lvbnMubGVuZ3RoOyBpKyspIHtcbiAgICAvLyBCaW5hcnkg4oaSIDI1NSAodmlld2FibGUsIHN0YW5kYXJkKS4gTXVsdGktY2xhc3Mg4oaSIDEtYmFzZWQgY2xhc3MgaWQuXG4gICAgY29uc3QgdmFsdWUgPSBtdWx0aWNsYXNzID8gaSArIDEgOiAyNTU7XG4gICAgZm9yIChjb25zdCBwYXJ0IG9mIHJlZ2lvbnNbaV0pIHtcbiAgICAgIGNvbnN0IG0gPSByYXN0ZXJpemUocGFydC54cG9pbnRzLCBwYXJ0Lnlwb2ludHMsIFcsIEgsIHBhcnQuaG9sZXMpO1xuICAgICAgaWYgKCFtKSBjb250aW51ZTtcbiAgICAgIHBhaW50ZWQgPSB0cnVlO1xuICAgICAgZm9yIChsZXQgeSA9IDA7IHkgPCBtLmJoOyB5KyspIHtcbiAgICAgICAgY29uc3Qgc3JjUm93ID0geSAqIG0uYnc7XG4gICAgICAgIGNvbnN0IGRzdFJvdyA9IChtLmJ5ICsgeSkgKiBXICsgbS5ieDtcbiAgICAgICAgZm9yIChsZXQgeCA9IDA7IHggPCBtLmJ3OyB4KyspIHtcbiAgICAgICAgICBpZiAobS5tYXNrW3NyY1JvdyArIHhdKSBvdXRbZHN0Um93ICsgeF0gPSB2YWx1ZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICBvblByb2dyZXNzPy4oaSArIDEsIHJlZ2lvbnMubGVuZ3RoKTtcbiAgfVxuICByZXR1cm4gcGFpbnRlZCA/IHsgZGF0YTogb3V0LCBiaXREZXB0aCB9IDogbnVsbDtcbn1cbiJdfQ==
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory for the mask-export Web Worker, isolated in its own module so the
|
|
3
|
+
* `import.meta.url` reference (required by the bundler to locate the worker) is
|
|
4
|
+
* never pulled into the ts-jest CommonJS compile of the component — the unit
|
|
5
|
+
* tests `jest.mock` this module instead.
|
|
6
|
+
*/
|
|
7
|
+
export function createMaskWorker() {
|
|
8
|
+
return new Worker(new URL('./mask.worker', import.meta.url), { type: 'module' });
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFzay13b3JrZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL3JlZ2lvbi1lZGl0b3IvbWFzay13b3JrZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsZ0JBQWdCO0lBQzlCLE9BQU8sSUFBSSxNQUFNLENBQUMsSUFBSSxHQUFHLENBQUMsZUFBZSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQztBQUNuRixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBGYWN0b3J5IGZvciB0aGUgbWFzay1leHBvcnQgV2ViIFdvcmtlciwgaXNvbGF0ZWQgaW4gaXRzIG93biBtb2R1bGUgc28gdGhlXG4gKiBgaW1wb3J0Lm1ldGEudXJsYCByZWZlcmVuY2UgKHJlcXVpcmVkIGJ5IHRoZSBidW5kbGVyIHRvIGxvY2F0ZSB0aGUgd29ya2VyKSBpc1xuICogbmV2ZXIgcHVsbGVkIGludG8gdGhlIHRzLWplc3QgQ29tbW9uSlMgY29tcGlsZSBvZiB0aGUgY29tcG9uZW50IOKAlCB0aGUgdW5pdFxuICogdGVzdHMgYGplc3QubW9ja2AgdGhpcyBtb2R1bGUgaW5zdGVhZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZU1hc2tXb3JrZXIoKTogV29ya2VyIHtcbiAgcmV0dXJuIG5ldyBXb3JrZXIobmV3IFVSTCgnLi9tYXNrLndvcmtlcicsIGltcG9ydC5tZXRhLnVybCksIHsgdHlwZTogJ21vZHVsZScgfSk7XG59XG4iXX0=
|