@photivra/engine 2.11.1 → 2.13.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 (55) hide show
  1. package/CHANGELOG.md +94 -1
  2. package/CITATION.cff +1 -1
  3. package/NODE_SUPPORT.md +2 -4
  4. package/README.md +6 -6
  5. package/SECURITY.md +1 -1
  6. package/THIRD_PARTY.md +1 -0
  7. package/dist/capture/export-jpeg.d.ts +1 -1
  8. package/dist/capture/export-jpeg.js +6 -4
  9. package/dist/capture/export-jpeg.js.map +1 -1
  10. package/dist/core/version.d.ts +1 -1
  11. package/dist/core/version.js +1 -1
  12. package/dist/core/version.js.map +1 -1
  13. package/dist/exposure/auto-iso-slowest-shutter.d.ts +120 -0
  14. package/dist/exposure/auto-iso-slowest-shutter.js +216 -0
  15. package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -0
  16. package/dist/exposure/exposure-mode-resolver.d.ts +57 -1
  17. package/dist/exposure/exposure-mode-resolver.js +238 -33
  18. package/dist/exposure/exposure-mode-resolver.js.map +1 -1
  19. package/dist/facade/diffraction-provider.js +12 -13
  20. package/dist/facade/diffraction-provider.js.map +1 -1
  21. package/dist/facade/display-view.d.ts +27 -0
  22. package/dist/facade/display-view.js +234 -65
  23. package/dist/facade/display-view.js.map +1 -1
  24. package/dist/facade/simulate-capture.d.ts +159 -3
  25. package/dist/facade/simulate-capture.js +203 -10
  26. package/dist/facade/simulate-capture.js.map +1 -1
  27. package/dist/facade/simulate-display-view.js +2 -2
  28. package/dist/facade/simulate-display-view.js.map +1 -1
  29. package/dist/gpu/display-view-quality-gpu.d.ts +2 -1
  30. package/dist/gpu/display-view-quality-gpu.js +24 -14
  31. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  32. package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +13 -1
  33. package/dist/gpu/wgsl-display-view-quality-kernel.js +36 -2
  34. package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
  35. package/dist/index.d.ts +4 -3
  36. package/dist/index.js +2 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/optics/irradiance-convolution.d.ts +10 -0
  39. package/dist/optics/irradiance-convolution.js +48 -13
  40. package/dist/optics/irradiance-convolution.js.map +1 -1
  41. package/dist/output/print-jpeg.js +12 -9
  42. package/dist/output/print-jpeg.js.map +1 -1
  43. package/dist/output/text-metadata.d.ts +22 -2
  44. package/dist/output/text-metadata.js +58 -9
  45. package/dist/output/text-metadata.js.map +1 -1
  46. package/docs/API_REFERENCE.md +23 -1
  47. package/docs/QUICK_START.md +109 -3
  48. package/docs/USAGE.md +234 -1
  49. package/docs/api/core-version.md +1 -1
  50. package/docs/api/exports.json +77 -2
  51. package/docs/api/exposure-auto-iso-slowest-shutter.md +139 -0
  52. package/docs/api/exposure-exposure-mode-resolver.md +68 -0
  53. package/docs/api/facade-simulate-capture.md +153 -5
  54. package/docs/api/output-text-metadata.md +34 -0
  55. package/package.json +1 -1
@@ -198,6 +198,32 @@ export interface AutoIsoBaselinePolicy {
198
198
  isoBaseline: "minimum-selectable";
199
199
  isoQuantizationPolicy:
200
200
  "nearest-log2-lower-on-tie";
201
+ /** #505: optional photographer-set Auto ISO limits inside the body's Auto ISO range. */
202
+ autoIsoLimits?: AutoIsoLimits;
203
+ }
204
+ ```
205
+
206
+ ## AutoIsoLimits
207
+
208
+ #505: the photographer's Auto ISO limits, a policy choice kept separate from what the body allows
209
+ (`capabilities.iso.autoIsoMinimum`/`autoIsoMaximum`, else the ISO range). Cameras let the
210
+ photographer cap Auto ISO, and some also raise its floor (for example the Nikon D850's "maximum
211
+ sensitivity", Fujifilm's per-preset default and maximum sensitivity, and Panasonic's separate upper
212
+ and lower Auto ISO limits; makers' manuals, see docs/USAGE.md).
213
+
214
+ Both values are ISO settings: inside the body's Auto ISO range and, on a discrete ISO grid, one of
215
+ its values, with `minimum` at most `maximum`. Anything else fails with
216
+ `InvalidScientificInputError`; the engine never clips a limit silently. When a limit is strictly
217
+ inside the body's range and the ISO is held there, the limiting constraint is `user-iso-maximum` or
218
+ `user-iso-minimum`; a limit equal to the body's bound adds nothing, and the body's `iso-maximum` or
219
+ `iso-minimum` is reported.
220
+
221
+ ```ts
222
+ export interface AutoIsoLimits {
223
+ /** The highest ISO Auto ISO may choose. */
224
+ maximum: number;
225
+ /** The lowest ISO Auto ISO may choose, and so its baseline. Default: the body's Auto ISO minimum. */
226
+ minimum?: number;
201
227
  }
