@photivra/engine 2.18.0 → 2.20.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 (335) hide show
  1. package/CHANGELOG.md +179 -1
  2. package/CITATION.cff +1 -1
  3. package/NODE_SUPPORT.md +7 -5
  4. package/README.md +6 -6
  5. package/SECURITY.md +1 -1
  6. package/THIRD_PARTY.md +14 -10
  7. package/dist/capture/dng-lens-correction-opcodes.d.ts +32 -0
  8. package/dist/capture/dng-lens-correction-opcodes.js +119 -0
  9. package/dist/capture/dng-lens-correction-opcodes.js.map +1 -0
  10. package/dist/capture/focus-control.js +2 -75
  11. package/dist/capture/focus-control.js.map +1 -1
  12. package/dist/capture/native-environment-lean.d.ts +1 -0
  13. package/dist/capture/native-environment-lean.js +2 -6
  14. package/dist/capture/native-environment-lean.js.map +1 -1
  15. package/dist/capture/native-environment-optical-stage.d.ts +1 -0
  16. package/dist/capture/native-environment-optical-stage.js +2 -5
  17. package/dist/capture/native-environment-optical-stage.js.map +1 -1
  18. package/dist/capture/native-environment-raw.js +1 -5
  19. package/dist/capture/native-environment-raw.js.map +1 -1
  20. package/dist/capture/native-raw-dng.d.ts +6 -0
  21. package/dist/capture/native-raw-dng.js +16 -2
  22. package/dist/capture/native-raw-dng.js.map +1 -1
  23. package/dist/capture/release-sequence.js +2 -47
  24. package/dist/capture/release-sequence.js.map +1 -1
  25. package/dist/capture/sensor-raw-producer.js +3 -5
  26. package/dist/capture/sensor-raw-producer.js.map +1 -1
  27. package/dist/color/white-balance.d.ts +2 -0
  28. package/dist/color/white-balance.js +3 -56
  29. package/dist/color/white-balance.js.map +1 -1
  30. package/dist/composition/image-formation-plan.d.ts +1 -0
  31. package/dist/composition/image-formation-plan.js +3 -49
  32. package/dist/composition/image-formation-plan.js.map +1 -1
  33. package/dist/core/evidence-provenance.js +1 -16
  34. package/dist/core/evidence-provenance.js.map +1 -1
  35. package/dist/core/record-validation.d.ts +54 -0
  36. package/dist/core/record-validation.js +176 -0
  37. package/dist/core/record-validation.js.map +1 -1
  38. package/dist/core/scientific-assurance.js +1 -18
  39. package/dist/core/scientific-assurance.js.map +1 -1
  40. package/dist/core/shared-helpers.d.ts +35 -0
  41. package/dist/core/shared-helpers.js +38 -0
  42. package/dist/core/shared-helpers.js.map +1 -0
  43. package/dist/core/spectral-composition.js +1 -36
  44. package/dist/core/spectral-composition.js.map +1 -1
  45. package/dist/core/spectral.js +1 -19
  46. package/dist/core/spectral.js.map +1 -1
  47. package/dist/core/version.d.ts +1 -1
  48. package/dist/core/version.js +1 -1
  49. package/dist/core/version.js.map +1 -1
  50. package/dist/equipment/exposure-capabilities.d.ts +1 -0
  51. package/dist/equipment/exposure-capabilities.js +2 -36
  52. package/dist/equipment/exposure-capabilities.js.map +1 -1
  53. package/dist/equipment/generic-lens-catalogue.d.ts +8 -2
  54. package/dist/equipment/generic-lens-catalogue.js +31 -2
  55. package/dist/equipment/generic-lens-catalogue.js.map +1 -1
  56. package/dist/equipment/generic-tier-assets.js +2 -3
  57. package/dist/equipment/generic-tier-assets.js.map +1 -1
  58. package/dist/equipment/generic-tier-presets.js +2 -3
  59. package/dist/equipment/generic-tier-presets.js.map +1 -1
  60. package/dist/equipment/iso-capabilities.js +2 -49
  61. package/dist/equipment/iso-capabilities.js.map +1 -1
  62. package/dist/equipment/metering-capabilities.js +1 -26
  63. package/dist/equipment/metering-capabilities.js.map +1 -1
  64. package/dist/equipment/release-capabilities.js +1 -53
  65. package/dist/equipment/release-capabilities.js.map +1 -1
  66. package/dist/exposure/auto-iso-slowest-shutter.js +1 -7
  67. package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -1
  68. package/dist/exposure/exposure-duration-control.js +1 -16
  69. package/dist/exposure/exposure-duration-control.js.map +1 -1
  70. package/dist/exposure/exposure-mode-resolver.js +1 -17
  71. package/dist/exposure/exposure-mode-resolver.js.map +1 -1
  72. package/dist/exposure/filtered-exposure-time.d.ts +93 -0
  73. package/dist/exposure/filtered-exposure-time.js +129 -0
  74. package/dist/exposure/filtered-exposure-time.js.map +1 -0
  75. package/dist/exposure/flash-ttl.d.ts +1 -0
  76. package/dist/exposure/flash-ttl.js +1 -1
  77. package/dist/exposure/flash-ttl.js.map +1 -1
  78. package/dist/exposure/flash.js +1 -49
  79. package/dist/exposure/flash.js.map +1 -1
  80. package/dist/exposure/metering-scene-radiance.js +1 -25
  81. package/dist/exposure/metering-scene-radiance.js.map +1 -1
  82. package/dist/exposure/metering-target.js +1 -30
  83. package/dist/exposure/metering-target.js.map +1 -1
  84. package/dist/exposure/metering-temporal.js +1 -8
  85. package/dist/exposure/metering-temporal.js.map +1 -1
  86. package/dist/exposure/metering.js +1 -72
  87. package/dist/exposure/metering.js.map +1 -1
  88. package/dist/exposure/program-line.js +1 -45
  89. package/dist/exposure/program-line.js.map +1 -1
  90. package/dist/facade/develop-joined-parts.d.ts +29 -0
  91. package/dist/facade/develop-joined-parts.js +33 -0
  92. package/dist/facade/develop-joined-parts.js.map +1 -0
  93. package/dist/facade/display-view-gpu.d.ts +7 -4
  94. package/dist/facade/display-view-gpu.js +4 -4
  95. package/dist/facade/display-view-gpu.js.map +1 -1
  96. package/dist/facade/display-view-lens-signal.d.ts +1 -1
  97. package/dist/facade/display-view-lens-signal.js +5 -3
  98. package/dist/facade/display-view-lens-signal.js.map +1 -1
  99. package/dist/facade/display-view.d.ts +3 -1
  100. package/dist/facade/display-view.js +1 -1
  101. package/dist/facade/display-view.js.map +1 -1
  102. package/dist/facade/fast-methods.d.ts +50 -0
  103. package/dist/facade/fast-methods.js +50 -0
  104. package/dist/facade/fast-methods.js.map +1 -0
  105. package/dist/facade/fast-resolution.d.ts +12 -0
  106. package/dist/facade/fast-resolution.js +18 -0
  107. package/dist/facade/fast-resolution.js.map +1 -0
  108. package/dist/facade/generic-lens-capture.d.ts +6 -3
  109. package/dist/facade/generic-lens-capture.js +6 -3
  110. package/dist/facade/generic-lens-capture.js.map +1 -1
  111. package/dist/facade/simulate-capture.d.ts +88 -16
  112. package/dist/facade/simulate-capture.js +279 -153
  113. package/dist/facade/simulate-capture.js.map +1 -1
  114. package/dist/facade/simulate-display-view.d.ts +3 -2
  115. package/dist/facade/simulate-display-view.js +22 -12
  116. package/dist/facade/simulate-display-view.js.map +1 -1
  117. package/dist/gpu/chart-composite-gpu.js +1 -3
  118. package/dist/gpu/chart-composite-gpu.js.map +1 -1
  119. package/dist/gpu/chart-radiance-gpu.js +1 -5
  120. package/dist/gpu/chart-radiance-gpu.js.map +1 -1
  121. package/dist/gpu/display-view-quality-gpu.d.ts +13 -1
  122. package/dist/gpu/display-view-quality-gpu.js +4 -8
  123. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  124. package/dist/gpu/display-view-speed-gpu.js +3 -25
  125. package/dist/gpu/display-view-speed-gpu.js.map +1 -1
  126. package/dist/gpu/sensor-stage-gpu.js +1 -5
  127. package/dist/gpu/sensor-stage-gpu.js.map +1 -1
  128. package/dist/gpu/webgpu-runtime.d.ts +2 -0
  129. package/dist/gpu/webgpu-runtime.js +3 -0
  130. package/dist/gpu/webgpu-runtime.js.map +1 -1
  131. package/dist/index.d.ts +11 -1
  132. package/dist/index.js +8 -0
  133. package/dist/index.js.map +1 -1
  134. package/dist/motion/camera-rotation.js +1 -6
  135. package/dist/motion/camera-rotation.js.map +1 -1
  136. package/dist/motion/capture-rotation-inverse-mapping.d.ts +12 -0
  137. package/dist/motion/capture-rotation-inverse-mapping.js +2 -2
  138. package/dist/motion/capture-rotation-inverse-mapping.js.map +1 -1
  139. package/dist/motion/capture-rotation-temporal-quadrature.d.ts +1 -0
  140. package/dist/motion/capture-rotation-temporal-quadrature.js +1 -1
  141. package/dist/motion/capture-rotation-temporal-quadrature.js.map +1 -1
  142. package/dist/motion/capture-rotation-trajectory.js +2 -17
  143. package/dist/motion/capture-rotation-trajectory.js.map +1 -1
  144. package/dist/motion/capture-translation-parallax-temporal-quadrature.d.ts +1 -0
  145. package/dist/motion/capture-translation-parallax-temporal-quadrature.js +3 -14
  146. package/dist/motion/capture-translation-parallax-temporal-quadrature.js.map +1 -1
  147. package/dist/motion/extended-object-projection.js +5 -40
  148. package/dist/motion/extended-object-projection.js.map +1 -1
  149. package/dist/optics/aperture-diameter.d.ts +47 -0
  150. package/dist/optics/aperture-diameter.js +50 -0
  151. package/dist/optics/aperture-diameter.js.map +1 -0
  152. package/dist/optics/atmospheric-haze.js +1 -7
  153. package/dist/optics/atmospheric-haze.js.map +1 -1
  154. package/dist/optics/brown-conrady-distortion.js +1 -7
  155. package/dist/optics/brown-conrady-distortion.js.map +1 -1
  156. package/dist/optics/complex-pupil-psf.d.ts +1 -0
  157. package/dist/optics/complex-pupil-psf.js +2 -84
  158. package/dist/optics/complex-pupil-psf.js.map +1 -1
  159. package/dist/optics/detail-limit.d.ts +182 -0
  160. package/dist/optics/detail-limit.js +336 -0
  161. package/dist/optics/detail-limit.js.map +1 -0
  162. package/dist/optics/focus-state.js +1 -11
  163. package/dist/optics/focus-state.js.map +1 -1
  164. package/dist/optics/front-of-lens-filter.d.ts +5 -0
  165. package/dist/optics/front-of-lens-filter.js +3 -65
  166. package/dist/optics/front-of-lens-filter.js.map +1 -1
  167. package/dist/optics/generic-lens-psf.d.ts +18 -2
  168. package/dist/optics/generic-lens-psf.js +25 -2
  169. package/dist/optics/generic-lens-psf.js.map +1 -1
  170. package/dist/optics/generic-nd-filter-presets.d.ts +12 -0
  171. package/dist/optics/generic-nd-filter-presets.js +48 -0
  172. package/dist/optics/generic-nd-filter-presets.js.map +1 -0
  173. package/dist/optics/illumination-vignetting.js +1 -6
  174. package/dist/optics/illumination-vignetting.js.map +1 -1
  175. package/dist/optics/lens-psf-profile.d.ts +4 -1
  176. package/dist/optics/lens-psf-profile.js +12 -111
  177. package/dist/optics/lens-psf-profile.js.map +1 -1
  178. package/dist/optics/longitudinal-chromatic-aberration.js +1 -7
  179. package/dist/optics/longitudinal-chromatic-aberration.js.map +1 -1
  180. package/dist/optics/pupil-vignetting.js +1 -7
  181. package/dist/optics/pupil-vignetting.js.map +1 -1
  182. package/dist/optics/radial-distortion.js +1 -6
  183. package/dist/optics/radial-distortion.js.map +1 -1
  184. package/dist/optics/scene-to-sensor-irradiance.d.ts +13 -0
  185. package/dist/optics/scene-to-sensor-irradiance.js +11 -96
  186. package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
  187. package/dist/optics/stray-light.d.ts +1 -0
  188. package/dist/optics/stray-light.js +1 -1
  189. package/dist/optics/stray-light.js.map +1 -1
  190. package/dist/optics/zernike-wavefront.js +1 -7
  191. package/dist/optics/zernike-wavefront.js.map +1 -1
  192. package/dist/output/capture-geometry.js +1 -8
  193. package/dist/output/capture-geometry.js.map +1 -1
  194. package/dist/output/crop-to-match-focal-length.d.ts +62 -0
  195. package/dist/output/crop-to-match-focal-length.js +63 -0
  196. package/dist/output/crop-to-match-focal-length.js.map +1 -0
  197. package/dist/output/developed-lens-corrections.d.ts +62 -0
  198. package/dist/output/developed-lens-corrections.js +135 -0
  199. package/dist/output/developed-lens-corrections.js.map +1 -0
  200. package/dist/output/generic-lens-corrections.d.ts +5 -7
  201. package/dist/output/generic-lens-corrections.js +11 -10
  202. package/dist/output/generic-lens-corrections.js.map +1 -1
  203. package/dist/output/geometric-transforms.js +1 -4
  204. package/dist/output/geometric-transforms.js.map +1 -1
  205. package/dist/output/lens-corrections.d.ts +12 -2
  206. package/dist/output/lens-corrections.js +22 -7
  207. package/dist/output/lens-corrections.js.map +1 -1
  208. package/dist/output/native-capture-sdr.d.ts +2 -2
  209. package/dist/output/native-capture-sdr.js +3 -2
  210. package/dist/output/native-capture-sdr.js.map +1 -1
  211. package/dist/output/print-contrast-reference.js +1 -1
  212. package/dist/output/print-contrast-reference.js.map +1 -1
  213. package/dist/output/print-size-blur-noise.d.ts +110 -0
  214. package/dist/output/print-size-blur-noise.js +138 -0
  215. package/dist/output/print-size-blur-noise.js.map +1 -0
  216. package/dist/processing/development-detail.d.ts +29 -0
  217. package/dist/processing/development-detail.js +128 -107
  218. package/dist/processing/development-detail.js.map +1 -1
  219. package/dist/schema/illumination-temporal.js +3 -72
  220. package/dist/schema/illumination-temporal.js.map +1 -1
  221. package/dist/schema/illumination.d.ts +1 -0
  222. package/dist/schema/illumination.js +2 -65
  223. package/dist/schema/illumination.js.map +1 -1
  224. package/dist/schema/scene-radiance-temporal.js +2 -14
  225. package/dist/schema/scene-radiance-temporal.js.map +1 -1
  226. package/dist/schema/scene-radiance.js +2 -60
  227. package/dist/schema/scene-radiance.js.map +1 -1
  228. package/dist/schema/validation.d.ts +2 -0
  229. package/dist/schema/validation.js +2 -17
  230. package/dist/schema/validation.js.map +1 -1
  231. package/dist/sensor/accumulated-charge.js +2 -59
  232. package/dist/sensor/accumulated-charge.js.map +1 -1
  233. package/dist/sensor/architecture.js +1 -16
  234. package/dist/sensor/architecture.js.map +1 -1
  235. package/dist/sensor/camera-saturation-capacity.d.ts +1 -0
  236. package/dist/sensor/camera-saturation-capacity.js +3 -55
  237. package/dist/sensor/camera-saturation-capacity.js.map +1 -1
  238. package/dist/sensor/capture-color-sampling-binding.js +1 -26
  239. package/dist/sensor/capture-color-sampling-binding.js.map +1 -1
  240. package/dist/sensor/capture-mode-timing.js +1 -39
  241. package/dist/sensor/capture-mode-timing.js.map +1 -1
  242. package/dist/sensor/capture-mode.js +1 -23
  243. package/dist/sensor/capture-mode.js.map +1 -1
  244. package/dist/sensor/color-sampling.js +1 -26
  245. package/dist/sensor/color-sampling.js.map +1 -1
  246. package/dist/sensor/constant-rate-temporal-integration.js +2 -46
  247. package/dist/sensor/constant-rate-temporal-integration.js.map +1 -1
  248. package/dist/sensor/dark-current.js +2 -45
  249. package/dist/sensor/dark-current.js.map +1 -1
  250. package/dist/sensor/eqe-electron-rate.js +2 -41
  251. package/dist/sensor/eqe-electron-rate.js.map +1 -1
  252. package/dist/sensor/exposure-window.d.ts +7 -0
  253. package/dist/sensor/exposure-window.js +9 -24
  254. package/dist/sensor/exposure-window.js.map +1 -1
  255. package/dist/sensor/iso-gain.js +1 -7
  256. package/dist/sensor/iso-gain.js.map +1 -1
  257. package/dist/sensor/iso-signal-chain.js +1 -43
  258. package/dist/sensor/iso-signal-chain.js.map +1 -1
  259. package/dist/sensor/optical-stack.js +1 -25
  260. package/dist/sensor/optical-stack.js.map +1 -1
  261. package/dist/sensor/photon-energy.js +1 -26
  262. package/dist/sensor/photon-energy.js.map +1 -1
  263. package/dist/sensor/physical-charge-capacity.js +3 -77
  264. package/dist/sensor/physical-charge-capacity.js.map +1 -1
  265. package/dist/sensor/radiometry-readiness.js +2 -23
  266. package/dist/sensor/radiometry-readiness.js.map +1 -1
  267. package/dist/sensor/raw-readout.d.ts +3 -0
  268. package/dist/sensor/raw-readout.js +4 -66
  269. package/dist/sensor/raw-readout.js.map +1 -1
  270. package/dist/sensor/raw-reconstruction.js +2 -64
  271. package/dist/sensor/raw-reconstruction.js.map +1 -1
  272. package/dist/sensor/readout-exposure-linkage.js +1 -9
  273. package/dist/sensor/readout-exposure-linkage.js.map +1 -1
  274. package/dist/sensor/readout-timing.js +4 -79
  275. package/dist/sensor/readout-timing.js.map +1 -1
  276. package/dist/sensor/response-application-compatibility.d.ts +2 -0
  277. package/dist/sensor/response-application-compatibility.js +4 -35
  278. package/dist/sensor/response-application-compatibility.js.map +1 -1
  279. package/dist/sensor/response-operating-range.d.ts +1 -0
  280. package/dist/sensor/response-operating-range.js +3 -79
  281. package/dist/sensor/response-operating-range.js.map +1 -1
  282. package/dist/sensor/responsivity-photocurrent.js +4 -114
  283. package/dist/sensor/responsivity-photocurrent.js.map +1 -1
  284. package/dist/sensor/sampling-aperture.js +1 -25
  285. package/dist/sensor/sampling-aperture.js.map +1 -1
  286. package/dist/sensor/sensor-characteristics.d.ts +137 -0
  287. package/dist/sensor/sensor-characteristics.js +115 -0
  288. package/dist/sensor/sensor-characteristics.js.map +1 -0
  289. package/dist/sensor/spatial-sample-reduction.js +3 -24
  290. package/dist/sensor/spatial-sample-reduction.js.map +1 -1
  291. package/dist/sensor/spatial-sampling-quadrature.js +1 -3
  292. package/dist/sensor/spatial-sampling-quadrature.js.map +1 -1
  293. package/dist/sensor/spatio-spectral-reduction.js +1 -18
  294. package/dist/sensor/spatio-spectral-reduction.js.map +1 -1
  295. package/dist/sensor/spectral-coverage.d.ts +10 -1
  296. package/dist/sensor/spectral-coverage.js +1 -1
  297. package/dist/sensor/spectral-coverage.js.map +1 -1
  298. package/dist/sensor/spectral-quadrature.js +3 -41
  299. package/dist/sensor/spectral-quadrature.js.map +1 -1
  300. package/dist/sensor/spectral-response.js +1 -47
  301. package/dist/sensor/spectral-response.js.map +1 -1
  302. package/dist/sensor/temporal-photo-signal.d.ts +3 -0
  303. package/dist/sensor/temporal-photo-signal.js +5 -2
  304. package/dist/sensor/temporal-photo-signal.js.map +1 -1
  305. package/dist/stabilization/camera-shake.js +1 -6
  306. package/dist/stabilization/camera-shake.js.map +1 -1
  307. package/dist/stabilization/system.d.ts +9 -0
  308. package/dist/stabilization/system.js +7 -86
  309. package/dist/stabilization/system.js.map +1 -1
  310. package/docs/AGENT_GUIDE.md +5 -0
  311. package/docs/API_REFERENCE.md +116 -3
  312. package/docs/QUICK_START.md +178 -14
  313. package/docs/USAGE.md +111 -3
  314. package/docs/api/capture-native-raw-dng.md +6 -0
  315. package/docs/api/core-version.md +1 -1
  316. package/docs/api/equipment-generic-lens-catalogue.md +7 -2
  317. package/docs/api/exports.json +203 -3
  318. package/docs/api/exposure-filtered-exposure-time.md +105 -0
  319. package/docs/api/facade-develop-joined-parts.md +32 -0
  320. package/docs/api/facade-display-view-gpu.md +5 -2
  321. package/docs/api/facade-fast-methods.md +91 -0
  322. package/docs/api/facade-simulate-capture.md +72 -16
  323. package/docs/api/facade-simulate-display-view.md +3 -2
  324. package/docs/api/optics-aperture-diameter.md +51 -0
  325. package/docs/api/optics-detail-limit.md +203 -0
  326. package/docs/api/optics-generic-nd-filter-presets.md +34 -0
  327. package/docs/api/optics-lens-psf-profile.md +3 -1
  328. package/docs/api/output-crop-to-match-focal-length.md +67 -0
  329. package/docs/api/output-developed-lens-corrections.md +12 -0
  330. package/docs/api/output-generic-lens-corrections.md +3 -10
  331. package/docs/api/output-lens-corrections.md +11 -1
  332. package/docs/api/output-print-size-blur-noise.md +115 -0
  333. package/docs/api/sensor-sensor-characteristics.md +105 -0
  334. package/llms.txt +5 -0
  335. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"native-capture-sdr.js","sourceRoot":"","sources":["../../src/output/native-capture-sdr.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Native SDR rendering of large captures, tile by tile, within fixed memory limits. The engine\n * owns the task; the caller supplies the pixel tiles. See docs/NATIVE_CAPTURE_SDR.md.\n */\nimport { parseSimulatedCapture, type SimulatedCapture, type CaptureLinearPlane } from \"../capture/simulated-capture.js\";\nimport { LINEAR_CAPTURE_RGB_PROFILE, resolveCaptureColorModel } from \"../color/capture-color.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { requireAllowlistedRecord, requirePublicOpaqueId } from \"../core/record-validation.js\";\nimport { freezeOwnedData } from \"../core/owned-data.js\";\nimport { FULL_RESOLUTION_CEILING_PIXELS, resolveRaisedLimit } from \"../core/pixel-limits.js\";\nimport { encodeSrgbComponent } from \"../color/srgb-transfer.js\";\nimport { InvalidScientificResultError } from \"../core/validation.js\";\nimport { parseSdrRenderingProfile, prepareSdrPixelTone, type SdrPixelTone, type SdrRenderingProfile, type SdrRenderingResult } from \"./sdr-rendering.js\";\n\nexport const NATIVE_CAPTURE_SDR_SCHEMA_VERSION = \"0.1.0\" as const;\n/** `maximumPixels` is the default pixel limit; a request may raise it with its own `maximumPixels`, up to `ceilingPixels`. */\nexport const NATIVE_CAPTURE_SDR_LIMITS = /* @__PURE__ */ Object.freeze({ maximumPixels: 24_000_000, ceilingPixels: FULL_RESOLUTION_CEILING_PIXELS,\n maximumDimension: 16_384, tileWidth: 256, tileHeight: 32, maximumTileStorageSamples: 32_768 });\n/** Input. The capture holds only metadata; float samples are read from external storage. Samples must already have colour and white balance applied. */\nexport interface NativeCaptureSdrInput {\n capture: SimulatedCapture;\n sourcePlaneId: string;\n outputImageStateId: string;\n profile: SdrRenderingProfile;\n /** Limit on the size of the integer output. Checked before anything is allocated or any tile is read. */\n maximumOutputBytes: number;\n /**\n * Optional opt-in pixel limit for large captures: an integer from 1 to `NATIVE_CAPTURE_SDR_LIMITS.ceilingPixels`.\n * Default `NATIVE_CAPTURE_SDR_LIMITS.maximumPixels` (24 000 000). `maximumOutputBytes` still applies.\n */\n maximumPixels?: number;\n}\n/** Tile request. Coordinates are absolute in the selected plane's declared pixel grid, never relative to a crop. */\nexport interface NativeCaptureSdrTileRequest {\n captureId: string;\n sourcePlaneId: string;\n artifactId: string;\n sha256: string;\n x: number; y: number; width: number; height: number;\n}\n/** A tile returned by the provider. It belongs to this call alone; do not change or reuse it. Row padding is not image data. */\nexport interface NativeCaptureSdrTile extends NativeCaptureSdrTileRequest {\n rowStrideSamples: number;\n samples: Float32Array | Float64Array;\n}\n/** Supplies tiles, one call at a time. The caller must release decoder and GPU resources when the task aborts or finishes. */\nexport interface NativeCaptureSdrProvider {\n readTile(request: Readonly<NativeCaptureSdrTileRequest>, signal: AbortSignal): Promise<NativeCaptureSdrTile>;\n /** Must yield to the host event loop, so cancellation and input events can run between tiles. */\n yieldControl(signal: AbortSignal): Promise<void>;\n /**\n * Optional progress observer, called synchronously after each committed tile. It cannot change\n * output; an exception it throws fails the task like any other error.\n */\n onProgress?(progress: NativeTaskProgress): void;\n}\n/** Tile progress of a native task: `completedTileCount` of `totalTileCount` tiles are committed. */\nexport interface NativeTaskProgress { readonly completedTileCount: number; readonly totalTileCount: number }\nexport interface NativeCaptureSdrPlan {\n schemaVersion: typeof NATIVE_CAPTURE_SDR_SCHEMA_VERSION;\n capture: SimulatedCapture;\n sourcePlaneId: string;\n outputImageStateId: string;\n profile: SdrRenderingProfile;\n outputBytes: number;\n /** Upper bound on the bytes held in working arrays. It is not peak JavaScript heap, GPU or process memory, and does not show that a device can run the task. */\n maximumScratchPayloadBytes: number;\n tileCount: number;\n}\nexport interface NativeCaptureSdrOutput {\n plan: NativeCaptureSdrPlan;\n /** Packed RGB codes in row-major order, owned by the caller. 16-bit codes are numbers, not bytes in a particular byte order. */\n integerSamples: Uint8Array | Uint16Array;\n diagnostics: SdrRenderingResult[\"diagnostics\"];\n outputEncoding: SdrRenderingResult[\"outputEncoding\"];\n displayAdaptation: SdrRenderingResult[\"displayAdaptation\"];\n}\nexport type NativeCaptureSdrTaskState = \"ready\" | \"running\" | \"completed\" | \"cancelled\" | \"failed\" | \"disposed\" | \"transferred\";\n\n/** Validates the input and plans the task, including its limits, without reading samples or allocating an image. */\nexport function calculateNativeCaptureSdrPlan(input: NativeCaptureSdrInput): NativeCaptureSdrPlan {\n const r = requireAllowlistedRecord(input, [\"capture\", \"sourcePlaneId\", \"outputImageStateId\", \"profile\", \"maximumOutputBytes\", \"maximumPixels\"], \"Invalid native SDR input.\");\n const maximumPixels = resolveRaisedLimit(r.maximumPixels, NATIVE_CAPTURE_SDR_LIMITS.maximumPixels, NATIVE_CAPTURE_SDR_LIMITS.ceilingPixels,\n \"Native SDR maximumPixels must be an integer from 1 to \" + NATIVE_CAPTURE_SDR_LIMITS.ceilingPixels + \".\");\n // Only external-storage manifests are accepted, so unrelated inline sample buffers are never copied.\n const c = r.capture as SimulatedCapture | undefined;\n if (!c || !Array.isArray(c.planes) || c.planes.length > 32 || c.planes.some(p => !p || p.storage?.kind === \"inline-float64\")) {\n throw new InvalidConfigurationError(\"Native SDR requires external-only capture manifests.\");\n }\n const capture = parseSimulatedCapture(c);\n const sourcePlaneId = requirePublicOpaqueId(r.sourcePlaneId, \"Invalid native SDR plane ID.\");\n const outputImageStateId = requirePublicOpaqueId(r.outputImageStateId, \"Invalid native SDR output state ID.\");\n const plane = capture.planes.find(p => p.id === sourcePlaneId);\n const white = resolveCaptureColorModel().referenceWhiteXyz;\n if (!plane || plane.imageState !== \"color-transformed-linear-rgb\" ||\n plane.colorProfile?.id !== LINEAR_CAPTURE_RGB_PROFILE.id || plane.colorProfile.version !== LINEAR_CAPTURE_RGB_PROFILE.version ||\n plane.encodingReferenceWhiteXyz?.x !== white.x || plane.encodingReferenceWhiteXyz.y !== white.y || plane.encodingReferenceWhiteXyz.z !== white.z ||\n plane.whiteBalanceApplication === \"intent-only\" || (plane.whiteBalanceApplication === \"not-applicable\" && capture.whiteBalanceIntent !== null) ||\n capture.planes.some(p => p.imageStateId === outputImageStateId)) {\n throw new InvalidConfigurationError(\"Native SDR requires exact resolved linear-sRGB/D65 and a new output state ID.\");\n }\n const pixels = plane.pixelWidth * plane.pixelHeight, profile = parseSdrRenderingProfile(r.profile);\n const outputBytes = pixels * 3 * (profile.bitDepth / 8);\n const scale = 2 ** profile.renderingExposureEv / plane.referenceWhiteValue;\n if (!Number.isFinite(scale) || scale <= 0) throw new InvalidConfigurationError(\"Native SDR scale is unrepresentable.\");\n if (pixels > maximumPixels || plane.pixelWidth > NATIVE_CAPTURE_SDR_LIMITS.maximumDimension ||\n plane.pixelHeight > NATIVE_CAPTURE_SDR_LIMITS.maximumDimension || typeof r.maximumOutputBytes !== \"number\" ||\n !Number.isSafeInteger(r.maximumOutputBytes) || r.maximumOutputBytes < outputBytes) {\n throw new InvalidConfigurationError(\"Native SDR raster/output exceeds declared admission limits.\");\n }\n // A cautious declared bound, kept from the earlier executor that called the reference renderer: provider payload +\n // packed copy + reference parser copy + four reference output arrays. The typed kernel itself keeps only the provider payload.\n const maximumScratchPayloadBytes = NATIVE_CAPTURE_SDR_LIMITS.maximumTileStorageSamples * 8 +\n NATIVE_CAPTURE_SDR_LIMITS.tileWidth * NATIVE_CAPTURE_SDR_LIMITS.tileHeight * 3 * 8 * 6;\n return freezeOwnedData({ schemaVersion: NATIVE_CAPTURE_SDR_SCHEMA_VERSION, capture, sourcePlaneId, outputImageStateId, profile,\n outputBytes, maximumScratchPayloadBytes,\n tileCount: Math.ceil(plane.pixelWidth / NATIVE_CAPTURE_SDR_LIMITS.tileWidth) * Math.ceil(plane.pixelHeight / NATIVE_CAPTURE_SDR_LIMITS.tileHeight) });\n}\n\n/**\n * Tile checks shared by every native SDR backend: identity, precision, row stride, storage and valid\n * input values. Returns a view of the provider's samples (not a copy). Padding is not checked for\n * finite values. Not exported from the package.\n */\nexport function validateNativeCaptureSdrTile(tile: NativeCaptureSdrTile, request: NativeCaptureSdrTileRequest,\n plane: CaptureLinearPlane): { source: Float32Array | Float64Array; stride: number } {\n const r = requireAllowlistedRecord(tile, [\"captureId\", \"sourcePlaneId\", \"artifactId\", \"sha256\", \"x\", \"y\", \"width\", \"height\", \"rowStrideSamples\", \"samples\"], \"Invalid native SDR tile.\");\n for (const key of Object.keys(request) as (keyof NativeCaptureSdrTileRequest)[]) {\n if (r[key] !== request[key]) throw new InvalidConfigurationError(\"Native SDR tile identity/rectangle mismatch.\");\n }\n const samples = r.samples;\n const expectedType = plane.storage.kind === \"external-float32\" ? Float32Array : Float64Array;\n if (!(samples instanceof expectedType) || !(samples.buffer instanceof ArrayBuffer) ||\n samples.buffer.byteLength > NATIVE_CAPTURE_SDR_LIMITS.maximumTileStorageSamples * expectedType.BYTES_PER_ELEMENT ||\n typeof r.rowStrideSamples !== \"number\" || !Number.isSafeInteger(r.rowStrideSamples) || r.rowStrideSamples < request.width * 3 ||\n samples.length !== r.rowStrideSamples * request.height || samples.length > NATIVE_CAPTURE_SDR_LIMITS.maximumTileStorageSamples) {\n throw new InvalidConfigurationError(\"Native SDR tile precision/stride/storage mismatch.\");\n }\n const source: Float32Array | Float64Array = samples, stride = r.rowStrideSamples, rowLength = request.width * 3;\n // Check every image sample (not padding) against the reference renderer's input rules before any rendering.\n for (let y = 0; y < request.height; y++) {\n for (let i = y * stride, end = i + rowLength; i < end; i++) {\n if (!Number.isFinite(source[i]!)) throw new InvalidConfigurationError(\"Unsupported SDR input domain/raster.\");\n }\n }\n return { source, stride };\n}\n\ntype NativeSdrDiagnosticCounts = Omit<SdrRenderingResult[\"diagnostics\"], \"captureSaturation\">;\n/**\n * Fast tile kernel on typed arrays. It applies the same equations as the SDR reference renderer\n * (`calculateSdrRendering`), with the same floating-point operation order per sample and the same\n * error precedence, and writes integer codes straight into the packed output.\n *\n * Steps: input check → rendering exposure → tone → gamut → sRGB transfer → quantization (round to\n * nearest, ties up). `test/native-capture-sdr-kernel-parity.test.ts` shows that codes and diagnostics\n * match the unchanged reference exactly.\n */\nfunction renderTileInto(tile: NativeCaptureSdrTile, request: NativeCaptureSdrTileRequest, plane: CaptureLinearPlane,\n profile: SdrRenderingProfile, tone: SdrPixelTone, scale: number, maximum: number, codes: Uint8Array | Uint16Array, diagnostics: NativeSdrDiagnosticCounts): void {\n const { source, stride } = validateNativeCaptureSdrTile(tile, request, plane);\n // Tiling is safe because every supported rendering operation works on one pixel at a time.\n const outputRowStride = plane.pixelWidth * 3;\n const counts = { negative: 0, above: 0, changed: 0, low: 0, high: 0, outOfGamut: false }, scratch = new Float64Array(3);\n for (let y = 0; y < request.height; y++) {\n renderSdrRunInto(source, y * stride, request.width, profile, tone, scale, maximum, codes, (request.y + y) * outputRowStride + request.x * 3, scratch, counts);\n }\n if (counts.outOfGamut) throw new InvalidConfigurationError(\"Tone output exceeds the declared SDR gamut range.\");\n diagnostics.renderingNegativeSampleCount += counts.negative; diagnostics.renderingAboveReferenceSampleCount += counts.above;\n diagnostics.toneChangedSampleCount += counts.changed; diagnostics.gamutClippedLowSampleCount += counts.low; diagnostics.gamutClippedHighSampleCount += counts.high;\n}\n/** Reference 8-bit code of a linear sample in [0,1]: Math.round(encodeSrgbComponent(x)·255). */\nfunction referenceSrgb8(linear: number): number { return Math.round(encodeSrgbComponent(linear) * 255); }\n/** Exact 8-bit sRGB quantizer tables; see {@link srgb8Quantizer}. */\nexport interface Srgb8Quantizer {\n /** t[k]: smallest binary64 x in [0,1] with referenceSrgb8(x) ≥ k; t[256] = +∞ sentinel. */\n readonly thresholds: Float64Array;\n /** start[b]: reference code of b/4096, the first code of bucket [b/4096, (b+1)/4096). */\n readonly start: Uint8Array;\n}\nlet srgb8: Srgb8Quantizer | null | undefined;\n/**\n * Exact 8-bit sRGB quantization without evaluating the transfer curve for each sample.\n *\n * The code thresholds are found by bisection on this engine's own `encodeSrgbComponent`. Comparing\n * against them gives Math.round(encodeSrgbComponent(x)·255) for every x, provided that function never\n * decreases (true when pow is monotone). Construction checks every threshold and the 16 neighbouring\n * doubles on each side. If any check fails, the direct transfer is used instead, so output never\n * silently depends on that assumption.\n *\n * A 4097-entry bucket index (x·4096 is exact) gives the starting code. Even the steepest part of the\n * curve (slope 12.92·255) covers less than one code per bucket, so one or two comparisons finish.\n */\nfunction srgb8Quantizer(): Srgb8Quantizer | null {\n if (srgb8 !== undefined) return srgb8;\n const t = new Float64Array(257), bits = new Float64Array(1), word = new BigUint64Array(bits.buffer);\n const step = (x: number, n: number): number => { bits[0] = x; word[0] = word[0]! + BigInt(n); return bits[0]!; };\n for (let k = 1; k < 256; k++) {\n let lo = t[k - 1]!, hi = 1;\n for (;;) {\n const mid = lo + (hi - lo) / 2;\n if (mid === lo || mid === hi) break;\n if (referenceSrgb8(mid) >= k) hi = mid; else lo = mid;\n }\n t[k] = hi;\n }\n t[256] = Infinity;\n let valid = referenceSrgb8(0) === 0 && referenceSrgb8(1) === 255;\n for (let k = 1; valid && k < 256; k++) {\n const x = t[k]!;\n if (!(x > t[k - 1]!) || referenceSrgb8(x) !== k) valid = false;\n for (let n = 1; valid && n <= 16; n++) {\n const below = step(x, -n), above = step(x, n);\n if ((below >= 0 && referenceSrgb8(below) >= k) || (above <= 1 && referenceSrgb8(above) < k)) valid = false;\n }\n }\n const start = new Uint8Array(4097);\n for (let i = 0; valid && i <= 4096; i++) { start[i] = referenceSrgb8(i / 4096); if (i > 0 && start[i]! < start[i - 1]!) valid = false; }\n srgb8 = valid ? { thresholds: t, start } : null;\n return srgb8;\n}\n/** Largest k with t[k] ≤ x for x in [0,1]. */\nfunction quantizeSrgb8(q: Srgb8Quantizer, x: number): number {\n const t = q.thresholds;\n let k = q.start[(x * 4096) | 0]!;\n while (x >= t[k + 1]!) k++;\n return k;\n}\n/** Internal, for tests: the exact quantizer (null when its construction check failed). */\nexport function nativeSrgb8QuantizerForTest(): { quantizer: Srgb8Quantizer | null; quantize(x: number): number } {\n const quantizer = srgb8Quantizer();\n return { quantizer, quantize: (x) => quantizer ? quantizeSrgb8(quantizer, x) : referenceSrgb8(x) };\n}\n/** Per-tile counters for {@link renderSdrRunInto}. `outOfGamut` delays the rejection, as the reference does. */\nexport interface SdrPixelCounts { negative: number; above: number; changed: number; low: number; high: number; outOfGamut: boolean }\n/**\n * Internal SDR kernel on typed arrays, shared by native SDR and RAW development.\n *\n * For `pixelCount` interleaved linear-sRGB pixels starting at `source[start]`, it runs exactly the\n * reference (`calculateSdrRendering`) operations in the same order per pixel: -0 → +0 → rendering\n * exposure → shared tone operator → gamut → sRGB transfer → quantization (nearest, ties up) into\n * `codes[outOffset]`. A gamut rejection is delayed via `counts.outOfGamut`, because the reference\n * finishes the whole tone pass (and its exposure errors) first. `scratch` must hold at least 3\n * values. Not exported from the package.\n */\nexport function renderSdrRunInto(source: Float32Array | Float64Array, start: number, pixelCount: number, profile: SdrRenderingProfile,\n tone: SdrPixelTone, scale: number, maximum: number, codes: Uint8Array | Uint16Array, outOffset: number, scratch: Float64Array, counts: SdrPixelCounts): void {\n const reject = profile.gamutHandling === \"reject-out-of-range\", quantizer = maximum === 255 ? srgb8Quantizer() : null;\n let negative = 0, above = 0, changed = 0, low = 0, high = 0, outOfGamut = false;\n for (let p = 0, i = start, o = outOffset; p < pixelCount; p++, i += 3) {\n // The reference parser normalizes -0 to +0 before exposure.\n const red = source[i]!, green = source[i + 1]!, blue = source[i + 2]!;\n const er = (red === 0 ? 0 : red) * scale, eg = (green === 0 ? 0 : green) * scale, eb = (blue === 0 ? 0 : blue) * scale;\n if (!Number.isFinite(er) || !Number.isFinite(eg) || !Number.isFinite(eb)) throw new InvalidScientificResultError(\"Rendering exposure produced a non-finite sample.\");\n tone(er, eg, eb, scratch);\n for (let c = 0; c < 3; c++, o++) {\n const exposed = c === 0 ? er : c === 1 ? eg : eb;\n if (exposed < 0) negative++;\n if (exposed > 1) above++;\n let mapped = scratch[c]!;\n if (mapped !== exposed) changed++;\n if (mapped === 0) mapped = 0;\n let linear = mapped;\n if (mapped < 0 || mapped > 1) {\n if (reject) outOfGamut = true;\n else if (mapped < 0) { low++; linear = 0; }\n else { high++; linear = 1; }\n }\n // Encoded values are in [0,1]: Math.round is exact nearest-ties-up here.\n codes[o] = quantizer ? quantizeSrgb8(quantizer, linear) : Math.round(encodeSrgbComponent(linear) * maximum);\n }\n }\n counts.negative += negative; counts.above += above; counts.changed += changed; counts.low += low; counts.high += high;\n if (outOfGamut) counts.outOfGamut = true;\n}\n\n/** The reference's output-encoding declaration for the given code maximum. */\nexport function sdrOutputEncoding(maximum: number): SdrRenderingResult[\"outputEncoding\"] {\n return { imageState: \"output-referred-sdr\", colorSpace: \"srgb\", primariesXy: { red: [.64, .33], green: [.30, .60], blue: [.15, .06] },\n whitePointXy: [.3127, .3290], referenceWhiteLuminanceCdM2: 80, codeMinimum: 0, codeMaximum: maximum };\n}\n\n/** A single-use task. No partial output is ever returned, and cancel or dispose stops a late result from being published. */\nexport interface NativeCaptureSdrTask {\n readonly plan: NativeCaptureSdrPlan;\n readonly state: NativeCaptureSdrTaskState;\n readonly completedTileCount: number;\n run(): Promise<void>;\n cancel(): void;\n dispose(): void;\n /** Hands over the only copy of the output buffer, once. Dispose cannot take back a buffer already handed over. */\n takeOutput(): NativeCaptureSdrOutput;\n}\n/** Creates a single-use task. Limits are checked before any buffer is allocated or tile is read. */\nexport function createNativeCaptureSdrTask(input: NativeCaptureSdrInput, provider: NativeCaptureSdrProvider): NativeCaptureSdrTask {\n const plan = calculateNativeCaptureSdrPlan(input);\n if (!provider || typeof provider.readTile !== \"function\" || typeof provider.yieldControl !== \"function\") {\n throw new InvalidConfigurationError(\"Native SDR requires a tile reader and event-loop yield.\");\n }\n if (provider.onProgress !== undefined && typeof provider.onProgress !== \"function\") throw new InvalidConfigurationError(\"Native SDR onProgress must be a function.\");\n // Also capture the callback functions now, so later app changes cannot swap the reader mid-task.\n const readTile = provider.readTile.bind(provider), yieldControl = provider.yieldControl.bind(provider),\n onProgress = provider.onProgress?.bind(provider);\n const plane = plan.capture.planes.find(p => p.id === plan.sourcePlaneId)!;\n if (plane.storage.kind === \"inline-float64\") throw new InvalidConfigurationError(\"External native source required.\");\n const storage = plane.storage, abort = new AbortController();\n let state: NativeCaptureSdrTaskState = \"ready\", count = 0, output: NativeCaptureSdrOutput | null = null;\n let codes: Uint8Array | Uint16Array | null = null;\n function release(): void { codes?.fill(0); codes = null; output = null; }\n function checkActive(): void {\n if (abort.signal.aborted) throw new InvalidConfigurationError(\"Native SDR task cancelled/disposed; no output available.\");\n }\n return {\n plan,\n get state(): NativeCaptureSdrTaskState { return state; },\n get completedTileCount(): number { return count; },\n async run(): Promise<void> {\n if (state !== \"ready\") throw new InvalidConfigurationError(\"Native SDR task is single-use.\");\n state = \"running\";\n try {\n codes = plan.profile.bitDepth === 8 ? new Uint8Array(plan.outputBytes) : new Uint16Array(plan.outputBytes / 2);\n const diagnostics: SdrRenderingResult[\"diagnostics\"] = { renderingNegativeSampleCount: 0, renderingAboveReferenceSampleCount: 0,\n toneChangedSampleCount: 0, gamutClippedLowSampleCount: 0, gamutClippedHighSampleCount: 0, captureSaturation: \"not-consumed\" };\n // Same scale expression and code range as the reference. The plan already rejected a scale that cannot be represented.\n const scale = 2 ** plan.profile.renderingExposureEv / plane.referenceWhiteValue, maximum = 2 ** plan.profile.bitDepth - 1,\n tone = prepareSdrPixelTone(plan.profile);\n for (let y = 0; y < plane.pixelHeight; y += NATIVE_CAPTURE_SDR_LIMITS.tileHeight) {\n for (let x = 0; x < plane.pixelWidth; x += NATIVE_CAPTURE_SDR_LIMITS.tileWidth) {\n checkActive();\n const request = Object.freeze({ captureId: plan.capture.captureId, sourcePlaneId: plane.id,\n artifactId: storage.artifactId, sha256: storage.sha256, x, y,\n width: Math.min(NATIVE_CAPTURE_SDR_LIMITS.tileWidth, plane.pixelWidth - x),\n height: Math.min(NATIVE_CAPTURE_SDR_LIMITS.tileHeight, plane.pixelHeight - y) });\n {\n const tile = await readTile(request, abort.signal);\n checkActive();\n // Diagnostics are aggregated only after the whole tile renders.\n renderTileInto(tile, request, plane, plan.profile, tone, scale, maximum, codes!, diagnostics);\n count++;\n onProgress?.({ completedTileCount: count, totalTileCount: plan.tileCount });\n }\n // Do not keep tile or reference arrays past the next tile read or host yield.\n await yieldControl(abort.signal);\n checkActive();\n }\n }\n output = { plan, integerSamples: codes!, diagnostics, outputEncoding: sdrOutputEncoding(maximum),\n displayAdaptation: { kind: \"external-platform\", applied: false, displayCapabilities: \"unknown\" } };\n state = \"completed\";\n } catch (error) {\n release();\n if (!abort.signal.aborted) { state = \"failed\"; abort.abort(); }\n throw error;\n }\n },\n cancel(): void {\n if (state === \"ready\" || state === \"running\" || state === \"completed\") { state = \"cancelled\"; abort.abort(); release(); }\n },\n dispose(): void { if (state !== \"transferred\") { state = \"disposed\"; abort.abort(); release(); } },\n takeOutput(): NativeCaptureSdrOutput {\n if (state !== \"completed\" || !output) throw new InvalidConfigurationError(\"Native SDR output is unavailable.\");\n const result = output; output = null; codes = null; state = \"transferred\";\n return result;\n }\n };\n}\n"]}
