@photivra/engine 2.0.0 → 2.2.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 (77) hide show
  1. package/CHANGELOG.md +39 -1
  2. package/CITATION.cff +1 -1
  3. package/README.md +6 -6
  4. package/SECURITY.md +1 -1
  5. package/dist/capture/export-jpeg.js +7 -1
  6. package/dist/capture/export-jpeg.js.map +1 -1
  7. package/dist/capture/native-environment-lean.js +5 -2
  8. package/dist/capture/native-environment-lean.js.map +1 -1
  9. package/dist/capture/native-environment-raw.js +2 -2
  10. package/dist/capture/native-environment-raw.js.map +1 -1
  11. package/dist/capture/native-raw-development.js +1 -1
  12. package/dist/capture/native-raw-development.js.map +1 -1
  13. package/dist/capture/native-raw.d.ts +16 -0
  14. package/dist/capture/native-raw.js +16 -3
  15. package/dist/capture/native-raw.js.map +1 -1
  16. package/dist/capture/packed-sensor-raw.d.ts +7 -0
  17. package/dist/capture/packed-sensor-raw.js +6 -2
  18. package/dist/capture/packed-sensor-raw.js.map +1 -1
  19. package/dist/core/pixel-limits.d.ts +14 -0
  20. package/dist/core/pixel-limits.js +40 -0
  21. package/dist/core/pixel-limits.js.map +1 -0
  22. package/dist/core/version.d.ts +1 -1
  23. package/dist/core/version.js +1 -1
  24. package/dist/core/version.js.map +1 -1
  25. package/dist/facade/chart-radiance-batch.d.ts +7 -0
  26. package/dist/facade/chart-radiance-batch.js +7 -3
  27. package/dist/facade/chart-radiance-batch.js.map +1 -1
  28. package/dist/facade/diffraction-provider.d.ts +30 -0
  29. package/dist/facade/diffraction-provider.js +127 -0
  30. package/dist/facade/diffraction-provider.js.map +1 -0
  31. package/dist/facade/host-yield.d.ts +1 -1
  32. package/dist/facade/host-yield.js +0 -4
  33. package/dist/facade/host-yield.js.map +1 -1
  34. package/dist/facade/simulate-capture.d.ts +88 -2
  35. package/dist/facade/simulate-capture.js +343 -41
  36. package/dist/facade/simulate-capture.js.map +1 -1
  37. package/dist/index.d.ts +5 -2
  38. package/dist/index.js +4 -2
  39. package/dist/index.js.map +1 -1
  40. package/dist/motion/temporal-exposure-sum.d.ts +7 -0
  41. package/dist/motion/temporal-exposure-sum.js +6 -2
  42. package/dist/motion/temporal-exposure-sum.js.map +1 -1
  43. package/dist/optics/diffraction-psf-profile.d.ts +118 -0
  44. package/dist/optics/diffraction-psf-profile.js +252 -0
  45. package/dist/optics/diffraction-psf-profile.js.map +1 -0
  46. package/dist/optics/irradiance-convolution.d.ts +7 -0
  47. package/dist/optics/irradiance-convolution.js +6 -2
  48. package/dist/optics/irradiance-convolution.js.map +1 -1
  49. package/dist/output/native-capture-sdr.d.ts +7 -0
  50. package/dist/output/native-capture-sdr.js +6 -3
  51. package/dist/output/native-capture-sdr.js.map +1 -1
  52. package/dist/output/png-encoder.d.ts +10 -1
  53. package/dist/output/png-encoder.js +10 -4
  54. package/dist/output/png-encoder.js.map +1 -1
  55. package/dist/output/print-jpeg.d.ts +18 -0
  56. package/dist/output/print-jpeg.js +15 -4
  57. package/dist/output/print-jpeg.js.map +1 -1
  58. package/dist/sensor/sampling-aperture.d.ts +15 -0
  59. package/dist/sensor/sampling-aperture.js +31 -10
  60. package/dist/sensor/sampling-aperture.js.map +1 -1
  61. package/docs/API_REFERENCE.md +27 -1
  62. package/docs/QUICK_START.md +285 -13
  63. package/docs/USAGE.md +3 -1
  64. package/docs/api/capture-native-raw.md +11 -2
  65. package/docs/api/capture-packed-sensor-raw.md +7 -1
  66. package/docs/api/core-pixel-limits.md +19 -0
  67. package/docs/api/core-version.md +1 -1
  68. package/docs/api/exports.json +62 -2
  69. package/docs/api/facade-chart-radiance-batch.md +8 -2
  70. package/docs/api/facade-simulate-capture.md +83 -3
  71. package/docs/api/motion-temporal-exposure-sum.md +7 -1
  72. package/docs/api/optics-diffraction-psf-profile.md +96 -0
  73. package/docs/api/optics-irradiance-convolution.md +7 -1
  74. package/docs/api/output-native-capture-sdr.md +7 -2
  75. package/docs/api/output-png-encoder.md +8 -1
  76. package/docs/api/output-print-jpeg.md +17 -0
  77. package/package.json +3 -2