202
228
  ```
203
229
 
@@ -219,6 +245,8 @@ export type ExposureResolutionConstraint =
219
245
  | "none"
220
246
  | "iso-minimum"
221
247
  | "iso-maximum"
248
+ | "user-iso-minimum"
249
+ | "user-iso-maximum"
222
250
  | "iso-grid-quantization"
223
251
  | "shutter-minimum"
224
252
  | "shutter-maximum"
@@ -366,6 +394,8 @@ export interface FullAutoExposurePolicy {
366
394
  "minimum-selectable";
367
395
  isoQuantizationPolicy:
368
396
  "nearest-log2-lower-on-tie";
397
+ /** #505: optional photographer-set Auto ISO limits inside the body's Auto ISO range. */
398
+ autoIsoLimits?: AutoIsoLimits;
369
399
  }
370
400
  ```
371
401
 
@@ -467,6 +497,8 @@ export type ManualIsoControl =
467
497
  kind: "automatic";
468
498
  quantizationPolicy:
469
499
  "nearest-log2-lower-on-tie";
500
+ /** #505: optional photographer-set Auto ISO limits inside the body's Auto ISO range. */
501
+ autoIsoLimits?: AutoIsoLimits;
470
502
  };
471
503
  ```
472
504
 
@@ -521,6 +553,16 @@ export type ProgramAutoExposureModeResolution =
521
553
  ExposureTargetResidual,
522
554
  { status: "resolved" }
523
555
  >;
556
+ /**
557
+ * #504: present only with `isoControl.slowestPreferredShutter`. `applied` is true when the line's
558
+ * shutter was longer than the slowest preferred shutter and was held; `afterMaximumIsoFallbackUsed`
559
+ * when the slower-shutter fallback ran at the Auto ISO maximum. `programLineSelection` stays the
560
+ * line's own selection.
561
+ */
562
+ slowestPreferredShutter?: ProgramSlowestPreferredShutterPolicy & {
563
+ applied: boolean;
564
+ afterMaximumIsoFallbackUsed: boolean;
565
+ };
524
566
  })
525
567
  | {
526
568
  resolverVersion:
@@ -573,6 +615,14 @@ export type ProgramAutoIsoControl =
573
615
  "minimum-selectable";
574
616
  isoQuantizationPolicy:
575
617
  "nearest-log2-lower-on-tie";
618
+ /** #505: optional photographer-set Auto ISO limits inside the body's Auto ISO range. */
619
+ autoIsoLimits?: AutoIsoLimits;
620
+ /**
621
+ * #504: optionally hold the program line's shutter at or faster than a slowest preferred shutter
622
+ * before the ISO rises (for example from `estimateAutoIsoSlowestShutter()`). Absent, the program line
623
+ * is followed as before.
624
+ */
625
+ slowestPreferredShutter?: ProgramSlowestPreferredShutterPolicy;
576
626
  };
577
627
  ```
578
628
 
@@ -611,6 +661,24 @@ export interface ProgramLineSelectionDiagnostics {
611
661
  }
