@photivra/engine 2.7.0 → 2.8.1

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 (49) hide show
  1. package/CHANGELOG.md +114 -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 +25 -12
  6. package/dist/capture/export-jpeg.js.map +1 -1
  7. package/dist/capture/native-environment-lean.d.ts +35 -5
  8. package/dist/capture/native-environment-lean.js +175 -16
  9. package/dist/capture/native-environment-lean.js.map +1 -1
  10. package/dist/capture/native-environment-raw.d.ts +9 -1
  11. package/dist/capture/native-environment-raw.js +14 -9
  12. package/dist/capture/native-environment-raw.js.map +1 -1
  13. package/dist/capture/native-environment-tabulated.d.ts +58 -0
  14. package/dist/capture/native-environment-tabulated.js +91 -0
  15. package/dist/capture/native-environment-tabulated.js.map +1 -0
  16. package/dist/core/internal-radiance-evaluator.d.ts +28 -0
  17. package/dist/core/internal-radiance-evaluator.js +30 -0
  18. package/dist/core/internal-radiance-evaluator.js.map +1 -1
  19. package/dist/core/version.d.ts +1 -1
  20. package/dist/core/version.js +1 -1
  21. package/dist/core/version.js.map +1 -1
  22. package/dist/facade/diffraction-provider.js +33 -12
  23. package/dist/facade/diffraction-provider.js.map +1 -1
  24. package/dist/facade/display-view.d.ts +14 -83
  25. package/dist/facade/display-view.js +331 -846
  26. package/dist/facade/display-view.js.map +1 -1
  27. package/dist/facade/expected-signal-setup.d.ts +65 -0
  28. package/dist/facade/expected-signal-setup.js +52 -0
  29. package/dist/facade/expected-signal-setup.js.map +1 -0
  30. package/dist/facade/expected-signal.d.ts +83 -0
  31. package/dist/facade/expected-signal.js +571 -0
  32. package/dist/facade/expected-signal.js.map +1 -0
  33. package/dist/facade/scene-radiance.d.ts +6 -0
  34. package/dist/facade/scene-radiance.js +31 -10
  35. package/dist/facade/scene-radiance.js.map +1 -1
  36. package/dist/facade/simulate-capture.js +109 -28
  37. package/dist/facade/simulate-capture.js.map +1 -1
  38. package/dist/facade/simulate-display-view.js +26 -49
  39. package/dist/facade/simulate-display-view.js.map +1 -1
  40. package/dist/optics/site-diffraction-kernel.d.ts +5 -0
  41. package/dist/optics/site-diffraction-kernel.js +32 -0
  42. package/dist/optics/site-diffraction-kernel.js.map +1 -0
  43. package/dist/output/print-jpeg.js +70 -42
  44. package/dist/output/print-jpeg.js.map +1 -1
  45. package/docs/API_REFERENCE.md +1 -1
  46. package/docs/QUICK_START.md +1 -1
  47. package/docs/api/core-version.md +1 -1
  48. package/docs/api/exports.json +2 -2
  49. package/package.json +1 -1
@@ -9,12 +9,11 @@ import { ENGINE_API_VERSION } from "../core/version.js";
9
9
  import { resolvePortableColorTemperatureWhiteBalance } from "../color/white-balance.js";
10
10
  import { resolveCaptureColorModel } from "../color/capture-color.js";
11
11
  import { prepareSensorColorDevelopment } from "../color/sensor-color-development.js";
12
- import { resolveLensPsfConvolutionKernels } from "../optics/lens-psf-kernels.js";
13
- import { resolveProjectionDistanceMm } from "../motion/camera-rotation.js";
12
+ import { buildExpectedSignalInput } from "./expected-signal-setup.js";
14
13
  import { resolveSensorReadoutRegime } from "../sensor/raw-readout.js";
15
14
  import { resolvePortableChartScene } from "./chart-scenes.js";
16
15
  import { resolvePortableCaptureIlluminant, calculateIlluminantIrradianceScale } from "./scene-radiance.js";
17
- import { ahdCandidateKernels, createDisplayViewExpectedSignal, renderDisplayView } from "./display-view.js";
16
+ import { ahdCandidateKernels, createDisplayViewExpectedSignal, renderDisplayView, EXACT_AREA_MEAN_MAX_PIXELS } from "./display-view.js";
18
17
  import { runCapture, apertureNodeOffsets, bilinearPhaseProfiles, clockMs, fail, sceneIlluminantWhiteBalance, CAMERA_TO_XYZ, CHANNELS, DARK_CURRENT_ELECTRONS_PER_SECOND, FACADE_EVIDENCE, SIMULATE_CAPTURE_FACADE_VERSION, DISPLAY_VIEW_WITHOUT_VIEW, allowKeys, parseDisplayViewOption } from "./simulate-capture.js";
19
18
  import { createHostYield } from "./host-yield.js";