1
+ {"version":3,"file":"native-capture-sdr.js","sourceRoot":"","sources":["../../src/output/native-capture-sdr.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Native SDR rendering of large captures, tile by tile, within fixed memory limits. The engine\n * owns the task; the caller supplies the pixel tiles. See docs/NATIVE_CAPTURE_SDR.md.\n */\nimport { parseSimulatedCapture, type SimulatedCapture, type CaptureLinearPlane } from \"../capture/simulated-capture.js\";\nimport { LINEAR_CAPTURE_RGB_PROFILE, resolveCaptureColorModel } from \"../color/capture-color.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { requireAllowlistedRecord, requirePublicOpaqueId } from \"../core/record-validation.js\";\nimport { freezeOwnedData } from \"../core/owned-data.js\";\nimport { FULL_RESOLUTION_CEILING_PIXELS, resolveRaisedLimit } from \"../core/pixel-limits.js\";\nimport { encodeSrgbComponent } from \"../color/srgb-transfer.js\";\nimport { InvalidScientificResultError } from \"../core/validation.js\";\nimport { parseSdrRenderingProfile, prepareSdrPixelTone, type SdrPixelTone, type SdrRenderingProfile, type SdrRenderingResult } from \"./sdr-rendering.js\";\n\nexport const NATIVE_CAPTURE_SDR_SCHEMA_VERSION = \"0.1.0\" as const;\n/** `maximumPixels` is the default pixel limit; a request may raise it with its own `maximumPixels`, up to `ceilingPixels`. */\nexport const NATIVE_CAPTURE_SDR_LIMITS = /* @__PURE__ */ Object.freeze({ maximumPixels: 24_000_000, ceilingPixels: FULL_RESOLUTION_CEILING_PIXELS,\n maximumDimension: 16_384, tileWidth: 256, tileHeight: 32, maximumTileStorageSamples: 32_768 });\n/** Input. The capture holds only metadata; float samples are read from external storage. Samples must already have colour and white balance applied. */\nexport interface NativeCaptureSdrInput {\n capture: SimulatedCapture;\n sourcePlaneId: string;\n outputImageStateId: string;\n profile: SdrRenderingProfile;\n /** Limit on the size of the integer output. Checked before anything is allocated or any tile is read. */\n maximumOutputBytes: number;\n /**\n * Optional opt-in pixel limit for large captures: an integer from 1 to `NATIVE_CAPTURE_SDR_LIMITS.ceilingPixels`.\n * Default `NATIVE_CAPTURE_SDR_LIMITS.maximumPixels` (24 000 000). `maximumOutputBytes` still applies.\n */\n maximumPixels?: number;\n}\n/** Tile request. Coordinates are absolute in the selected plane's declared pixel grid, never relative to a crop. */\nexport interface NativeCaptureSdrTileRequest {\n captureId: string;\n sourcePlaneId: string;\n artifactId: string;\n sha256: string;\n x: number; y: number; width: number; height: number;\n}\n/** A tile returned by the provider. It belongs to this call alone; do not change or reuse it. Row padding is not image data. */\nexport interface NativeCaptureSdrTile extends NativeCaptureSdrTileRequest {\n rowStrideSamples: number;\n samples: Float32Array | Float64Array;\n}\n/** Supplies tiles, one call at a time. The caller must release decoder and GPU resources when the task aborts or finishes. */\nexport interface NativeCaptureSdrProvider {\n readTile(request: Readonly<NativeCaptureSdrTileRequest>, signal: AbortSignal): Promise<NativeCaptureSdrTile>;\n /** Must yield to the host event loop, so cancellation and input events can run between tiles. */\n yieldControl(signal: AbortSignal): Promise<void>;\n /**\n * Optional progress observer, called synchronously after each committed tile. It cannot change\n * output; an exception it throws fails the task like any other error.\n */\n onProgress?(progress: NativeTaskProgress): void;\n}\n/** Tile progress of a native task: `completedTileCount` of `totalTileCount` tiles are committed. */\nexport interface NativeTaskProgress { readonly completedTileCount: number; readonly totalTileCount: number }\nexport interface NativeCaptureSdrPlan {\n schemaVersion: typeof NATIVE_CAPTURE_SDR_SCHEMA_VERSION;\n capture: SimulatedCapture;\n sourcePlaneId: string;\n outputImageStateId: string;\n profile: SdrRenderingProfile;\n outputBytes: number;\n /** Upper bound on the bytes held in working arrays. It is not peak JavaScript heap, GPU or process memory, and does not show that a device can run the task. */\n maximumScratchPayloadBytes: number;\n tileCount: number;\n}\nexport interface NativeCaptureSdrOutput {\n plan: NativeCaptureSdrPlan;\n /** Packed RGB codes in row-major order, owned by the caller. 16-bit codes are numbers, not bytes in a particular byte order. */\n integerSamples: Uint8Array | Uint16Array;\n diagnostics: SdrRenderingResult[\"diagnostics\"];\n outputEncoding: SdrRenderingResult[\"outputEncoding\"];\n displayAdaptation: SdrRenderingResult[\"displayAdaptation\"];\n}\nexport type NativeCaptureSdrTaskState = \"ready\" | \"running\" | \"completed\" | \"cancelled\" | \"failed\" | \"disposed\" | \"transferred\";\n\n/** Validates the input and plans the task, including its limits, without reading samples or allocating an image. */\nexport function calculateNativeCaptureSdrPlan(input: NativeCaptureSdrInput): NativeCaptureSdrPlan {\n const r = requireAllowlistedRecord(input, [\"capture\", \"sourcePlaneId\", \"outputImageStateId\", \"profile\", \"maximumOutputBytes\", \"maximumPixels\"], \"Invalid native SDR input.\");\n const maximumPixels = resolveRaisedLimit(r.maximumPixels, NATIVE_CAPTURE_SDR_LIMITS.maximumPixels, NATIVE_CAPTURE_SDR_LIMITS.ceilingPixels,\n \"Native SDR maximumPixels must be an integer from 1 to \" + NATIVE_CAPTURE_SDR_LIMITS.ceilingPixels + \".\");\n // Only external-storage manifests are accepted, so unrelated inline sample buffers are never copied.\n const c = r.capture as SimulatedCapture | undefined;\n if (!c || !Array.isArray(c.planes) || c.planes.length > 32 || c.planes.some(p => !p || p.storage?.kind === \"inline-float64\")) {\n throw new InvalidConfigurationError(\"Native SDR requires external-only capture manifests.\");\n }\n const capture = parseSimulatedCapture(c);\n const sourcePlaneId = requirePublicOpaqueId(r.sourcePlaneId, \"Invalid native SDR plane ID.\");\n const outputImageStateId = requirePublicOpaqueId(r.outputImageStateId, \"Invalid native SDR output state ID.\");\n const plane = capture.planes.find(p => p.id === sourcePlaneId);\n const white = resolveCaptureColorModel().referenceWhiteXyz;\n if (!plane || plane.imageState !== \"color-transformed-linear-rgb\" ||\n plane.colorProfile?.id !== LINEAR_CAPTURE_RGB_PROFILE.id || plane.colorProfile.version !== LINEAR_CAPTURE_RGB_PROFILE.version ||\n plane.encodingReferenceWhiteXyz?.x !== white.x || plane.encodingReferenceWhiteXyz.y !== white.y || plane.encodingReferenceWhiteXyz.z !== white.z ||\n plane.whiteBalanceApplication === \"intent-only\" || (plane.whiteBalanceApplication === \"not-applicable\" && capture.whiteBalanceIntent !== null) ||\n capture.planes.some(p => p.imageStateId === outputImageStateId)) {\n throw new InvalidConfigurationError(\"Native SDR requires exact resolved linear-sRGB/D65 and a new output state ID.\");\n }\n const pixels = plane.pixelWidth * plane.pixelHeight, profile = parseSdrRenderingProfile(r.profile);\n const outputBytes = pixels * 3 * (profile.bitDepth / 8);\n const scale = 2 ** profile.renderingExposureEv / plane.referenceWhiteValue;\n if (!Number.isFinite(scale) || scale <= 0) throw new InvalidConfigurationError(\"Native SDR scale is unrepresentable.\");\n if (pixels > maximumPixels || plane.pixelWidth > NATIVE_CAPTURE_SDR_LIMITS.maximumDimension ||\n plane.pixelHeight > NATIVE_CAPTURE_SDR_LIMITS.maximumDimension || typeof r.maximumOutputBytes !== \"number\" ||\n !Number.isSafeInteger(r.maximumOutputBytes) || r.maximumOutputBytes < outputBytes) {\n throw new InvalidConfigurationError(\"Native SDR raster/output exceeds declared admission limits.\");\n }\n // A cautious declared bound, kept from the earlier executor that called the reference renderer: provider payload +\n // packed copy + reference parser copy + four reference output arrays. The typed kernel itself keeps only the provider payload.\n const maximumScratchPayloadBytes = NATIVE_CAPTURE_SDR_LIMITS.maximumTileStorageSamples * 8 +\n NATIVE_CAPTURE_SDR_LIMITS.tileWidth * NATIVE_CAPTURE_SDR_LIMITS.tileHeight * 3 * 8 * 6;\n return freezeOwnedData({ schemaVersion: NATIVE_CAPTURE_SDR_SCHEMA_VERSION, capture, sourcePlaneId, outputImageStateId, profile,\n outputBytes, maximumScratchPayloadBytes,\n tileCount: Math.ceil(plane.pixelWidth / NATIVE_CAPTURE_SDR_LIMITS.tileWidth) * Math.ceil(plane.pixelHeight / NATIVE_CAPTURE_SDR_LIMITS.tileHeight) });\n}\n\n/**\n * Tile checks shared by every native SDR backend: identity, precision, row stride, storage and valid\n * input values. Returns a view of the provider's samples (not a copy). Padding is not checked for\n * finite values. Not exported from the package.\n */\nexport function validateNativeCaptureSdrTile(tile: NativeCaptureSdrTile, request: NativeCaptureSdrTileRequest,\n plane: CaptureLinearPlane): { source: Float32Array | Float64Array; stride: number } {\n const r = requireAllowlistedRecord(tile, [\"captureId\", \"sourcePlaneId\", \"artifactId\", \"sha256\", \"x\", \"y\", \"width\", \"height\", \"rowStrideSamples\", \"samples\"], \"Invalid native SDR tile.\");\n for (const key of Object.keys(request) as (keyof NativeCaptureSdrTileRequest)[]) {\n if (r[key] !== request[key]) throw new InvalidConfigurationError(\"Native SDR tile identity/rectangle mismatch.\");\n }\n const samples = r.samples;\n const expectedType = plane.storage.kind === \"external-float32\" ? Float32Array : Float64Array;\n if (!(samples instanceof expectedType) || !(samples.buffer instanceof ArrayBuffer) ||\n samples.buffer.byteLength > NATIVE_CAPTURE_SDR_LIMITS.maximumTileStorageSamples * expectedType.BYTES_PER_ELEMENT ||\n typeof r.rowStrideSamples !== \"number\" || !Number.isSafeInteger(r.rowStrideSamples) || r.rowStrideSamples < request.width * 3 ||\n samples.length !== r.rowStrideSamples * request.height || samples.length > NATIVE_CAPTURE_SDR_LIMITS.maximumTileStorageSamples) {\n throw new InvalidConfigurationError(\"Native SDR tile precision/stride/storage mismatch.\");\n }\n const source: Float32Array | Float64Array = samples, stride = r.rowStrideSamples, rowLength = request.width * 3;\n // Check every image sample (not padding) against the reference renderer's input rules before any rendering.\n for (let y = 0; y < request.height; y++) {\n for (let i = y * stride, end = i + rowLength; i < end; i++) {\n if (!Number.isFinite(source[i]!)) throw new InvalidConfigurationError(\"Unsupported SDR input domain/raster.\");\n }\n }\n return { source, stride };\n}\n\ntype NativeSdrDiagnosticCounts = Omit<SdrRenderingResult[\"diagnostics\"], \"captureSaturation\">;\n/**\n * Fast tile kernel on typed arrays. It applies the same equations as the SDR reference renderer\n * (`calculateSdrRendering`), with the same floating-point operation order per sample and the same\n * error precedence, and writes integer codes straight into the packed output.\n *\n * Steps: input check → rendering exposure → tone → gamut → sRGB transfer → quantization (round to\n * nearest, ties up). `test/native-capture-sdr-kernel-parity.test.ts` shows that codes and diagnostics\n * match the unchanged reference exactly.\n */\nfunction renderTileInto(tile: NativeCaptureSdrTile, request: NativeCaptureSdrTileRequest, plane: CaptureLinearPlane,\n profile: SdrRenderingProfile, tone: SdrPixelTone, scale: number, maximum: number, codes: Uint8Array | Uint16Array, diagnostics: NativeSdrDiagnosticCounts): void {\n const { source, stride } = validateNativeCaptureSdrTile(tile, request, plane);\n // Tiling is safe because every supported rendering operation works on one pixel at a time.\n const outputRowStride = plane.pixelWidth * 3;\n const counts = { negative: 0, above: 0, changed: 0, low: 0, high: 0, outOfGamut: false }, scratch = new Float64Array(3);\n for (let y = 0; y < request.height; y++) {\n renderSdrRunInto(source, y * stride, request.width, profile, tone, scale, maximum, codes, (request.y + y) * outputRowStride + request.x * 3, scratch, counts);\n }\n if (counts.outOfGamut) throw new InvalidConfigurationError(\"Tone output exceeds the declared SDR gamut range.\");\n diagnostics.renderingNegativeSampleCount += counts.negative; diagnostics.renderingAboveReferenceSampleCount += counts.above;\n diagnostics.toneChangedSampleCount += counts.changed; diagnostics.gamutClippedLowSampleCount += counts.low; diagnostics.gamutClippedHighSampleCount += counts.high;\n}\n/** Reference 8-bit code of a linear sample in [0,1]: Math.round(encodeSrgbComponent(x)·255). */\nfunction referenceSrgb8(linear: number): number { return Math.round(encodeSrgbComponent(linear) * 255); }\n/** Exact 8-bit sRGB quantizer tables; see {@link srgb8Quantizer}. */\nexport interface Srgb8Quantizer {\n /** t[k]: smallest binary64 x in [0,1] with referenceSrgb8(x) ≥ k; t[256] = +∞ sentinel. */\n readonly thresholds: Float64Array;\n /** start[b]: reference code of b/4096, the first code of bucket [b/4096, (b+1)/4096). */\n readonly start: Uint8Array;\n}\nlet srgb8: Srgb8Quantizer | null | undefined;\n/**\n * Exact 8-bit sRGB quantization without evaluating the transfer curve for each sample.\n *\n * The code thresholds are found by bisection on this engine's own `encodeSrgbComponent`. Comparing\n * against them gives Math.round(encodeSrgbComponent(x)·255) for every x, provided that function never\n * decreases (true when pow is monotone). Construction checks every threshold and the 16 neighbouring\n * doubles on each side. If any check fails, the direct transfer is used instead, so output never\n * silently depends on that assumption.\n *\n * A 4097-entry bucket index (x·4096 is exact) gives the starting code. Even the steepest part of the\n * curve (slope 12.92·255) covers less than one code per bucket, so one or two comparisons finish.\n */\nfunction srgb8Quantizer(): Srgb8Quantizer | null {\n if (srgb8 !== undefined) return srgb8;\n const t = new Float64Array(257), bits = new Float64Array(1), word = new BigUint64Array(bits.buffer);\n const step = (x: number, n: number): number => { bits[0] = x; word[0] = word[0]! + BigInt(n); return bits[0]!; };\n for (let k = 1; k < 256; k++) {\n let lo = t[k - 1]!, hi = 1;\n for (;;) {\n const mid = lo + (hi - lo) / 2;\n if (mid === lo || mid === hi) break;\n if (referenceSrgb8(mid) >= k) hi = mid; else lo = mid;\n }\n t[k] = hi;\n }\n t[256] = Infinity;\n let valid = referenceSrgb8(0) === 0 && referenceSrgb8(1) === 255;\n for (let k = 1; valid && k < 256; k++) {\n const x = t[k]!;\n if (!(x > t[k - 1]!) || referenceSrgb8(x) !== k) valid = false;\n for (let n = 1; valid && n <= 16; n++) {\n const below = step(x, -n), above = step(x, n);\n if ((below >= 0 && referenceSrgb8(below) >= k) || (above <= 1 && referenceSrgb8(above) < k)) valid = false;\n }\n }\n const start = new Uint8Array(4097);\n for (let i = 0; valid && i <= 4096; i++) { start[i] = referenceSrgb8(i / 4096); if (i > 0 && start[i]! < start[i - 1]!) valid = false; }\n srgb8 = valid ? { thresholds: t, start } : null;\n return srgb8;\n}\n/** Largest k with t[k] ≤ x for x in [0,1]. */\nfunction quantizeSrgb8(q: Srgb8Quantizer, x: number): number {\n const t = q.thresholds;\n let k = q.start[(x * 4096) | 0]!;\n while (x >= t[k + 1]!) k++;\n return k;\n}\n/** Internal, for tests: the exact quantizer (null when its construction check failed). */\nexport function nativeSrgb8QuantizerForTest(): { quantizer: Srgb8Quantizer | null; quantize(x: number): number } {\n const quantizer = srgb8Quantizer();\n return { quantizer, quantize: (x) => quantizer ? quantizeSrgb8(quantizer, x) : referenceSrgb8(x) };\n}\n/** Per-tile counters for {@link renderSdrRunInto}. `outOfGamut` delays the rejection, as the reference does. */\nexport interface SdrPixelCounts { negative: number; above: number; changed: number; low: number; high: number; outOfGamut: boolean }\n/**\n * Internal SDR kernel on typed arrays, shared by native SDR and RAW development.\n *\n * For `pixelCount` interleaved linear-sRGB pixels starting at `source[start]`, it runs exactly the\n * reference (`calculateSdrRendering`) operations in the same order per pixel: -0 → +0 → rendering\n * exposure → shared tone operator → gamut → sRGB transfer → quantization (nearest, ties up) into\n * `codes[outOffset]` (with `maximum` 0, the encoded value unquantized). A gamut rejection is delayed via `counts.outOfGamut`, because the reference\n * finishes the whole tone pass (and its exposure errors) first. `scratch` must hold at least 3\n * values. Not exported from the package.\n */\nexport function renderSdrRunInto(source: Float32Array | Float64Array, start: number, pixelCount: number, profile: SdrRenderingProfile,\n tone: SdrPixelTone, scale: number, maximum: number, codes: Uint8Array | Uint16Array | Float64Array, outOffset: number, scratch: Float64Array, counts: SdrPixelCounts): void {\n const reject = profile.gamutHandling === \"reject-out-of-range\", quantizer = maximum === 255 ? srgb8Quantizer() : null;\n let negative = 0, above = 0, changed = 0, low = 0, high = 0, outOfGamut = false;\n for (let p = 0, i = start, o = outOffset; p < pixelCount; p++, i += 3) {\n // The reference parser normalizes -0 to +0 before exposure.\n const red = source[i]!, green = source[i + 1]!, blue = source[i + 2]!;\n const er = (red === 0 ? 0 : red) * scale, eg = (green === 0 ? 0 : green) * scale, eb = (blue === 0 ? 0 : blue) * scale;\n if (!Number.isFinite(er) || !Number.isFinite(eg) || !Number.isFinite(eb)) throw new InvalidScientificResultError(\"Rendering exposure produced a non-finite sample.\");\n tone(er, eg, eb, scratch);\n for (let c = 0; c < 3; c++, o++) {\n const exposed = c === 0 ? er : c === 1 ? eg : eb;\n if (exposed < 0) negative++;\n if (exposed > 1) above++;\n let mapped = scratch[c]!;\n if (mapped !== exposed) changed++;\n if (mapped === 0) mapped = 0;\n let linear = mapped;\n if (mapped < 0 || mapped > 1) {\n if (reject) outOfGamut = true;\n else if (mapped < 0) { low++; linear = 0; }\n else { high++; linear = 1; }\n }\n // Encoded values are in [0,1]: Math.round is exact nearest-ties-up here.\n // maximum 0 (#571): the encoded value itself, unquantized, into a Float64Array (the unsharp mask's input).\n codes[o] = quantizer ? quantizeSrgb8(quantizer, linear) : maximum === 0 ? encodeSrgbComponent(linear) : Math.round(encodeSrgbComponent(linear) * maximum);\n }\n }\n counts.negative += negative; counts.above += above; counts.changed += changed; counts.low += low; counts.high += high;\n if (outOfGamut) counts.outOfGamut = true;\n}\n\n/** The reference's output-encoding declaration for the given code maximum. */\nexport function sdrOutputEncoding(maximum: number): SdrRenderingResult[\"outputEncoding\"] {\n return { imageState: \"output-referred-sdr\", colorSpace: \"srgb\", primariesXy: { red: [.64, .33], green: [.30, .60], blue: [.15, .06] },\n whitePointXy: [.3127, .3290], referenceWhiteLuminanceCdM2: 80, codeMinimum: 0, codeMaximum: maximum };\n}\n\n/** A single-use task. No partial output is ever returned, and cancel or dispose stops a late result from being published. */\nexport interface NativeCaptureSdrTask {\n readonly plan: NativeCaptureSdrPlan;\n readonly state: NativeCaptureSdrTaskState;\n readonly completedTileCount: number;\n run(): Promise<void>;\n cancel(): void;\n dispose(): void;\n /** Hands over the only copy of the output buffer, once. Dispose cannot take back a buffer already handed over. */\n takeOutput(): NativeCaptureSdrOutput;\n}\n/** Creates a single-use task. Limits are checked before any buffer is allocated or tile is read. */\nexport function createNativeCaptureSdrTask(input: NativeCaptureSdrInput, provider: NativeCaptureSdrProvider): NativeCaptureSdrTask {\n const plan = calculateNativeCaptureSdrPlan(input);\n if (!provider || typeof provider.readTile !== \"function\" || typeof provider.yieldControl !== \"function\") {\n throw new InvalidConfigurationError(\"Native SDR requires a tile reader and event-loop yield.\");\n }\n if (provider.onProgress !== undefined && typeof provider.onProgress !== \"function\") throw new InvalidConfigurationError(\"Native SDR onProgress must be a function.\");\n // Also capture the callback functions now, so later app changes cannot swap the reader mid-task.\n const readTile = provider.readTile.bind(provider), yieldControl = provider.yieldControl.bind(provider),\n onProgress = provider.onProgress?.bind(provider);\n const plane = plan.capture.planes.find(p => p.id === plan.sourcePlaneId)!;\n if (plane.storage.kind === \"inline-float64\") throw new InvalidConfigurationError(\"External native source required.\");\n const storage = plane.storage, abort = new AbortController();\n let state: NativeCaptureSdrTaskState = \"ready\", count = 0, output: NativeCaptureSdrOutput | null = null;\n let codes: Uint8Array | Uint16Array | null = null;\n function release(): void { codes?.fill(0); codes = null; output = null; }\n function checkActive(): void {\n if (abort.signal.aborted) throw new InvalidConfigurationError(\"Native SDR task cancelled/disposed; no output available.\");\n }\n return {\n plan,\n get state(): NativeCaptureSdrTaskState { return state; },\n get completedTileCount(): number { return count; },\n async run(): Promise<void> {\n if (state !== \"ready\") throw new InvalidConfigurationError(\"Native SDR task is single-use.\");\n state = \"running\";\n try {\n codes = plan.profile.bitDepth === 8 ? new Uint8Array(plan.outputBytes) : new Uint16Array(plan.outputBytes / 2);\n const diagnostics: SdrRenderingResult[\"diagnostics\"] = { renderingNegativeSampleCount: 0, renderingAboveReferenceSampleCount: 0,\n toneChangedSampleCount: 0, gamutClippedLowSampleCount: 0, gamutClippedHighSampleCount: 0, captureSaturation: \"not-consumed\" };\n // Same scale expression and code range as the reference. The plan already rejected a scale that cannot be represented.\n const scale = 2 ** plan.profile.renderingExposureEv / plane.referenceWhiteValue, maximum = 2 ** plan.profile.bitDepth - 1,\n tone = prepareSdrPixelTone(plan.profile);\n for (let y = 0; y < plane.pixelHeight; y += NATIVE_CAPTURE_SDR_LIMITS.tileHeight) {\n for (let x = 0; x < plane.pixelWidth; x += NATIVE_CAPTURE_SDR_LIMITS.tileWidth) {\n checkActive();\n const request = Object.freeze({ captureId: plan.capture.captureId, sourcePlaneId: plane.id,\n artifactId: storage.artifactId, sha256: storage.sha256, x, y,\n width: Math.min(NATIVE_CAPTURE_SDR_LIMITS.tileWidth, plane.pixelWidth - x),\n height: Math.min(NATIVE_CAPTURE_SDR_LIMITS.tileHeight, plane.pixelHeight - y) });\n {\n const tile = await readTile(request, abort.signal);\n checkActive();\n // Diagnostics are aggregated only after the whole tile renders.\n renderTileInto(tile, request, plane, plan.profile, tone, scale, maximum, codes!, diagnostics);\n count++;\n onProgress?.({ completedTileCount: count, totalTileCount: plan.tileCount });\n }\n // Do not keep tile or reference arrays past the next tile read or host yield.\n await yieldControl(abort.signal);\n checkActive();\n }\n }\n output = { plan, integerSamples: codes!, diagnostics, outputEncoding: sdrOutputEncoding(maximum),\n displayAdaptation: { kind: \"external-platform\", applied: false, displayCapabilities: \"unknown\" } };\n state = \"completed\";\n } catch (error) {\n release();\n if (!abort.signal.aborted) { state = \"failed\"; abort.abort(); }\n throw error;\n }\n },\n cancel(): void {\n if (state === \"ready\" || state === \"running\" || state === \"completed\") { state = \"cancelled\"; abort.abort(); release(); }\n },\n dispose(): void { if (state !== \"transferred\") { state = \"disposed\"; abort.abort(); release(); } },\n takeOutput(): NativeCaptureSdrOutput {\n if (state !== \"completed\" || !output) throw new InvalidConfigurationError(\"Native SDR output is unavailable.\");\n const result = output; output = null; codes = null; state = \"transferred\";\n return result;\n }\n };\n}\n"]}
@@ -36,7 +36,7 @@ export function parsePrintContrastReferenceInput(value) {
36
36
  eccentricityDegrees: number("eccentricityDegrees", false), modulationMichelson };