612
662
  ```
613
663
 
664
+ ## ProgramSlowestPreferredShutterPolicy
665
+
666
+ #504: Program + Auto ISO's slowest preferred shutter, the two-axis policy of Aperture Priority + Auto
667
+ ISO applied to the program line. When the line asks for a longer shutter than
668
+ `slowestPreferredShutterSeconds`, the shutter is held there (snapped, never longer) with the line's
669
+ aperture, and Auto ISO makes up the rest. At the Auto ISO maximum, `afterMaximumIso` decides:
670
+ `"allow-slower-shutter"` lengthens the shutter toward the target, but never past the program line's
671
+ own shutter; `"hold-preferred-shutter"` keeps it and leaves the exposure under target.
672
+
673
+ ```ts
674
+ export interface ProgramSlowestPreferredShutterPolicy {
675
+ slowestPreferredShutterSeconds: number;
676
+ afterMaximumIso:
677
+ | "allow-slower-shutter"
678
+ | "hold-preferred-shutter";
679
+ }
680
+ ```
681
+
614
682
  ## RelativeExposureControlAnchor
615
683
 
616
684
  The exact contract is declared below; use the domain guide for assumptions and staged integration.
@@ -21,8 +21,12 @@ export interface DisplayViewResult {
21
21
  */
22
22
  view: { width: number; height: number; frameWidth: number; frameHeight: number; scaleX: number; scaleY: number; samplesPerAxis: number;
23
23
  region?: { x: number; y: number; width: number; height: number } };
24
- /** The resolved exposure of the capture this view shows. */
25
- exposure: { mode: SimulateCaptureExposureMode; settings: { fNumber: number; shutterSeconds: number; iso: number }; ev100: number };
24
+ /** The resolved exposure of the capture this view shows; `standardExposureDeviationStops` and `standardExposureIlluminanceLux` as in {@link SimulateCaptureResult}. */
25
+ exposure: { mode: SimulateCaptureExposureMode; settings: { fNumber: number; shutterSeconds: number; iso: number }; ev100: number; standardExposureDeviationStops: number | null;
26
+ /** As in the capture result. */
27
+ standardExposureIlluminanceLux: number | null;
28
+ /** #516: as in the capture result. */
29
+ isoControl: "manual" | "automatic"; autoIso?: SimulateCaptureAutoIsoReport };
26
30
  scene: SimulateCaptureResult["scene"];
27
31
  /**
28
32
  * Work done: real pixels sampled, real-site expectations evaluated, scene queries, display pixels sampled in full (detail) or with
@@ -53,7 +57,9 @@ export interface IdealReferenceResult {
53
57
  engineApiVersion: string;
54
58
  image: { width: number; height: number; format: SimulateCaptureOutputFormat; samples: Uint8Array | Uint16Array; png?: Uint8Array; colorEncoding?: SimulateCaptureColorEncoding };
55
59
  /** The resolved exposure of the capture this reference matches. */
56
- exposure: { mode: SimulateCaptureExposureMode; settings: { fNumber: number; shutterSeconds: number; iso: number }; ev100: number };
60
+ exposure: { mode: SimulateCaptureExposureMode; settings: { fNumber: number; shutterSeconds: number; iso: number }; ev100: number;
61
+ /** #516: as in the capture result. */
62
+ isoControl: "manual" | "automatic"; autoIso?: SimulateCaptureAutoIsoReport };
57
63
  scene: SimulateCaptureResult["scene"];
58
64
  region?: { x: number; y: number; width: number; height: number; frameWidth: number; frameHeight: number };
59
65
  diagnostics: { sampleCount: number; timingsMs: { reference: number; total: number } };
@@ -163,6 +169,104 @@ export interface SimulateCaptureAssumption {
163
169
  }
164
170
  ```
165
171
 