@@ -24,10 +24,11 @@ This call is synchronous; any supplied provider must follow its explicit synchro
24
24
 
25
25
  ## CHART_RADIANCE_BATCH_LIMITS
26
26
 
27
- The exact contract is declared below; use the domain guide for assumptions and staged integration.
27
+ `maximumQueries` is the default batch limit; a request may raise it with its own `maximumQueries`, up to `ceilingQueries`.
28
28
 
29
29
  ```ts
30
- CHART_RADIANCE_BATCH_LIMITS = Object.freeze({ maximumQueries: 24_000_000, maximumWavelengths: 256, maximumChannels: 4 })
30
+ CHART_RADIANCE_BATCH_LIMITS = Object.freeze({ maximumQueries: 24_000_000, ceilingQueries: FULL_RESOLUTION_CEILING_PIXELS, maximumWavelengths: 256,
31
+ maximumChannels: 4 })
31
32
  ```
32
33
 
33
34
  ## CHART_RADIANCE_BATCH_SCHEMA_VERSION
@@ -59,6 +60,11 @@ export interface ChartRadianceBatchInput {
59
60
  wavelengthIndices?: Uint8Array | Uint16Array;
60
61
  /** Spectral integration: per channel (1–4), one finite weight per wavelength. */
61
62
  channelWeights?: readonly (readonly number[])[];
63
+ /**
64
+ * Optional opt-in batch limit: an integer from 1 to `CHART_RADIANCE_BATCH_LIMITS.ceilingQueries`. Default
65
+ * `CHART_RADIANCE_BATCH_LIMITS.maximumQueries` (24 000 000). It does not change the values.
66
+ */
67
+ maximumQueries?: number;
62
68
  }
63
69
  ```
64
70
 
@@ -2,6 +2,18 @@
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
+ ## SIMULATE_CAPTURE_COST_OPTIONS
6
+
7
+ Options reported in `diagnostics.options` (2.1), in this order, when they ran: the ones that change how much work
8
+ a capture does. `motion.rotation` is a non-zero angular velocity; `sensor.temporalSampleCount` a count above 1.
9
+
10
+ ```ts
11
+ SIMULATE_CAPTURE_COST_OPTIONS = Object.freeze(["lens.pupilSampling", "lens.projection", "lens.distortion", "lens.vignetting", "lens.psf",
12
+ "lens.diffraction", "sensor.antiAliasing", "sensor.temporalSampleCount", "motion.rotation", "motion.shutter.rolling", "output.borderHandling.mirror",
13
+ "output.whiteBalance.auto", "output.demosaic.ahd", "output.highlightMode.reconstruct", "output.colorEncoding.wide-gamut", "output.gamutMapping.perceptual",
14
+ "output.format.jpeg", "output.format.png", "output.format.dng"] as const)
15
+ ```
16
+
5
17
  ## SIMULATE_CAPTURE_DEFAULT_ILLUMINANCE_LUX
6
18
 
7
19
  Default illuminance, lux: an 18 % Lambertian grey card (L = 0.18·E/π) then gets exactly the CIPA DC-004 standard
@@ -70,12 +82,22 @@ PQ and HLG (BT.2100) are not part of 2.0; see THIRD_PARTY.md.
70
82
  export type SimulateCaptureColorEncoding = "srgb" | "display-p3" | "rec2020";
71
83
  ```
72
84
 
85
+ ## SimulateCaptureCostOption
86
+
87
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
88
+
89
+ ```ts
90
+ export type SimulateCaptureCostOption = typeof SIMULATE_CAPTURE_COST_OPTIONS[number];
91
+ ```
92
+
73
93
  ## SimulateCaptureDemosaic