20
19
  /**
@@ -96,48 +95,16 @@ function prepareView(context) {
96
95
  fail("simulateDisplayView: the chart's materials could not be resolved portably.");
97
96
  const { electronsPerRadiance, wavelengths, relativePerElectron } = axisChannelResponse("simulateDisplayView");
98
97
  const perNode = CHANNELS.map((c) => Float64Array.from(wavelengths, (nm) => electronsPerRadiance[c].get(nm) ?? 0));
99
- // Query offsets of each site: pixel-aperture nodes minus anti-aliasing split offsets (sites, +x right, +y down), as the environment forms them.
100
- const spots = antiAliasing ? antiAliasing.components : [{ offsetMicrometers: { x: 0, y: 0 }, normalizedWeight: 1 }];
101
- const offsets = apertureNodeOffsets(boxSamples).flatMap((node) => spots.map((c) => ({ x: node.x - c.offsetMicrometers.x / pitch, y: node.y - c.offsetMicrometers.y / pitch,
102
- weight: c.normalizedWeight / (boxSamples * boxSamples) })));
103
- // Diffraction: the same per-node site kernels as the object-space convolution (createDiffractedPlanarProvider).
104
- let kernels;
105
- if (diffraction) {
106
- const radius = diffraction.kernelRadiusSamples;
107
- kernels = { radius, perNode: wavelengths.map((wavelengthNm) => {
108
- if (!diffraction.profile.axes.wavelengthNm.includes(wavelengthNm))
109
- fail("simulateDisplayView: a spectral node has no diffraction kernel.");
110
- const k = resolveLensPsfConvolutionKernels({ schemaVersion: "0.1.0", psf: diffractionPsf.configuration.psf, spectralWeights: [{ wavelengthNm, weight: 1 }],
111
- grid: { width: 1, height: 1, sitePitchMicrometers: { x: pitch, y: pitch }, opticalAxisSite: { x: 0.5, y: 0.5 } }, patchWidth: 1, patchHeight: 1, kernelRadius: radius }).value;
112
- if (!k.exactSiteSampling || k.kernelRadius !== radius)
113
- fail("simulateDisplayView: diffraction kernels must sample the site pitch exactly.");
114
- return Float64Array.from(k.kernels);
115
- }) };
116
- }
117
- // Chart presets and (2.7) caller material scenes take the material path: each material tabulated once per node.
98
+ // The tabulated expected signal's input (#394: shared with the capture's tabulated RAW path).
118
99
  const sceneMaterials = viewChart ? viewChart.materials : callerScene?.kind === "material-function" ? callerScene.materials : undefined;
119
- const scene = sceneMaterials && provider.chartMaterialAt ? { kind: "materials", materialAt: provider.chartMaterialAt,
120
- materials: [...sceneMaterials.map((m, index) => (m.kind === "neutral" ? { neutral: m.reflectance } : { radiance: Float64Array.from(wavelengths, (nm) => viewProvider.materialRadiance(index, nm)) })),
121
- ...(chartSize ? [{ neutral: chartSize.surroundReflectance }] : [])],
122
- whiteRadiance: Float64Array.from(wavelengths, (nm) => viewIrradianceScale * viewIlluminant.shape.evaluate(nm) / Math.PI) } :
123
- { kind: "radiance", radianceAt: viewProvider.radianceAt };
124
- const channelIndex = (x, y) => ((y & 1) === 0 ? ((x & 1) === 0 ? 0 : 1) : ((x & 1) === 0 ? 1 : 2));
125
- // Without diffraction the environment's own query arithmetic (native-environment-lean.ts `geometry`, then the rectilinear
126
- // still-camera ray and the planar provider's plane point), so boundary points resolve as in the capture.
127
- const projectionDistance = resolveProjectionDistanceMm(focalLengthMm, focusDistanceM).projectionDistanceMm, siteWidthMm = pitch / 1000;
128
- const nodePhases = apertureNodeOffsets(boxSamples).flatMap((_, index) => spots.map((c) => ({ xPhase: (index % boxSamples + 0.5) / boxSamples, yPhase: (Math.floor(index / boxSamples) + 0.5) / boxSamples,
129
- aaX: c.offsetMicrometers.x / 1000, aaY: c.offsetMicrometers.y / 1000 })));
130
- const queryPoint = (x, y, o, out) => {
131
- const node = nodePhases[o];
132
- const centerX = -imagingArea.widthMm / 2 + (pitch / 2 + x * pitch + 0) / 1000, centerY = -imagingArea.heightMm / 2 + (pitch / 2 + y * pitch + 0) / 1000;
133
- const sourceX = (centerX - siteWidthMm / 2 + node.xPhase * siteWidthMm) - node.aaX, sourceY = (centerY - siteWidthMm / 2 + node.yPhase * siteWidthMm) - node.aaY;
134
- const rx = projectionDistance * (sourceX / projectionDistance), ry = projectionDistance * (-sourceY / projectionDistance);
135
- const length = Math.hypot(rx, ry, projectionDistance), t = subjectDistanceM / (projectionDistance / length);
136
- out[0] = rx / length * t / metresPerFrameUnit;
137
- out[1] = ry / length * t / metresPerFrameUnit;
138
- };
139
- const signalInput = { nativeWidth, nativeHeight, outputWidth: width, wavelengthsNm: wavelengths, electronsPerRadiance: perNode, offsets,
140
- ...(kernels ? { kernels } : { queryPoint }), scene, channelAt: channelIndex };
100
+ const { offsets, signalInput } = buildExpectedSignalInput({ nativeWidth, nativeHeight, width, wavelengths, perNode, apertureNodes: apertureNodeOffsets(boxSamples), boxSamples,
101
+ antiAliasing, pitch, diffraction: diffraction ? { radius: diffraction.kernelRadiusSamples, psf: diffractionPsf.configuration.psf, profileWavelengths: diffraction.profile.axes.wavelengthNm } : undefined,
102
+ materials: sceneMaterials && provider.chartMaterialAt ? { materialAt: provider.chartMaterialAt,
103
+ list: [...sceneMaterials.map((m, index) => (m.kind === "neutral" ? { neutral: m.reflectance } : { radiance: (nm) => viewProvider.materialRadiance(index, nm) })),
104
+ ...(chartSize ? [{ neutral: chartSize.surroundReflectance }] : [])],
105
+ whiteRadiance: (nm) => viewIrradianceScale * viewIlluminant.shape.evaluate(nm) / Math.PI } : undefined,
106
+ radianceAt: viewProvider.radianceAt, imagingArea, focalLengthMm, focusDistanceM, subjectDistanceM, metresPerFrameUnit, caller: "simulateDisplayView" });
107
+ const kernels = signalInput.kernels;
141
108
  // Readout moments per channel: normalized value k·e with variance k²·(e + σ_read²) plus the ADC quantization (1/12 code², times d² before a digital gain d).
142
109
  const readout = CHANNELS.map((channel) => {
143
110
  const { readoutProfile, regimeId } = regimeByChannel[channel];
@@ -177,12 +144,22 @@ function prepareView(context) {
177
144
  assume("view.definition", "model", { resampling: "area-average-linear-light", width: v.width, height: v.height, frameWidth: width, frameHeight: height, scaleX, scaleY }, "The image the full-resolution capture of this request would give after resizing to " + v.width + " × " + v.height + ": each display pixel is the area-weighted mean, in linear light (sRGB samples decoded with the sRGB EOTF), " +
178
145
  "of the real output pixels its rectangle covers (" + scaleX + " × " + scaleY + " real pixels), then sRGB-encoded and rounded. The real capture's own samples are rounded to the view's bit depth before averaging.");
179
146
  const uniformSamplesPerAxis = Math.max(1, Math.round(v.samplesPerAxis / 4));
180
- assume("view.estimation", "model", { method: "stratified-monte-carlo-over-real-pixels", samplesPerAxis: v.samplesPerAxis, uniformSamplesPerAxis,
181
- detailPixels: rendered.detailPixelCount, uniformPixels: rendered.uniformPixelCount }, "Each display pixel's expected value is the mean over K × K strata of its rectangle, one seeded uniformly jittered real pixel in each, so it is unbiased; the error is zero where the rectangle is uniform " +
182
- "and falls as K^-3/2 at an edge. K = " + v.samplesPerAxis + " where a fixed pilot grid over the rectangle (widened by the demosaic, filter and kernel reach) or a neighbouring pixel's grid finds more than one scene value, " +
183
- "K = " + uniformSamplesPerAxis + " elsewhere. For each sampled pixel the expected electrons of the real sites its demosaic reads are exact at the real pitch (the scene sampled at those sites through the pixel aperture, " +
147
+ const siteSentence = "the expected electrons of the real sites its demosaic reads are exact at the real pitch (the scene sampled at those sites through the pixel aperture, " +
184
148
  "anti-aliasing filter and diffraction kernels, with the capture's own query arithmetic, so the colour mosaic and its aliasing are the real sensor's), and the per-site clip at white, the highlight clip, the tone curve " +
185
- "and the clip to [0, 1] act on the pixel's own noise distribution, approximated as normal.");
149
+ "and the clip to [0, 1] act on the pixel's own noise distribution, approximated as normal.";
150
+ // 2.8 (#393): where a footprint holds at most 25 real pixels (and at most K²), every real pixel of it is evaluated once with its exact weight.
151
+ if (rendered.estimation === "exact-area-mean") {
152
+ assume("view.estimation", "model", { method: "exact-area-mean-over-real-pixels", samplesPerAxis: v.samplesPerAxis, realPixelsPerDisplayPixel: scaleX * scaleY,
153
+ evaluatedRealPixels: rendered.sampleCount }, "Each display pixel's expected value is the exact area mean of the real pixels its rectangle covers: every real pixel it touches is evaluated once and weighted by its overlap with the rectangle " +
154
+ "(w_j / A, summing to 1), so there is no sampling error. This applies because each rectangle holds " + scaleX * scaleY + " real pixels (" + scaleX + " × " + scaleY + "), at most " + EXACT_AREA_MEAN_MAX_PIXELS + " and at most K² = " + v.samplesPerAxis * v.samplesPerAxis + "; coarser views use a stratified estimate, which costs less there. " +
155
+ "For each real pixel " + siteSentence);
156
+ }
157
+ else {
158
+ assume("view.estimation", "model", { method: "stratified-monte-carlo-over-real-pixels", samplesPerAxis: v.samplesPerAxis, uniformSamplesPerAxis,
159
+ detailPixels: rendered.detailPixelCount, uniformPixels: rendered.uniformPixelCount }, "Each display pixel's expected value is the mean over K × K strata of its rectangle, one seeded uniformly jittered real pixel in each, so it is unbiased; the error is zero where the rectangle is uniform " +
160
+ "and falls as K^-3/2 at an edge. K = " + v.samplesPerAxis + " where a fixed pilot grid over the rectangle (widened by the demosaic, filter and kernel reach) or a neighbouring pixel's grid finds more than one scene value, " +
161
+ "K = " + uniformSamplesPerAxis + " elsewhere. For each sampled pixel " + siteSentence);
162
+ }
186
163
  assume("view.noise", "model", { method: "variance-of-the-area-mean", distribution: "normal" }, "Noise of the mean of the real pixels: the covariance of the weighted sum of independent site noises (shot, dark, read and ADC), with the demosaic correlation (B_c = Σ β², in closed form from the rectangle) and the " +
187
164
  "expected slopes of the clips and the tone curve (Stein's lemma); the variance the linearization misses and the real pixels' output rounding are added per pixel. One correlated normal draw per display pixel, seeded " +
188
165
  "by sensor.noiseSeed: the same statistics as the resized capture, not the same realization.");
@@ -1 +1 @@
1
- {"version":3,"file":"simulate-display-view.js","sourceRoot":"","sources":["../../src/facade/simulate-display-view.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * The public entry point of the display view (`simulateDisplayView`) and its renderer. `simulate-capture.ts` resolves the\n * request (`runCapture`) and calls the renderer it is given, so a bundle that imports only `simulateCapture` or\n * `simulateIdealReference` does not contain the display view (`display-view.ts`) or the portable evaluations it uses.\n * The design and its error analysis are in docs/DISPLAY_VIEW.md.\n */\n\nimport { ENGINE_API_VERSION } from \"../core/version.js\";\nimport { resolvePortableColorTemperatureWhiteBalance, type ResolvedWhiteBalanceState } from \"../color/white-balance.js\";\nimport { resolveCaptureColorModel } from \"../color/capture-color.js\";\nimport { prepareSensorColorDevelopment } from \"../color/sensor-color-development.js\";\nimport { resolveLensPsfConvolutionKernels } from \"../optics/lens-psf-kernels.js\";\nimport { resolveProjectionDistanceMm } from \"../motion/camera-rotation.js\";\nimport { resolveSensorReadoutRegime } from \"../sensor/raw-readout.js\";\nimport { resolvePortableChartScene, type ChartScene } from \"./chart-scenes.js\";\nimport { resolvePortableCaptureIlluminant, calculateIlluminantIrradianceScale } from \"./scene-radiance.js\";\nimport { ahdCandidateKernels, createDisplayViewExpectedSignal, renderDisplayView, type DisplayViewChannelReadout, type DisplayViewExpectedSignal, type DisplayViewRenderResult,\n type DisplayViewScene } from \"./display-view.js\";\nimport { runCapture, apertureNodeOffsets, bilinearPhaseProfiles, clockMs, fail, sceneIlluminantWhiteBalance, CAMERA_TO_XYZ, CHANNELS, DARK_CURRENT_ELECTRONS_PER_SECOND, FACADE_EVIDENCE,\n SIMULATE_CAPTURE_FACADE_VERSION, DISPLAY_VIEW_WITHOUT_VIEW, allowKeys, parseDisplayViewOption, type DisplayViewResult, type DisplayViewRunContext, type ParsedDisplayView,\n type SimulateCaptureAssumption, type SimulateCaptureOptions, type SimulateDisplayViewOptions } from \"./simulate-capture.js\";\nimport { createHostYield } from \"./host-yield.js\";\n\n/**\n * The display view of a capture request: the image the real full-resolution capture (`simulateCapture(options)`) would\n * give after it is resized to `view.width` × `view.height` by area averaging in linear light, estimated at a cost that\n * follows the display pixels, not the capture's. Optics (lens diffraction, the anti-aliasing filter, the pixel aperture)\n * act at the real pixel pitch; the colour mosaic, demosaic and development are those of the real pixels; the noise is\n * that of the average of the real pixels, demosaic correlation included; exposure, white balance and tone are the\n * capture's. The result is labelled `kind: \"display-view\"`; it is not a capture.\n * @see docs/DISPLAY_VIEW.md and docs/QUICK_START.md, \"Display view\".\n */\nexport async function simulateDisplayView(options: SimulateCaptureOptions, view: SimulateDisplayViewOptions): Promise<DisplayViewResult> {\n return await runCapture(options, \"display-view\", view, (context) => renderDisplayViewResult(context)) as DisplayViewResult;\n}\n/** Options of one {@link PreparedDisplayView} render. */\nexport interface PreparedDisplayViewRenderOptions {\n /** Aborting cancels this render only; the promise rejects with a domain error. The request's own `signal` applies to preparing only. */\n signal?: AbortSignal;\n}\n/**\n * 2.7: a display-view request resolved once (`prepareDisplayView`). `render(view)` gives the same result as\n * `simulateDisplayView(options, view)` for the prepared options, byte for byte (samples, view, exposure, scene, diagnostics\n * and assumptions; only `diagnostics.timingsMs` differ, and they cover the render only), without repeating the request's\n * setup: use it to render one view in many regions, for example on each worker of a progressive display.\n */\nexport interface PreparedDisplayView {\n /** Renders a view of the prepared request. Renders may run concurrently; they share no state except read-only setup. */\n render(view: SimulateDisplayViewOptions, renderOptions?: PreparedDisplayViewRenderOptions): Promise<DisplayViewResult>;\n}\n/**\n * 2.7 (#387): resolves a {@link simulateDisplayView} request once, without a view, and returns a handle whose `render(view)`\n * renders its views. The setup it does once (request validation, the camera, lens, scene, chart and illuminant, the channel\n * responses through the environment chain, the diffraction kernels, readout moments, demosaic tables and colour matrix) costs\n * about 0.3–1.3 s per call on a 24 MP request; a region costs only its own pixels. Errors in the request are raised here;\n * errors in the view by `render`.\n * @see docs/QUICK_START.md, \"Display view\".\n */\nexport async function prepareDisplayView(options: SimulateCaptureOptions): Promise<PreparedDisplayView> {\n let frameWidth = 0, frameHeight = 0;\n const renderPrepared = await runCapture(options, \"display-view\", DISPLAY_VIEW_WITHOUT_VIEW, (context) => {\n frameWidth = context.width; frameHeight = context.height;\n return Promise.resolve(prepareView(context));\n }) as RenderPreparedView;\n return Object.freeze({\n async render(view: SimulateDisplayViewOptions, renderOptions?: PreparedDisplayViewRenderOptions): Promise<DisplayViewResult> {\n const started = Date.now(), viewStarted = clockMs();\n const signal = renderOptions === undefined ? undefined : allowKeys(renderOptions, \"render options\", [\"signal\"]).signal as AbortSignal | undefined;\n if (signal !== undefined && (typeof signal !== \"object\" || signal === null || typeof signal.aborted !== \"boolean\")) fail(\"signal must be an AbortSignal.\");\n const checkAbort = (): void => { if (signal?.aborted) fail(\"simulateCapture aborted.\"); };\n checkAbort();\n const parsed = parseDisplayViewOption(view, frameWidth, frameHeight);\n return await renderPrepared(parsed, { started, viewStarted, checkAbort, yieldControl: createHostYield().yieldControl });\n }\n });\n}\n/**\n * Internal (not exported from the package): the display view plus its noise-free expected linear values and noise standard\n * deviations, for the validation scripts and tests.\n */\nexport async function simulateDisplayViewWithInternals(options: SimulateCaptureOptions, view: SimulateDisplayViewOptions):\n Promise<{ result: DisplayViewResult; internals: DisplayViewRenderResult; signal: DisplayViewExpectedSignal }> {\n const internals: { value?: DisplayViewRenderResult; signal?: DisplayViewExpectedSignal } = {};\n const result = await runCapture(options, \"display-view\", view, (context) => renderDisplayViewResult(context, internals)) as DisplayViewResult;\n return { result, internals: internals.value!, signal: internals.signal! };\n}\n\n/** AHD low-signal correction (display view): draws per flat-neighbourhood state, and the pixel noise (linear light) above which it applies. */\nconst DISPLAY_VIEW_AHD_CORRECTION_DRAWS = 2048, DISPLAY_VIEW_AHD_CORRECTION_THRESHOLD = 0.01;\n\ntype ViewInternals = { value?: DisplayViewRenderResult; signal?: DisplayViewExpectedSignal };\n/** What one render of a prepared view needs from its call: its start times, cancellation and yielding. */\ninterface ViewRun { started: number; viewStarted: number; checkAbort: () => void; yieldControl: () => Promise<void> }\ntype RenderPreparedView = (view: ParsedDisplayView, run: ViewRun, viewInternals?: ViewInternals) => Promise<DisplayViewResult>;\n\n/** Renders the display view of a resolved request (the last step of `runCapture` for \"display-view\"). */\nasync function renderDisplayViewResult(context: DisplayViewRunContext, viewInternals?: ViewInternals): Promise<DisplayViewResult> {\n const viewStarted = clockMs();\n return await prepareView(context)(context.parsedView!, { started: context.started, viewStarted, checkAbort: context.checkAbort, yieldControl: context.yieldControl }, viewInternals);\n}\n\n/**\n * Internal (#387): the part of a display view that depends only on the request, done once (the portable illuminant, chart and\n * white balance, the channel responses, query offsets, diffraction kernels, readout moments, demosaic tables and colour\n * matrix). The returned function renders one view from it; it keeps no state between renders (each has its own expected-\n * signal caches, lookup count and assumptions), so its results are those of separate calls.\n */\nfunction prepareView(context: DisplayViewRunContext): RenderPreparedView {\n const { ahd, antiAliasing, aspect, axisChannelResponse, boxSamples, buildProvider, callerScene, chart, chartReport, chartSize, diffraction, diffractionPsf, fNumber, focalLengthMm, focusDistanceM, height, highlightMode, illuminanceLux, illuminant, illuminantOption, imagingArea, intentOf, metresPerFrameUnit, noiseSeed, outputOptions, pitch, preset, provider, regimeByChannel, resolvedIso, sceneOption, scenePreset, shutterSeconds, subjectDistanceM, toneCurve, whiteBalanceOption, whiteBalanceState, width, margin, mode, nativeHeight, nativeWidth, sourceSha256 } = context;\n const kelvinInput = context.kelvinWhiteBalanceInput;\n const portableKelvinWhiteBalance = kelvinInput ? (): ResolvedWhiteBalanceState => resolvePortableColorTemperatureWhiteBalance(kelvinInput) : undefined;\n // The engine's illuminant formulas (Planckian, CIE A, daylight locus) evaluated with the portable power and e^x − 1, so\n // the view's radiances, irradiance scale and scene-illuminant white balance have the same bits in every JavaScript\n // engine; they differ from the capture's own by at most a few ulps (docs/DISPLAY_VIEW.md, \"Same bytes in every engine\").\n const viewIlluminant = resolvePortableCaptureIlluminant(illuminantOption), viewIrradianceScale = calculateIlluminantIrradianceScale(viewIlluminant.shape, illuminanceLux);\n const viewProvider = buildProvider(viewIlluminant, viewIrradianceScale);\n const viewWhiteBalanceState = whiteBalanceOption === \"scene-illuminant\" ? sceneIlluminantWhiteBalance(viewIlluminant) : portableKelvinWhiteBalance ? portableKelvinWhiteBalance() : whiteBalanceState;\n // The chart's materials likewise (a step wedge's geometric levels use the portable power); same geometry and order.\n const viewChart = chart ? resolvePortableChartScene({ scene: sceneOption as ChartScene, aspect: chartSize ? chartSize.heightM / chartSize.widthM : aspect }) : undefined;\n if (chart && viewChart!.materials.length !== chart.materials.length) fail(\"simulateDisplayView: the chart's materials could not be resolved portably.\");\n const { electronsPerRadiance, wavelengths, relativePerElectron } = axisChannelResponse(\"simulateDisplayView\");\n const perNode = CHANNELS.map((c) => Float64Array.from(wavelengths, (nm) => electronsPerRadiance[c].get(nm) ?? 0)) as [Float64Array, Float64Array, Float64Array];\n // Query offsets of each site: pixel-aperture nodes minus anti-aliasing split offsets (sites, +x right, +y down), as the environment forms them.\n const spots = antiAliasing ? antiAliasing.components : [{ offsetMicrometers: { x: 0, y: 0 }, normalizedWeight: 1 }];\n const offsets = apertureNodeOffsets(boxSamples).flatMap((node) => spots.map((c) => ({ x: node.x - c.offsetMicrometers.x / pitch, y: node.y - c.offsetMicrometers.y / pitch,\n weight: c.normalizedWeight / (boxSamples * boxSamples) })));\n // Diffraction: the same per-node site kernels as the object-space convolution (createDiffractedPlanarProvider).\n let kernels: { radius: number; perNode: Float64Array[] } | undefined;\n if (diffraction) {\n const radius = diffraction.kernelRadiusSamples;\n kernels = { radius, perNode: wavelengths.map((wavelengthNm) => {\n if (!diffraction!.profile.axes.wavelengthNm.includes(wavelengthNm)) fail(\"simulateDisplayView: a spectral node has no diffraction kernel.\");\n const k = resolveLensPsfConvolutionKernels({ schemaVersion: \"0.1.0\", psf: diffractionPsf!.configuration.psf, spectralWeights: [{ wavelengthNm, weight: 1 }],\n grid: { width: 1, height: 1, sitePitchMicrometers: { x: pitch, y: pitch }, opticalAxisSite: { x: 0.5, y: 0.5 } }, patchWidth: 1, patchHeight: 1, kernelRadius: radius }).value;\n if (!k.exactSiteSampling || k.kernelRadius !== radius) fail(\"simulateDisplayView: diffraction kernels must sample the site pitch exactly.\");\n return Float64Array.from(k.kernels);\n }) };\n }\n // Chart presets and (2.7) caller material scenes take the material path: each material tabulated once per node.\n const sceneMaterials = viewChart ? viewChart.materials : callerScene?.kind === \"material-function\" ? callerScene.materials : undefined;\n const scene: DisplayViewScene = sceneMaterials && provider.chartMaterialAt ? { kind: \"materials\", materialAt: provider.chartMaterialAt,\n materials: [...sceneMaterials.map((m, index) => (m.kind === \"neutral\" ? { neutral: m.reflectance } : { radiance: Float64Array.from(wavelengths, (nm) => viewProvider.materialRadiance!(index, nm)) })),\n ...(chartSize ? [{ neutral: chartSize.surroundReflectance }] : [])],\n whiteRadiance: Float64Array.from(wavelengths, (nm) => viewIrradianceScale * viewIlluminant.shape.evaluate(nm) / Math.PI) } :\n { kind: \"radiance\", radianceAt: viewProvider.radianceAt };\n const channelIndex = (x: number, y: number): number => ((y & 1) === 0 ? ((x & 1) === 0 ? 0 : 1) : ((x & 1) === 0 ? 1 : 2));\n // Without diffraction the environment's own query arithmetic (native-environment-lean.ts `geometry`, then the rectilinear\n // still-camera ray and the planar provider's plane point), so boundary points resolve as in the capture.\n const projectionDistance = resolveProjectionDistanceMm(focalLengthMm, focusDistanceM).projectionDistanceMm, siteWidthMm = pitch / 1000;\n const nodePhases = apertureNodeOffsets(boxSamples).flatMap((_, index) => spots.map((c) => ({ xPhase: (index % boxSamples + 0.5) / boxSamples, yPhase: (Math.floor(index / boxSamples) + 0.5) / boxSamples,\n aaX: c.offsetMicrometers.x / 1000, aaY: c.offsetMicrometers.y / 1000 })));\n const queryPoint = (x: number, y: number, o: number, out: Float64Array): void => {\n const node = nodePhases[o]!;\n const centerX = -imagingArea.widthMm / 2 + (pitch / 2 + x * pitch + 0) / 1000, centerY = -imagingArea.heightMm / 2 + (pitch / 2 + y * pitch + 0) / 1000;\n const sourceX = (centerX - siteWidthMm / 2 + node.xPhase * siteWidthMm) - node.aaX, sourceY = (centerY - siteWidthMm / 2 + node.yPhase * siteWidthMm) - node.aaY;\n const rx = projectionDistance * (sourceX / projectionDistance), ry = projectionDistance * (-sourceY / projectionDistance);\n const length = Math.hypot(rx, ry, projectionDistance), t = subjectDistanceM / (projectionDistance / length);\n out[0] = rx / length * t / metresPerFrameUnit; out[1] = ry / length * t / metresPerFrameUnit;\n };\n const signalInput: Parameters<typeof createDisplayViewExpectedSignal>[0] = { nativeWidth, nativeHeight, outputWidth: width, wavelengthsNm: wavelengths, electronsPerRadiance: perNode, offsets,\n ...(kernels ? { kernels } : { queryPoint }), scene, channelAt: channelIndex };\n // Readout moments per channel: normalized value k·e with variance k²·(e + σ_read²) plus the ADC quantization (1/12 code², times d² before a digital gain d).\n const readout = CHANNELS.map((channel): DisplayViewChannelReadout => {\n const { readoutProfile, regimeId } = regimeByChannel[channel];\n const resolved = resolveSensorReadoutRegime({ profile: readoutProfile as Parameters<typeof resolveSensorReadoutRegime>[0][\"profile\"], regimeId });\n const k = relativePerElectron[channel], digitalGain = resolved.regime.adc.digitalGain ?? 1, gain = resolved.regime.systemConversionGainElectronsPerCode.value;\n const codeSpan = digitalGain / (gain * k), read = resolved.combinedReadNoiseRmsElectrons;\n const quantizationCodes = digitalGain * digitalGain / 12 + (digitalGain === 1 ? 0 : 1 / 12);\n return { perElectron: k, darkElectrons: DARK_CURRENT_ELECTRONS_PER_SECOND * shutterSeconds, constantVariance: k * k * read * read + quantizationCodes / (codeSpan * codeSpan) };\n }) as [DisplayViewChannelReadout, DisplayViewChannelReadout, DisplayViewChannelReadout];\n const phases = bilinearPhaseProfiles(), demosaicTable: { dx: number; dy: number; weight: number }[][][] = [];\n for (const phase of phases) demosaicTable[phase.phaseY * 2 + phase.phaseX] = CHANNELS.map((c) => phase.profile.kernels.find((k) => k.outputChannelId === c)!.contributions\n .map((t) => ({ dx: t.offsetX, dy: t.offsetY, weight: t.weight })));\n const viewIntent = intentOf(viewWhiteBalanceState), viewGains = viewIntent ? [viewIntent.channelGains.red, viewIntent.channelGains.green, viewIntent.channelGains.blue] : null;\n const colour = prepareSensorColorDevelopment(CAMERA_TO_XYZ as unknown as Parameters<typeof prepareSensorColorDevelopment>[0], viewGains, highlightMode);\n const toSrgb = resolveCaptureColorModel().xyzToCameraRgb, m = colour.cameraToXyz;\n const matrix = [0, 1, 2].map((r) => [0, 1, 2].map((c) => toSrgb[r]![0]! * m[0]![c]! + toSrgb[r]![1]! * m[1]![c]! + toSrgb[r]![2]! * m[2]![c]!));\n // Reach of a footprint's pixels into the scene, in sites: the demosaic (1), the largest query offset and the kernel radius.\n const reachSites = (ahd ? 3 : 1) + Math.ceil(Math.max(0, ...offsets.map((o) => Math.max(Math.abs(o.x), Math.abs(o.y))))) + (kernels ? kernels.radius : 0);\n return async (v, { started, viewStarted, checkAbort, yieldControl }, viewInternals) => {\n const assumptions: SimulateCaptureAssumption[] = [...context.assumptions];\n const assume = (id: string, kind: SimulateCaptureAssumption[\"kind\"], value: unknown, explanation: string): void => { assumptions.push({ id, kind, value, explanation }); };\n const signal = createDisplayViewExpectedSignal(signalInput);\n const viewRegion = v.region ?? { x: 0, y: 0, width: v.width, height: v.height };\n const rendered = await renderDisplayView({ frameWidth: width, frameHeight: height, margin, nativeWidth, nativeHeight, viewWidth: v.width, viewHeight: v.height,\n region: viewRegion, samplesPerAxis: v.samplesPerAxis, reachSites, bitDepth: v.bitDepth, seed: noiseSeed, signal, readout, demosaic: demosaicTable,\n ...(ahd ? { candidates: [0, 1, 2, 3].map((p) => ahdCandidateKernels(p % 2, Math.floor(p / 2))),\n ahdCorrection: { labMatrix: CAMERA_TO_XYZ.map((row) => row.map((value, c) => value * (viewGains ? viewGains[c]! : 1))), drawsPerState: DISPLAY_VIEW_AHD_CORRECTION_DRAWS,\n thresholdVariance: DISPLAY_VIEW_AHD_CORRECTION_THRESHOLD * DISPLAY_VIEW_AHD_CORRECTION_THRESHOLD } } : {}),\n gains: colour.appliedGains as [number, number, number], clipLevel: Math.min(...colour.appliedGains), matrix, toneCurve: toneCurve as \"identity\" | \"positive-reinhard-per-channel\",\n yieldControl, checkAbort });\n if (viewInternals) { viewInternals.value = rendered; viewInternals.signal = signal; }\n const viewMs = clockMs() - viewStarted, scaleX = width / v.width, scaleY = height / v.height;\n assume(\"view.definition\", \"model\", { resampling: \"area-average-linear-light\", width: v.width, height: v.height, frameWidth: width, frameHeight: height, scaleX, scaleY },\n \"The image the full-resolution capture of this request would give after resizing to \" + v.width + \" × \" + v.height + \": each display pixel is the area-weighted mean, in linear light (sRGB samples decoded with the sRGB EOTF), \" +\n \"of the real output pixels its rectangle covers (\" + scaleX + \" × \" + scaleY + \" real pixels), then sRGB-encoded and rounded. The real capture's own samples are rounded to the view's bit depth before averaging.\");\n const uniformSamplesPerAxis = Math.max(1, Math.round(v.samplesPerAxis / 4));\n assume(\"view.estimation\", \"model\", { method: \"stratified-monte-carlo-over-real-pixels\", samplesPerAxis: v.samplesPerAxis, uniformSamplesPerAxis,\n detailPixels: rendered.detailPixelCount, uniformPixels: rendered.uniformPixelCount },\n \"Each display pixel's expected value is the mean over K × K strata of its rectangle, one seeded uniformly jittered real pixel in each, so it is unbiased; the error is zero where the rectangle is uniform \" +\n \"and falls as K^-3/2 at an edge. K = \" + v.samplesPerAxis + \" where a fixed pilot grid over the rectangle (widened by the demosaic, filter and kernel reach) or a neighbouring pixel's grid finds more than one scene value, \" +\n \"K = \" + uniformSamplesPerAxis + \" elsewhere. For each sampled pixel the expected electrons of the real sites its demosaic reads are exact at the real pitch (the scene sampled at those sites through the pixel aperture, \" +\n \"anti-aliasing filter and diffraction kernels, with the capture's own query arithmetic, so the colour mosaic and its aliasing are the real sensor's), and the per-site clip at white, the highlight clip, the tone curve \" +\n \"and the clip to [0, 1] act on the pixel's own noise distribution, approximated as normal.\");\n assume(\"view.noise\", \"model\", { method: \"variance-of-the-area-mean\", distribution: \"normal\" },\n \"Noise of the mean of the real pixels: the covariance of the weighted sum of independent site noises (shot, dark, read and ADC), with the demosaic correlation (B_c = Σ β², in closed form from the rectangle) and the \" +\n \"expected slopes of the clips and the tone curve (Stein's lemma); the variance the linearization misses and the real pixels' output rounding are added per pixel. One correlated normal draw per display pixel, seeded \" +\n \"by sensor.noiseSeed: the same statistics as the resized capture, not the same realization.\");\n assume(\"view.demosaic\", \"model\", ahd ? { capture: \"ahd\", view: \"ahd-candidates-with-low-signal-correction\", correctedPixels: rendered.ahdCorrectedPixelCount,\n correctionDrawsPerState: DISPLAY_VIEW_AHD_CORRECTION_DRAWS, correctionThresholdLinearStd: DISPLAY_VIEW_AHD_CORRECTION_THRESHOLD } : { capture: \"bilinear\", view: \"bilinear\" },\n ahd ? \"AHD is non-linear only in its choice between two linear candidates (green along the rows or the columns, red and blue from colour differences). The view propagates both candidates' exact linear statistics \" +\n \"and averages them. Where a sampled pixel's noise exceeds \" + DISPLAY_VIEW_AHD_CORRECTION_THRESHOLD + \" (linear light), the choice reacts to the noise and shifts the colour, so the pixel adds the mean \" +\n \"difference between the engine's AHD steps and the two candidates on \" + DISPLAY_VIEW_AHD_CORRECTION_DRAWS + \" drawn noisy flat neighbourhoods (Poisson electrons plus read and ADC noise) with its channel means, \" +\n \"computed once per state of a logarithmic grid (8 steps per octave of the channel means) and interpolated trilinearly, a control variate with no per-pixel sampling noise; \" +\n \"the flat-neighbourhood state is an approximation at edges (docs/DISPLAY_VIEW.md, validation).\" :\n \"The bilinear demosaic's linear kernels, exactly.\");\n assume(\"view.source\", \"model\", { rendering: \"srgb\", format: v.format, ignoredCaptureOutput: [\"format\", \"colorEncoding\", \"bitDepth\", \"gamutMapping\", \"jpegQuantizationStep\", \"dng\", \"metadata\", \"maximumPixels\"].filter((k) => outputOptions[k] !== undefined) },\n \"The view shows the capture's sRGB rendering (its tone curve, white balance and highlight mode); the capture's file-format options do not apply to it.\");\n return Object.freeze({ kind: \"display-view\" as const, label: \"Display view: the full-resolution capture resized to \" + v.width + \" × \" + v.height + \" (estimated); not a capture.\",\n facadeVersion: SIMULATE_CAPTURE_FACADE_VERSION, engineApiVersion: ENGINE_API_VERSION,\n image: { width: viewRegion.width, height: viewRegion.height, format: v.format, samples: rendered.samples },\n view: { width: v.width, height: v.height, frameWidth: width, frameHeight: height, scaleX, scaleY, samplesPerAxis: v.samplesPerAxis, ...(v.region ? { region: { ...v.region } } : {}) },\n exposure: { mode, settings: { fNumber, shutterSeconds, iso: resolvedIso }, ev100: Math.log2(fNumber ** 2 / shutterSeconds) },\n scene: { sceneId: chart ? \"chart:\" + chart.preset : callerScene!.sceneId, preset: scenePreset, parameters: chart ? chart.parameters : {},\n illuminant: { id: illuminant.id, description: illuminant.description, illuminanceLux }, distanceM: subjectDistanceM,\n framedWidthM: 2 * metresPerFrameUnit, framedHeightM: 2 * metresPerFrameUnit * aspect, ...(chartReport ? { chart: chartReport } : {}), sourceSha256 },\n diagnostics: { sampleCount: rendered.sampleCount, siteEvaluations: rendered.siteCount, sceneLookups: signal.lookups, detailPixelCount: rendered.detailPixelCount,\n uniformPixelCount: rendered.uniformPixelCount, ahdCorrectedPixelCount: rendered.ahdCorrectedPixelCount, timingsMs: { view: viewMs, total: Date.now() - started } },\n provenance: { scientificStatus: \"approximation\" as const, cameraPresetId: preset.presetId, cameraPresetVersion: preset.presetVersion, evidence: [...FACADE_EVIDENCE, ...preset.evidence],\n composedContracts: [\"calculateEnvironmentSensorPhotoSignal\", \"resolveSensorReadoutRegime\", \"calculateSiteEffectiveSaturationCode\", ...(diffraction ? [\"createDiffractionLimitedPsfProfile\", \"resolveLensPsfConvolutionKernels\"] : []),\n ...(antiAliasing ? [\"resolveAntiAliasingSpatialKernel\"] : []), \"prepareSensorColorDevelopment\", \"display view estimator\"],\n limitations: [\"Estimated display view of the full-resolution capture: its expected value is a stratified Monte Carlo estimate and its noise is drawn from the variance of the area mean, so it has the resized capture's statistics, not its exact pixels; not a capture and not a measurement.\"] },\n assumptions: Object.freeze(assumptions.map((a) => Object.freeze(a))) });\n };\n}\n"]}
1
+ {"version":3,"file":"simulate-display-view.js","sourceRoot":"","sources":["../../src/facade/simulate-display-view.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * The public entry point of the display view (`simulateDisplayView`) and its renderer. `simulate-capture.ts` resolves the\n * request (`runCapture`) and calls the renderer it is given, so a bundle that imports only `simulateCapture` or\n * `simulateIdealReference` does not contain the display view (`display-view.ts`) or the portable evaluations it uses.\n * The design and its error analysis are in docs/DISPLAY_VIEW.md.\n */\n\nimport { ENGINE_API_VERSION } from \"../core/version.js\";\nimport { resolvePortableColorTemperatureWhiteBalance, type ResolvedWhiteBalanceState } from \"../color/white-balance.js\";\nimport { resolveCaptureColorModel } from \"../color/capture-color.js\";\nimport { prepareSensorColorDevelopment } from \"../color/sensor-color-development.js\";\nimport { buildExpectedSignalInput } from \"./expected-signal-setup.js\";\nimport { resolveSensorReadoutRegime } from \"../sensor/raw-readout.js\";\nimport { resolvePortableChartScene, type ChartScene } from \"./chart-scenes.js\";\nimport { resolvePortableCaptureIlluminant, calculateIlluminantIrradianceScale } from \"./scene-radiance.js\";\nimport { ahdCandidateKernels, createDisplayViewExpectedSignal, renderDisplayView, type DisplayViewChannelReadout, type DisplayViewExpectedSignal, type DisplayViewRenderResult,\n EXACT_AREA_MEAN_MAX_PIXELS } from \"./display-view.js\";\nimport { runCapture, apertureNodeOffsets, bilinearPhaseProfiles, clockMs, fail, sceneIlluminantWhiteBalance, CAMERA_TO_XYZ, CHANNELS, DARK_CURRENT_ELECTRONS_PER_SECOND, FACADE_EVIDENCE,\n SIMULATE_CAPTURE_FACADE_VERSION, DISPLAY_VIEW_WITHOUT_VIEW, allowKeys, parseDisplayViewOption, type DisplayViewResult, type DisplayViewRunContext, type ParsedDisplayView,\n type SimulateCaptureAssumption, type SimulateCaptureOptions, type SimulateDisplayViewOptions } from \"./simulate-capture.js\";\nimport { createHostYield } from \"./host-yield.js\";\n\n/**\n * The display view of a capture request: the image the real full-resolution capture (`simulateCapture(options)`) would\n * give after it is resized to `view.width` × `view.height` by area averaging in linear light, estimated at a cost that\n * follows the display pixels, not the capture's. Optics (lens diffraction, the anti-aliasing filter, the pixel aperture)\n * act at the real pixel pitch; the colour mosaic, demosaic and development are those of the real pixels; the noise is\n * that of the average of the real pixels, demosaic correlation included; exposure, white balance and tone are the\n * capture's. The result is labelled `kind: \"display-view\"`; it is not a capture.\n * @see docs/DISPLAY_VIEW.md and docs/QUICK_START.md, \"Display view\".\n */\nexport async function simulateDisplayView(options: SimulateCaptureOptions, view: SimulateDisplayViewOptions): Promise<DisplayViewResult> {\n return await runCapture(options, \"display-view\", view, (context) => renderDisplayViewResult(context)) as DisplayViewResult;\n}\n/** Options of one {@link PreparedDisplayView} render. */\nexport interface PreparedDisplayViewRenderOptions {\n /** Aborting cancels this render only; the promise rejects with a domain error. The request's own `signal` applies to preparing only. */\n signal?: AbortSignal;\n}\n/**\n * 2.7: a display-view request resolved once (`prepareDisplayView`). `render(view)` gives the same result as\n * `simulateDisplayView(options, view)` for the prepared options, byte for byte (samples, view, exposure, scene, diagnostics\n * and assumptions; only `diagnostics.timingsMs` differ, and they cover the render only), without repeating the request's\n * setup: use it to render one view in many regions, for example on each worker of a progressive display.\n */\nexport interface PreparedDisplayView {\n /** Renders a view of the prepared request. Renders may run concurrently; they share no state except read-only setup. */\n render(view: SimulateDisplayViewOptions, renderOptions?: PreparedDisplayViewRenderOptions): Promise<DisplayViewResult>;\n}\n/**\n * 2.7 (#387): resolves a {@link simulateDisplayView} request once, without a view, and returns a handle whose `render(view)`\n * renders its views. The setup it does once (request validation, the camera, lens, scene, chart and illuminant, the channel\n * responses through the environment chain, the diffraction kernels, readout moments, demosaic tables and colour matrix) costs\n * about 0.3–1.3 s per call on a 24 MP request; a region costs only its own pixels. Errors in the request are raised here;\n * errors in the view by `render`.\n * @see docs/QUICK_START.md, \"Display view\".\n */\nexport async function prepareDisplayView(options: SimulateCaptureOptions): Promise<PreparedDisplayView> {\n let frameWidth = 0, frameHeight = 0;\n const renderPrepared = await runCapture(options, \"display-view\", DISPLAY_VIEW_WITHOUT_VIEW, (context) => {\n frameWidth = context.width; frameHeight = context.height;\n return Promise.resolve(prepareView(context));\n }) as RenderPreparedView;\n return Object.freeze({\n async render(view: SimulateDisplayViewOptions, renderOptions?: PreparedDisplayViewRenderOptions): Promise<DisplayViewResult> {\n const started = Date.now(), viewStarted = clockMs();\n const signal = renderOptions === undefined ? undefined : allowKeys(renderOptions, \"render options\", [\"signal\"]).signal as AbortSignal | undefined;\n if (signal !== undefined && (typeof signal !== \"object\" || signal === null || typeof signal.aborted !== \"boolean\")) fail(\"signal must be an AbortSignal.\");\n const checkAbort = (): void => { if (signal?.aborted) fail(\"simulateCapture aborted.\"); };\n checkAbort();\n const parsed = parseDisplayViewOption(view, frameWidth, frameHeight);\n return await renderPrepared(parsed, { started, viewStarted, checkAbort, yieldControl: createHostYield().yieldControl });\n }\n });\n}\n/**\n * Internal (not exported from the package): the display view plus its noise-free expected linear values and noise standard\n * deviations, for the validation scripts and tests.\n */\nexport async function simulateDisplayViewWithInternals(options: SimulateCaptureOptions, view: SimulateDisplayViewOptions):\n Promise<{ result: DisplayViewResult; internals: DisplayViewRenderResult; signal: DisplayViewExpectedSignal }> {\n const internals: { value?: DisplayViewRenderResult; signal?: DisplayViewExpectedSignal } = {};\n const result = await runCapture(options, \"display-view\", view, (context) => renderDisplayViewResult(context, internals)) as DisplayViewResult;\n return { result, internals: internals.value!, signal: internals.signal! };\n}\n\n/** AHD low-signal correction (display view): draws per flat-neighbourhood state, and the pixel noise (linear light) above which it applies. */\nconst DISPLAY_VIEW_AHD_CORRECTION_DRAWS = 2048, DISPLAY_VIEW_AHD_CORRECTION_THRESHOLD = 0.01;\n\ntype ViewInternals = { value?: DisplayViewRenderResult; signal?: DisplayViewExpectedSignal };\n/** What one render of a prepared view needs from its call: its start times, cancellation and yielding. */\ninterface ViewRun { started: number; viewStarted: number; checkAbort: () => void; yieldControl: () => Promise<void> }\ntype RenderPreparedView = (view: ParsedDisplayView, run: ViewRun, viewInternals?: ViewInternals) => Promise<DisplayViewResult>;\n\n/** Renders the display view of a resolved request (the last step of `runCapture` for \"display-view\"). */\nasync function renderDisplayViewResult(context: DisplayViewRunContext, viewInternals?: ViewInternals): Promise<DisplayViewResult> {\n const viewStarted = clockMs();\n return await prepareView(context)(context.parsedView!, { started: context.started, viewStarted, checkAbort: context.checkAbort, yieldControl: context.yieldControl }, viewInternals);\n}\n\n/**\n * Internal (#387): the part of a display view that depends only on the request, done once (the portable illuminant, chart and\n * white balance, the channel responses, query offsets, diffraction kernels, readout moments, demosaic tables and colour\n * matrix). The returned function renders one view from it; it keeps no state between renders (each has its own expected-\n * signal caches, lookup count and assumptions), so its results are those of separate calls.\n */\nfunction prepareView(context: DisplayViewRunContext): RenderPreparedView {\n const { ahd, antiAliasing, aspect, axisChannelResponse, boxSamples, buildProvider, callerScene, chart, chartReport, chartSize, diffraction, diffractionPsf, fNumber, focalLengthMm, focusDistanceM, height, highlightMode, illuminanceLux, illuminant, illuminantOption, imagingArea, intentOf, metresPerFrameUnit, noiseSeed, outputOptions, pitch, preset, provider, regimeByChannel, resolvedIso, sceneOption, scenePreset, shutterSeconds, subjectDistanceM, toneCurve, whiteBalanceOption, whiteBalanceState, width, margin, mode, nativeHeight, nativeWidth, sourceSha256 } = context;\n const kelvinInput = context.kelvinWhiteBalanceInput;\n const portableKelvinWhiteBalance = kelvinInput ? (): ResolvedWhiteBalanceState => resolvePortableColorTemperatureWhiteBalance(kelvinInput) : undefined;\n // The engine's illuminant formulas (Planckian, CIE A, daylight locus) evaluated with the portable power and e^x − 1, so\n // the view's radiances, irradiance scale and scene-illuminant white balance have the same bits in every JavaScript\n // engine; they differ from the capture's own by at most a few ulps (docs/DISPLAY_VIEW.md, \"Same bytes in every engine\").\n const viewIlluminant = resolvePortableCaptureIlluminant(illuminantOption), viewIrradianceScale = calculateIlluminantIrradianceScale(viewIlluminant.shape, illuminanceLux);\n const viewProvider = buildProvider(viewIlluminant, viewIrradianceScale);\n const viewWhiteBalanceState = whiteBalanceOption === \"scene-illuminant\" ? sceneIlluminantWhiteBalance(viewIlluminant) : portableKelvinWhiteBalance ? portableKelvinWhiteBalance() : whiteBalanceState;\n // The chart's materials likewise (a step wedge's geometric levels use the portable power); same geometry and order.\n const viewChart = chart ? resolvePortableChartScene({ scene: sceneOption as ChartScene, aspect: chartSize ? chartSize.heightM / chartSize.widthM : aspect }) : undefined;\n if (chart && viewChart!.materials.length !== chart.materials.length) fail(\"simulateDisplayView: the chart's materials could not be resolved portably.\");\n const { electronsPerRadiance, wavelengths, relativePerElectron } = axisChannelResponse(\"simulateDisplayView\");\n const perNode = CHANNELS.map((c) => Float64Array.from(wavelengths, (nm) => electronsPerRadiance[c].get(nm) ?? 0)) as [Float64Array, Float64Array, Float64Array];\n // The tabulated expected signal's input (#394: shared with the capture's tabulated RAW path).\n const sceneMaterials = viewChart ? viewChart.materials : callerScene?.kind === \"material-function\" ? callerScene.materials : undefined;\n const { offsets, signalInput } = buildExpectedSignalInput({ nativeWidth, nativeHeight, width, wavelengths, perNode, apertureNodes: apertureNodeOffsets(boxSamples), boxSamples,\n antiAliasing, pitch, diffraction: diffraction ? { radius: diffraction.kernelRadiusSamples, psf: diffractionPsf!.configuration.psf, profileWavelengths: diffraction.profile.axes.wavelengthNm } : undefined,\n materials: sceneMaterials && provider.chartMaterialAt ? { materialAt: provider.chartMaterialAt,\n list: [...sceneMaterials.map((m, index) => (m.kind === \"neutral\" ? { neutral: m.reflectance } : { radiance: (nm: number): number => viewProvider.materialRadiance!(index, nm) })),\n ...(chartSize ? [{ neutral: chartSize.surroundReflectance }] : [])],\n whiteRadiance: (nm: number): number => viewIrradianceScale * viewIlluminant.shape.evaluate(nm) / Math.PI } : undefined,\n radianceAt: viewProvider.radianceAt, imagingArea, focalLengthMm, focusDistanceM, subjectDistanceM, metresPerFrameUnit, caller: \"simulateDisplayView\" });\n const kernels = signalInput.kernels;\n // Readout moments per channel: normalized value k·e with variance k²·(e + σ_read²) plus the ADC quantization (1/12 code², times d² before a digital gain d).\n const readout = CHANNELS.map((channel): DisplayViewChannelReadout => {\n const { readoutProfile, regimeId } = regimeByChannel[channel];\n const resolved = resolveSensorReadoutRegime({ profile: readoutProfile as Parameters<typeof resolveSensorReadoutRegime>[0][\"profile\"], regimeId });\n const k = relativePerElectron[channel], digitalGain = resolved.regime.adc.digitalGain ?? 1, gain = resolved.regime.systemConversionGainElectronsPerCode.value;\n const codeSpan = digitalGain / (gain * k), read = resolved.combinedReadNoiseRmsElectrons;\n const quantizationCodes = digitalGain * digitalGain / 12 + (digitalGain === 1 ? 0 : 1 / 12);\n return { perElectron: k, darkElectrons: DARK_CURRENT_ELECTRONS_PER_SECOND * shutterSeconds, constantVariance: k * k * read * read + quantizationCodes / (codeSpan * codeSpan) };\n }) as [DisplayViewChannelReadout, DisplayViewChannelReadout, DisplayViewChannelReadout];\n const phases = bilinearPhaseProfiles(), demosaicTable: { dx: number; dy: number; weight: number }[][][] = [];\n for (const phase of phases) demosaicTable[phase.phaseY * 2 + phase.phaseX] = CHANNELS.map((c) => phase.profile.kernels.find((k) => k.outputChannelId === c)!.contributions\n .map((t) => ({ dx: t.offsetX, dy: t.offsetY, weight: t.weight })));\n const viewIntent = intentOf(viewWhiteBalanceState), viewGains = viewIntent ? [viewIntent.channelGains.red, viewIntent.channelGains.green, viewIntent.channelGains.blue] : null;\n const colour = prepareSensorColorDevelopment(CAMERA_TO_XYZ as unknown as Parameters<typeof prepareSensorColorDevelopment>[0], viewGains, highlightMode);\n const toSrgb = resolveCaptureColorModel().xyzToCameraRgb, m = colour.cameraToXyz;\n const matrix = [0, 1, 2].map((r) => [0, 1, 2].map((c) => toSrgb[r]![0]! * m[0]![c]! + toSrgb[r]![1]! * m[1]![c]! + toSrgb[r]![2]! * m[2]![c]!));\n // Reach of a footprint's pixels into the scene, in sites: the demosaic (1), the largest query offset and the kernel radius.\n const reachSites = (ahd ? 3 : 1) + Math.ceil(Math.max(0, ...offsets.map((o) => Math.max(Math.abs(o.x), Math.abs(o.y))))) + (kernels ? kernels.radius : 0);\n return async (v, { started, viewStarted, checkAbort, yieldControl }, viewInternals) => {\n const assumptions: SimulateCaptureAssumption[] = [...context.assumptions];\n const assume = (id: string, kind: SimulateCaptureAssumption[\"kind\"], value: unknown, explanation: string): void => { assumptions.push({ id, kind, value, explanation }); };\n const signal = createDisplayViewExpectedSignal(signalInput);\n const viewRegion = v.region ?? { x: 0, y: 0, width: v.width, height: v.height };\n const rendered = await renderDisplayView({ frameWidth: width, frameHeight: height, margin, nativeWidth, nativeHeight, viewWidth: v.width, viewHeight: v.height,\n region: viewRegion, samplesPerAxis: v.samplesPerAxis, reachSites, bitDepth: v.bitDepth, seed: noiseSeed, signal, readout, demosaic: demosaicTable,\n ...(ahd ? { candidates: [0, 1, 2, 3].map((p) => ahdCandidateKernels(p % 2, Math.floor(p / 2))),\n ahdCorrection: { labMatrix: CAMERA_TO_XYZ.map((row) => row.map((value, c) => value * (viewGains ? viewGains[c]! : 1))), drawsPerState: DISPLAY_VIEW_AHD_CORRECTION_DRAWS,\n thresholdVariance: DISPLAY_VIEW_AHD_CORRECTION_THRESHOLD * DISPLAY_VIEW_AHD_CORRECTION_THRESHOLD } } : {}),\n gains: colour.appliedGains as [number, number, number], clipLevel: Math.min(...colour.appliedGains), matrix, toneCurve: toneCurve as \"identity\" | \"positive-reinhard-per-channel\",\n yieldControl, checkAbort });\n if (viewInternals) { viewInternals.value = rendered; viewInternals.signal = signal; }\n const viewMs = clockMs() - viewStarted, scaleX = width / v.width, scaleY = height / v.height;\n assume(\"view.definition\", \"model\", { resampling: \"area-average-linear-light\", width: v.width, height: v.height, frameWidth: width, frameHeight: height, scaleX, scaleY },\n \"The image the full-resolution capture of this request would give after resizing to \" + v.width + \" × \" + v.height + \": each display pixel is the area-weighted mean, in linear light (sRGB samples decoded with the sRGB EOTF), \" +\n \"of the real output pixels its rectangle covers (\" + scaleX + \" × \" + scaleY + \" real pixels), then sRGB-encoded and rounded. The real capture's own samples are rounded to the view's bit depth before averaging.\");\n const uniformSamplesPerAxis = Math.max(1, Math.round(v.samplesPerAxis / 4));\n const siteSentence = \"the expected electrons of the real sites its demosaic reads are exact at the real pitch (the scene sampled at those sites through the pixel aperture, \" +\n \"anti-aliasing filter and diffraction kernels, with the capture's own query arithmetic, so the colour mosaic and its aliasing are the real sensor's), and the per-site clip at white, the highlight clip, the tone curve \" +\n \"and the clip to [0, 1] act on the pixel's own noise distribution, approximated as normal.\";\n // 2.8 (#393): where a footprint holds at most 25 real pixels (and at most K²), every real pixel of it is evaluated once with its exact weight.\n if (rendered.estimation === \"exact-area-mean\") {\n assume(\"view.estimation\", \"model\", { method: \"exact-area-mean-over-real-pixels\", samplesPerAxis: v.samplesPerAxis, realPixelsPerDisplayPixel: scaleX * scaleY,\n evaluatedRealPixels: rendered.sampleCount },\n \"Each display pixel's expected value is the exact area mean of the real pixels its rectangle covers: every real pixel it touches is evaluated once and weighted by its overlap with the rectangle \" +\n \"(w_j / A, summing to 1), so there is no sampling error. This applies because each rectangle holds \" + scaleX * scaleY + \" real pixels (\" + scaleX + \" × \" + scaleY + \"), at most \" + EXACT_AREA_MEAN_MAX_PIXELS + \" and at most K² = \" + v.samplesPerAxis * v.samplesPerAxis + \"; coarser views use a stratified estimate, which costs less there. \" +\n \"For each real pixel \" + siteSentence);\n } else {\n assume(\"view.estimation\", \"model\", { method: \"stratified-monte-carlo-over-real-pixels\", samplesPerAxis: v.samplesPerAxis, uniformSamplesPerAxis,\n detailPixels: rendered.detailPixelCount, uniformPixels: rendered.uniformPixelCount },\n \"Each display pixel's expected value is the mean over K × K strata of its rectangle, one seeded uniformly jittered real pixel in each, so it is unbiased; the error is zero where the rectangle is uniform \" +\n \"and falls as K^-3/2 at an edge. K = \" + v.samplesPerAxis + \" where a fixed pilot grid over the rectangle (widened by the demosaic, filter and kernel reach) or a neighbouring pixel's grid finds more than one scene value, \" +\n \"K = \" + uniformSamplesPerAxis + \" elsewhere. For each sampled pixel \" + siteSentence);\n }\n assume(\"view.noise\", \"model\", { method: \"variance-of-the-area-mean\", distribution: \"normal\" },\n \"Noise of the mean of the real pixels: the covariance of the weighted sum of independent site noises (shot, dark, read and ADC), with the demosaic correlation (B_c = Σ β², in closed form from the rectangle) and the \" +\n \"expected slopes of the clips and the tone curve (Stein's lemma); the variance the linearization misses and the real pixels' output rounding are added per pixel. One correlated normal draw per display pixel, seeded \" +\n \"by sensor.noiseSeed: the same statistics as the resized capture, not the same realization.\");\n assume(\"view.demosaic\", \"model\", ahd ? { capture: \"ahd\", view: \"ahd-candidates-with-low-signal-correction\", correctedPixels: rendered.ahdCorrectedPixelCount,\n correctionDrawsPerState: DISPLAY_VIEW_AHD_CORRECTION_DRAWS, correctionThresholdLinearStd: DISPLAY_VIEW_AHD_CORRECTION_THRESHOLD } : { capture: \"bilinear\", view: \"bilinear\" },\n ahd ? \"AHD is non-linear only in its choice between two linear candidates (green along the rows or the columns, red and blue from colour differences). The view propagates both candidates' exact linear statistics \" +\n \"and averages them. Where a sampled pixel's noise exceeds \" + DISPLAY_VIEW_AHD_CORRECTION_THRESHOLD + \" (linear light), the choice reacts to the noise and shifts the colour, so the pixel adds the mean \" +\n \"difference between the engine's AHD steps and the two candidates on \" + DISPLAY_VIEW_AHD_CORRECTION_DRAWS + \" drawn noisy flat neighbourhoods (Poisson electrons plus read and ADC noise) with its channel means, \" +\n \"computed once per state of a logarithmic grid (8 steps per octave of the channel means) and interpolated trilinearly, a control variate with no per-pixel sampling noise; \" +\n \"the flat-neighbourhood state is an approximation at edges (docs/DISPLAY_VIEW.md, validation).\" :\n \"The bilinear demosaic's linear kernels, exactly.\");\n assume(\"view.source\", \"model\", { rendering: \"srgb\", format: v.format, ignoredCaptureOutput: [\"format\", \"colorEncoding\", \"bitDepth\", \"gamutMapping\", \"jpegQuantizationStep\", \"dng\", \"metadata\", \"maximumPixels\"].filter((k) => outputOptions[k] !== undefined) },\n \"The view shows the capture's sRGB rendering (its tone curve, white balance and highlight mode); the capture's file-format options do not apply to it.\");\n return Object.freeze({ kind: \"display-view\" as const, label: \"Display view: the full-resolution capture resized to \" + v.width + \" × \" + v.height + \" (estimated); not a capture.\",\n facadeVersion: SIMULATE_CAPTURE_FACADE_VERSION, engineApiVersion: ENGINE_API_VERSION,\n image: { width: viewRegion.width, height: viewRegion.height, format: v.format, samples: rendered.samples },\n view: { width: v.width, height: v.height, frameWidth: width, frameHeight: height, scaleX, scaleY, samplesPerAxis: v.samplesPerAxis, ...(v.region ? { region: { ...v.region } } : {}) },\n exposure: { mode, settings: { fNumber, shutterSeconds, iso: resolvedIso }, ev100: Math.log2(fNumber ** 2 / shutterSeconds) },\n scene: { sceneId: chart ? \"chart:\" + chart.preset : callerScene!.sceneId, preset: scenePreset, parameters: chart ? chart.parameters : {},\n illuminant: { id: illuminant.id, description: illuminant.description, illuminanceLux }, distanceM: subjectDistanceM,\n framedWidthM: 2 * metresPerFrameUnit, framedHeightM: 2 * metresPerFrameUnit * aspect, ...(chartReport ? { chart: chartReport } : {}), sourceSha256 },\n diagnostics: { sampleCount: rendered.sampleCount, siteEvaluations: rendered.siteCount, sceneLookups: signal.lookups, detailPixelCount: rendered.detailPixelCount,\n uniformPixelCount: rendered.uniformPixelCount, ahdCorrectedPixelCount: rendered.ahdCorrectedPixelCount, timingsMs: { view: viewMs, total: Date.now() - started } },\n provenance: { scientificStatus: \"approximation\" as const, cameraPresetId: preset.presetId, cameraPresetVersion: preset.presetVersion, evidence: [...FACADE_EVIDENCE, ...preset.evidence],\n composedContracts: [\"calculateEnvironmentSensorPhotoSignal\", \"resolveSensorReadoutRegime\", \"calculateSiteEffectiveSaturationCode\", ...(diffraction ? [\"createDiffractionLimitedPsfProfile\", \"resolveLensPsfConvolutionKernels\"] : []),\n ...(antiAliasing ? [\"resolveAntiAliasingSpatialKernel\"] : []), \"prepareSensorColorDevelopment\", \"display view estimator\"],\n limitations: [\"Estimated display view of the full-resolution capture: its expected value is a stratified Monte Carlo estimate and its noise is drawn from the variance of the area mean, so it has the resized capture's statistics, not its exact pixels; not a capture and not a measurement.\"] },\n assumptions: Object.freeze(assumptions.map((a) => Object.freeze(a))) });\n };\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import { resolveLensPsfConvolutionKernels } from "./lens-psf-kernels.js";
2
+ type Psf = Parameters<typeof resolveLensPsfConvolutionKernels>[0]["psf"];
3
+ /** The (2·radius + 1)² kernel of `psf` at `wavelengthNm` on a square site grid of `pitchMicrometers`; `failure` is the error message when the kernel cannot sample the pitch exactly. */
4
+ export declare function siteDiffractionKernel(psf: Psf, wavelengthNm: number, pitchMicrometers: number, radius: number, failure: string): Float64Array;
5
+ export {};
@@ -0,0 +1,32 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * Internal (not exported from the package): one wavelength's diffraction kernel sampled on the site pitch, as the object-space
4
+ * convolution (facade/diffraction-provider.ts) and the tabulated expected signal (facade/expected-signal-setup.ts) use it.
5
+ *
6
+ * `resolveLensPsfConvolutionKernels` parses and samples the PSF profile on every call (about 10 ms per wavelength), and both users
7
+ * ask for the same kernels, so they are kept while the PSF record lives. The kernel is a function of its inputs alone; each caller
8
+ * gets its own copy.
9
+ */
10
+ import { InvalidConfigurationError } from "../core/configuration-error.js";
11
+ import { resolveLensPsfConvolutionKernels } from "./lens-psf-kernels.js";
12
+ /** Kernels per PSF record (by identity: the record is large, so it is not serialized), then per wavelength, pitch and radius. */
13
+ const cache = new WeakMap();
14
+ /** The (2·radius + 1)² kernel of `psf` at `wavelengthNm` on a square site grid of `pitchMicrometers`; `failure` is the error message when the kernel cannot sample the pitch exactly. */
15
+ export function siteDiffractionKernel(psf, wavelengthNm, pitchMicrometers, radius, failure) {
16
+ let kernels = cache.get(psf);
17
+ if (!kernels)
18
+ cache.set(psf, (kernels = new Map()));
19
+ const key = wavelengthNm + ":" + pitchMicrometers + ":" + radius;
20
+ let kernel = kernels.get(key);
21
+ if (!kernel) {
22
+ const k = resolveLensPsfConvolutionKernels({ schemaVersion: "0.1.0", psf, spectralWeights: [{ wavelengthNm, weight: 1 }],
23
+ grid: { width: 1, height: 1, sitePitchMicrometers: { x: pitchMicrometers, y: pitchMicrometers }, opticalAxisSite: { x: 0.5, y: 0.5 } }, patchWidth: 1, patchHeight: 1,
24
+ kernelRadius: radius }).value;
25
+ if (!k.exactSiteSampling || k.kernelRadius !== radius)
26
+ throw new InvalidConfigurationError(failure);
27
+ kernel = Float64Array.from(k.kernels);
28
+ kernels.set(key, kernel);
29
+ }
30
+ return Float64Array.from(kernel);
31
+ }
32
+ //# sourceMappingURL=site-diffraction-kernel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-diffraction-kernel.js","sourceRoot":"","sources":["../../src/optics/site-diffraction-kernel.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;;;;;GAOG;AAEH,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,gCAAgC,EAAE,MAAM,uBAAuB,CAAC;AAIzE,iIAAiI;AACjI,MAAM,KAAK,GAAG,IAAI,OAAO,EAAqC,CAAC;AAE/D,yLAAyL;AACzL,MAAM,UAAU,qBAAqB,CAAC,GAAQ,EAAE,YAAoB,EAAE,gBAAwB,EAAE,MAAc,EAAE,OAAe;IAC7H,IAAI,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,OAAO;QAAE,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC;IACpD,MAAM,GAAG,GAAG,YAAY,GAAG,GAAG,GAAG,gBAAgB,GAAG,GAAG,GAAG,MAAM,CAAC;IACjE,IAAI,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,CAAC,GAAG,gCAAgC,CAAC,EAAE,aAAa,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,EAAE,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;YACtH,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,oBAAoB,EAAE,EAAE,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,gBAAgB,EAAE,EAAE,eAAe,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC;YACrK,YAAY,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC;QAChC,IAAI,CAAC,CAAC,CAAC,iBAAiB,IAAI,CAAC,CAAC,YAAY,KAAK,MAAM;YAAE,MAAM,IAAI,yBAAyB,CAAC,OAAO,CAAC,CAAC;QACpG,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Internal (not exported from the package): one wavelength's diffraction kernel sampled on the site pitch, as the object-space\n * convolution (facade/diffraction-provider.ts) and the tabulated expected signal (facade/expected-signal-setup.ts) use it.\n *\n * `resolveLensPsfConvolutionKernels` parses and samples the PSF profile on every call (about 10 ms per wavelength), and both users\n * ask for the same kernels, so they are kept while the PSF record lives. The kernel is a function of its inputs alone; each caller\n * gets its own copy.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { resolveLensPsfConvolutionKernels } from \"./lens-psf-kernels.js\";\n\ntype Psf = Parameters<typeof resolveLensPsfConvolutionKernels>[0][\"psf\"];\n\n/** Kernels per PSF record (by identity: the record is large, so it is not serialized), then per wavelength, pitch and radius. */\nconst cache = new WeakMap<object, Map<string, Float64Array>>();\n\n/** The (2·radius + 1)² kernel of `psf` at `wavelengthNm` on a square site grid of `pitchMicrometers`; `failure` is the error message when the kernel cannot sample the pitch exactly. */\nexport function siteDiffractionKernel(psf: Psf, wavelengthNm: number, pitchMicrometers: number, radius: number, failure: string): Float64Array {\n let kernels = cache.get(psf);\n if (!kernels) cache.set(psf, (kernels = new Map()));\n const key = wavelengthNm + \":\" + pitchMicrometers + \":\" + radius;\n let kernel = kernels.get(key);\n if (!kernel) {\n const k = resolveLensPsfConvolutionKernels({ schemaVersion: \"0.1.0\", psf, spectralWeights: [{ wavelengthNm, weight: 1 }],\n grid: { width: 1, height: 1, sitePitchMicrometers: { x: pitchMicrometers, y: pitchMicrometers }, opticalAxisSite: { x: 0.5, y: 0.5 } }, patchWidth: 1, patchHeight: 1,\n kernelRadius: radius }).value;\n if (!k.exactSiteSampling || k.kernelRadius !== radius) throw new InvalidConfigurationError(failure);\n kernel = Float64Array.from(k.kernels);\n kernels.set(key, kernel);\n }\n return Float64Array.from(kernel);\n}\n"]}
@@ -137,56 +137,84 @@ export function createPrintJpegTask(input, provider) {
137
137
  }
138
138
  };
139
139
  const blocksX = Math.ceil(plan.outputWidth / 8), sums = new Float64Array(blocksX * 192);
140
- for (let by = 0; by < plan.outputHeight; by += 8) {
141
- check();
142
- sums.fill(0);
143
- const height = Math.min(8, plan.outputHeight - by), top = plan.crop.y + by * scaleY, bottom = by + height === plan.outputHeight ? cropBottom : plan.crop.y + (by + height) * scaleY;
144
- // Accumulation order per output pixel is unchanged from the per-block reader: 32-row bands from
145
- // floor(top), then 256-column chunks from floor(left), then rows, then columns. Identical IEEE sums.
146
- for (let sy = Math.floor(top); sy < Math.ceil(bottom); sy += 32) {
147
- const bandHeight = Math.min(32, Math.ceil(bottom) - sy);
148
- await ensureRows(sy, bandHeight);
140
+ // At 1:1 every output pixel is one source pixel with weight exactly 1, so its sum is exactly lut[code] and its
141
+ // sample is a function of the code alone: the same bytes from a 256-entry table, without the accumulation.
142
+ const identity = scaleX === 1 && scaleY === 1;
143
+ const direct = identity ? Uint8Array.from(lut, (value) => Math.min(255, Math.max(0, Math.round(encodeSrgbComponent(value) * 255)))) : undefined;
144
+ if (direct)
145
+ for (let by = 0; by < plan.outputHeight; by += 8) {
149
146
  check();
150
- for (let bx = 0, block = 0; bx < plan.outputWidth; bx += 8, block++) {
151
- const width = Math.min(8, plan.outputWidth - bx), left = plan.crop.x + bx * scaleX, right = bx + width === plan.outputWidth ? cropRight : plan.crop.x + (bx + width) * scaleX, base = block * 192;
152
- for (let sx = Math.floor(left); sx < Math.ceil(right); sx += 256) {
153
- const chunkWidth = Math.min(256, Math.ceil(right) - sx);
154
- for (let iy = 0; iy < bandHeight; iy++)
155
- for (let ix = 0; ix < chunkWidth; ix++) {
156
- const px = sx + ix, py = sy + iy;
157
- const firstX = Math.max(0, Math.floor((px - plan.crop.x) / scaleX) - bx), lastX = Math.min(width - 1, Math.ceil((px + 1 - plan.crop.x) / scaleX) - bx - 1);
158
- const firstY = Math.max(0, Math.floor((py - plan.crop.y) / scaleY) - by), lastY = Math.min(height - 1, Math.ceil((py + 1 - plan.crop.y) / scaleY) - by - 1);
159
- const index = (py - cacheTop) * rowBytes + (px - plan.crop.x) * 3, r = lut[cache[index]], g = lut[cache[index + 1]], b = lut[cache[index + 2]];
160
- for (let oy = firstY; oy <= lastY; oy++)
161
- for (let ox = firstX; ox <= lastX; ox++) {
162
- const x0 = plan.crop.x + (bx + ox) * scaleX, y0 = plan.crop.y + (by + oy) * scaleY;
163
- const weight = Math.max(0, Math.min(px + 1, x0 + scaleX) - Math.max(px, x0)) * Math.max(0, Math.min(py + 1, y0 + scaleY) - Math.max(py, y0)) / area;
164
- const i = base + (oy * 8 + ox) * 3;
165
- sums[i] = sums[i] + r * weight;
166
- sums[i + 1] = sums[i + 1] + g * weight;
167
- sums[i + 2] = sums[i + 2] + b * weight;
168
- }
169
- }
147
+ const height = Math.min(8, plan.outputHeight - by);
148
+ await ensureRows(plan.crop.y + by, height);
149
+ check();
150
+ for (let bx = 0; bx < plan.outputWidth; bx += 8) {
151
+ const width = Math.min(8, plan.outputWidth - bx), samples = [];
152
+ for (let y = 0; y < 8; y++) {
153
+ const row = (plan.crop.y + by + Math.min(y, height - 1) - cacheTop) * rowBytes + bx * 3;
154
+ for (let x = 0; x < 8; x++) {
155
+ const index = row + Math.min(x, width - 1) * 3;
156
+ samples.push(direct[cache[index]], direct[cache[index + 1]], direct[cache[index + 2]]);
157
+ }
158
+ }
159
+ check();
160
+ encoder.writeRgbBlock(samples);
161
+ if (++blocks % 32 === 0) {
162
+ await yieldControl(abort.signal);
163
+ check();
170
164
  }
171
165
  }
172
166
  }
173
- for (let bx = 0, block = 0; bx < plan.outputWidth; bx += 8, block++) {
174
- const width = Math.min(8, plan.outputWidth - bx), base = block * 192, samples = [];
175
- for (let y = 0; y < 8; y++)
176
- for (let x = 0; x < 8; x++)
177
- for (let c = 0; c < 3; c++) {
178
- const value = sums[base + (Math.min(y, height - 1) * 8 + Math.min(x, width - 1)) * 3 + c];
179
- // Nearest code, ties up. Math.round is used because floor(x+0.5) wrongly rounds 0.49999999999999994 up.
180
- samples.push(Math.min(255, Math.max(0, Math.round(encodeSrgbComponent(value) * 255))));
181
- }
167
+ else
168
+ for (let by = 0; by < plan.outputHeight; by += 8) {
182
169
  check();
183
- encoder.writeRgbBlock(samples);
184
- if (++blocks % 32 === 0) {
185
- await yieldControl(abort.signal);
170
+ sums.fill(0);
171
+ const height = Math.min(8, plan.outputHeight - by), top = plan.crop.y + by * scaleY, bottom = by + height === plan.outputHeight ? cropBottom : plan.crop.y + (by + height) * scaleY;
172
+ // Accumulation order per output pixel is unchanged from the per-block reader: 32-row bands from
173
+ // floor(top), then 256-column chunks from floor(left), then rows, then columns. Identical IEEE sums.
174
+ for (let sy = Math.floor(top); sy < Math.ceil(bottom); sy += 32) {
175
+ const bandHeight = Math.min(32, Math.ceil(bottom) - sy);
176
+ await ensureRows(sy, bandHeight);
186
177
  check();
178
+ for (let bx = 0, block = 0; bx < plan.outputWidth; bx += 8, block++) {
179
+ const width = Math.min(8, plan.outputWidth - bx), left = plan.crop.x + bx * scaleX, right = bx + width === plan.outputWidth ? cropRight : plan.crop.x + (bx + width) * scaleX, base = block * 192;
180
+ for (let sx = Math.floor(left); sx < Math.ceil(right); sx += 256) {
181
+ const chunkWidth = Math.min(256, Math.ceil(right) - sx);
182
+ for (let iy = 0; iy < bandHeight; iy++)
183
+ for (let ix = 0; ix < chunkWidth; ix++) {
184
+ const px = sx + ix, py = sy + iy;
185
+ const firstX = Math.max(0, Math.floor((px - plan.crop.x) / scaleX) - bx), lastX = Math.min(width - 1, Math.ceil((px + 1 - plan.crop.x) / scaleX) - bx - 1);
186
+ const firstY = Math.max(0, Math.floor((py - plan.crop.y) / scaleY) - by), lastY = Math.min(height - 1, Math.ceil((py + 1 - plan.crop.y) / scaleY) - by - 1);
187
+ const index = (py - cacheTop) * rowBytes + (px - plan.crop.x) * 3, r = lut[cache[index]], g = lut[cache[index + 1]], b = lut[cache[index + 2]];
188
+ for (let oy = firstY; oy <= lastY; oy++)
189
+ for (let ox = firstX; ox <= lastX; ox++) {
190
+ const x0 = plan.crop.x + (bx + ox) * scaleX, y0 = plan.crop.y + (by + oy) * scaleY;
191
+ const weight = Math.max(0, Math.min(px + 1, x0 + scaleX) - Math.max(px, x0)) * Math.max(0, Math.min(py + 1, y0 + scaleY) - Math.max(py, y0)) / area;
192
+ const i = base + (oy * 8 + ox) * 3;
193
+ sums[i] = sums[i] + r * weight;
194
+ sums[i + 1] = sums[i + 1] + g * weight;
195
+ sums[i + 2] = sums[i + 2] + b * weight;
196
+ }
197
+ }
198
+ }
199
+ }
200
+ }
201
+ for (let bx = 0, block = 0; bx < plan.outputWidth; bx += 8, block++) {
202
+ const width = Math.min(8, plan.outputWidth - bx), base = block * 192, samples = [];
203
+ for (let y = 0; y < 8; y++)
204
+ for (let x = 0; x < 8; x++)
205
+ for (let c = 0; c < 3; c++) {
206
+ const value = sums[base + (Math.min(y, height - 1) * 8 + Math.min(x, width - 1)) * 3 + c];
207
+ // Nearest code, ties up. Math.round is used because floor(x+0.5) wrongly rounds 0.49999999999999994 up.
208
+ samples.push(Math.min(255, Math.max(0, Math.round(encodeSrgbComponent(value) * 255))));
209
+ }
210
+ check();
211
+ encoder.writeRgbBlock(samples);
212
+ if (++blocks % 32 === 0) {
213
+ await yieldControl(abort.signal);
214
+ check();
215
+ }
187
216
  }
188
217
  }
189
- }
190
218
  await yieldControl(abort.signal);
191
219
  check();
192
220
  const bytes = encoder.finish();