172
+ ## SimulateCaptureAutoIsoAfterMaximum
173
+
174
+ #516: what aperture priority does with Auto ISO once the ISO reaches the body's Auto ISO maximum and the exposure is
175
+ still under the meter target: `"allow-slower-shutter"` lengthens the shutter past the slowest preferred shutter to reach
176
+ the target; `"hold-preferred-shutter"` keeps the preferred shutter and leaves the exposure under target.
177
+
178
+ ```ts
179
+ export type SimulateCaptureAutoIsoAfterMaximum = "allow-slower-shutter" | "hold-preferred-shutter";
180
+ ```
181
+
182
+ ## SimulateCaptureAutoIsoReport
183
+
184
+ #516: how Auto ISO chose the ISO (`exposure.iso: "auto"`), reported in the result's `exposure.autoIso`. The full
185
+ resolver output stays in the capture's `exposure.resolution`.
186
+
187
+ ```ts
188
+ export interface SimulateCaptureAutoIsoReport {
189
+ /** The ISO the meter target asked for at the resolved aperture and shutter, before the Auto ISO range and the ISO grid. */
190
+ idealIsoBeforeConstraints: number;
191
+ /**
192
+ * The body's Auto ISO range: its Auto ISO bounds, or its ISO range when it declares none (the tiers: ISO 100 to their
193
+ * maximum). Without `limits` the ISO was chosen in this range.
194
+ */
195
+ autoIsoMinimum: number;
196
+ autoIsoMaximum: number;
197
+ /**
198
+ * #505: present only with `exposure.autoIso.limits`: the photographer's Auto ISO range the ISO was chosen in, inside the
199
+ * body's (`minimum` is the body's Auto ISO minimum when the option omits it).
200
+ */
201
+ limits?: { minimum: number; maximum: number };
202
+ /** Whether the ISO was held at an end of the Auto ISO range. */
203
+ clamped: false | "minimum" | "maximum";
204
+ /**
205
+ * The constraint that limited the resolved exposure: the resolver's `targetResidual.limitingConstraint`, for example
206
+ * `"iso-maximum"` (the exposure stays under target at the body's Auto ISO maximum), `"user-iso-maximum"` (#505: at the
207
+ * photographer's `limits.maximum`, below the body's), `"iso-grid-quantization"` (the nearest selectable ISO) or `"none"`
208
+ * (on target).
209
+ */
210
+ limitingConstraint: ExposureResolutionConstraint;
211
+ /** The resolver's residual, log2(target / achieved exposure), in stops: positive when the exposure is under the meter target, negative when over it. */
212
+ residualStops: number;
213
+ /**
214
+ * Aperture priority, and program with a slowest preferred shutter (#504): the policy and slowest preferred shutter used,
215
+ * and whether the slower-shutter fallback ran.
216
+ */
217
+ afterMaximumIso?: SimulateCaptureAutoIsoAfterMaximum;
218
+ slowestPreferredShutterSeconds?: number;
219
+ afterMaximumIsoFallbackUsed?: boolean;
220
+ /** #504, program only: whether the program line's shutter was longer than the slowest preferred shutter and was held. */
221
+ slowestPreferredShutterApplied?: boolean;
222
+ /** #504: present only with `slowestPreferredShutterSeconds: "auto"`: how the engine derived it (`estimateAutoIsoSlowestShutter`). */
223
+ automaticSlowestShutter?: SimulateCaptureAutomaticSlowestShutterReport;
224
+ }
225
+ ```
226
+
227
+ ## SimulateCaptureAutomaticSlowestShutterOptions
228
+
229
+ #504: the settings of `exposure.autoIso.slowestPreferredShutterSeconds: "auto"`. Every field is optional; the defaults are
230
+ labelled generic values, recorded as `default` assumptions.
231
+
232
+ ```ts
233
+ export interface SimulateCaptureAutomaticSlowestShutterOptions {
234
+ /** The bias in whole stops, as cameras' Auto minimum-shutter settings offer: +1 one stop faster, −1 one stop slower; −2 to +2. Default 0. */
235
+ fasterByStops?: number;
236
+ /** The shake blur allowed, in sensor pixels of `sensor.pixelPitchMicrometers`. Default 1 (a generic pixel-level budget). */
237
+ blurBudgetPixels?: number;
238
+ /**
239
+ * The hand's constant angular velocity, rad/s (signs as in `motion.angularVelocityRadPerSec`; only the magnitude matters).
240
+ * Default `GENERIC_HANDHELD_ANGULAR_VELOCITY_RAD_PER_SEC`: 4.39 °/s on yaw, a cited generic value for a 100 g phone held in
241
+ * two hands (Xiao et al., Proc. SPIE 7250, 2009), not a measurement of you or of this camera.
242
+ */
243
+ angularVelocityRadPerSec?: { yaw: number; pitch: number };
244
+ /** Stabilization in equivalent stops (≥ 0, at most 10), scaling the motion by 2^−stops. Default 0: the tiers declare no stabilization. */
245
+ stabilizationStopsEquivalent?: number;
246
+ }
247
+ ```
248
+
249
+ ## SimulateCaptureAutomaticSlowestShutterReport
250
+
251
+ #504: the automatic slowest preferred shutter in the Auto ISO report.
252
+
253
+ ```ts
254
+ export interface SimulateCaptureAutomaticSlowestShutterReport {
255
+ /** The exposure time whose camera-shake blur equals the budget (`estimateCameraShakeBlur`), before the bias and the grid. */
256
+ derivedShutterSeconds: number;
257
+ fasterByStops: number;
258
+ /** derivedShutterSeconds · 2^−fasterByStops. */
259
+ biasedShutterSeconds: number;
260
+ /** The slowest preferred shutter used: the biased time within the body's shutter range. */
261
+ shutterSeconds: number;
262
+ blurBudgetPixels: number;
263
+ /** The shake blur at `shutterSeconds`, in pixels. */
264
+ blurAtShutterPixels: number;
265
+ angularVelocityRadPerSec: { yaw: number; pitch: number };
266
+ stabilizationStopsEquivalent: number;
267
+ }
268
+ ```
269
+
166
270
  ## SimulateCaptureBorderHandling