74
94
 
75
- Built-in Bayer demosaic kernels (`createBuiltInBayerDemosaicProfiles`).
95
+ Demosaic. `bilinear`: the built-in Bayer kernels (`createBuiltInBayerDemosaicProfiles`). `ahd` (2.1): adaptive
96
+ homogeneity-directed demosaicing (`createAhdDemosaicTask`, after Hirakawa & Parks 2005), a non-linear
97
+ edge-directed method that reduces zipper and false colour on sharp edges.
76
98
 
77
99
  ```ts
78
- export type SimulateCaptureDemosaic = "bilinear";
100
+ export type SimulateCaptureDemosaic = "bilinear" | "ahd";
79
101
  ```
80
102
 
81
103
  ## SimulateCaptureExposureMode
@@ -139,6 +161,13 @@ export interface SimulateCaptureOptions {
139
161
  * focus must equal the resolved capture settings exactly. Not combinable with pupilSampling.
140
162
  */
141
163
  psf?: Extract<CalculateEnvironmentSensorPhotoSignalInput["psf"], { kind: "sampled-local" }>;
164
+ /**
165
+ * Opt-in (2.1) diffraction blur of an ideal, aberration-free, in-focus lens with a circular pupil at the
166
+ * capture's resolved f-number: the engine builds the Airy kernels (`createDiffractionLimitedPsfProfile`)
167
+ * on the site pitch. `kernelRadiusSamples` (0–32) sets the kernel window; default: the third dark ring at
168
+ * the longest spectral node. Not combinable with `psf` or `pupilSampling`. Default off.
169
+ */
170
+ diffraction?: SimulateCaptureLensDiffraction;
142
171
  };
