@photivra/engine 2.21.0 → 2.23.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 (172) hide show
  1. package/CHANGELOG.md +150 -1
  2. package/CITATION.cff +1 -1
  3. package/README.md +6 -6
  4. package/SECURITY.md +1 -1
  5. package/THIRD_PARTY.md +11 -0
  6. package/dist/capture/c2pa-embed.d.ts +116 -0
  7. package/dist/capture/c2pa-embed.js +493 -0
  8. package/dist/capture/c2pa-embed.js.map +1 -0
  9. package/dist/capture/dng-import.d.ts +73 -8
  10. package/dist/capture/dng-import.js +315 -80
  11. package/dist/capture/dng-import.js.map +1 -1
  12. package/dist/capture/dng-opcodes.d.ts +96 -0
  13. package/dist/capture/dng-opcodes.js +391 -0
  14. package/dist/capture/dng-opcodes.js.map +1 -0
  15. package/dist/capture/dng-profile-maps.d.ts +99 -0
  16. package/dist/capture/dng-profile-maps.js +381 -0
  17. package/dist/capture/dng-profile-maps.js.map +1 -0
  18. package/dist/capture/dng-stage-image.d.ts +23 -0
  19. package/dist/capture/dng-stage-image.js +65 -0
  20. package/dist/capture/dng-stage-image.js.map +1 -0
  21. package/dist/capture/jpeg-xl-tiles.d.ts +22 -0
  22. package/dist/capture/jpeg-xl-tiles.js +151 -0
  23. package/dist/capture/jpeg-xl-tiles.js.map +1 -0
  24. package/dist/capture/lossy-jpeg.d.ts +17 -0
  25. package/dist/capture/lossy-jpeg.js +369 -0
  26. package/dist/capture/lossy-jpeg.js.map +1 -0
  27. package/dist/capture/native-raw-development.d.ts +2 -1
  28. package/dist/capture/native-raw-development.js.map +1 -1
  29. package/dist/color/neighbor-highlight-reconstruction.d.ts +76 -0
  30. package/dist/color/neighbor-highlight-reconstruction.js +257 -0
  31. package/dist/color/neighbor-highlight-reconstruction.js.map +1 -0
  32. package/dist/color/sensor-color-development.d.ts +5 -0
  33. package/dist/color/sensor-color-development.js +7 -1
  34. package/dist/color/sensor-color-development.js.map +1 -1
  35. package/dist/color/spectral-spikiness.d.ts +11 -0
  36. package/dist/color/spectral-spikiness.js +23 -0
  37. package/dist/color/spectral-spikiness.js.map +1 -0
  38. package/dist/core/execution-error.d.ts +21 -0
  39. package/dist/core/execution-error.js +12 -0
  40. package/dist/core/execution-error.js.map +1 -0
  41. package/dist/core/version.d.ts +1 -1
  42. package/dist/core/version.js +1 -1
  43. package/dist/core/version.js.map +1 -1
  44. package/dist/facade/catalogue-lens-signal.d.ts +26 -1
  45. package/dist/facade/catalogue-lens-signal.js +25 -7
  46. package/dist/facade/catalogue-lens-signal.js.map +1 -1
  47. package/dist/facade/diffraction-provider.d.ts +1 -1
  48. package/dist/facade/diffraction-provider.js +4 -4
  49. package/dist/facade/diffraction-provider.js.map +1 -1
  50. package/dist/facade/display-view-gpu-session.js +38 -6
  51. package/dist/facade/display-view-gpu-session.js.map +1 -1
  52. package/dist/facade/display-view-gpu.d.ts +13 -5
  53. package/dist/facade/display-view-gpu.js.map +1 -1
  54. package/dist/facade/display-view-normal.d.ts +15 -0
  55. package/dist/facade/display-view-normal.js +44 -0
  56. package/dist/facade/display-view-normal.js.map +1 -0
  57. package/dist/facade/display-view-rendering.d.ts +102 -0
  58. package/dist/facade/display-view-rendering.js +633 -0
  59. package/dist/facade/display-view-rendering.js.map +1 -0
  60. package/dist/facade/display-view.d.ts +37 -65
  61. package/dist/facade/display-view.js +147 -476
  62. package/dist/facade/display-view.js.map +1 -1
  63. package/dist/facade/expected-signal-setup.js +2 -2
  64. package/dist/facade/expected-signal-setup.js.map +1 -1
  65. package/dist/facade/fast-methods.d.ts +1 -1
  66. package/dist/facade/fast-methods.js +37 -8
  67. package/dist/facade/fast-methods.js.map +1 -1
  68. package/dist/facade/generic-lens-capture.d.ts +1 -1
  69. package/dist/facade/generic-lens-capture.js +1 -1
  70. package/dist/facade/generic-lens-capture.js.map +1 -1
  71. package/dist/facade/image-plane.js +41 -10
  72. package/dist/facade/image-plane.js.map +1 -1
  73. package/dist/facade/on-demand-kits.d.ts +14 -0
  74. package/dist/facade/on-demand-kits.js +36 -0
  75. package/dist/facade/on-demand-kits.js.map +1 -0
  76. package/dist/facade/opt-in-kit.d.ts +9 -0
  77. package/dist/facade/opt-in-kit.js +11 -0
  78. package/dist/facade/opt-in-kit.js.map +1 -0
  79. package/dist/facade/quality-kit.d.ts +21 -0
  80. package/dist/facade/quality-kit.js +23 -0
  81. package/dist/facade/quality-kit.js.map +1 -0
  82. package/dist/facade/quality-paths.d.ts +22 -0
  83. package/dist/facade/quality-paths.js +44 -0
  84. package/dist/facade/quality-paths.js.map +1 -0
  85. package/dist/facade/scene-radiance.d.ts +18 -2
  86. package/dist/facade/scene-radiance.js +6 -4
  87. package/dist/facade/scene-radiance.js.map +1 -1
  88. package/dist/facade/simulate-capture.d.ts +32 -13
  89. package/dist/facade/simulate-capture.js +162 -69
  90. package/dist/facade/simulate-capture.js.map +1 -1
  91. package/dist/facade/simulate-display-view.d.ts +10 -12
  92. package/dist/facade/simulate-display-view.js +57 -48
  93. package/dist/facade/simulate-display-view.js.map +1 -1
  94. package/dist/gpu/display-view-gpu-renderer.d.ts +43 -0
  95. package/dist/gpu/display-view-gpu-renderer.js +134 -0
  96. package/dist/gpu/display-view-gpu-renderer.js.map +1 -0
  97. package/dist/gpu/display-view-image-material-module.d.ts +3 -2
  98. package/dist/gpu/display-view-image-material-module.js +25 -3
  99. package/dist/gpu/display-view-image-material-module.js.map +1 -1
  100. package/dist/gpu/display-view-image-quality.d.ts +43 -1
  101. package/dist/gpu/display-view-image-quality.js +256 -11
  102. package/dist/gpu/display-view-image-quality.js.map +1 -1
  103. package/dist/gpu/display-view-quality-gpu.d.ts +13 -64
  104. package/dist/gpu/display-view-quality-gpu.js +55 -240
  105. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  106. package/dist/gpu/display-view-speed-gpu.d.ts +16 -16
  107. package/dist/gpu/display-view-speed-gpu.js +62 -19
  108. package/dist/gpu/display-view-speed-gpu.js.map +1 -1
  109. package/dist/gpu/image-kernel-accelerator.js +3 -0
  110. package/dist/gpu/image-kernel-accelerator.js.map +1 -1
  111. package/dist/gpu/image-kernel-execution.d.ts +2 -13
  112. package/dist/gpu/image-kernel-execution.js +2 -10
  113. package/dist/gpu/image-kernel-execution.js.map +1 -1
  114. package/dist/gpu/wgsl-display-view-coupled-stub.d.ts +13 -0
  115. package/dist/gpu/wgsl-display-view-coupled-stub.js +29 -0
  116. package/dist/gpu/wgsl-display-view-coupled-stub.js.map +1 -0
  117. package/dist/gpu/wgsl-display-view-coupled.d.ts +91 -0
  118. package/dist/gpu/wgsl-display-view-coupled.js +365 -0
  119. package/dist/gpu/wgsl-display-view-coupled.js.map +1 -0
  120. package/dist/gpu/wgsl-display-view-neighbors.d.ts +46 -0
  121. package/dist/gpu/wgsl-display-view-neighbors.js +114 -0
  122. package/dist/gpu/wgsl-display-view-neighbors.js.map +1 -0
  123. package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +6 -9
  124. package/dist/gpu/wgsl-display-view-quality-kernel.js +113 -113
  125. package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
  126. package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +1 -41
  127. package/dist/gpu/wgsl-display-view-speed-kernel.js +73 -10
  128. package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
  129. package/dist/index.d.ts +11 -3
  130. package/dist/index.js +9 -1
  131. package/dist/index.js.map +1 -1
  132. package/dist/output/camera-channel-response.d.ts +50 -0
  133. package/dist/output/camera-channel-response.js +69 -0
  134. package/dist/output/camera-channel-response.js.map +1 -0
  135. package/dist/output/native-capture-sdr.js +5 -2
  136. package/dist/output/native-capture-sdr.js.map +1 -1
  137. package/dist/output/sdr-rendering.d.ts +18 -1
  138. package/dist/output/sdr-rendering.js +39 -2
  139. package/dist/output/sdr-rendering.js.map +1 -1
  140. package/dist/sensor/native-sensor-report.d.ts +10 -0
  141. package/dist/sensor/native-sensor-report.js +16 -0
  142. package/dist/sensor/native-sensor-report.js.map +1 -0
  143. package/dist/sensor/sensor-characteristics.d.ts +81 -13
  144. package/dist/sensor/sensor-characteristics.js +10 -6
  145. package/dist/sensor/sensor-characteristics.js.map +1 -1
  146. package/docs/API_REFERENCE.md +117 -4
  147. package/docs/QUICK_START.md +37 -8
  148. package/docs/USAGE.md +105 -22
  149. package/docs/api/capture-c2pa-embed.md +177 -0
  150. package/docs/api/capture-dng-import.md +86 -10
  151. package/docs/api/capture-dng-opcodes.md +93 -0
  152. package/docs/api/capture-dng-profile-maps.md +141 -0
  153. package/docs/api/capture-dng-stage-image.md +43 -0
  154. package/docs/api/capture-jpeg-xl-tiles.md +33 -0
  155. package/docs/api/capture-lossy-jpeg.md +41 -0
  156. package/docs/api/capture-native-raw-development.md +2 -1
  157. package/docs/api/core-execution-error.md +43 -0
  158. package/docs/api/core-version.md +1 -1
  159. package/docs/api/exports.json +255 -5
  160. package/docs/api/facade-display-view-gpu.md +5 -2
  161. package/docs/api/facade-display-view.md +11 -15
  162. package/docs/api/facade-fast-methods.md +38 -9
  163. package/docs/api/facade-quality-paths.md +24 -0
  164. package/docs/api/facade-scene-radiance.md +1 -1
  165. package/docs/api/facade-simulate-capture.md +24 -7
  166. package/docs/api/facade-simulate-display-view.md +10 -12
  167. package/docs/api/gpu-image-kernel-execution.md +0 -36
  168. package/docs/api/output-camera-channel-response.md +55 -0
  169. package/docs/api/output-sdr-rendering.md +6 -1
  170. package/docs/api/sensor-sensor-characteristics.md +54 -2
  171. package/llms.txt +1 -0
  172. package/package.json +6 -5
