@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.
Files changed (311) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +404 -0
  3. package/esm2022/index.mjs +52 -0
  4. package/esm2022/jax-data-science-sci-image-visualizer.mjs +5 -0
  5. package/esm2022/lib/channel-histogram/channel-histogram.component.mjs +398 -0
  6. package/esm2022/lib/contracts/capabilities.contract.mjs +32 -0
  7. package/esm2022/lib/contracts/cell-segmenter.contract.mjs +12 -0
  8. package/esm2022/lib/contracts/channel-histogram-api.contract.mjs +14 -0
  9. package/esm2022/lib/contracts/colormap-lut.mjs +95 -0
  10. package/esm2022/lib/contracts/coordinate-transform.contract.mjs +2 -0
  11. package/esm2022/lib/contracts/display-types.mjs +8 -0
  12. package/esm2022/lib/contracts/image.contract.mjs +11 -0
  13. package/esm2022/lib/contracts/intensity.mjs +28 -0
  14. package/esm2022/lib/contracts/plot-type.mjs +112 -0
  15. package/esm2022/lib/contracts/ports/image-state.port.mjs +3 -0
  16. package/esm2022/lib/contracts/ports/preferences.port.mjs +3 -0
  17. package/esm2022/lib/contracts/ports/region-io.port.mjs +3 -0
  18. package/esm2022/lib/contracts/ports/tile-access.port.mjs +3 -0
  19. package/esm2022/lib/contracts/region-editor-api.contract.mjs +4 -0
  20. package/esm2022/lib/contracts/region-overlay.contract.mjs +2 -0
  21. package/esm2022/lib/contracts/region-store.contract.mjs +2 -0
  22. package/esm2022/lib/contracts/sam.contract.mjs +12 -0
  23. package/esm2022/lib/contracts/toolbar-config.mjs +8 -0
  24. package/esm2022/lib/contracts/visualizer.contract.mjs +10 -0
  25. package/esm2022/lib/contracts/viz-config.mjs +3 -0
  26. package/esm2022/lib/hex-color-picker/hex-color-picker.component.mjs +260 -0
  27. package/esm2022/lib/implementations/base-store-visualizer.mjs +76 -0
  28. package/esm2022/lib/implementations/napari-js/napari-axes-labels.mjs +106 -0
  29. package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +847 -0
  30. package/esm2022/lib/implementations/napari-js/napari-scale-bar.mjs +92 -0
  31. package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +2283 -0
  32. package/esm2022/lib/implementations/napari-js/napari-volume-z-handle.mjs +126 -0
  33. package/esm2022/lib/implementations/osd/display-pipeline.mjs +143 -0
  34. package/esm2022/lib/implementations/osd/histogram-sampler.mjs +217 -0
  35. package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +1780 -0
  36. package/esm2022/lib/implementations/osd/osd-coordinate-transform.mjs +25 -0
  37. package/esm2022/lib/implementations/osd/osd-coords.mjs +53 -0
  38. package/esm2022/lib/implementations/osd/osd-lib.mjs +20 -0
  39. package/esm2022/lib/implementations/osd/osd-region-overlay.mjs +1237 -0
  40. package/esm2022/lib/implementations/osd/osd-scale-bar.mjs +88 -0
  41. package/esm2022/lib/implementations/osd/slice-cache.mjs +488 -0
  42. package/esm2022/lib/implementations/osd/tile-client.mjs +29 -0
  43. package/esm2022/lib/implementations/plotly/plotly-coordinate-transform.mjs +37 -0
  44. package/esm2022/lib/implementations/plotly/plotly-region-overlay.mjs +31 -0
  45. package/esm2022/lib/implementations/plotly/plotly-trace-builders.mjs +183 -0
  46. package/esm2022/lib/implementations/plotly/plotly.service.mjs +2137 -0
  47. package/esm2022/lib/implementations/simple-slice-access.service.mjs +99 -0
  48. package/esm2022/lib/models/bezier.mjs +120 -0
  49. package/esm2022/lib/models/class-preset.mjs +46 -0
  50. package/esm2022/lib/models/geometry.mjs +198 -0
  51. package/esm2022/lib/models/region.mjs +166 -0
  52. package/esm2022/lib/models/shape.mjs +53 -0
  53. package/esm2022/lib/plot.utilities.mjs +693 -0
  54. package/esm2022/lib/processing/image-converter.service.mjs +90 -0
  55. package/esm2022/lib/processing/image-crop.mjs +62 -0
  56. package/esm2022/lib/processing/processing-image.mjs +50 -0
  57. package/esm2022/lib/provide-visualization.mjs +63 -0
  58. package/esm2022/lib/region-editor/mask-raster.mjs +95 -0
  59. package/esm2022/lib/region-editor/mask-worker.mjs +10 -0
  60. package/esm2022/lib/region-editor/region-editor.component.mjs +1190 -0
  61. package/esm2022/lib/region-ops.service.mjs +301 -0
  62. package/esm2022/lib/render-orchestrator.mjs +87 -0
  63. package/esm2022/lib/routing-visualizer.service.mjs +533 -0
  64. package/esm2022/lib/store/class-color.util.mjs +52 -0
  65. package/esm2022/lib/store/region-store.service.mjs +1029 -0
  66. package/esm2022/lib/store/visualizer-store.service.mjs +352 -0
  67. package/esm2022/lib/toolbar/brush/brush-tool.service.mjs +546 -0
  68. package/esm2022/lib/toolbar/crop/slide-crop.mjs +57 -0
  69. package/esm2022/lib/toolbar/segmentation/cell-segment-tool.service.mjs +115 -0
  70. package/esm2022/lib/toolbar/segmentation/cellpose-segmenter.service.mjs +84 -0
  71. package/esm2022/lib/toolbar/segmentation/onnx-sam-session.mjs +133 -0
  72. package/esm2022/lib/toolbar/segmentation/ort-runtime-config.mjs +31 -0
  73. package/esm2022/lib/toolbar/segmentation/sam-model-registry.mjs +108 -0
  74. package/esm2022/lib/toolbar/segmentation/sam-onnx-core.mjs +159 -0
  75. package/esm2022/lib/toolbar/segmentation/sam-point-tool.service.mjs +255 -0
  76. package/esm2022/lib/toolbar/segmentation/sam-prompt.mjs +94 -0
  77. package/esm2022/lib/toolbar/segmentation/sam-tool.service.mjs +188 -0
  78. package/esm2022/lib/toolbar/toolbar.component.mjs +311 -0
  79. package/esm2022/lib/toolbar/vertex-eraser/vertex-eraser-tool.service.mjs +234 -0
  80. package/esm2022/lib/toolbar/wand/wand-tool.service.mjs +499 -0
  81. package/esm2022/lib/toolbar/wand/wand.service.mjs +921 -0
  82. package/esm2022/lib/toolbar/zoom-to-box/zoom-to-box-tool.service.mjs +157 -0
  83. package/esm2022/lib/visualization.module.mjs +162 -0
  84. package/esm2022/lib/visualizer.component.mjs +1597 -0
  85. package/fesm2022/jax-data-science-sci-image-visualizer-mask-worker-uhepXUhM.mjs +12 -0
  86. package/fesm2022/jax-data-science-sci-image-visualizer-mask-worker-uhepXUhM.mjs.map +1 -0
  87. package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs +327 -0
  88. package/fesm2022/jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs.map +1 -0
  89. package/fesm2022/jax-data-science-sci-image-visualizer.mjs +21314 -0
  90. package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -0
  91. package/fesm2022/mask.worker.js +989 -0
  92. package/fesm2022/onnx-sam.worker.js +202 -0
  93. package/index.d.ts +39 -0
  94. package/lib/channel-histogram/channel-histogram.component.d.ts +112 -0
  95. package/lib/contracts/capabilities.contract.d.ts +30 -0
  96. package/lib/contracts/cell-segmenter.contract.d.ts +40 -0
  97. package/lib/contracts/channel-histogram-api.contract.d.ts +98 -0
  98. package/lib/contracts/colormap-lut.d.ts +21 -0
  99. package/lib/contracts/coordinate-transform.contract.d.ts +43 -0
  100. package/lib/contracts/display-types.d.ts +41 -0
  101. package/lib/contracts/image.contract.d.ts +72 -0
  102. package/lib/contracts/intensity.d.ts +18 -0
  103. package/lib/contracts/plot-type.d.ts +108 -0
  104. package/lib/contracts/ports/image-state.port.d.ts +29 -0
  105. package/lib/contracts/ports/preferences.port.d.ts +19 -0
  106. package/lib/contracts/ports/region-io.port.d.ts +29 -0
  107. package/lib/contracts/ports/tile-access.port.d.ts +27 -0
  108. package/lib/contracts/region-editor-api.contract.d.ts +83 -0
  109. package/lib/contracts/region-overlay.contract.d.ts +47 -0
  110. package/lib/contracts/region-store.contract.d.ts +74 -0
  111. package/lib/contracts/sam.contract.d.ts +98 -0
  112. package/lib/contracts/toolbar-config.d.ts +24 -0
  113. package/lib/contracts/visualizer.contract.d.ts +346 -0
  114. package/lib/contracts/viz-config.d.ts +25 -0
  115. package/lib/hex-color-picker/hex-color-picker.component.d.ts +47 -0
  116. package/lib/implementations/base-store-visualizer.d.ts +71 -0
  117. package/lib/implementations/napari-js/napari-axes-labels.d.ts +47 -0
  118. package/lib/implementations/napari-js/napari-region-overlay.d.ts +115 -0
  119. package/lib/implementations/napari-js/napari-scale-bar.d.ts +31 -0
  120. package/lib/implementations/napari-js/napari-visualizer.service.d.ts +455 -0
  121. package/lib/implementations/napari-js/napari-volume-z-handle.d.ts +46 -0
  122. package/lib/implementations/osd/display-pipeline.d.ts +47 -0
  123. package/lib/implementations/osd/histogram-sampler.d.ts +85 -0
  124. package/lib/implementations/osd/openseadragon-visualizer.service.d.ts +476 -0
  125. package/lib/implementations/osd/osd-coordinate-transform.d.ts +17 -0
  126. package/lib/implementations/osd/osd-coords.d.ts +16 -0
  127. package/lib/implementations/osd/osd-lib.d.ts +19 -0
  128. package/lib/implementations/osd/osd-region-overlay.d.ts +191 -0
  129. package/lib/implementations/osd/osd-scale-bar.d.ts +17 -0
  130. package/lib/implementations/osd/slice-cache.d.ts +161 -0
  131. package/lib/implementations/osd/tile-client.d.ts +32 -0
  132. package/lib/implementations/plotly/plotly-coordinate-transform.d.ts +19 -0
  133. package/lib/implementations/plotly/plotly-region-overlay.d.ts +23 -0
  134. package/lib/implementations/plotly/plotly-trace-builders.d.ts +60 -0
  135. package/lib/implementations/plotly/plotly.service.d.ts +594 -0
  136. package/lib/implementations/simple-slice-access.service.d.ts +64 -0
  137. package/lib/models/bezier.d.ts +59 -0
  138. package/lib/models/class-preset.d.ts +39 -0
  139. package/lib/models/geometry.d.ts +66 -0
  140. package/lib/models/region.d.ts +132 -0
  141. package/lib/models/shape.d.ts +44 -0
  142. package/lib/plot.utilities.d.ts +107 -0
  143. package/lib/processing/image-converter.service.d.ts +28 -0
  144. package/lib/processing/image-crop.d.ts +18 -0
  145. package/lib/processing/processing-image.d.ts +17 -0
  146. package/lib/provide-visualization.d.ts +34 -0
  147. package/lib/region-editor/mask-raster.d.ts +61 -0
  148. package/lib/region-editor/mask-worker.d.ts +7 -0
  149. package/lib/region-editor/region-editor.component.d.ts +305 -0
  150. package/lib/region-ops.service.d.ts +92 -0
  151. package/lib/render-orchestrator.d.ts +59 -0
  152. package/lib/routing-visualizer.service.d.ts +227 -0
  153. package/lib/store/class-color.util.d.ts +26 -0
  154. package/lib/store/region-store.service.d.ts +315 -0
  155. package/lib/store/visualizer-store.service.d.ts +123 -0
  156. package/lib/toolbar/brush/brush-tool.service.d.ts +143 -0
  157. package/lib/toolbar/crop/slide-crop.d.ts +30 -0
  158. package/lib/toolbar/segmentation/cell-segment-tool.service.d.ts +32 -0
  159. package/lib/toolbar/segmentation/cellpose-segmenter.service.d.ts +39 -0
  160. package/lib/toolbar/segmentation/onnx-sam-session.d.ts +25 -0
  161. package/lib/toolbar/segmentation/ort-runtime-config.d.ts +4 -0
  162. package/lib/toolbar/segmentation/sam-model-registry.d.ts +36 -0
  163. package/lib/toolbar/segmentation/sam-onnx-core.d.ts +48 -0
  164. package/lib/toolbar/segmentation/sam-point-tool.service.d.ts +59 -0
  165. package/lib/toolbar/segmentation/sam-prompt.d.ts +34 -0
  166. package/lib/toolbar/segmentation/sam-tool.service.d.ts +50 -0
  167. package/lib/toolbar/toolbar.component.d.ts +167 -0
  168. package/lib/toolbar/vertex-eraser/vertex-eraser-tool.service.d.ts +64 -0
  169. package/lib/toolbar/wand/wand-tool.service.d.ts +149 -0
  170. package/lib/toolbar/wand/wand.service.d.ts +133 -0
  171. package/lib/toolbar/zoom-to-box/zoom-to-box-tool.service.d.ts +49 -0
  172. package/lib/visualization.module.d.ts +45 -0
  173. package/lib/visualizer.component.d.ts +378 -0
  174. package/package.json +73 -0
  175. package/src/lib/assets/3d-coordinates.svg +49 -0
  176. package/src/lib/assets/cells.svg +1 -0
  177. package/src/lib/assets/click.svg +1 -0
  178. package/src/lib/assets/colormap-luts.json +1 -0
  179. package/src/lib/assets/contour.svg +48 -0
  180. package/src/lib/assets/cube-3d.svg +1 -0
  181. package/src/lib/assets/edit-selection-white.svg +47 -0
  182. package/src/lib/assets/heatmap.svg +1 -0
  183. package/src/lib/assets/icons/colormap-Pastel1.png +0 -0
  184. package/src/lib/assets/icons/colormap-accent.png +0 -0
  185. package/src/lib/assets/icons/colormap-afmhot.png +0 -0
  186. package/src/lib/assets/icons/colormap-autumn.png +0 -0
  187. package/src/lib/assets/icons/colormap-berlin.png +0 -0
  188. package/src/lib/assets/icons/colormap-binary.png +0 -0
  189. package/src/lib/assets/icons/colormap-blackbody.png +0 -0
  190. package/src/lib/assets/icons/colormap-bluered.png +0 -0
  191. package/src/lib/assets/icons/colormap-blues.png +0 -0
  192. package/src/lib/assets/icons/colormap-bone.png +0 -0
  193. package/src/lib/assets/icons/colormap-brbg.png +0 -0
  194. package/src/lib/assets/icons/colormap-brg.png +0 -0
  195. package/src/lib/assets/icons/colormap-bugn.png +0 -0
  196. package/src/lib/assets/icons/colormap-bupu.png +0 -0
  197. package/src/lib/assets/icons/colormap-bwr.png +0 -0
  198. package/src/lib/assets/icons/colormap-cividis.png +0 -0
  199. package/src/lib/assets/icons/colormap-cmrmap.png +0 -0
  200. package/src/lib/assets/icons/colormap-cool.png +0 -0
  201. package/src/lib/assets/icons/colormap-coolwarm.png +0 -0
  202. package/src/lib/assets/icons/colormap-copper.png +0 -0
  203. package/src/lib/assets/icons/colormap-cubehelix.png +0 -0
  204. package/src/lib/assets/icons/colormap-dark2.png +0 -0
  205. package/src/lib/assets/icons/colormap-earth.png +0 -0
  206. package/src/lib/assets/icons/colormap-electric.png +0 -0
  207. package/src/lib/assets/icons/colormap-flag.png +0 -0
  208. package/src/lib/assets/icons/colormap-gist_earth.png +0 -0
  209. package/src/lib/assets/icons/colormap-gist_gray.png +0 -0
  210. package/src/lib/assets/icons/colormap-gist_heat.png +0 -0
  211. package/src/lib/assets/icons/colormap-gist_ncar.png +0 -0
  212. package/src/lib/assets/icons/colormap-gist_rainbow.png +0 -0
  213. package/src/lib/assets/icons/colormap-gist_stern.png +0 -0
  214. package/src/lib/assets/icons/colormap-gist_yarg.png +0 -0
  215. package/src/lib/assets/icons/colormap-gnbu.png +0 -0
  216. package/src/lib/assets/icons/colormap-gnuplot.png +0 -0
  217. package/src/lib/assets/icons/colormap-gnuplot2.png +0 -0
  218. package/src/lib/assets/icons/colormap-gray.png +0 -0
  219. package/src/lib/assets/icons/colormap-greens-inv.png +0 -0
  220. package/src/lib/assets/icons/colormap-greens.png +0 -0
  221. package/src/lib/assets/icons/colormap-greys-inv.png +0 -0
  222. package/src/lib/assets/icons/colormap-greys.png +0 -0
  223. package/src/lib/assets/icons/colormap-hot.png +0 -0
  224. package/src/lib/assets/icons/colormap-hsv.png +0 -0
  225. package/src/lib/assets/icons/colormap-inferno.png +0 -0
  226. package/src/lib/assets/icons/colormap-jet.png +0 -0
  227. package/src/lib/assets/icons/colormap-magma.png +0 -0
  228. package/src/lib/assets/icons/colormap-managua.png +0 -0
  229. package/src/lib/assets/icons/colormap-nipy_spectral.png +0 -0
  230. package/src/lib/assets/icons/colormap-ocean.png +0 -0
  231. package/src/lib/assets/icons/colormap-oranges.png +0 -0
  232. package/src/lib/assets/icons/colormap-orrd.png +0 -0
  233. package/src/lib/assets/icons/colormap-pastel2.png +0 -0
  234. package/src/lib/assets/icons/colormap-picnic.png +0 -0
  235. package/src/lib/assets/icons/colormap-pink.png +0 -0
  236. package/src/lib/assets/icons/colormap-piyg.png +0 -0
  237. package/src/lib/assets/icons/colormap-placeholder.png +0 -0
  238. package/src/lib/assets/icons/colormap-plasma.png +0 -0
  239. package/src/lib/assets/icons/colormap-portland.png +0 -0
  240. package/src/lib/assets/icons/colormap-prgn.png +0 -0
  241. package/src/lib/assets/icons/colormap-prism.png +0 -0
  242. package/src/lib/assets/icons/colormap-pubu.png +0 -0
  243. package/src/lib/assets/icons/colormap-pubugn.png +0 -0
  244. package/src/lib/assets/icons/colormap-puor.png +0 -0
  245. package/src/lib/assets/icons/colormap-purd.png +0 -0
  246. package/src/lib/assets/icons/colormap-purples.png +0 -0
  247. package/src/lib/assets/icons/colormap-rainbow.png +0 -0
  248. package/src/lib/assets/icons/colormap-rdbu.png +0 -0
  249. package/src/lib/assets/icons/colormap-rdgy.png +0 -0
  250. package/src/lib/assets/icons/colormap-rdpu.png +0 -0
  251. package/src/lib/assets/icons/colormap-rdylbu.png +0 -0
  252. package/src/lib/assets/icons/colormap-rdylgn.png +0 -0
  253. package/src/lib/assets/icons/colormap-reds.png +0 -0
  254. package/src/lib/assets/icons/colormap-seismic.png +0 -0
  255. package/src/lib/assets/icons/colormap-set1.png +0 -0
  256. package/src/lib/assets/icons/colormap-set2.png +0 -0
  257. package/src/lib/assets/icons/colormap-set3.png +0 -0
  258. package/src/lib/assets/icons/colormap-spectral.png +0 -0
  259. package/src/lib/assets/icons/colormap-spring.png +0 -0
  260. package/src/lib/assets/icons/colormap-summer.png +0 -0
  261. package/src/lib/assets/icons/colormap-tab10.png +0 -0
  262. package/src/lib/assets/icons/colormap-tab20.png +0 -0
  263. package/src/lib/assets/icons/colormap-tab20b.png +0 -0
  264. package/src/lib/assets/icons/colormap-tab20c.png +0 -0
  265. package/src/lib/assets/icons/colormap-terrain.png +0 -0
  266. package/src/lib/assets/icons/colormap-turbo.png +0 -0
  267. package/src/lib/assets/icons/colormap-twilight.png +0 -0
  268. package/src/lib/assets/icons/colormap-twilight_shifted.png +0 -0
  269. package/src/lib/assets/icons/colormap-vanimo.png +0 -0
  270. package/src/lib/assets/icons/colormap-viridis.png +0 -0
  271. package/src/lib/assets/icons/colormap-winter.png +0 -0
  272. package/src/lib/assets/icons/colormap-wistia.png +0 -0
  273. package/src/lib/assets/icons/colormap-ylgn.png +0 -0
  274. package/src/lib/assets/icons/colormap-ylgnbu-inv.png +0 -0
  275. package/src/lib/assets/icons/colormap-ylgnbu.png +0 -0
  276. package/src/lib/assets/icons/colormap-ylorbr.png +0 -0
  277. package/src/lib/assets/icons/colormap-ylorrd-inv.png +0 -0
  278. package/src/lib/assets/icons/colormap-ylorrd.png +0 -0
  279. package/src/lib/assets/interactive-segmentation-cursor.svg +1 -0
  280. package/src/lib/assets/invert.svg +1 -0
  281. package/src/lib/assets/isosurface.svg +7 -0
  282. package/src/lib/assets/mask.svg +1 -0
  283. package/src/lib/assets/paintbrush.svg +1 -0
  284. package/src/lib/assets/picture-segmentation.svg +43 -0
  285. package/src/lib/assets/polygon-vertices.svg +13 -0
  286. package/src/lib/assets/polyline.svg +3 -0
  287. package/src/lib/assets/redo.svg +39 -0
  288. package/src/lib/assets/region-editor.svg +37 -0
  289. package/src/lib/assets/region-move.svg +7 -0
  290. package/src/lib/assets/smooth.svg +56 -0
  291. package/src/lib/assets/spline-chart.svg +7 -0
  292. package/src/lib/assets/surface.svg +37 -0
  293. package/src/lib/assets/test/download-379.svg +1 -0
  294. package/src/lib/assets/test/download-98.svg +1 -0
  295. package/src/lib/assets/test/json-1.svg +1 -0
  296. package/src/lib/assets/test/load-53.svg +1 -0
  297. package/src/lib/assets/test/mask-44.svg +1 -0
  298. package/src/lib/assets/test/mask-download-44.svg +15 -0
  299. package/src/lib/assets/test/mask-save-44.svg +11 -0
  300. package/src/lib/assets/test/overlay-1.svg +1 -0
  301. package/src/lib/assets/test/overlay-4.svg +1 -0
  302. package/src/lib/assets/test/region-26.svg +1 -0
  303. package/src/lib/assets/test/save-56.svg +1 -0
  304. package/src/lib/assets/test/upload-43.svg +1 -0
  305. package/src/lib/assets/to-bezier.svg +15 -0
  306. package/src/lib/assets/to-polygon.svg +11 -0
  307. package/src/lib/assets/undo.svg +38 -0
  308. package/src/lib/assets/vertex-add.svg +12 -0
  309. package/src/lib/assets/vertex-delete.svg +12 -0
  310. package/src/lib/assets/wand.svg +54 -0
  311. 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=