143
172
  exposure?: {
144
173
  /** Default "manual". */
@@ -189,6 +218,13 @@ export interface SimulateCaptureOptions {
189
218
  borderHandling?: SimulateCaptureBorderHandling;
190
219
  /** DNG only: optional capture-event time (YYYY-MM-DDTHH:mm:ss.sssZ); without it no date tags are written. */
191
220
  dng?: { capturedAtUtc?: string };
221
+ /**
222
+ * Opt-in large images: the most native sensor sites (the output plus the demosaic margin on each side) the
223
+ * capture may allocate, an integer up to `NATIVE_RAW_LIMITS.ceilingPixels` (2^27). With it, `width` and
224
+ * `height` may each reach 16 384 sites including the margin; without it they stay at most 4094. Time and
225
+ * memory grow with the pixel count (see "Large images" in the quick start); browsers may fail at large sizes.
226
+ */
227
+ maximumPixels?: number;
192
228
  };
193
229
  /** Camera motion and shutter type. Default: still camera, global electronic shutter. */
194
230
  motion?: SimulateCaptureMotion;
@@ -201,6 +237,11 @@ export interface SimulateCaptureOptions {
201
237
  temporalSampleCount?: number;
202
238
  /** Noise seed (uint32). Default 1. */
203
239
  noiseSeed?: number;
240
+ /**
241
+ * Opt-in (2.1) optical anti-aliasing filter (birefringent point splitting, `SimulateCaptureSensorAntiAliasing`).
242
+ * Multiplies the scene queries by the number of split points. Default off (the tiers declare no filter).
243
+ */
244
+ antiAliasing?: SimulateCaptureSensorAntiAliasing;
204
245
  };
205
246
  /** Aborting cancels the running task; the promise rejects with a domain error and returns no partial image. */
206
247
  signal?: AbortSignal;
@@ -262,6 +303,14 @@ export interface SimulateCaptureResult {
262
303
  siteInput(nativeIndex: number): SimulateEnvironmentSensorRawFrameInput["sites"][number];
263
304
  development: Omit<NativeRawDevelopmentInput, "raw">;
264
305
  whiteBalanceState: ResolvedWhiteBalanceState | null;
306
+ /**
307
+ * Present with lens.diffraction (2.1): the engine-built Airy profile, the equivalent environment sampled PSF
308
+ * selection, and how it was applied. With `object-space-convolution` the site inputs declare no PSF and the
309
+ * provider answers with the PSF-convolved radiance; putting `psf` into each site input and using the plain
310
+ * scene provider gives the same expected signal (to float rounding) through the per-tap environment quadrature.
311
+ */
312
+ diffraction?: { profile: DiffractionLimitedPsfProfile; psf: Extract<CalculateEnvironmentSensorPhotoSignalInput["psf"], { kind: "sampled-local" }>;
313
+ execution: "object-space-convolution" | "environment-psf-quadrature" };
265
314
  };
266
315
  exposure: {
267
316
  mode: SimulateCaptureExposureMode;
@@ -295,7 +344,20 @@ export interface SimulateCaptureResult {
295
344
  rendering: SdrRenderingResult["diagnostics"];
296
345
  /** Present with output.gamutMapping "perceptual": pixels whose chroma or lightness the mapping changed. */
297
346
  gamutMapping?: { method: "cielab-hue-preserving-chroma-compression"; kneeFraction: number; adjustedPixelCount: number; lightnessClippedPixelCount: number };
298
- timingsMs: { raw: number; whiteBalance: number; development: number; jpeg: number; total: number };
347
+ /**
348
+ * Wall-clock milliseconds per stage. `raw`, `whiteBalance` (the second RAW pass of auto white balance), `development`
349
+ * (includes DNG writing), `jpeg` and `total` are whole milliseconds (`Date.now()`). Since 2.1, where a stage ran:
350
+ * `diffraction` (the object-space convolution of `lens.diffraction`; part of `raw` and `whiteBalance`), `demosaic`
351
+ * (the AHD demosaic; part of `development`) and `encode` (PNG, JPEG or DNG serialization), with sub-millisecond
352
+ * resolution where the host has `performance.now()`. Timings vary from run to run and never enter any digest or output byte.
353
+ */
354
+ timingsMs: { raw: number; whiteBalance: number; development: number; jpeg: number; total: number; diffraction?: number; demosaic?: number; encode?: number };
355
+ /**
356
+ * 2.1: the opt-in options that changed the work of this run, in a fixed order (see `SIMULATE_CAPTURE_COST_OPTIONS`).
357
+ * Empty for a capture with every default. With `timingsMs` it lets an application measure each option's cost on
358
+ * the user's device.
359
+ */
360
+ options: readonly SimulateCaptureCostOption[];
299
361
  };
300
362
  provenance: {
301
363
  scientificStatus: "approximation";
@@ -310,6 +372,24 @@ export interface SimulateCaptureResult {
310
372
  }
311
373
  ```
312
374
 
375
+ ## SimulateCaptureSensorAntiAliasing
376
+
377
+ Optical anti-aliasing (low-pass) filter (2.1), modelled as birefringent point splitting: each image point is
378
+ split into equally bright copies (the engine's `normalized-point-splitting-kernel` optical-stack response).
379
+ - `four-spot`: two crossed birefringent plates; copies at (±d/2, ±d/2), weight 1/4 each, MTF |cos(π·f_x·d)·cos(π·f_y·d)|.
380
+ - `two-spot`: one plate splitting along `axis`; copies at ±d/2, weight 1/2 each, MTF |cos(π·f·d)|.
381
+ - `point-splitting`: the engine contract itself, offsets in micrometres (+x right, +y down) and positive weights
382
+ summing to 1 (at most 16 components, each within 4 sites).
383
+ d = `separationSites` × pixel pitch, 0 < separationSites ≤ 4, default 1 (first MTF zero at the sensor's Nyquist
384
+ frequency 1/(2·pitch)).
385
+
386
+ ```ts
387
+ export type SimulateCaptureSensorAntiAliasing =
388
+ | { kind: "four-spot"; separationSites?: number }
389
+ | { kind: "two-spot"; axis: "horizontal" | "vertical"; separationSites?: number }
390
+ | { kind: "point-splitting"; components: readonly AntiAliasingPointSplitComponent[] };
391
+ ```
392
+
313
393
  ## SimulateCaptureWhiteBalance
314
394
 
315
395
  The exact contract is declared below; use the domain guide for assumptions and staged integration.
@@ -24,11 +24,12 @@ This call is synchronous; any supplied provider must follow its explicit synchro
24
24
 
25
25
  ## TEMPORAL_EXPOSURE_SUM_LIMITS
26
26
 
27
- The exact contract is declared below; use the domain guide for assumptions and staged integration.
27
+ `maximumPixels` is the default grid limit; a request may raise it with its own `maximumPixels`, up to `ceilingPixels`.
28
28
 
29
29
  ```ts
30
30
  TEMPORAL_EXPOSURE_SUM_LIMITS = Object.freeze({
31
31
  maximumPixels: 24_000_000,
32
+ ceilingPixels: FULL_RESOLUTION_CEILING_PIXELS,
32
33
  maximumDimension: 16_384,
33
34
  maximumTemporalSamples: 256,
34
35
  maximumDisplacementSites: 4_096,
@@ -63,6 +64,11 @@ export interface TemporalExposureSumInput {
63
64
  /** Quadrature weights `[k][row]` (nodes × height), finite and non-negative, for example seconds of each row's window near t_k. */
64
65
  rowWeights: Float32Array | Float64Array;
65
66
  boundary: "zero-outside-grid";
67
+ /**
68
+ * Optional opt-in grid limit for large images: an integer from 1 to `TEMPORAL_EXPOSURE_SUM_LIMITS.ceilingPixels`.
69
+ * Default `TEMPORAL_EXPOSURE_SUM_LIMITS.maximumPixels` (24 000 000). It does not change the result.
70
+ */
71
+ maximumPixels?: number;
66
72
  }
67
73
  ```
68
74
 
@@ -0,0 +1,96 @@
1
+ # optics/diffraction-psf-profile.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
+ ## createDiffractionLimitedPsfProfile
6
+
7
+ Creates a sampled lens PSF profile for an ideal diffraction-limited lens with a circular pupil: one
8
+ unit-sum Airy kernel per wavelength on one shared (2R+1)² grid, with the truncated energy reported per
9
+ wavelength. The profile declares the given focal length, f-number and focus exactly, and the field axes
10
+ ±`fieldHalfExtentMm` with the same kernel at every field node.
11
+
12
+ ```ts
13
+ export function createDiffractionLimitedPsfProfile(input: CreateDiffractionLimitedPsfProfileInput): CalculationResult<DiffractionLimitedPsfProfile>;
14
+ ```
15
+
16
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
17
+
18
+ ## CreateDiffractionLimitedPsfProfileInput
19
+
20
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
21
+
22
+ ```ts
23
+ export interface CreateDiffractionLimitedPsfProfileInput {
24
+ /** Only `circular` in 2.1.0 (polygon pupils: `calculateIdealPolygonDiffractionPsf` gives point densities). */
25
+ aperture: "circular";
26
+ focalLengthMm: number;
27
+ apertureFNumber: number;
28
+ focus: FocusPlane;
29
+ /** Strictly increasing wavelengths, nm, in the medium of propagation (no air/vacuum conversion is applied). 1–64. */
30
+ wavelengthsNm: readonly number[];
31
+ /** Square sample pitch of every kernel, µm. */
32
+ samplePitchMicrometers: number;
33
+ /** Kernel half-width in samples; each kernel is (2R + 1)². 0–32. */
34
+ kernelRadiusSamples: number;
35
+ /** Half-extent of the image field over which the on-axis PSF is declared (profile field axes ±x, ±y), mm. */
36
+ fieldHalfExtentMm: { x: number; y: number };
37
+ profileId: string;
38
+ }
39
+ ```
40
+
41
+ ## DIFFRACTION_LIMITED_PSF_PROFILE_LIMITS
42
+
43
+ Input limits: at most 64 wavelengths, kernel radius 0–32 samples (the irradiance-convolution limit).
44
+
45
+ ```ts
46
+ DIFFRACTION_LIMITED_PSF_PROFILE_LIMITS = Object.freeze({ maximumWavelengths: 64, maximumKernelRadiusSamples: 32 })
47
+ ```
48
+
49
+ ## DIFFRACTION_LIMITED_PSF_PROFILE_MODEL
50
+
51
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
52
+
53
+ ```ts
54
+ DIFFRACTION_LIMITED_PSF_PROFILE_MODEL = Object.freeze({
55
+ modelId: "photivra-diffraction-limited-circular-pupil-psf" as const, modelVersion: "1.0.0" as const })
56
+ ```
57
+
58
+ ## DiffractionLimitedPsfProfile
59
+
60
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
61
+
62
+ ```ts
63
+ export interface DiffractionLimitedPsfProfile {
64
+ profile: LensSampledPsfProfile;
65
+ model: typeof DIFFRACTION_LIMITED_PSF_PROFILE_MODEL;
66
+ pupilDiameterMm: number;
67
+ pupilToImageDistanceMm: number;
68
+ workingFNumber: number;
69
+ kernelRadiusSamples: number;
70
+ samplePitchMicrometers: number;
71
+ wavelengths: readonly DiffractionLimitedPsfWavelength[];
72
+ /** Largest truncated energy over the wavelengths. */
73
+ maximumTruncatedEnergyFraction: number;
74
+ quadrature: { method: "polar-frequency-domain-trapezoid-gauss-legendre"; maximumRadialNodes: number; maximumAngularNodes: number };
75
+ }
76
+ ```
77
+
78
+ ## DiffractionLimitedPsfWavelength
79
+
80
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
81
+
82
+ ```ts
83
+ export interface DiffractionLimitedPsfWavelength {
84
+ wavelengthNm: number;
85
+ /** f_c = 1/(λ·N_w), cycles per µm. */
86
+ cutoffFrequencyCyclesPerMicrometer: number;
87
+ /** 1.21967·λ·N_w, µm. */
88
+ firstZeroRadiusMicrometers: number;
89
+ /** Σ of the cell energies inside the window before renormalization (of a total of 1). */
90
+ capturedEnergyFraction: number;
91
+ /** 1 − capturedEnergyFraction: the Airy tail energy outside the window, put back by renormalization. */
92
+ truncatedEnergyFraction: number;
93
+ /** Fraction of the renormalized kernel in the centre sample. */
94
+ centreSampleFraction: number;
95
+ }
96
+ ```
@@ -24,11 +24,12 @@ This call is synchronous; any supplied provider must follow its explicit synchro
24
24
 
25
25
  ## IRRADIANCE_CONVOLUTION_LIMITS
26
26
 
27
- The exact contract is declared below; use the domain guide for assumptions and staged integration.
27
+ `maximumPixels` is the default grid limit; a request may raise it with its own `maximumPixels`, up to `ceilingPixels`.
28
28
 
29
29
  ```ts
30
30
  IRRADIANCE_CONVOLUTION_LIMITS = Object.freeze({
31
31
  maximumPixels: 24_000_000,
32
+ ceilingPixels: FULL_RESOLUTION_CEILING_PIXELS,
32
33
  maximumDimension: 16_384,
33
34
  maximumKernelRadius: 32,
34
35
  maximumPatchCount: 4_096,
@@ -70,6 +71,11 @@ export interface IrradianceConvolutionInput {
70
71
  */
71
72
  kernels: Float32Array | Float64Array;
72
73
  boundary: "zero-outside-grid";
74
+ /**
75
+ * Optional opt-in grid limit for large images: an integer from 1 to `IRRADIANCE_CONVOLUTION_LIMITS.ceilingPixels`.
76
+ * Default `IRRADIANCE_CONVOLUTION_LIMITS.maximumPixels` (24 000 000). It does not change the result.
77
+ */
78
+ maximumPixels?: number;
73
79
  }
74
80
  ```
75
81
 
@@ -24,10 +24,10 @@ This call is synchronous; any supplied provider must follow its explicit synchro
24
24
 
25
25
  ## NATIVE_CAPTURE_SDR_LIMITS
26
26
 
27
- The exact contract is declared below; use the domain guide for assumptions and staged integration.
27
+ `maximumPixels` is the default pixel limit; a request may raise it with its own `maximumPixels`, up to `ceilingPixels`.
28
28
 
29
29
  ```ts
30
- NATIVE_CAPTURE_SDR_LIMITS = Object.freeze({ maximumPixels: 24_000_000,
30
+ NATIVE_CAPTURE_SDR_LIMITS = Object.freeze({ maximumPixels: 24_000_000, ceilingPixels: FULL_RESOLUTION_CEILING_PIXELS,
31
31
  maximumDimension: 16_384, tileWidth: 256, tileHeight: 32, maximumTileStorageSamples: 32_768 })
32
32
  ```
33
33
 
@@ -51,6 +51,11 @@ export interface NativeCaptureSdrInput {
51
51
  profile: SdrRenderingProfile;
52
52
  /** Limit on the size of the integer output. Checked before anything is allocated or any tile is read. */
53
53
  maximumOutputBytes: number;
54
+ /**
55
+ * Optional opt-in pixel limit for large captures: an integer from 1 to `NATIVE_CAPTURE_SDR_LIMITS.ceilingPixels`.
56
+ * Default `NATIVE_CAPTURE_SDR_LIMITS.maximumPixels` (24 000 000). `maximumOutputBytes` still applies.
57
+ */
58
+ maximumPixels?: number;
54
59
  }
55
60
  ```
56
61
 
@@ -13,9 +13,11 @@ PNG_ENCODER_ID = "photivra-png-rgb-v1" as const
13
13
  ## PNG_ENCODER_LIMITS
14
14
 
15
15
  Limits, checked before allocation. 100 MP (megapixels) of 16-bit RGB is about 600 MB of filtered scanlines.
16
+ `maximumPixels` is the default; a request may raise it with its own `maximumPixels`, up to `ceilingPixels`.
16
17
 
17
18
  ```ts
18
- PNG_ENCODER_LIMITS = Object.freeze({ maximumDimension: 65535, maximumPixels: 100_000_000, maximumIccProfileBytes: 4_000_000 })
19
+ PNG_ENCODER_LIMITS = Object.freeze({ maximumDimension: 65535, maximumPixels: 100_000_000, ceilingPixels: FULL_RESOLUTION_CEILING_PIXELS,
20
+ maximumIccProfileBytes: 4_000_000 })
19
21
  ```
20
22
 
21
23
  ## PngCicpPrimaries
@@ -74,6 +76,11 @@ export interface PngEncodeInput {
74
76
  compression?: "stored" | "fixed-huffman";
75
77
  /** PNG filter type 0..4 for every row, or the specification's minimum-sum-of-absolute-differences heuristic. Default "adaptive". */
76
78
  filter?: "none" | "sub" | "up" | "average" | "paeth" | "adaptive";
79
+ /**
80
+ * Optional opt-in pixel limit: an integer from 1 to `PNG_ENCODER_LIMITS.ceilingPixels`. Default
81
+ * `PNG_ENCODER_LIMITS.maximumPixels` (100 000 000). It does not change the encoded bytes.
82
+ */
83
+ maximumPixels?: number;
77
84
  }
78
85
  ```
79
86
 
@@ -22,6 +22,17 @@ export function parsePrintJpegInput(input:PrintJpegInput):PrintJpegInput;
22
22
 
23
23
  This call is synchronous; any supplied provider must follow its explicit synchronous contract.
24
24
 
25
+ ## PRINT_JPEG_LIMITS
26
+
27
+ Admission limits. `maximumPixels` is the default source limit; a request may raise it with its own
28
+ `maximumPixels`, up to `ceilingPixels`. A raised limit raises the `maximumEncodedBytes` ceiling in proportion
29
+ (256 000 000 bytes per 24 000 000 source pixels).
30
+
31
+ ```ts
32
+ PRINT_JPEG_LIMITS=Object.freeze({maximumPixels:24_000_000,ceilingPixels:FULL_RESOLUTION_CEILING_PIXELS,maximumDimension:16_384,
33
+ maximumEncodedBytes:256_000_000,maximumProviderReads:1_000_000})
34
+ ```
35
+
25
36
  ## PRINT_JPEG_SCHEMA_VERSION
26
37
 
27
38
  The exact contract is declared below; use the domain guide for assumptions and staged integration.
@@ -57,6 +68,12 @@ export interface PrintJpegInput {
57
68
  * carries 72/1 with ResolutionUnit = 2; that is the standard's unknown-resolution marker, not a print size.
58
69
  */
59
70
  pixelsPerInch?:number;
71
+ /**
72
+ * Optional opt-in source pixel limit for large images: an integer from 1 to `PRINT_JPEG_LIMITS.ceilingPixels`.
73
+ * Default `PRINT_JPEG_LIMITS.maximumPixels` (24 000 000). When given it is recorded in the XMP export plan, like the
74
+ * other admission budgets; the image data are unchanged.
75
+ */
76
+ maximumPixels?:number;
60
77
  }
61
78
  ```
62
79
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@photivra/engine",
3
- "version": "2.0.0",
3
+ "version": "2.2.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",
@@ -35,7 +35,8 @@
35
35
  "dist/schema",
36
36
  "dist/sensor",
37
37
  "dist/simulation",
38
- "dist/stabilization","dist/processing",
38
+ "dist/stabilization",
39
+ "dist/processing",
39
40
  "!dist/capture/browser-native-reference-*",
40
41
  "!dist/schema/camera.js",
41
42
  "!dist/schema/camera.js.map",