@@ -70,10 +70,14 @@ export interface DisplayViewResult {
70
70
  sensor: SimulateCaptureSensorReport;
71
71
  /**
72
72
  * Work done: real pixels sampled, real-site expectations evaluated, scene queries, display pixels sampled in full (detail) or with
73
- * a quarter of the samples per axis (uniform), display pixels whose value includes the AHD low-signal correction, and wall-clock
74
- * milliseconds (diagnostic only; never affects the samples).
73
+ * a quarter of the samples per axis (uniform), and wall-clock milliseconds (diagnostic only; never affects the samples).
75
74
  */
76
- diagnostics: { sampleCount: number; siteEvaluations: number; sceneLookups: number; detailPixelCount: number; uniformPixelCount: number; ahdCorrectedPixelCount: number;
75
+ diagnostics: { sampleCount: number; siteEvaluations: number; sceneLookups: number; detailPixelCount: number; uniformPixelCount: number;
76
+ /**
77
+ * Display pixels whose value included the AHD low-signal correction (2.10–2.21).
78
+ * @deprecated Since 2.22.0 the view has no such correction, and this is always 0. Removed in 3.0.0.
79
+ */
80
+ ahdCorrectedPixelCount: number;
77
81
  timingsMs: { view: number; total: number };
78
82
  /** #395: exact or fast, as requested and as run; 2.11 (#481): plus `execution`, where the render ran (see {@link DisplayViewFastReport}). */
79
83
  fast: DisplayViewFastReport;
@@ -144,7 +148,7 @@ a capture does. `motion.rotation` is a non-zero angular velocity; `sensor.tempor
144
148
 
145
149
  ```ts
146
150
  SIMULATE_CAPTURE_COST_OPTIONS = Object.freeze(["lens.pupilSampling", "lens.projection", "lens.distortion", "lens.vignetting", "lens.psf",
147
- "lens.diffraction", "lens.defocus", "lens.catalogue", "sensor.antiAliasing", "sensor.pixelAperture.box", "sensor.temporalSampleCount", "motion.rotation", "motion.subject", "motion.shutter.rolling", "output.borderHandling.mirror", "output.region",
151
+ "lens.diffraction", "lens.defocus", "lens.catalogue", "sensor.antiAliasing", "sensor.pixelAperture.box", "sensor.temporalSampleCount", "sensor.spectralStepNanometers", "motion.rotation", "motion.subject", "motion.shutter.rolling", "output.borderHandling.mirror", "output.region",
148
152
  "output.whiteBalance.auto", "output.demosaic.ahd", "output.highlightMode.reconstruct", "output.colorEncoding.wide-gamut", "output.gamutMapping.perceptual",
149
153
  "output.diffractionCorrection", "output.sharpening", "output.lensCorrections", "output.format.jpeg", "output.format.png", "output.format.dng"] as const)
150
154
  ```
@@ -175,7 +179,8 @@ the record form with `fast` and is reported in `diagnostics.fast.applied` when i
175
179
  whether or not it has a fast method yet; one without runs exact and reports `false`.
176
180
 
177
181
  ```ts
178
- SIMULATE_CAPTURE_FAST_OPTIONS = Object.freeze(["lens.diffraction", "lens.catalogue", "sensor.antiAliasing", "sensor.pixelAperture.box", "output.demosaic.ahd", "motion.subject"] as const)
182
+ SIMULATE_CAPTURE_FAST_OPTIONS = Object.freeze(["lens.diffraction", "lens.catalogue", "sensor.antiAliasing", "sensor.pixelAperture.box", "output.demosaic.ahd", "motion.subject",
183
+ "sensor.spectralStepNanometers"] as const)
179
184
  ```
180
185
 
181
186
  ## SIMULATE_CAPTURE_METERING_MODES
@@ -712,8 +717,12 @@ export interface SimulateCaptureOptions {
712
717
  toneCurve?: SdrToneCurve;
713
718
  /** Required by the luminance tone curves (see `SdrToneCurveParameters`). The facade does not make one up. */
714
719
  toneCurveParameters?: SdrToneCurveParameters;
715
- /** Default "clip". */
716
- highlightMode?: RawHighlightMode;
720
+ /**
721
+ * Default "clip". 2.22.0 (#602, opt-in): "reconstruct-neighbors" rebuilds clipped channels from color ratios of the unclipped
722
+ * pixels around each clipped region (an estimate; `src/color/neighbor-highlight-reconstruction.ts`): the ring's mean ratios
723
+ * (fast) or their harmonic interpolation inward (full). Whole-image, so not with `output.region`.
724
+ */
725
+ highlightMode?: SimulateCaptureHighlightMode;
717
726
  /**
718
727
  * Opt-in (#556) development exposure push in stops, −2 to +5 (`NATIVE_RAW_DEVELOPMENT_EXPOSURE_RANGE_STOPS`); default 0.
719
728
  * A linear gain 2^stops on the RAW after black subtraction and white normalization, before demosaic, white balance,
@@ -724,6 +733,14 @@ export interface SimulateCaptureOptions {
724
733
  * honour it. Reported in the assumption `output.exposureCompensationStops`. 0 or omitted: byte-identical to before.
725
734
  */
726
735
  exposureCompensationStops?: number;
736
+ /**
737
+ * #601 (2.22.0, opt-in): the sRGB rendering's gamut handling after the tone curve. "clip-components" (the default) clips
738
+ * each component to [0, 1]; "desaturate-to-luminance" moves a pixel whose largest component exceeds 1 toward the gray of its
739
+ * own luminance until that component is 1, keeping its luminance and hue (`desaturateToLuminance`, src/output/sdr-rendering.ts),
740
+ * instead of shifting bright saturated colors toward the secondaries. sRGB output only (not with a wide-gamut `colorEncoding`
741
+ * or `gamutMapping`). Reported in the assumption `output.gamutHandling`. Omitted: byte-identical to before.
742
+ */
743
+ gamutHandling?: SdrRenderingProfile["gamutHandling"] & ("clip-components" | "desaturate-to-luminance");
727
744
  /** Default "scene-illuminant". */
728
745
  whiteBalance?: SimulateCaptureWhiteBalance;
729
746
  /** JPEG constant quantization step 1-255 (not a manufacturer 'quality'). Default 1 (2.20.0; 2 before), the full-quality export. */
@@ -12,8 +12,7 @@ setup: use it to render one view in many regions, for example on each worker of
12
12
  ```ts
13
13
  export interface PreparedDisplayView {
14
14
  /**
15
- * Renders a view of the prepared request. Renders may run concurrently; they share no state except read-only setup and the
16
- * realm's AHD correction states, which never change a byte (docs/DISPLAY_VIEW.md, "AHD at low signal"). With `gpu: "auto"`
15
+ * Renders a view of the prepared request. Renders may run concurrently; they share no state except read-only setup. With `gpu: "auto"`
17
16
  * or `"require"` (#481) GPU renders of one prepared view run one at a time on its device, and `diagnostics.fast.execution`
18
17
  * says whether the GPU or the CPU reference rendered the view, and why.
19
18
  */
@@ -25,22 +24,21 @@ export interface PreparedDisplayView {
25
24
  */
26
25
  dispose(): void;
27
26
  /**
28
- * 2.10 (#447): the key of this request's AHD low-signal correction states, or null when the request has no AHD
29
- * (`output.demosaic` other than "ahd"). Every input of the key is fixed when the request is prepared; two prepared views
30
- * with the same key read the same states.
27
+ * 2.10 (#447), deprecated in 2.22.0: was the key of this request's AHD low-signal correction states. The view has no such
28
+ * states since 2.22.0 (docs/DISPLAY_VIEW.md, "AHD at low signal"), so this returns null for every request.
29
+ * @deprecated Since 2.22.0: always null. Removed in 3.0.0.
31
30
  */
32
31
  correctionKey(): string | null;
33
32
  /**
34
- * 2.10 (#447): the grid keys of every correction state a render of `view` reads (ascending, whether or not this realm holds
35
- * them), found by a render of the view's region that records the states instead of computing them. No image is returned;
36
- * it costs about one render whose states are all held. Fails with an InvalidConfigurationError when the request has no AHD.
33
+ * 2.10 (#447), deprecated in 2.22.0: was the grid keys of the correction states a render of `view` read. Since 2.22.0 it checks
34
+ * `view` as `render` does and returns `{ key: "", gridKeys }` with no grid keys, for every request; it renders nothing.
35
+ * @deprecated Since 2.22.0: returns no grid keys. Removed in 3.0.0.
37
36
  */
38
37
  collectCorrectionStates(view: SimulateDisplayViewOptions, renderOptions?: PreparedDisplayViewRenderOptions): Promise<DisplayViewCorrectionStateKeys>;
39
38
  /**
40
- * 2.10 (#447): computes the correction states of `gridKeys` (from `collectCorrectionStates`, or a share of them) as a render
41
- * would, stores them in this realm's cache and returns their values, for {@link bindDisplayViewCorrectionStates} in
42
- * another realm. States this realm holds are read, not computed. Yields and honours `signal` like `render`. Fails with an
43
- * InvalidConfigurationError when the request has no AHD or a grid key is out of range.
39
+ * 2.10 (#447), deprecated in 2.22.0: computed correction states. Since 2.22.0 it checks that `gridKeys` is an Int32Array of
40
+ * grid keys (integers in [0, 97³)) and returns `{ key: "", gridKeys, values }` with no states, for every request; it computes nothing.
41
+ * @deprecated Since 2.22.0: returns no states. Removed in 3.0.0.
44
42
  */
45
43
  computeCorrectionStates(gridKeys: Int32Array, renderOptions?: PreparedDisplayViewRenderOptions): Promise<DisplayViewCorrectionStateSet>;
46
44
  }
@@ -139,42 +139,6 @@ export interface ImageKernelExecution {
139
139
  }
140
140
  ```
141
141
 
142
- ## ImageKernelExecutionError
143
-
144
- Domain error for getting and running a backend. Input validation still throws the engine's existing errors.
145
-
146
- ```ts
147
- export class ImageKernelExecutionError extends Error {
148
- readonly code = "IMAGE_KERNEL_EXECUTION";
149
- readonly reason: ImageKernelFailureReason;
150
-
151
- constructor(reason: ImageKernelFailureReason, message: string) {
152
- super(message);
153
- this.name = "ImageKernelExecutionError";
154
- this.reason = reason;
155
- }
156
- }
157
- ```
158
-
159
- ## ImageKernelFailureReason
160
-
161
- Failure classes reported by {@link ImageKernelExecutionError}. `conformance-failed` (#481): a device failed the display view's
162
- run-time self-check with `gpu: "require"`.
163
-
164
- ```ts
165
- export type ImageKernelFailureReason = ImageKernelFallbackReason | "device-lost" | "out-of-memory" | "gpu-validation" |
166
- "aborted" | "disposed" | "busy" | "unsupported-on-backend" | "unsupported-float32-domain" | "indeterminate-float32-classification" | "conformance-failed";
167
- ```
168
-
169
- ## ImageKernelFallbackReason
170
-
171
- Why a requested `webgpu-f32` backend ran the CPU reference instead. `webgpu-disabled`: `setWebGpuEnabled(false)` in this realm.
172
-
173
- ```ts
174
- export type ImageKernelFallbackReason = "webgpu-unavailable" | "webgpu-disabled" | "adapter-unavailable" | "device-request-failed" |
175
- "insufficient-limits" | "unsupported-platform-endianness";
176
- ```
177
-
178
142
  ## ImageKernelRelativeErrorBound
179
143
 
180
144
  Non-negative linear sums: |v_webgpu − v_reference| ≤ relative·v_reference + absolute for every sample.
@@ -0,0 +1,55 @@
1
+ # output/camera-channel-response.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## calculateCameraChannelResponse
6
+
7
+ #591: the camera's channel response on its spectral grid and, with `illuminant`, each channel's effective dispersion (pass it
8
+ as `channelDispersion` to `calculateLateralColorFringe` or `createGenericLensCorrectionProfile`). Strict input. A labelled
9
+ approximation: the facade's synthetic colorimetric sensor, not a measured one.
10
+
11
+ ```ts
12
+ export function calculateCameraChannelResponse(input: CalculateCameraChannelResponseInput = {}): CalculationResult<CameraChannelResponse>;
13
+ ```
14
+
15
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
16
+
17
+ ## CalculateCameraChannelResponseInput
18
+
19
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
20
+
21
+ ```ts
22
+ export interface CalculateCameraChannelResponseInput {
23
+ /**
24
+ * The capture's `sensor.profile` record (a `SensorProfile`, for example `resolveSensorProfile(id)`): only its
25
+ * `peakQuantumEfficiency` is read, the one field that changes the channels. Omitted: a tier body (peak QE 0.5).
26
+ */
27
+ sensorProfile?: Pick<SensorProfile, "peakQuantumEfficiency">;
28
+ /** Peak quantum efficiency in (0, 1] (as `sensor.peakQuantumEfficiency`); wins over the profile's. */
29
+ peakQuantumEfficiency?: number;
30
+ /**
31
+ * The capture's `sensor.spectralStepNanometers`, 1–100. Default 5: the calculator runs at full quality, and a spiky light
32
+ * needs the fine grid. Pass the capture's step to reproduce a capture's channels.
33
+ */
34
+ spectralStepNanometers?: number;
35
+ /** A `simulateCapture` illuminant: when given, the result also carries each channel's effective dispersion under it. */
36
+ illuminant?: CaptureIlluminant;
37
+ }
38
+ ```
39
+
40
+ ## CameraChannelResponse
41
+
42
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
43
+
44
+ ```ts
45
+ export interface CameraChannelResponse {
46
+ /** The capture's midpoint nodes, nm, increasing. */
47
+ wavelengthsNm: number[];
48
+ /** Electrons per joule of incident light at each node, per channel: EQE_c(λ)·λ/(h·c). */
49
+ red: number[];
50
+ green: number[];
51
+ blue: number[];
52
+ /** With `illuminant`: D̄_c, as `calculateChannelEffectiveDispersion` returns for these channels; else null. */
53
+ channelDispersion: ChannelDispersion | null;
54
+ }
55
+ ```
@@ -83,7 +83,12 @@ export interface SdrRenderingProfile {
83
83
  toneCurve: SdrToneCurve;
84
84
  /** Present exactly for `extended-reinhard-luminance` ({whiteLuminance}) and `naka-rushton-luminance`. */
85
85
  toneCurveParameters?: SdrToneCurveParameters;
86
- gamutHandling: "clip-components" | "reject-out-of-range";
86
+ /**
87
+ * After the tone curve: clip each component to [0, 1], reject an out-of-range pixel, or (#601, 2.22.0) desaturate a pixel whose
88
+ * largest component exceeds 1 toward the gray of its own luminance until that component is 1 (`desaturateToLuminance`), then
89
+ * clip what remains below 0.
90
+ */
91
+ gamutHandling: "clip-components" | "reject-out-of-range" | "desaturate-to-luminance";
87
92
  outputDynamicRange: "sdr";
88
93
  transferFunction: "srgb";
89
94
  bitDepth: 8 | 16;
@@ -2,6 +2,49 @@
2
2
 
3
3
  The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
4
 
5
+ ## SimulateCaptureMidGreyFigure
6
+
7
+ #535: the mid-grey noise figure of {@link SimulateCaptureSensorReport.midGrey}.
8
+
9
+ ```ts
10
+ export interface SimulateCaptureMidGreyFigure { reflectance: 0.18; channel: "green"; signalElectrons: number; noiseElectrons: SimulateCaptureNoiseComponents; snr: number; snrDecibels: number; clipped: boolean }
11
+ ```
12
+
13
+ ## SimulateCaptureNativeSensorReport
14
+
15
+ #595: {@link SimulateCaptureSensorReport.native}, the figures of one native photosite.
16
+
17
+ ```ts
18
+ export interface SimulateCaptureNativeSensorReport {
19
+ /** The native photosite pitch, µm (tiers: 6; a profile: its own). */
20
+ pixelPitchMicrometers: number;
21
+ /** N = (rendered pitch / native pitch)²: the photosites one rendered site stands for (below 1, a site smaller than a photosite). */
22
+ photositesPerSite: number;
23
+ /** Full-well capacity of one photosite, e⁻. */
24
+ fullWellElectrons: number;
25
+ /** Read noise of one photosite at the capture's ISO, RMS e⁻ (the capture's readout regime). */
26
+ readNoiseElectrons: number;
27
+ /** Engineering dynamic range of one photosite at the capture's ISO, stops (the definition of `dynamicRangeStops`). */
28
+ dynamicRangeStops: number;
29
+ /** The same per ISO setting of the body. */
30
+ dynamicRangeByIso: readonly { iso: number; stops: number }[];
31
+ /**
32
+ * EMVA 1288 Release 4.0 (§2.7, Eq. 26–28) at the capture's ISO: the absolute sensitivity threshold μ_e.min = √(σ_d² + σ_q² + 1/4) + 1/2
33
+ * (σ_d the read noise, dark current excluded; σ_q the quantization noise in e⁻), DR = μ_e.sat / μ_e.min in stops and dB, and the
34
+ * saturation capacity per area μ_e.sat / p² (Table 2).
35
+ */
36
+ emva1288: { absoluteSensitivityThresholdElectrons: number; dynamicRangeStops: number; dynamicRangeDecibels: number; saturationCapacityElectronsPerSquareMicrometer: number };
37
+ /**
38
+ * A convention, not part of EMVA 1288: the dynamic range of the whole frame's native photosites resampled to 8 million output
39
+ * pixels, each summing framePhotosites / 8e6 photosites with their read noise (EMVA 1288 R4.0 §6.4, binning):
40
+ * DR + ½·log₂(framePhotosites / 8e6), for the engineering and the EMVA figure. No ADC limit is applied to the sum.
41
+ */
42
+ normalized8Megapixel: { referencePixels: 8_000_000; framePhotosites: number; dynamicRangeStops: number; emva1288DynamicRangeStops: number };
43
+ /** The mid-grey figure for one photosite (its charge (native/rendered pitch)² of the site's), present when `midGrey` is. */
44
+ midGrey?: SimulateCaptureMidGreyFigure;
45
+ }
46
+ ```
47
+
5
48
  ## SimulateCaptureNoiseComponents
6
49
 
7
50
  One RMS noise component of the mid-grey figure, e⁻ (input-referred).
@@ -45,7 +88,9 @@ export interface SimulateCaptureSensorCharacteristics {
45
88
  */
46
89
  fullWell?: { electrons: number; link?: "pixel-area" | "none"; referencePixelPitchMicrometers?: number };
47
90
  /**
48
- * Read noise, RMS e⁻ (0–100), of the low and the high conversion gain, and the dual-gain switch: the ISO setting from
91
+ * Read noise, RMS e⁻ (0–100), of one physical photosite, as a sensor datasheet gives it (2.22.0: on a tier too; a rendered site that
92
+ * sums N photosites, above the tier's 6 µm or a profile's own pitch, takes it ×√N), of the low and the high conversion gain, and
93
+ * the dual-gain switch: the ISO setting from
49
94
  * which the high conversion gain is used (any ISO from 100; above the body's highest ISO, the low gain is used throughout).
50
95
  * An omitted field keeps the body's value. Needs readout regimes that declare their exposure index (the tiers do).
51
96
  */
@@ -100,6 +145,13 @@ export interface SimulateCaptureSensorReport {
100
145
  * the figure no longer describes the image. Computed only when the request resolves to exact (`fast: false`, the default for
101
146
  * DNG, JPEG and PNG exports; omitted for a fast request).
102
147
  */
103
- midGrey?: { reflectance: 0.18; channel: "green"; signalElectrons: number; noiseElectrons: SimulateCaptureNoiseComponents; snr: number; snrDecibels: number; clipped: boolean };
148
+ midGrey?: SimulateCaptureMidGreyFigure;
149
+ /**
150
+ * #595 (2.22.0): the same figures for one of the body's native photosites, whatever pitch the capture renders (the fields above
151
+ * are the as-rendered site's). The native pitch is a generic tier's (or a caller preset's) declared 6 µm reference, or a sensor
152
+ * profile's own photosite; the request's sensor characteristics apply as they would at that pitch. Exact arithmetic under the
153
+ * engine's model (model figures, not measurements); see docs/QUICK_START.md, "Native-pixel dynamic range (#595)".
154
+ */
155
+ native: SimulateCaptureNativeSensorReport;
104
156
  }
105
157
  ```
package/llms.txt CHANGED
@@ -9,6 +9,7 @@
9
9
  - [Color patches](https://github.com/Photivra/photivra/blob/main/docs/COLOR_PATCHES.md): the built-in color chart patches (`color-patches` preset, `{ kind: "color-patch", patchId }`), their spectra and CIELAB
10
10
  - [Image-plane scene](https://github.com/Photivra/photivra/blob/main/docs/IMAGE_PLANE.md): a photo as a flat print in front of the camera (`{ kind: "image-plane" }`), its input, placement, print reflectance lattice and accuracy
11
11
  - [Light sources](https://github.com/Photivra/photivra/blob/main/docs/LIGHT_SOURCES.md): representative fluorescent and LED spectra designed by Photivra (`createRepresentativeLightSourceIlluminant`), their CCT and color rendering
12
+ - [Content Credentials](https://github.com/Photivra/photivra/blob/main/docs/CONTENT_CREDENTIALS.md): embed a caller-built C2PA manifest store in a JPEG, PNG or DNG export (`bindC2paManifestStore`), hash the bound bytes (`calculateC2paDataHash`), write the signed store at the same length (`setC2paManifestStore`) and find an existing store (`parseC2paManifestStore`); no keys, signature checks or network
12
13
  - [Usage guide](https://github.com/Photivra/photivra/blob/main/docs/USAGE.md): worked examples for every public area
13
14
  - [API reference](https://github.com/Photivra/photivra/blob/main/docs/API_REFERENCE.md): every root export, with a task-to-function index
14
15
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@photivra/engine",
3
- "version": "2.21.0",
3
+ "version": "2.23.0",
4
4
  "private": false,
5
5
  "description": "Scientific TypeScript engine for camera optics, exposure, focus, motion, sensor sampling, diffraction, and digital imaging calculations.",
6
6
  "type": "module",
@@ -105,13 +105,14 @@
105
105
  },
106
106
  "devDependencies": {
107
107
  "@eslint/js": "10.0.1",
108
+ "@types/node": "24.13.6",
109
+ "@vitest/coverage-v8": "5.0.1",
108
110
  "eslint": "10.10.0",
111
+ "jxl-oxide-wasm": "0.12.6",
112
+ "rolldown": "1.2.9",
109
113
  "typescript": "6.0.3",
110
114
  "typescript-eslint": "8.70.0",
111
- "vitest": "5.0.1",
112
- "@types/node": "24.13.6",
113
- "@vitest/coverage-v8": "5.0.1",
114
- "rolldown": "1.2.9"
115
+ "vitest": "5.0.1"
115
116
  },
116
117
  "keywords": [
117
118
  "photography",