167
271
 
168
272
  `margin` renders extra native sites around the output, as far as the kernel reaches. `mirror` renders
@@ -338,8 +442,34 @@ export interface SimulateCaptureOptions {
338
442
  exposure?: {
339
443
  /** Default "manual". */
340
444
  mode?: SimulateCaptureExposureMode;
341
- /** Default 100. */
342
- iso?: number;
445
+ /**
446
+ * Default 100. #516: `"auto"` lets the body's Auto ISO choose the ISO from the meter target, within its Auto ISO range
447
+ * (the tiers: ISO 100 to their maximum), in every mode: manual keeps the aperture and shutter; aperture priority keeps the
448
+ * aperture and follows `autoIso`; shutter priority picks the aperture at the lowest Auto ISO (no wider than the target
449
+ * needs, so at most the widest) and raises the ISO for the rest; program picks the program line at the lowest Auto ISO and
450
+ * raises the ISO beyond the line's end. The result reports the ISO in `exposure.settings.iso` and how it was chosen in
451
+ * `exposure.autoIso`.
452
+ */
453
+ iso?: number | "auto";
454
+ /**
455
+ * Auto ISO settings, with `iso: "auto"` only.
456
+ *
457
+ * - `slowestPreferredShutterSeconds` and `afterMaximumIso` (#516): aperture priority, where the slowest shutter is
458
+ * required, and (#504) program, where it is optional. The shutter gets as long as `slowestPreferredShutterSeconds`
459
+ * (seconds, within the body's shutter range) at the lowest Auto ISO; beyond it the ISO rises; at the Auto ISO maximum
460
+ * `afterMaximumIso` decides (default `"allow-slower-shutter"`; in program the shutter then never gets longer than the
461
+ * program line's own). Without it, program follows its line as before.
462
+ * - `slowestPreferredShutterSeconds: "auto"` (#504): the engine derives it from the focal length, the pixel pitch and a
463
+ * hand-shake profile with `estimateAutoIsoSlowestShutter` (the camera-shake model, not the 1/focal-length rule of
464
+ * thumb); `automaticSlowestShutter` sets the bias, the blur budget, the shake and the stabilization.
465
+ * - `limits` (#505): every mode. The photographer's Auto ISO range `{ maximum, minimum? }`, a choice kept apart from
466
+ * the body's Auto ISO capability: both are selectable ISOs inside the body's Auto ISO range, `minimum` at most
467
+ * `maximum` (default: the body's minimum). Outside the body's range or off its ISO grid the request fails; the
468
+ * engine never clips a limit. When the ISO stops at a limit strictly inside the body's range, the report's
469
+ * `limitingConstraint` is `"user-iso-maximum"` or `"user-iso-minimum"`.
470
+ */
471
+ autoIso?: { slowestPreferredShutterSeconds?: number | "auto"; afterMaximumIso?: SimulateCaptureAutoIsoAfterMaximum; limits?: AutoIsoLimits;
472
+ automaticSlowestShutter?: SimulateCaptureAutomaticSlowestShutterOptions };
343
473
  /** Shutter for manual and shutter-priority modes. Default 1/125 s. */
344
474
  shutterSeconds?: number;
345
475
  /** Exposure compensation applied to the meter target, in stops. Default 0. */
@@ -548,7 +678,25 @@ export interface SimulateCaptureResult {
548
678
  settings: { fNumber: number; shutterSeconds: number; iso: number };
549
679
  /** Exposure value at ISO 100 of the settings, log2(N²/t). */
550
680
  ev100: number;
681
+ /** #516: `"automatic"` when the request asked for `iso: "auto"`, else `"manual"`. */
682
+ isoControl: "manual" | "automatic";
683
+ /** #516: present with `iso: "auto"`: how Auto ISO chose `settings.iso` and what limited it. */
684
+ autoIso?: SimulateCaptureAutoIsoReport;
551
685
  meteredSceneLuminanceCdPerSquareMeter: number;
686
+ /**
687
+ * #515: deviation, in stops, of the exposure used from the CIPA DC-004-2020 standard exposure for the metered luminance L:
688
+ * log2(N²/t) − log2(0.65·L·S/10). Positive is darker than the standard exposure (under), negative brighter (over). In an
689
+ * automatic mode it is the compensation with the opposite sign plus any step or limit the resolver hit. Finite, or null when
690
+ * the metered luminance is 0 (a black scene has no standard exposure).
691
+ */
692
+ standardExposureDeviationStops: number | null;
693
+ /**
694
+ * The illuminance, lux, at which the settings used give the standard exposure: E·L_ref(N, t, S)/L = E·2^Δ, with E the
695
+ * request's `illuminanceLux`. Exact for a reflective scene (a chart, a reflectance function or a material scene), whose
696
+ * metered luminance is proportional to E. Null for a black scene (L = 0) and for a radiance-function scene, whose
697
+ * radiance does not depend on the illuminance. It may exceed the 1e7 lux that `illuminanceLux` accepts.
698
+ */
699
+ standardExposureIlluminanceLux: number | null;
552
700
  /** Full output of the existing exposure-mode resolver. */
553
701
  resolution: unknown;
554
702
  };
@@ -2,6 +2,40 @@
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
+ ## addJpegTextMetadata
6
+
7
+ Returns a copy of a baseline JPEG with the text fields added: merged into its XMP APP1 packet (or a new packet
8
+ after the last leading APPn segment when it has none), plus a COM segment for `comment`. Every other byte,
9
+ including the entropy-coded data, is unchanged. Reads marker segments only up to SOS. The engine's own
10
+ `output.metadata` path calls this function, so for the same JPEG and text the bytes are the same.
11
+
12
+ Root export (#388). `metadata` is checked as `output.metadata` is (see the module notes); a JPEG without SOI,
13
+ a malformed or truncated segment before SOS, an XMP APP1 segment after a non-APPn segment, an XMP packet that is
14
+ not UTF-8 or has no `rdf:RDF`, or text that makes the packet larger than one APP1 segment fails with
15
+ `InvalidConfigurationError`. The input is not changed.
16
+
17
+ ```ts
18
+ export function addJpegTextMetadata(jpeg: Uint8Array, metadata: Readonly<OutputTextMetadata>): Uint8Array;
19
+ ```
20
+
21
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
22
+
23
+ ## addPngTextMetadata
24
+
25
+ Returns a copy of a PNG with one text chunk per field inserted before the first IDAT chunk. Other chunks are
26
+ unchanged. The engine's own `output.metadata` path calls this function, so for the same PNG and text the bytes
27
+ are the same.
28
+
29
+ Root export (#388). `metadata` is checked as `output.metadata` is (see the module notes); a file without the PNG
30
+ signature, or without an IDAT chunk inside its bytes, fails with `InvalidConfigurationError`. Chunk CRCs before
31
+ IDAT are not checked. The input is not changed.
32
+
33
+ ```ts
34
+ export function addPngTextMetadata(png: Uint8Array, metadata: Readonly<OutputTextMetadata>): Uint8Array;
35
+ ```
36
+
37
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
38
+
5
39
  ## OutputTextMetadata
6
40
 
7
41
  Caller-supplied text written into JPEG, PNG and DNG outputs. Every field is optional; at least one must be set.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@photivra/engine",
3
- "version": "2.11.1",
3
+ "version": "2.13.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",