37
37
  }
38
38
  // Independently written scalar evaluation of the licensed numerical model.
39
- // Using log1p/expm1 keeps the small hyperbolic saturation term accurate.
39
+ // Using log1p/expm1 keeps the hyperbolic terms accurate where the small quantity k_s4/Y (about 5.6e-10 at 1000 cd/m²) would cancel.
40
40
  function luminanceDependency(p, luminance) {
41
41
  if (p.length === 2)
42
42
  return p[1] * luminance ** p[0];
@@ -1 +1 @@
1
- {"version":3,"file":"print-contrast-reference.js","sourceRoot":"","sources":["../../src/output/print-contrast-reference.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n// castleCSF numerical model material: Copyright (c) 2023 Graphics and Displays\n// group - University of Cambridge. MIT terms retained in NOTICE (the full\n// licence text). Original bounded TypeScript adapter.\n\n/** Contrast detection threshold for a static, neutral (D65 white) Gabor patch, from the castleCSF\n * contrast sensitivity function (CSF) model. A Gabor patch is a sine grating under a Gaussian envelope.\n * It does not say whether detail in a photograph will be visible.\n * @see docs/PRINT_CONTRAST_REFERENCE.md for exact source, applicability and review gates.\n */\nimport { estimatedResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { freezeOwnedData } from \"../core/owned-data.js\";\nimport { requireAllowlistedRecord, requirePublicOpaqueId } from \"../core/record-validation.js\";\nimport { ENGINE_API_VERSION } from \"../core/version.js\";\n\nexport const PRINT_CONTRAST_REFERENCE_MODEL_VERSION = \"0.1.0\" as const;\n/** Identifies the exact MIT-licensed model source. It is not a local calibration. */\nexport const PRINT_CONTRAST_REFERENCE_UPSTREAM_REVISION = \"f4b0b722af83001d7af979281e06ca642d36e4e8\" as const;\n\n/** The narrow reference stimulus. It is separate from any measured photographic region of interest (ROI).\n * Positive values outside the model's range return \"unsupported\" instead of being extrapolated.\n */\nexport interface PrintContrastReferenceInput {\n referenceId: string;\n stimulus: \"static-neutral-d65-gabor\";\n observer: \"published-binocular-natural-pupil-reference\";\n /** Mean luminance of the uniform D65 background and grating. Not the average of a photographic ROI. */\n meanLuminanceCdPerSquareMeter: number;\n spatialFrequencyCyclesPerDegree: number;\n gaussianEnvelopeSigmaDegrees: number;\n temporalFrequencyHz: number;\n eccentricityDegrees: number;\n /** Fractional amplitude of the neutral grating before the Gaussian envelope. Not taken from ROI minimum and maximum values. */\n modulationMichelson: number;\n}\nexport interface PrintContrastReference {\n referenceId: string;\n engineApiVersion: string;\n modelVersion: typeof PRINT_CONTRAST_REFERENCE_MODEL_VERSION;\n upstreamRevision: typeof PRINT_CONTRAST_REFERENCE_UPSTREAM_REVISION;\n input: PrintContrastReferenceInput;\n status: \"model-estimate\" | \"unsupported\";\n blockers: string[];\n estimate: { contrastSensitivity: number; thresholdModulationMichelson: number; suppliedModulationToModelThresholdRatio: number } | null;\n observerApplicability: \"population-reference-unverified-for-individual\";\n naturalImageVisibility: \"unassessed\";\n printStimulusMatch: \"unassessed\";\n uncertainty: \"not-quantified\";\n overallPrintVerdict: \"not-offered\";\n}\n\n/** Strictly parses and copies the explicit reference conditions. No defaults are filled in to imply an observer. */\nexport function parsePrintContrastReferenceInput(value: unknown): PrintContrastReferenceInput {\n const r = requireAllowlistedRecord(value, [\"referenceId\", \"stimulus\", \"observer\", \"meanLuminanceCdPerSquareMeter\", \"spatialFrequencyCyclesPerDegree\",\n \"gaussianEnvelopeSigmaDegrees\", \"temporalFrequencyHz\", \"eccentricityDegrees\", \"modulationMichelson\"], \"Invalid Print contrast reference fields.\");\n const number = (key: string, positive: boolean): number => {\n const v = r[key];\n if (typeof v !== \"number\" || !Number.isFinite(v) || (positive ? v <= 0 : v < 0)) throw new InvalidConfigurationError(\"Print contrast reference quantities must be finite and in their numeric domain.\");\n return v === 0 ? 0 : v;\n };\n if (r.stimulus !== \"static-neutral-d65-gabor\" || r.observer !== \"published-binocular-natural-pupil-reference\") throw new InvalidConfigurationError(\"Unknown Print reference stimulus/observer.\");\n const modulationMichelson = number(\"modulationMichelson\", false);\n if (modulationMichelson > 1) throw new InvalidConfigurationError(\"Reference Michelson modulation must be at most one.\");\n return { referenceId: requirePublicOpaqueId(r.referenceId, \"Invalid public Print contrast reference identity.\"), stimulus: r.stimulus, observer: r.observer,\n meanLuminanceCdPerSquareMeter: number(\"meanLuminanceCdPerSquareMeter\", true), spatialFrequencyCyclesPerDegree: number(\"spatialFrequencyCyclesPerDegree\", true),\n gaussianEnvelopeSigmaDegrees: number(\"gaussianEnvelopeSigmaDegrees\", true), temporalFrequencyHz: number(\"temporalFrequencyHz\", false),\n eccentricityDegrees: number(\"eccentricityDegrees\", false), modulationMichelson };\n}\n\n// Independently written scalar evaluation of the licensed numerical model.\n// Using log1p/expm1 keeps the small hyperbolic saturation term accurate.\nfunction luminanceDependency(p: readonly number[], luminance: number): number {\n if (p.length === 2) return p[1]! * luminance ** p[0]!;\n const first = p[0]! * Math.exp(-p[2]! * Math.log1p(p[1]! / luminance));\n return p.length === 3 ? first : first * -Math.expm1(-p[4]! * Math.log1p(p[3]! / luminance));\n}\nfunction component(frequency: number, area: number, peak: number, peakFrequency: number, bandwidth: number, lowFrequencyFloor: number, a0: number, f0: number): number {\n const logDifference = Math.log10(frequency) - Math.log10(peakFrequency);\n const parabola = 10 ** (-logDifference * logDifference / 2 ** bandwidth);\n const shape = frequency < peakFrequency ? Math.max(parabola, lowFrequencyFloor) : parabola;\n const criticalArea = a0 / (1 + (frequency / f0) ** 2);\n return peak * shape * Math.sqrt(criticalArea / (1 + criticalArea / area)) * frequency;\n}\nfunction staticNeutralSensitivity(luminance: number, frequency: number): number {\n const area = Math.PI * 1.5 ** 2;\n const sustained = component(frequency, area, luminanceDependency([56.4947, 7.54726, .144532, 5.58341e-7, 9.66862e9], luminance),\n luminanceDependency([1.78119, 91.5718, .256682], luminance), .000213047, 1 - .100207, 157.103, .702338);\n const transient = component(frequency, area, luminanceDependency([.193434, 2748.09], luminance), .000316696, 2.6761, 1 - .000241177, 3.81611, 3.01389);\n const omegaPeak = Math.log10(luminance) * 2.41482 + 4.7036;\n const achromatic = sustained + transient * Math.exp(-((omegaPeak ** .1898) ** 2) / .0844836);\n const redGreen = component(frequency, area, luminanceDependency([681.434, 38.0038, .480386], luminance), .0178364, 2.42104, 1, 2816.44, .0711058);\n const yellowViolet = component(frequency, area, luminanceDependency([166.683, 62.8974, .41193], luminance), .00425753, 2.68197, 1, 2.82789e7, .000635093);\n // Modulate each cone channel of the D65 LMS background [L,M,S] by the same fraction.\n // All three mechanism responses are kept, including nonzero colour-opponent parts.\n const redGreenProjection = Math.abs(.6991 - 2.3112 * .3009);\n const yellowVioletProjection = Math.abs(-.6991 - .3009 + 50.9875 * .0198);\n return Math.hypot(achromatic, redGreenProjection * redGreen, yellowVioletProjection * yellowViolet);\n}\n\n/** Estimates the detection threshold from the published model, within its limited range. It does not claim that detail is invisible to any particular person. */\nexport function calculatePrintContrastReference(value: PrintContrastReferenceInput): CalculationResult<PrintContrastReference> {\n const input = parsePrintContrastReferenceInput(value), l = input.meanLuminanceCdPerSquareMeter, f = input.spatialFrequencyCyclesPerDegree;\n const result: PrintContrastReference = { referenceId: input.referenceId, engineApiVersion: ENGINE_API_VERSION, modelVersion: PRINT_CONTRAST_REFERENCE_MODEL_VERSION,\n upstreamRevision: PRINT_CONTRAST_REFERENCE_UPSTREAM_REVISION, input, status: \"unsupported\", blockers: [], estimate: null,\n observerApplicability: \"population-reference-unverified-for-individual\", naturalImageVisibility: \"unassessed\", printStimulusMatch: \"unassessed\",\n uncertainty: \"not-quantified\", overallPrintVerdict: \"not-offered\" };\n if (l < 1 || l > 1000 || f < .25 || f > 16 || input.gaussianEnvelopeSigmaDegrees !== 1.5 || input.temporalFrequencyHz !== 0 || input.eccentricityDegrees !== 0) result.blockers.push(\"outside-static-neutral-reference-domain\");\n else {\n const sensitivity = staticNeutralSensitivity(l, f), threshold = 1 / sensitivity, ratio = input.modulationMichelson * sensitivity;\n if (![sensitivity, threshold, ratio].every(Number.isFinite) || sensitivity <= 0 || threshold <= 0) result.blockers.push(\"contrast-reference-numeric-range-unsupported\");\n else { result.status = \"model-estimate\"; result.estimate = { contrastSensitivity: sensitivity, thresholdModulationMichelson: threshold, suppliedModulationToModelThresholdRatio: ratio }; }\n }\n return freezeOwnedData(estimatedResult(result, \"castlecsf-static-neutral-d65-reference\", PRINT_CONTRAST_REFERENCE_MODEL_VERSION,\n [\"Static foveal D65 equal-fraction LMS Gabor; Gaussian envelope sigma is exactly 1.5 degrees.\",\n \"Published population model reference; personal eyesight, natural-image masking and actual print-stimulus matching remain unassessed.\",\n \"The numerical envelope is restricted; published model-fit errors do not become an individual confidence interval or a print verdict.\"], {\n validRanges: [\n { parameterPath: \"meanLuminanceCdPerSquareMeter\", unit: \"cd/m^2\", minimum: 1, maximum: 1000, note: \"Restricted numerical reference envelope; no individual calibration.\" },\n { parameterPath: \"spatialFrequencyCyclesPerDegree\", unit: \"cycles/degree\", minimum: .25, maximum: 16 },\n { parameterPath: \"gaussianEnvelopeSigmaDegrees\", unit: \"degree\", minimum: 1.5, maximum: 1.5 },\n { parameterPath: \"temporalFrequencyHz\", unit: \"Hz\", minimum: 0, maximum: 0 },\n { parameterPath: \"eccentricityDegrees\", unit: \"degree\", minimum: 0, maximum: 0 }\n ], notes: [\"Uncertainty is not quantified; numerical conformance is distinct from stimulus/observer qualification.\"]\n }));\n}\n"]}
1
+ {"version":3,"file":"print-contrast-reference.js","sourceRoot":"","sources":["../../src/output/print-contrast-reference.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n// castleCSF numerical model material: Copyright (c) 2023 Graphics and Displays\n// group - University of Cambridge. MIT terms retained in NOTICE (the full\n// licence text). Original bounded TypeScript adapter.\n\n/** Contrast detection threshold for a static, neutral (D65 white) Gabor patch, from the castleCSF\n * contrast sensitivity function (CSF) model. A Gabor patch is a sine grating under a Gaussian envelope.\n * It does not say whether detail in a photograph will be visible.\n * @see docs/PRINT_CONTRAST_REFERENCE.md for exact source, applicability and review gates.\n */\nimport { estimatedResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { freezeOwnedData } from \"../core/owned-data.js\";\nimport { requireAllowlistedRecord, requirePublicOpaqueId } from \"../core/record-validation.js\";\nimport { ENGINE_API_VERSION } from \"../core/version.js\";\n\nexport const PRINT_CONTRAST_REFERENCE_MODEL_VERSION = \"0.1.0\" as const;\n/** Identifies the exact MIT-licensed model source. It is not a local calibration. */\nexport const PRINT_CONTRAST_REFERENCE_UPSTREAM_REVISION = \"f4b0b722af83001d7af979281e06ca642d36e4e8\" as const;\n\n/** The narrow reference stimulus. It is separate from any measured photographic region of interest (ROI).\n * Positive values outside the model's range return \"unsupported\" instead of being extrapolated.\n */\nexport interface PrintContrastReferenceInput {\n referenceId: string;\n stimulus: \"static-neutral-d65-gabor\";\n observer: \"published-binocular-natural-pupil-reference\";\n /** Mean luminance of the uniform D65 background and grating. Not the average of a photographic ROI. */\n meanLuminanceCdPerSquareMeter: number;\n spatialFrequencyCyclesPerDegree: number;\n gaussianEnvelopeSigmaDegrees: number;\n temporalFrequencyHz: number;\n eccentricityDegrees: number;\n /** Fractional amplitude of the neutral grating before the Gaussian envelope. Not taken from ROI minimum and maximum values. */\n modulationMichelson: number;\n}\nexport interface PrintContrastReference {\n referenceId: string;\n engineApiVersion: string;\n modelVersion: typeof PRINT_CONTRAST_REFERENCE_MODEL_VERSION;\n upstreamRevision: typeof PRINT_CONTRAST_REFERENCE_UPSTREAM_REVISION;\n input: PrintContrastReferenceInput;\n status: \"model-estimate\" | \"unsupported\";\n blockers: string[];\n estimate: { contrastSensitivity: number; thresholdModulationMichelson: number; suppliedModulationToModelThresholdRatio: number } | null;\n observerApplicability: \"population-reference-unverified-for-individual\";\n naturalImageVisibility: \"unassessed\";\n printStimulusMatch: \"unassessed\";\n uncertainty: \"not-quantified\";\n overallPrintVerdict: \"not-offered\";\n}\n\n/** Strictly parses and copies the explicit reference conditions. No defaults are filled in to imply an observer. */\nexport function parsePrintContrastReferenceInput(value: unknown): PrintContrastReferenceInput {\n const r = requireAllowlistedRecord(value, [\"referenceId\", \"stimulus\", \"observer\", \"meanLuminanceCdPerSquareMeter\", \"spatialFrequencyCyclesPerDegree\",\n \"gaussianEnvelopeSigmaDegrees\", \"temporalFrequencyHz\", \"eccentricityDegrees\", \"modulationMichelson\"], \"Invalid Print contrast reference fields.\");\n const number = (key: string, positive: boolean): number => {\n const v = r[key];\n if (typeof v !== \"number\" || !Number.isFinite(v) || (positive ? v <= 0 : v < 0)) throw new InvalidConfigurationError(\"Print contrast reference quantities must be finite and in their numeric domain.\");\n return v === 0 ? 0 : v;\n };\n if (r.stimulus !== \"static-neutral-d65-gabor\" || r.observer !== \"published-binocular-natural-pupil-reference\") throw new InvalidConfigurationError(\"Unknown Print reference stimulus/observer.\");\n const modulationMichelson = number(\"modulationMichelson\", false);\n if (modulationMichelson > 1) throw new InvalidConfigurationError(\"Reference Michelson modulation must be at most one.\");\n return { referenceId: requirePublicOpaqueId(r.referenceId, \"Invalid public Print contrast reference identity.\"), stimulus: r.stimulus, observer: r.observer,\n meanLuminanceCdPerSquareMeter: number(\"meanLuminanceCdPerSquareMeter\", true), spatialFrequencyCyclesPerDegree: number(\"spatialFrequencyCyclesPerDegree\", true),\n gaussianEnvelopeSigmaDegrees: number(\"gaussianEnvelopeSigmaDegrees\", true), temporalFrequencyHz: number(\"temporalFrequencyHz\", false),\n eccentricityDegrees: number(\"eccentricityDegrees\", false), modulationMichelson };\n}\n\n// Independently written scalar evaluation of the licensed numerical model.\n// Using log1p/expm1 keeps the hyperbolic terms accurate where the small quantity k_s4/Y (about 5.6e-10 at 1000 cd/m²) would cancel.\nfunction luminanceDependency(p: readonly number[], luminance: number): number {\n if (p.length === 2) return p[1]! * luminance ** p[0]!;\n const first = p[0]! * Math.exp(-p[2]! * Math.log1p(p[1]! / luminance));\n return p.length === 3 ? first : first * -Math.expm1(-p[4]! * Math.log1p(p[3]! / luminance));\n}\nfunction component(frequency: number, area: number, peak: number, peakFrequency: number, bandwidth: number, lowFrequencyFloor: number, a0: number, f0: number): number {\n const logDifference = Math.log10(frequency) - Math.log10(peakFrequency);\n const parabola = 10 ** (-logDifference * logDifference / 2 ** bandwidth);\n const shape = frequency < peakFrequency ? Math.max(parabola, lowFrequencyFloor) : parabola;\n const criticalArea = a0 / (1 + (frequency / f0) ** 2);\n return peak * shape * Math.sqrt(criticalArea / (1 + criticalArea / area)) * frequency;\n}\nfunction staticNeutralSensitivity(luminance: number, frequency: number): number {\n const area = Math.PI * 1.5 ** 2;\n const sustained = component(frequency, area, luminanceDependency([56.4947, 7.54726, .144532, 5.58341e-7, 9.66862e9], luminance),\n luminanceDependency([1.78119, 91.5718, .256682], luminance), .000213047, 1 - .100207, 157.103, .702338);\n const transient = component(frequency, area, luminanceDependency([.193434, 2748.09], luminance), .000316696, 2.6761, 1 - .000241177, 3.81611, 3.01389);\n const omegaPeak = Math.log10(luminance) * 2.41482 + 4.7036;\n const achromatic = sustained + transient * Math.exp(-((omegaPeak ** .1898) ** 2) / .0844836);\n const redGreen = component(frequency, area, luminanceDependency([681.434, 38.0038, .480386], luminance), .0178364, 2.42104, 1, 2816.44, .0711058);\n const yellowViolet = component(frequency, area, luminanceDependency([166.683, 62.8974, .41193], luminance), .00425753, 2.68197, 1, 2.82789e7, .000635093);\n // Modulate each cone channel of the D65 LMS background [L,M,S] by the same fraction.\n // All three mechanism responses are kept, including nonzero colour-opponent parts.\n const redGreenProjection = Math.abs(.6991 - 2.3112 * .3009);\n const yellowVioletProjection = Math.abs(-.6991 - .3009 + 50.9875 * .0198);\n return Math.hypot(achromatic, redGreenProjection * redGreen, yellowVioletProjection * yellowViolet);\n}\n\n/** Estimates the detection threshold from the published model, within its limited range. It does not claim that detail is invisible to any particular person. */\nexport function calculatePrintContrastReference(value: PrintContrastReferenceInput): CalculationResult<PrintContrastReference> {\n const input = parsePrintContrastReferenceInput(value), l = input.meanLuminanceCdPerSquareMeter, f = input.spatialFrequencyCyclesPerDegree;\n const result: PrintContrastReference = { referenceId: input.referenceId, engineApiVersion: ENGINE_API_VERSION, modelVersion: PRINT_CONTRAST_REFERENCE_MODEL_VERSION,\n upstreamRevision: PRINT_CONTRAST_REFERENCE_UPSTREAM_REVISION, input, status: \"unsupported\", blockers: [], estimate: null,\n observerApplicability: \"population-reference-unverified-for-individual\", naturalImageVisibility: \"unassessed\", printStimulusMatch: \"unassessed\",\n uncertainty: \"not-quantified\", overallPrintVerdict: \"not-offered\" };\n if (l < 1 || l > 1000 || f < .25 || f > 16 || input.gaussianEnvelopeSigmaDegrees !== 1.5 || input.temporalFrequencyHz !== 0 || input.eccentricityDegrees !== 0) result.blockers.push(\"outside-static-neutral-reference-domain\");\n else {\n const sensitivity = staticNeutralSensitivity(l, f), threshold = 1 / sensitivity, ratio = input.modulationMichelson * sensitivity;\n if (![sensitivity, threshold, ratio].every(Number.isFinite) || sensitivity <= 0 || threshold <= 0) result.blockers.push(\"contrast-reference-numeric-range-unsupported\");\n else { result.status = \"model-estimate\"; result.estimate = { contrastSensitivity: sensitivity, thresholdModulationMichelson: threshold, suppliedModulationToModelThresholdRatio: ratio }; }\n }\n return freezeOwnedData(estimatedResult(result, \"castlecsf-static-neutral-d65-reference\", PRINT_CONTRAST_REFERENCE_MODEL_VERSION,\n [\"Static foveal D65 equal-fraction LMS Gabor; Gaussian envelope sigma is exactly 1.5 degrees.\",\n \"Published population model reference; personal eyesight, natural-image masking and actual print-stimulus matching remain unassessed.\",\n \"The numerical envelope is restricted; published model-fit errors do not become an individual confidence interval or a print verdict.\"], {\n validRanges: [\n { parameterPath: \"meanLuminanceCdPerSquareMeter\", unit: \"cd/m^2\", minimum: 1, maximum: 1000, note: \"Restricted numerical reference envelope; no individual calibration.\" },\n { parameterPath: \"spatialFrequencyCyclesPerDegree\", unit: \"cycles/degree\", minimum: .25, maximum: 16 },\n { parameterPath: \"gaussianEnvelopeSigmaDegrees\", unit: \"degree\", minimum: 1.5, maximum: 1.5 },\n { parameterPath: \"temporalFrequencyHz\", unit: \"Hz\", minimum: 0, maximum: 0 },\n { parameterPath: \"eccentricityDegrees\", unit: \"degree\", minimum: 0, maximum: 0 }\n ], notes: [\"Uncertainty is not quantified; numerical conformance is distinct from stimulus/observer qualification.\"]\n }));\n}\n"]}
@@ -0,0 +1,110 @@
1
+ /** Print size limit that counts the capture's blur and noise as well as its pixels (#574).
2
+ * A pure function: it makes no image and is not used by the capture path.
3
+ * @see docs/PRINT_PLANNING.md, "Print size limit with blur and noise", for the equations, sources and limits.
4
+ */
5
+ import { type CalculationResult } from "../core/calculation-result.js";
6
+ import type { RasterDimensions } from "../sensor/sensor-geometry.js";
7
+ import type { PrintLength } from "./print-plan.js";
8
+ import type { SimulateCaptureSensorReport } from "../sensor/sensor-characteristics.js";
9
+ /** The capture's blur at the sensor plane, as blur-spot diameters in millimetres.
10
+ * `components` combines the parts by root-sum-square (an approximation, see the docs); `measured-total` is one measured diameter.
11
+ * A zero component is allowed and means that part is left out. Engine helpers give the parts: `calculateDefocusCircle` (defocus),
12
+ * `calculateAiryDisk` (diffraction, micrometres), `calculateProjectedMotionBlur` (motion length).
13
+ */
14
+ export type PrintBlurInput = {
15
+ kind: "components";
16
+ lensMm?: number;
17
+ defocusMm?: number;
18
+ diffractionMm?: number;
19
+ motionMm?: number;
20
+ } | {
21
+ kind: "measured-total";
22
+ diameterMm: number;
23
+ };
24
+ /** The noise of one pixel of the printed raster, for the tone the caller cares about (for example mid-gray).
25
+ * `shot-and-read-noise` uses the engine's noise model (`calculateSignalToNoise`: Poisson shot noise plus RMS read noise);
26
+ * `pixel-snr` is a per-pixel signal-to-noise ratio the caller already has. `capture-noise-figure` (2.20.0) takes a capture's own
27
+ * mid-gray noise figure (#535: `result.sensor.midGrey` of a `simulateCapture` with `fast: false`), whose SNR S/σ_total counts photon
28
+ * shot, read, dark-current, quantization and any fixed-pattern noise; it is one sensor site's, so it applies as the pixel SNR when one
29
+ * printed pixel is one capture pixel. A clipped figure (the patch at white) is refused.
30
+ */
31
+ export type PrintNoiseInput = {
32
+ kind: "shot-and-read-noise";
33
+ signalElectrons: number;
34
+ readNoiseElectrons: number;
35
+ } | {
36
+ kind: "pixel-snr";
37
+ snrLinear: number;
38
+ } | {
39
+ kind: "capture-noise-figure";
40
+ midGrey: NonNullable<SimulateCaptureSensorReport["midGrey"]>;
41
+ };
42
+ /** What "looks sharp and clean" means: the smallest angle the viewer resolves, and the lowest signal-to-noise ratio the viewer
43
+ * accepts after the eye averages over that angle. Both are the caller's choice; one arcminute is normal (20/20) acuity.
44
+ */
45
+ export interface PrintSharpCleanCriterion {
46
+ kind: "visual-resolution";
47
+ arcminutes: number;
48
+ minimumVisibleSnr: number;
49
+ }
50
+ /** Input. The raster is the retained image that is printed (after any crop), with square pixels; `pixelPitchMm` is the
51
+ * sensor length one of those pixels covers. The print keeps the raster's aspect ratio. The viewing distance is fixed.
52
+ */
53
+ export interface PrintSizeLimitWithBlurAndNoiseInput {
54
+ raster: RasterDimensions;
55
+ pixelPitchMm: number;
56
+ viewingDistance: PrintLength;
57
+ criterion: PrintSharpCleanCriterion;
58
+ blur: PrintBlurInput;
59
+ noise: PrintNoiseInput;
60
+ }
61
+ /** A print size in millimetres at the raster's aspect ratio. */
62
+ export interface PrintImageMm {
63
+ width: number;
64
+ height: number;
65
+ }
66
+ /** Result. Each factor's own limit is the largest print it allows on its own; null means that factor never limits.
67
+ * `limitingFactor` names the smallest; on a tie the order is pixels, blur, noise.
68
+ */
69
+ export interface PrintSizeLimitWithBlurAndNoise {
70
+ engineApiVersion: string;
71
+ status: "finite" | "no-positive-size";
72
+ limitingFactor: "pixels" | "blur" | "noise";
73
+ maximumImageMm: PrintImageMm;
74
+ limits: {
75
+ pixels: PrintImageMm;
76
+ blur: PrintImageMm | null;
77
+ noise: PrintImageMm | null;
78
+ };
79
+ viewingDistanceMm: number;
80
+ /** The criterion angle as a length on the print at the viewing distance: 2 d tan(θ/2). */
81
+ resolvedSpotMm: number;
82
+ blur: {
83
+ diameterMm: number;
84
+ diameterPixels: number;
85
+ largestComponent: "lens" | "defocus" | "diffraction" | "motion" | "measured-total" | null;
86
+ };
87
+ noise: {
88
+ pixelSnr: number;
89
+ visibleSnrAtMaximum: number;
90
+ };
91
+ }
92
+ /** Calculates the largest print, at a fixed viewing distance, at which the capture still looks sharp and clean, and names
93
+ * the factor that limits it: pixels, blur or noise. An approximation throughout; the equations are exact under its model.
94
+ *
95
+ * With θ the criterion angle, d the viewing distance, s = 2 d tan(θ/2) the length θ covers on the print, N_w the raster
96
+ * width in pixels, b the blur diameter in pixels and SNR_px the per-pixel SNR (print width W, height in proportion):
97
+ * - pixels: a print pixel (W / N_w) may cover at most s, so W ≤ N_w·s (as `calculatePrintPlan` with
98
+ * `angular-pixel-pitch`, before its whole-pixel rounding);
99
+ * - blur: the blur on the print (b·W / N_w) may cover at most s, so W ≤ N_w·s / b: the criterion of an acceptable circle
100
+ * of confusion with the eye's resolution as the limit;
101
+ * - noise: the eye averages the (W / N_w) wide pixels inside s, so the visible SNR is SNR_px·(N_w·s / W) while a pixel is
102
+ * smaller than s (the mean of n independent values has 1/√n of their standard deviation); W ≤ N_w·s·SNR_px / SNR_min,
103
+ * and noise never limits when SNR_px ≥ SNR_min.
104
+ * One arcminute is normal (20/20) acuity under optimal conditions (Z. Alomar, K. D. Bojikian, "Visual Acuity Testing in
105
+ * Adults", EyeWiki, American Academy of Ophthalmology, https://eyewiki.aao.org/Visual_Acuity_Testing_in_Adults, last edited
106
+ * June 16, 2026); the SNR threshold has no default and is the caller's. Blur parts add in root-sum-square, which is exact
107
+ * for Gaussian spots (variances of independent displacements add: C. M. Grinstead, J. L. Snell, "Introduction to
108
+ * Probability", 2nd rev. ed., AMS 1997, sec. 6.2, free under the GNU FDL) and an approximation for discs and Airy patterns.
109
+ */
110
+ export declare function calculatePrintSizeLimitWithBlurAndNoise(value: PrintSizeLimitWithBlurAndNoiseInput): CalculationResult<PrintSizeLimitWithBlurAndNoise>;
@@ -0,0 +1,138 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /** Print size limit that counts the capture's blur and noise as well as its pixels (#574).
3
+ * A pure function: it makes no image and is not used by the capture path.
4
+ * @see docs/PRINT_PLANNING.md, "Print size limit with blur and noise", for the equations, sources and limits.
5
+ */
6
+ import { approximationResult } from "../core/calculation-result.js";
7
+ import { InvalidConfigurationError } from "../core/configuration-error.js";
8
+ import { requireAllowlistedRecord, requireInputKeys, requireInputNonNegativeFinite, requireInputPositiveFinite } from "../core/record-validation.js";
9
+ import { requirePositiveInteger } from "../core/validation.js";
10
+ import { ENGINE_API_VERSION } from "../core/version.js";
11
+ import { calculateSignalToNoise } from "../sensor/signal-noise.js";
12
+ const UNIT_MM = { mm: 1, cm: 10, inches: 25.4, m: 1000 };
13
+ const RAD_PER_ARCMIN = Math.PI / 10800;
14
+ const BLUR_PARTS = ["lens", "defocus", "diffraction", "motion"];
15
+ function record(value, keys) {
16
+ return requireAllowlistedRecord(value, keys, "Invalid or unknown print size limit fields.");
17
+ }
18
+ function parseBlur(value) {
19
+ const r = record(value, ["kind", "lensMm", "defocusMm", "diffractionMm", "motionMm", "diameterMm"]);
20
+ if (r.kind === "measured-total") {
21
+ record(value, ["kind", "diameterMm"]);
22
+ const diameterMm = requireInputNonNegativeFinite(r.diameterMm, "blur.diameterMm");
23
+ return { diameterMm, largestComponent: diameterMm > 0 ? "measured-total" : null };
24
+ }
25
+ if (r.kind !== "components")
26
+ throw new InvalidConfigurationError("Invalid print blur kind.");
27
+ let sumSquares = 0, largest = 0, largestComponent = null;
28
+ for (const part of BLUR_PARTS) {
29
+ const raw = r[`${part}Mm`];
30
+ if (raw === undefined)
31
+ continue;
32
+ const d = requireInputNonNegativeFinite(raw, `blur.${part}Mm`);
33
+ sumSquares += d * d;
34
+ if (d > largest) {
35
+ largest = d;
36
+ largestComponent = part;
37
+ }
38
+ }
39
+ return { diameterMm: Math.sqrt(sumSquares), largestComponent };
40
+ }
41
+ /** The one place the noise input becomes a per-pixel SNR, including a capture's own noise figure (#535). */
42
+ function resolvePixelSnr(value) {
43
+ const r = record(value, ["kind", "signalElectrons", "readNoiseElectrons", "snrLinear", "midGrey"]);
44
+ switch (r.kind) {
45
+ case "shot-and-read-noise": {
46
+ record(value, ["kind", "signalElectrons", "readNoiseElectrons"]);
47
+ return calculateSignalToNoise({ signalElectrons: r.signalElectrons, readNoiseElectrons: r.readNoiseElectrons }).value.snrLinear;
48
+ }
49
+ case "pixel-snr":
50
+ record(value, ["kind", "snrLinear"]);
51
+ return requireInputNonNegativeFinite(r.snrLinear, "noise.snrLinear");
52
+ case "capture-noise-figure": {
53
+ record(value, ["kind", "midGrey"]);
54
+ const figure = r.midGrey;
55
+ if (typeof figure !== "object" || figure === null || typeof figure.noiseElectrons !== "object" || figure.noiseElectrons === null)
56
+ throw new InvalidConfigurationError("noise.midGrey must be a capture's result.sensor.midGrey (simulateCapture with fast: false).");
57
+ if (figure.clipped !== false)
58
+ throw new InvalidConfigurationError("noise.midGrey is clipped (or not a capture's figure): the patch reaches white, where the figure no longer describes the image.");
59
+ return requireInputNonNegativeFinite(figure.signalElectrons, "noise.midGrey.signalElectrons") / requireInputPositiveFinite(figure.noiseElectrons.total, "noise.midGrey.noiseElectrons.total");
60
+ }
61
+ default: throw new InvalidConfigurationError("Invalid print noise kind.");
62
+ }
63
+ }
64
+ /** Calculates the largest print, at a fixed viewing distance, at which the capture still looks sharp and clean, and names
65
+ * the factor that limits it: pixels, blur or noise. An approximation throughout; the equations are exact under its model.
66
+ *
67
+ * With θ the criterion angle, d the viewing distance, s = 2 d tan(θ/2) the length θ covers on the print, N_w the raster
68
+ * width in pixels, b the blur diameter in pixels and SNR_px the per-pixel SNR (print width W, height in proportion):
69
+ * - pixels: a print pixel (W / N_w) may cover at most s, so W ≤ N_w·s (as `calculatePrintPlan` with
70
+ * `angular-pixel-pitch`, before its whole-pixel rounding);
71
+ * - blur: the blur on the print (b·W / N_w) may cover at most s, so W ≤ N_w·s / b: the criterion of an acceptable circle
72
+ * of confusion with the eye's resolution as the limit;
73
+ * - noise: the eye averages the (W / N_w) wide pixels inside s, so the visible SNR is SNR_px·(N_w·s / W) while a pixel is
74
+ * smaller than s (the mean of n independent values has 1/√n of their standard deviation); W ≤ N_w·s·SNR_px / SNR_min,
75
+ * and noise never limits when SNR_px ≥ SNR_min.
76
+ * One arcminute is normal (20/20) acuity under optimal conditions (Z. Alomar, K. D. Bojikian, "Visual Acuity Testing in
77
+ * Adults", EyeWiki, American Academy of Ophthalmology, https://eyewiki.aao.org/Visual_Acuity_Testing_in_Adults, last edited
78
+ * June 16, 2026); the SNR threshold has no default and is the caller's. Blur parts add in root-sum-square, which is exact
79
+ * for Gaussian spots (variances of independent displacements add: C. M. Grinstead, J. L. Snell, "Introduction to
80
+ * Probability", 2nd rev. ed., AMS 1997, sec. 6.2, free under the GNU FDL) and an approximation for discs and Airy patterns.
81
+ */
82
+ export function calculatePrintSizeLimitWithBlurAndNoise(value) {
83
+ const keys = ["raster", "pixelPitchMm", "viewingDistance", "criterion", "blur", "noise"];
84
+ requireInputKeys(value, "calculatePrintSizeLimitWithBlurAndNoise", keys);
85
+ const r = record(value, keys);
86
+ const raster = record(r.raster, ["pixelWidth", "pixelHeight"]);
87
+ const pixelWidth = requireInputPositiveFinite(raster.pixelWidth, "raster.pixelWidth"), pixelHeight = requireInputPositiveFinite(raster.pixelHeight, "raster.pixelHeight");
88
+ requirePositiveInteger("raster.pixelWidth", pixelWidth);
89
+ requirePositiveInteger("raster.pixelHeight", pixelHeight);
90
+ const pixelPitchMm = requireInputPositiveFinite(r.pixelPitchMm, "pixelPitchMm");
91
+ const distance = record(r.viewingDistance, ["value", "unit"]);
92
+ if (typeof distance.unit !== "string" || !Object.hasOwn(UNIT_MM, distance.unit))
93
+ throw new InvalidConfigurationError("Invalid Print length unit.");
94
+ const viewingDistanceMm = requireInputPositiveFinite(distance.value, "viewingDistance.value") * UNIT_MM[distance.unit];
95
+ const criterion = record(r.criterion, ["kind", "arcminutes", "minimumVisibleSnr"]);
96
+ if (criterion.kind !== "visual-resolution")
97
+ throw new InvalidConfigurationError("Invalid print criterion kind.");
98
+ const arcminutes = requireInputPositiveFinite(criterion.arcminutes, "criterion.arcminutes");
99
+ if (arcminutes >= 10800)
100
+ throw new InvalidConfigurationError("criterion.arcminutes must lie in (0, 10800).");
101
+ const minimumVisibleSnr = requireInputPositiveFinite(criterion.minimumVisibleSnr, "criterion.minimumVisibleSnr");
102
+ const blur = parseBlur(r.blur);
103
+ const pixelSnr = resolvePixelSnr(r.noise);
104
+ const resolvedSpotMm = 2 * viewingDistanceMm * Math.tan((arcminutes * RAD_PER_ARCMIN) / 2);
105
+ const pixelLimitWidth = pixelWidth * resolvedSpotMm;
106
+ const blurPixels = blur.diameterMm / pixelPitchMm;
107
+ const blurLimitWidth = blurPixels > 0 ? pixelLimitWidth / blurPixels : null;
108
+ const noiseLimitWidth = pixelSnr < minimumVisibleSnr ? pixelLimitWidth * (pixelSnr / minimumVisibleSnr) : null;
109
+ let limitingFactor = "pixels", width = pixelLimitWidth;
110
+ if (blurLimitWidth !== null && blurLimitWidth < width) {
111
+ limitingFactor = "blur";
112
+ width = blurLimitWidth;
113
+ }
114
+ if (noiseLimitWidth !== null && noiseLimitWidth < width) {
115
+ limitingFactor = "noise";
116
+ width = noiseLimitWidth;
117
+ }
118
+ const size = (w) => ({ width: w, height: w * (pixelHeight / pixelWidth) });
119
+ const visibleSnrAtMaximum = width > 0 ? pixelSnr * Math.max(1, pixelLimitWidth / width) : 0;
120
+ const result = {
121
+ engineApiVersion: ENGINE_API_VERSION, status: width > 0 ? "finite" : "no-positive-size", limitingFactor,
122
+ maximumImageMm: size(width),
123
+ limits: { pixels: size(pixelLimitWidth), blur: blurLimitWidth === null ? null : size(blurLimitWidth), noise: noiseLimitWidth === null ? null : size(noiseLimitWidth) },
124
+ viewingDistanceMm, resolvedSpotMm,
125
+ blur: { diameterMm: blur.diameterMm, diameterPixels: blurPixels, largestComponent: blur.largestComponent },
126
+ noise: { pixelSnr, visibleSnrAtMaximum }
127
+ };
128
+ const numbers = [viewingDistanceMm, resolvedSpotMm, pixelLimitWidth, size(pixelLimitWidth).height, blurPixels, blurLimitWidth ?? 0, width, visibleSnrAtMaximum];
129
+ if (!numbers.every(Number.isFinite) || !(pixelLimitWidth > 0))
130
+ throw new InvalidConfigurationError("Print size limit lies outside the supported numeric range.");
131
+ return approximationResult(result, "print-size-limit-blur-noise", "1.0.0", [
132
+ "Flat print viewed at a fixed distance, at its center; one visual-resolution angle for pixels, blur and noise.",
133
+ "Blur parts combine in root-sum-square: exact for Gaussian spots, an approximation for discs and Airy patterns.",
134
+ "Pixel noise is white (independent pixels); demosaic, denoise and sharpening correlations are not modeled.",
135
+ "The eye averages noise over a square of the criterion angle; contrast sensitivity and print medium are not modeled."
136
+ ]);
137
+ }
138
+ //# sourceMappingURL=print-size-blur-noise.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"print-size-blur-noise.js","sourceRoot":"","sources":["../../src/output/print-size-blur-noise.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/** Print size limit that counts the capture's blur and noise as well as its pixels (#574).\n * A pure function: it makes no image and is not used by the capture path.\n * @see docs/PRINT_PLANNING.md, \"Print size limit with blur and noise\", for the equations, sources and limits.\n */\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { requireAllowlistedRecord, requireInputKeys, requireInputNonNegativeFinite, requireInputPositiveFinite } from \"../core/record-validation.js\";\nimport { requirePositiveInteger } from \"../core/validation.js\";\nimport { ENGINE_API_VERSION } from \"../core/version.js\";\nimport { calculateSignalToNoise } from \"../sensor/signal-noise.js\";\nimport type { RasterDimensions } from \"../sensor/sensor-geometry.js\";\nimport type { PrintLength } from \"./print-plan.js\";\nimport type { SimulateCaptureSensorReport } from \"../sensor/sensor-characteristics.js\";\n\n/** The capture's blur at the sensor plane, as blur-spot diameters in millimetres.\n * `components` combines the parts by root-sum-square (an approximation, see the docs); `measured-total` is one measured diameter.\n * A zero component is allowed and means that part is left out. Engine helpers give the parts: `calculateDefocusCircle` (defocus),\n * `calculateAiryDisk` (diffraction, micrometres), `calculateProjectedMotionBlur` (motion length).\n */\nexport type PrintBlurInput =\n | { kind: \"components\"; lensMm?: number; defocusMm?: number; diffractionMm?: number; motionMm?: number }\n | { kind: \"measured-total\"; diameterMm: number };\n/** The noise of one pixel of the printed raster, for the tone the caller cares about (for example mid-gray).\n * `shot-and-read-noise` uses the engine's noise model (`calculateSignalToNoise`: Poisson shot noise plus RMS read noise);\n * `pixel-snr` is a per-pixel signal-to-noise ratio the caller already has. `capture-noise-figure` (2.20.0) takes a capture's own\n * mid-gray noise figure (#535: `result.sensor.midGrey` of a `simulateCapture` with `fast: false`), whose SNR S/σ_total counts photon\n * shot, read, dark-current, quantization and any fixed-pattern noise; it is one sensor site's, so it applies as the pixel SNR when one\n * printed pixel is one capture pixel. A clipped figure (the patch at white) is refused.\n */\nexport type PrintNoiseInput =\n | { kind: \"shot-and-read-noise\"; signalElectrons: number; readNoiseElectrons: number }\n | { kind: \"pixel-snr\"; snrLinear: number }\n | { kind: \"capture-noise-figure\"; midGrey: NonNullable<SimulateCaptureSensorReport[\"midGrey\"]> };\n/** What \"looks sharp and clean\" means: the smallest angle the viewer resolves, and the lowest signal-to-noise ratio the viewer\n * accepts after the eye averages over that angle. Both are the caller's choice; one arcminute is normal (20/20) acuity.\n */\nexport interface PrintSharpCleanCriterion {\n kind: \"visual-resolution\";\n arcminutes: number;\n minimumVisibleSnr: number;\n}\n/** Input. The raster is the retained image that is printed (after any crop), with square pixels; `pixelPitchMm` is the\n * sensor length one of those pixels covers. The print keeps the raster's aspect ratio. The viewing distance is fixed.\n */\nexport interface PrintSizeLimitWithBlurAndNoiseInput {\n raster: RasterDimensions;\n pixelPitchMm: number;\n viewingDistance: PrintLength;\n criterion: PrintSharpCleanCriterion;\n blur: PrintBlurInput;\n noise: PrintNoiseInput;\n}\n/** A print size in millimetres at the raster's aspect ratio. */\nexport interface PrintImageMm { width: number; height: number }\n/** Result. Each factor's own limit is the largest print it allows on its own; null means that factor never limits.\n * `limitingFactor` names the smallest; on a tie the order is pixels, blur, noise.\n */\nexport interface PrintSizeLimitWithBlurAndNoise {\n engineApiVersion: string;\n status: \"finite\" | \"no-positive-size\";\n limitingFactor: \"pixels\" | \"blur\" | \"noise\";\n maximumImageMm: PrintImageMm;\n limits: { pixels: PrintImageMm; blur: PrintImageMm | null; noise: PrintImageMm | null };\n viewingDistanceMm: number;\n /** The criterion angle as a length on the print at the viewing distance: 2 d tan(θ/2). */\n resolvedSpotMm: number;\n blur: { diameterMm: number; diameterPixels: number; largestComponent: \"lens\" | \"defocus\" | \"diffraction\" | \"motion\" | \"measured-total\" | null };\n noise: { pixelSnr: number; visibleSnrAtMaximum: number };\n}\n\nconst UNIT_MM = { mm: 1, cm: 10, inches: 25.4, m: 1000 } as const;\nconst RAD_PER_ARCMIN = Math.PI / 10800;\nconst BLUR_PARTS = [\"lens\", \"defocus\", \"diffraction\", \"motion\"] as const;\n\nfunction record(value: unknown, keys: readonly string[]): Record<string, unknown> {\n return requireAllowlistedRecord(value, keys, \"Invalid or unknown print size limit fields.\");\n}\nfunction parseBlur(value: unknown): { diameterMm: number; largestComponent: PrintSizeLimitWithBlurAndNoise[\"blur\"][\"largestComponent\"] } {\n const r = record(value, [\"kind\", \"lensMm\", \"defocusMm\", \"diffractionMm\", \"motionMm\", \"diameterMm\"]);\n if (r.kind === \"measured-total\") {\n record(value, [\"kind\", \"diameterMm\"]);\n const diameterMm = requireInputNonNegativeFinite(r.diameterMm, \"blur.diameterMm\");\n return { diameterMm, largestComponent: diameterMm > 0 ? \"measured-total\" : null };\n }\n if (r.kind !== \"components\") throw new InvalidConfigurationError(\"Invalid print blur kind.\");\n let sumSquares = 0, largest = 0, largestComponent: PrintSizeLimitWithBlurAndNoise[\"blur\"][\"largestComponent\"] = null;\n for (const part of BLUR_PARTS) {\n const raw = r[`${part}Mm`];\n if (raw === undefined) continue;\n const d = requireInputNonNegativeFinite(raw, `blur.${part}Mm`);\n sumSquares += d * d;\n if (d > largest) { largest = d; largestComponent = part; }\n }\n return { diameterMm: Math.sqrt(sumSquares), largestComponent };\n}\n/** The one place the noise input becomes a per-pixel SNR, including a capture's own noise figure (#535). */\nfunction resolvePixelSnr(value: unknown): number {\n const r = record(value, [\"kind\", \"signalElectrons\", \"readNoiseElectrons\", \"snrLinear\", \"midGrey\"]);\n switch (r.kind) {\n case \"shot-and-read-noise\": {\n record(value, [\"kind\", \"signalElectrons\", \"readNoiseElectrons\"]);\n return calculateSignalToNoise({ signalElectrons: r.signalElectrons as number, readNoiseElectrons: r.readNoiseElectrons as number }).value.snrLinear;\n }\n case \"pixel-snr\":\n record(value, [\"kind\", \"snrLinear\"]);\n return requireInputNonNegativeFinite(r.snrLinear, \"noise.snrLinear\");\n case \"capture-noise-figure\": {\n record(value, [\"kind\", \"midGrey\"]);\n const figure = r.midGrey as { signalElectrons?: unknown; noiseElectrons?: { total?: unknown }; clipped?: unknown } | null;\n if (typeof figure !== \"object\" || figure === null || typeof figure.noiseElectrons !== \"object\" || figure.noiseElectrons === null)\n throw new InvalidConfigurationError(\"noise.midGrey must be a capture's result.sensor.midGrey (simulateCapture with fast: false).\");\n if (figure.clipped !== false) throw new InvalidConfigurationError(\"noise.midGrey is clipped (or not a capture's figure): the patch reaches white, where the figure no longer describes the image.\");\n return requireInputNonNegativeFinite(figure.signalElectrons, \"noise.midGrey.signalElectrons\") / requireInputPositiveFinite(figure.noiseElectrons.total, \"noise.midGrey.noiseElectrons.total\");\n }\n default: throw new InvalidConfigurationError(\"Invalid print noise kind.\");\n }\n}\n\n/** Calculates the largest print, at a fixed viewing distance, at which the capture still looks sharp and clean, and names\n * the factor that limits it: pixels, blur or noise. An approximation throughout; the equations are exact under its model.\n *\n * With θ the criterion angle, d the viewing distance, s = 2 d tan(θ/2) the length θ covers on the print, N_w the raster\n * width in pixels, b the blur diameter in pixels and SNR_px the per-pixel SNR (print width W, height in proportion):\n * - pixels: a print pixel (W / N_w) may cover at most s, so W ≤ N_w·s (as `calculatePrintPlan` with\n * `angular-pixel-pitch`, before its whole-pixel rounding);\n * - blur: the blur on the print (b·W / N_w) may cover at most s, so W ≤ N_w·s / b: the criterion of an acceptable circle\n * of confusion with the eye's resolution as the limit;\n * - noise: the eye averages the (W / N_w) wide pixels inside s, so the visible SNR is SNR_px·(N_w·s / W) while a pixel is\n * smaller than s (the mean of n independent values has 1/√n of their standard deviation); W ≤ N_w·s·SNR_px / SNR_min,\n * and noise never limits when SNR_px ≥ SNR_min.\n * One arcminute is normal (20/20) acuity under optimal conditions (Z. Alomar, K. D. Bojikian, \"Visual Acuity Testing in\n * Adults\", EyeWiki, American Academy of Ophthalmology, https://eyewiki.aao.org/Visual_Acuity_Testing_in_Adults, last edited\n * June 16, 2026); the SNR threshold has no default and is the caller's. Blur parts add in root-sum-square, which is exact\n * for Gaussian spots (variances of independent displacements add: C. M. Grinstead, J. L. Snell, \"Introduction to\n * Probability\", 2nd rev. ed., AMS 1997, sec. 6.2, free under the GNU FDL) and an approximation for discs and Airy patterns.\n */\nexport function calculatePrintSizeLimitWithBlurAndNoise(value: PrintSizeLimitWithBlurAndNoiseInput): CalculationResult<PrintSizeLimitWithBlurAndNoise> {\n const keys = [\"raster\", \"pixelPitchMm\", \"viewingDistance\", \"criterion\", \"blur\", \"noise\"];\n requireInputKeys(value, \"calculatePrintSizeLimitWithBlurAndNoise\", keys);\n const r = record(value, keys);\n const raster = record(r.raster, [\"pixelWidth\", \"pixelHeight\"]);\n const pixelWidth = requireInputPositiveFinite(raster.pixelWidth, \"raster.pixelWidth\"), pixelHeight = requireInputPositiveFinite(raster.pixelHeight, \"raster.pixelHeight\");\n requirePositiveInteger(\"raster.pixelWidth\", pixelWidth); requirePositiveInteger(\"raster.pixelHeight\", pixelHeight);\n const pixelPitchMm = requireInputPositiveFinite(r.pixelPitchMm, \"pixelPitchMm\");\n const distance = record(r.viewingDistance, [\"value\", \"unit\"]);\n if (typeof distance.unit !== \"string\" || !Object.hasOwn(UNIT_MM, distance.unit)) throw new InvalidConfigurationError(\"Invalid Print length unit.\");\n const viewingDistanceMm = requireInputPositiveFinite(distance.value, \"viewingDistance.value\") * UNIT_MM[distance.unit as PrintLength[\"unit\"]];\n const criterion = record(r.criterion, [\"kind\", \"arcminutes\", \"minimumVisibleSnr\"]);\n if (criterion.kind !== \"visual-resolution\") throw new InvalidConfigurationError(\"Invalid print criterion kind.\");\n const arcminutes = requireInputPositiveFinite(criterion.arcminutes, \"criterion.arcminutes\");\n if (arcminutes >= 10800) throw new InvalidConfigurationError(\"criterion.arcminutes must lie in (0, 10800).\");\n const minimumVisibleSnr = requireInputPositiveFinite(criterion.minimumVisibleSnr, \"criterion.minimumVisibleSnr\");\n const blur = parseBlur(r.blur);\n const pixelSnr = resolvePixelSnr(r.noise);\n\n const resolvedSpotMm = 2 * viewingDistanceMm * Math.tan((arcminutes * RAD_PER_ARCMIN) / 2);\n const pixelLimitWidth = pixelWidth * resolvedSpotMm;\n const blurPixels = blur.diameterMm / pixelPitchMm;\n const blurLimitWidth = blurPixels > 0 ? pixelLimitWidth / blurPixels : null;\n const noiseLimitWidth = pixelSnr < minimumVisibleSnr ? pixelLimitWidth * (pixelSnr / minimumVisibleSnr) : null;\n let limitingFactor: PrintSizeLimitWithBlurAndNoise[\"limitingFactor\"] = \"pixels\", width = pixelLimitWidth;\n if (blurLimitWidth !== null && blurLimitWidth < width) { limitingFactor = \"blur\"; width = blurLimitWidth; }\n if (noiseLimitWidth !== null && noiseLimitWidth < width) { limitingFactor = \"noise\"; width = noiseLimitWidth; }\n const size = (w: number): PrintImageMm => ({ width: w, height: w * (pixelHeight / pixelWidth) });\n const visibleSnrAtMaximum = width > 0 ? pixelSnr * Math.max(1, pixelLimitWidth / width) : 0;\n const result: PrintSizeLimitWithBlurAndNoise = {\n engineApiVersion: ENGINE_API_VERSION, status: width > 0 ? \"finite\" : \"no-positive-size\", limitingFactor,\n maximumImageMm: size(width),\n limits: { pixels: size(pixelLimitWidth), blur: blurLimitWidth === null ? null : size(blurLimitWidth), noise: noiseLimitWidth === null ? null : size(noiseLimitWidth) },\n viewingDistanceMm, resolvedSpotMm,\n blur: { diameterMm: blur.diameterMm, diameterPixels: blurPixels, largestComponent: blur.largestComponent },\n noise: { pixelSnr, visibleSnrAtMaximum }\n };\n const numbers = [viewingDistanceMm, resolvedSpotMm, pixelLimitWidth, size(pixelLimitWidth).height, blurPixels, blurLimitWidth ?? 0, width, visibleSnrAtMaximum];\n if (!numbers.every(Number.isFinite) || !(pixelLimitWidth > 0)) throw new InvalidConfigurationError(\"Print size limit lies outside the supported numeric range.\");\n return approximationResult(result, \"print-size-limit-blur-noise\", \"1.0.0\", [\n \"Flat print viewed at a fixed distance, at its center; one visual-resolution angle for pixels, blur and noise.\",\n \"Blur parts combine in root-sum-square: exact for Gaussian spots, an approximation for discs and Airy patterns.\",\n \"Pixel noise is white (independent pixels); demosaic, denoise and sharpening correlations are not modeled.\",\n \"The eye averages noise over a square of the criterion angle; contrast sensitivity and print medium are not modeled.\"\n ]);\n}\n"]}