@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
package/CHANGELOG.md CHANGED
@@ -1,6 +1,119 @@
1
1
  # Changelog
2
2
 
3
- ## 2.7.0 — unreleased (prepared October 24, 2026)
3
+ ## 2.8.1 — unreleased (prepared October 25, 2026)
4
+
5
+ A patch release: the same results as 2.8.0, faster. It fixes a 2.8.0 slowdown before a chart or material capture's first RAW tile and writes JPEGs about 3× faster.
6
+ - **Captures:** a 768-pixel Quality capture takes 5.6 s instead of 23.4 s on 2.8.0 (796 s on 2.7.0), and the slowest first tile measured takes 2.2 s instead of 24 s (Node, one thread).
7
+ - **JPEG:** a 24 MP JPEG is written in 5.0 s instead of 15.2 s, with byte-identical files.
8
+ - **No API change:** root exports are unchanged; results are the bytes of 2.8.0.
9
+
10
+ ### Faster, same output
11
+
12
+ - **JPEG writing about 3× faster, the same bytes.** A 24 MP baseline JPEG took 15.2 s to write and now takes 5.0 s (Node, one thread); every file is byte-identical.
13
+ - **1:1 Print JPEG:** where the output size equals the crop, each output pixel is one source pixel with weight exactly 1, so its sample is a function of its 8-bit code alone; `createPrintJpegTask` now takes it from a 256-entry table instead of the area-average accumulation.
14
+ - **DCT:** the encoder computes the eight v coefficients of a column in one pass over the block. Each coefficient still adds the same IEEE products in the same order and is scaled in the same order, so the coefficients are identical.
15
+ - **Checked:** identical SHA-256 of the files against 2.8.0 for 6000 × 4000, 1001 × 757, 9 × 7, 256 × 32 and 1543 × 1 images (smooth and noise); all JPEG hash locks pass.
16
+
17
+ - **Captures of chart and material scenes: a short start again (fixes a 2.8.0 slowdown before the first RAW tile).**
18
+ - **Cause:** the tabulated path's four verification sites ran the full chain through the diffraction convolution windowed to the whole render, so its first band convolved the full width before any tile: 24 s for a Quality preview of the 5.6 mm phone sensor at 768 pixels and f/32, against 0.55 s on 2.7.0.
19
+ - **What changed:**
20
+ - the verification uses a convolution windowed to its own 2 × 2 block, whose values are bit-identical to the whole render's (the window contract of 2.6);
21
+ - the whole-render convolution is built only when the environment path runs;
22
+ - the per-channel spectral responses are planned once per channel and kept across calls with the same sensor, exposure and optics;
23
+ - one wavelength's diffraction kernel is resolved once per PSF record, shared by the convolution and the tabulated signal.
24
+ - **Same values:** a capture with warm caches, after another request, equals the same capture from a fresh start (new test); all hash locks pass.
25
+ - **Measured** in Node, one thread, the app's requests tiled as the app renders them (24 MP full frame, f/8, unless noted), 2.8.0 → this change (2.7.0 for reference):
26
+
27
+ | Request | 2.7.0 | 2.8.0 | Now |
28
+ | --- | --- | --- | --- |
29
+ | 48-pixel quick frame, Speed / Quality | 1.3 / 8.3 s | 0.87 / 5.5 s | 0.70 / 4.6 s |
30
+ | 384-pixel capture, Speed / Quality | 43.9 / 193 s | 3.0 / 9.3 s | 1.1 / 2.4 s |
31
+ | 768-pixel capture, Speed / Quality | 185 / 796 s | 11.5 / 23.4 s | 3.3 / 5.6 s |
32
+ | 384-pixel Quality capture, 5.6 mm phone sensor, f/32 | 267 s | 25.4 s | 3.9 s |
33
+
34
+ The 768-pixel Quality preview of that phone sensor at f/32 reaches its first RAW tile in 2.2 s (0.89 s on a repeated call) instead of 24 s.
35
+
36
+ ## 2.8.0 — published October 25, 2026
37
+
38
+ A minor release that makes full-resolution captures of chart and material scenes about 50× faster (48–84× in the regions measured), and makes the display view exact at device-pixel sizes.
39
+ - **Faster captures, same bytes:** for chart presets and caller material scenes with a still, rectilinear camera, the RAW stage takes tabulated expected electrons and skips the per-site record pipeline. On every request tested, the samples, RAW codes and files are the bytes of 2.7.0.
40
+ - **Exact display view at device-pixel sizes:** a display pixel covering at most 25 real pixels is the exact area mean of the real pixels instead of a stratified estimate. Those views have new values (a deliberate reference change); the 155-case validation is the same as 2.6.0's or better.
41
+ - **Visible changes:** chart and material captures report a `raw.expectedSignal` assumption and count far fewer `providerEvaluationCount` evaluations; auto white balance no longer renders the RAW twice. Root exports are unchanged (1,727).
42
+ - **Known limit:** the Quality noise limit of 2.6.0 remains (#379), and its fix moves to 2.9.0.
43
+
44
+ Measured on one thread (24 MP frame, 6 µm, 50 mm f/8, 2 × 2 pixel averaging), with identical samples: a 512 × 512 Siemens star region takes 0.6 s instead of 31.7 s on 2.7.0 (Quality options: 2.8 s instead of 135 s); the whole frame takes 28.2 s as sRGB and 36.5 s as a JPEG.
45
+
46
+ ### Faster, same output
47
+
48
+ - **Captures of chart and material scenes: tabulated expected electrons, 10–13× faster again; the same bytes on every frame tested (#394, step 2).**
49
+ - **What changes:** a capture of a chart preset or a caller material scene, with a still, rectilinear, distortion-free camera (no rotation, lens projection or distortion, vignetting, pupil sampling or caller PSF), no longer runs each native site through the environment chain. It takes each site's expected photo electrons from the display view's tabulated expected signal: the same sum over the pixel-aperture nodes, anti-aliasing spots, diffraction taps and spectral nodes, formed in another order. The sensor stage is the native one, with each site's own seeds: dark charge, both Poisson draws, read noise and the ADC.
50
+ - **Same result, by verification:** one site of each CFA phase still runs the full chain. Its readout must be reproduced bit for bit, and its tabulated electrons must agree with the chain's within 1e-9 relative; otherwise the capture takes the environment path. Electrons that differ in the last bit could, rarely, draw a different integer at a site; on every frame tested none did.
51
+ - **Measured equality:** 0 differing RAW codes and 0 differing samples:
52
+ - 1,052,676 native sites of a 24 MP frame in Speed, for dead leaves and for a material scene;
53
+ - 272,484 sites with the Quality options;
54
+ - every frame of the 2.0.0 hash locks.
55
+
56
+ The verification sites agreed within 2 × 10⁻¹⁵ relative.
57
+ - **Visible changes:**
58
+ - a `raw.expectedSignal` assumption names the method and the verification;
59
+ - `diagnostics.providerEvaluationCount` counts the verification sites' evaluator calls and the table's scene lookups (120 instead of 720 for a 6 × 6 frame);
60
+ - auto white balance keeps the RAW instead of rendering it again, because its codes do not depend on the white-balance intent.
61
+
62
+ The 2.0.0 hash lock's byte digests are unchanged; its assumption and diagnostics digests were re-recorded.
63
+ - **Measured** on a 512 × 512 centre region of a 24 MP frame (6 µm, 50 mm f/8, 2 × 2 pixel averaging), one thread, one run each, against main with step 1:
64
+
65
+ | Scene | Speed | Quality options |
66
+ | --- | --- | --- |
67
+ | Siemens star preset | 6.5 → 0.6 s | 37.5 → 2.8 s |
68
+ | Siemens star as a caller material scene | 7.1 → 0.7 s | 40.4 → 3.0 s |
69
+
70
+ The whole 24 MP frame on one thread now takes 28.2 s as sRGB (RAW 24.2 s, development 3.9 s), 36.5 s as a JPEG (encoding 11.2 s) and 75.7 s as a Quality PNG.
71
+ - **Code:**
72
+ - the expected signal moved to `src/facade/expected-signal.ts`, so capture-only bundles still leave out the display view;
73
+ - the input setup is shared with the display view in `src/facade/expected-signal-setup.ts`, and the display view's hash locks are unchanged;
74
+ - the RAW task is in `src/capture/native-environment-tabulated.ts`.
75
+
76
+ - **Captures of chart and material scenes: 3.6–8.3× faster RAW stage, no byte changed (#394, step 1).**
77
+ - **Engine-built tiles without a private copy:** the native environment no longer `structuredClone`s the site records that `simulateCapture` builds for each tile. They are fresh engine records, never shown to supplied code or changed afterwards, so they are planned as engine-owned data directly. Shared records keep their identity, which also makes the template comparisons cheaper. Tiles from callers are still copied.
78
+ - **Key taps:** for a chart preset or a caller material scene, the engine-built evaluator's result for a query is a pure function of the material at the queried plane point and the wavelength (with object-space diffraction, of the convolved lattice site and the wavelength). A verified lean template now plans its sites' taps as those keys instead of frozen requests, and takes each radiance from the key: chart materials from a table filled once per material and spectral node, lattice sites from the convolved band. Without a PSF a tap's key is the same at every spectral node, so it is found once per spatial node. The evaluator is not called for such taps; the evaluation count is still added, and the abort check still runs.
79
+ - **Verified per template:** a template takes key taps only when its own site's key radiances equal every radiance of its full execution, tap by tap (`Object.is`). Key taps never apply with pupil rays, a lens field mapping, a caller reflectance or radiance function, or a batched provider. A tap that would need the full irradiance function (a non-finite or negative value, or a field throughput outside the fast path) sends its site through the full path before anything is counted. A caller `materialAt` that throws during key planning sends its site to the full path, which throws the same error.
80
+ - **Tests:** a new test compares lean and full captures (samples, diagnostics counts, assumptions) for chart presets and caller material scenes in Speed and Quality, with a fixed-size chart, rotation, two temporal samples, a rolling shutter, vignetting and a region, and checks that key taps ran; and that lens field mappings and caller function scenes stay on request taps. All hash locks and facade fingerprints are unchanged.
81
+ - **Measured** on a 512 × 512 centre region of a 24 MP frame (6 µm, 50 mm f/8, 2 × 2 pixel averaging), one thread, main before and this change after, one run each; samples identical:
82
+
83
+ | Scene | Speed | Quality options (diffraction, four-spot filter, AHD) |
84
+ | --- | --- | --- |
85
+ | Siemens star preset | 31.7 → 6.5 s (4.9×) | 135.2 → 37.5 s (3.6×) |
86
+ | Siemens star as a caller material scene | 58.6 → 7.1 s (8.3×) | 252.9 → 40.4 s (6.3×) |
87
+
88
+ The material scene gains more because it now also takes the engine-built evaluator's shorter result parsing that chart presets had since 2.6.
89
+ - **Display view: a sample's terms written in place; the clock read less often.** Each real pixel's recorded terms went through a new typed-array view (`subarray`, then `fill`) and `Date.now()` was read per display pixel. The terms are now written straight into the real pixel's memo slot (a scratch buffer only for the first sample of a one-material key), and the clock is read every 16 display pixels and at each row's end; yield timing never changes the output.
90
+ - **Same values:** 57 display views (Speed, a 2 × 2 box aperture and the Quality options; three presets, a caller material scene and a caller function scene; exact and stratified sizes) hash identically before and after: samples, expected values, noise and diagnostics. All hash locks pass.
91
+ - **Measured:** four 32-row bands of the app's 2028 × 1352 view of a 24 MP frame (a caller material scene, Speed, 2 × 2 box aperture), one thread, median of five alternating runs: 4.31 s → 4.00 s (about 7 %). A line profile puts the rest of the time in the per-real-pixel statistics and the scene's material lookups (the caller's `materialAt` with the frozen point it receives), which these changes leave as they are.
92
+
93
+ ### Changed
94
+
95
+ - **Display view: the exact area mean where a footprint holds at most 25 real pixels (#393).** This is a deliberate reference change.
96
+ - **Before:** each display pixel's expected value was a stratified estimate over K × K jittered real pixels.
97
+ - **Now:** where a display pixel's rectangle holds at most 25 real pixels (and at most `view.samplesPerAxis`²), so views at least 1/5 of the frame's width, every real pixel it touches is evaluated once and weighted by its exact overlap, w_j / A. The expected value is then the definition itself, D_P = Σ_j (w_j/A)·E[L_j], with no sampling error.
98
+ - **Noise:** the linear part keeps the closed-form B_c. The independent residual becomes Σ_j (w_j/A)²·r_j, the same model written exactly.
99
+ - **Shared terms:** a real pixel's recorded terms are shared by the display pixels that touch it, in a ring of native rows.
100
+ - **Unchanged:** coarser views keep the stratified estimator and their bytes. The `view.estimation` assumption names the method, and the internal render result reports it.
101
+ - **Tests:** a new test checks, in Speed and with diffraction, the four-spot filter and AHD, that a half-size and a 2/3-size view of a 48 × 32 frame have exactly the overlap-weighted means of the frame-size view's per-pixel expected values (within 1e-12 relative). Two of the six display-view hash locks and both memo locks were re-recorded (their views take the exact path); the four coarser locks are unchanged.
102
+ - **Measured** on the 2028 × 1352 view's 8-row strips of a 24 MP frame, the app's chart as a material scene, one thread, against 2.7.0:
103
+
104
+ | Strip | Speed | Quality options |
105
+ | --- | --- | --- |
106
+ | Rows 660 / 300 / 1000 | 1.04 / 1.01 / 0.97 s → 0.84 / 0.85 / 0.77 s | 2.9 / 3.1 / 2.7 s → 2.3 / 2.8 / 2.3 s |
107
+
108
+ Samples fell 3–9× and site evaluations 3–10×. The time fell less, because the 2.7 memos had already made repeated samples cheap: at device-pixel sizes the view now does about one real pixel's work per real pixel.
109
+ - **Why 25:** measured on 8-row strips of a 24 MP view (Siemens star and dead leaves, f/2.8, one thread, one run each), the exact path took the stratified estimator's time within run-to-run spread up to 25 real pixels per display pixel and longer above: 1.1–1.2× at 36, 1.4–1.5× at 64, 1.5–2.4× at 144, because it evaluates every real pixel while the estimator takes a quarter of its samples per axis where the pilot finds no detail.
110
+ - **Validation:** the 155-case validation was re-run on October 25 (`de77074`, [record](https://github.com/Photivra/photivra/blob/main/docs/validation/display-view-2026-10-25/README.md)): 145 cases are identical to the 2.6.0 record, and the 10 cases on the exact area mean (the phone at 800 wide) are the same or better (Siemens star: expected-value RMS 1.78 → 1.35 codes, SSIM 0.9931 → 0.9966). The known Quality noise miss of case 138 (#379) remains: 1.165 against the 1.15 limit.
111
+
112
+ ### Known limit
113
+
114
+ - **Quality noise at small footprints (#379), unchanged from 2.6.0.** With `output.demosaic: "ahd"` the display view's noise has too much colour where a display pixel covers few sensor pixels; validation case 138 is outside its limit (1.165 against 1.15). The exact covariance through AHD's two linear candidates was measured and makes it worse (1.351), because AHD's direction choice reacts to the noise; the remaining fix is a Monte Carlo model of the real AHD output's noise covariance, moved to 2.9.0 by the owner's decision of October 25, 2026. Speed (bilinear) is exact, and the Photivra site offers Speed only.
115
+
116
+ ## 2.7.0 — published October 24, 2026
4
117
 
5
118
  A minor release that makes the display view much faster at device-pixel sizes and adds two APIs for it.
6
119
  - **API for regions:** `prepareDisplayView` resolves a request once for many regions.
package/CITATION.cff CHANGED
@@ -9,7 +9,7 @@ abstract: "An open TypeScript engine for the science and mathematics of photogra
9
9
  repository-code: "https://github.com/photivra/photivra"
10
10
  url: "https://photivra.com"
11
11
  license: "Apache-2.0"
12
- version: "2.7.0"
12
+ version: "2.8.1"
13
13
  keywords:
14
14
  - photography
15
15
  - camera optics
package/README.md CHANGED
@@ -32,10 +32,10 @@ The source is licensed under Apache-2.0.
32
32
 
33
33
  ## Quick start
34
34
 
35
- Install the published ESM package below. The [2.7.0 release record](https://github.com/Photivra/photivra/blob/main/docs/RELEASE_2_7_0.md) and [changelog](https://github.com/Photivra/photivra/blob/main/CHANGELOG.md) describe the current release:
35
+ Install the published ESM package below. The [2.8.1 release record](https://github.com/Photivra/photivra/blob/main/docs/RELEASE_2_8_1.md) and [changelog](https://github.com/Photivra/photivra/blob/main/CHANGELOG.md) describe the current release:
36
36
 
37
37
  ```sh
38
- npm install @photivra/engine@2.7.0
38
+ npm install @photivra/engine@2.8.1
39
39
  ```
40
40
 
41
41
  Simulate a photograph of a test chart with a generic camera, then read the developed sRGB pixels:
@@ -75,7 +75,7 @@ console.log(horizontal.provenance);
75
75
 
76
76
  Start with the [Developer Guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Then use the [Usage Guide](https://github.com/Photivra/photivra/blob/main/docs/USAGE.md) and the [API reference](https://github.com/Photivra/photivra/blob/main/docs/API_REFERENCE.md), which is generated from the package exports.
77
77
 
78
- Repository `main` may describe newer, unreleased work. For the stable contract, use the docs bundled in the published package, or the exact `v2.7.0` tag.
78
+ Repository `main` may describe newer, unreleased work. For the stable contract, use the docs bundled in the published package, or the exact `v2.8.1` tag.
79
79
 
80
80
  ## Documentation
81
81
 
@@ -363,13 +363,13 @@ The application can then use those results, while the photography calculations t
363
363
 
364
364
  ## Status
365
365
 
366
- - Repository package version: `2.7.0` (release candidate prepared October 24, 2026; publication is separate)
367
- - Engine API contract: `2.7.0`
366
+ - Repository package version: `2.8.1` (release candidate prepared October 25, 2026; publication is separate)
367
+ - Engine API contract: `2.8.1`
368
368
  - Versioned generic tier reference assets: [GENERIC_TIER_PRESETS.md](https://github.com/Photivra/photivra/blob/main/docs/GENERIC_TIER_PRESETS.md).
369
369
  - Composed POC simulation API contract: `1.0.0`
370
370
  - Stability: 1.0 root-package compatibility policy; bounded scientific models and independent POC contract
371
371
 
372
- From 1.0.1, the package version and `ENGINE_API_VERSION` are equal. New captures/plans record 2.7.0; existing archives keep their original creator identities. Schema, model and POC contract versions stay independent.
372
+ From 1.0.1, the package version and `ENGINE_API_VERSION` are equal. New captures/plans record 2.8.1; existing archives keep their original creator identities. Schema, model and POC contract versions stay independent.
373
373
 
374
374
  A breaking change to the root contracts now needs a new package major version. Additive and corrective changes follow [compatibility and migration](https://github.com/Photivra/photivra/blob/main/docs/RELEASE_1_3_0.md). A 1.0 package is not a claim of calibrated physical accuracy.
375
375
 
package/SECURITY.md CHANGED
@@ -4,7 +4,7 @@ Please do not open a public issue that contains vulnerability details.
4
4
 
5
5
  ## Supported versions
6
6
 
7
- The published 1.0.0 release started the 1.x root-package compatibility line, and 2.0.0 started the 2.x line. The current release is **2.6.0**, published on October 20, 2026 (see [CHANGELOG.md](CHANGELOG.md)).
7
+ The published 1.0.0 release started the 1.x root-package compatibility line, and 2.0.0 started the 2.x line. The current release is **2.8.0**, published on October 25, 2026 (see [CHANGELOG.md](CHANGELOG.md)).
8
8
 
9
9
  Security fixes target the current maintained development and release line. Do not assume that older 1.x releases, snapshots or pre-1.0 releases will receive fixes.
10
10
 
@@ -174,7 +174,7 @@ export function createExportJpegEncoder(input) {
174
174
  const cosine = JPEG_DCT_COSINES;
175
175
  const previous = [0, 0, 0];
176
176
  const planes = [new Float64Array(64), new Float64Array(64), new Float64Array(64)];
177
- const rowProducts = new Float64Array(64), transformed = new Float64Array(64);
177
+ const transformed = new Float64Array(64);
178
178
  const blueScale = 2 * (1 - .114), redScale = 2 * (1 - .299);
179
179
  return {
180
180
  writeRgbBlock(samples) {
@@ -198,20 +198,33 @@ export function createExportJpegEncoder(input) {
198
198
  const block = planes[channel];
199
199
  for (let u = 0; u < 8; u++) {
200
200
  const uOffset = u * 8;
201
- for (let y = 0; y < 8; y++)
202
- for (let x = 0; x < 8; x++)
203
- rowProducts[y * 8 + x] = block[y * 8 + x] * cosine[uOffset + x];
204
201
  const uFactor = u === 0 ? Math.SQRT1_2 : 1;
205
- for (let v = 0; v < 8; v++) {
206
- const vOffset = v * 8;
207
- let sum = 0;
208
- for (let y = 0; y < 8; y++) {
209
- const cv = cosine[vOffset + y];
210
- for (let x = 0; x < 8; x++)
211
- sum += rowProducts[y * 8 + x] * cv;
202
+ // The eight v sums in one pass over (y, x): each sum still adds the same products f(x,y)·C(u,x)·C(v,y) in the same
203
+ // order (y, then x), and each row product is the same IEEE product, formed once per (x, y) instead of stored.
204
+ let s0 = 0, s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0, s6 = 0, s7 = 0;
205
+ for (let y = 0; y < 8; y++) {
206
+ const c0 = cosine[y], c1 = cosine[8 + y], c2 = cosine[16 + y], c3 = cosine[24 + y], c4 = cosine[32 + y], c5 = cosine[40 + y], c6 = cosine[48 + y], c7 = cosine[56 + y];
207
+ for (let x = 0; x < 8; x++) {
208
+ const r = block[y * 8 + x] * cosine[uOffset + x];
209
+ s0 += r * c0;
210
+ s1 += r * c1;
211
+ s2 += r * c2;
212
+ s3 += r * c3;
213
+ s4 += r * c4;
214
+ s5 += r * c5;
215
+ s6 += r * c6;
216
+ s7 += r * c7;
212
217
  }
213
- transformed[vOffset + u] = roundJpegCoefficient(sum * .25 * uFactor * (v === 0 ? Math.SQRT1_2 : 1) / q);
214
218
  }
219
+ // Scaled as before, left to right: sum·0.25·(u factor)·(v factor)/q.
220
+ transformed[u] = roundJpegCoefficient(s0 * .25 * uFactor * Math.SQRT1_2 / q);
221
+ transformed[8 + u] = roundJpegCoefficient(s1 * .25 * uFactor * 1 / q);
222
+ transformed[16 + u] = roundJpegCoefficient(s2 * .25 * uFactor * 1 / q);
223
+ transformed[24 + u] = roundJpegCoefficient(s3 * .25 * uFactor * 1 / q);
224
+ transformed[32 + u] = roundJpegCoefficient(s4 * .25 * uFactor * 1 / q);
225
+ transformed[40 + u] = roundJpegCoefficient(s5 * .25 * uFactor * 1 / q);
226
+ transformed[48 + u] = roundJpegCoefficient(s6 * .25 * uFactor * 1 / q);
227
+ transformed[56 + u] = roundJpegCoefficient(s7 * .25 * uFactor * 1 / q);
215
228
  }
216
229
  const difference = transformed[0] - previous[channel], size = amplitude(difference, 11);
217
230
  emit(size, 4);
@@ -1 +1 @@
1
- {"version":3,"file":"export-jpeg.js","sourceRoot":"","sources":["../../src/capture/export-jpeg.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Baseline JPEG encoder written for this project. It computes the DCT (discrete cosine transform)\n * directly and uses no third-party encoder source, default quantization tables or Huffman tables.\n * @see docs/SIMULATED_CAPTURE.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { FULL_RESOLUTION_CEILING_PIXELS, scaleBudgetCeiling } from \"../core/pixel-limits.js\";\n\n/**\n * Identifies the exact byte output of the baseline encoder.\n *\n * Version 2 (engine 2.0.0) uses the fixed cosine table below instead of runtime `Math.cos`, and rounds\n * quantized coefficients half away from zero. Compared with version 1, bytes can differ only where a\n * coefficient before rounding was within about one ULP (unit in the last place) of a half-integer, or\n * was exactly a negative half-integer. The repository's test images are unchanged.\n */\nexport const EXPORT_JPEG_ENCODER_ID = \"photivra-baseline-jpeg-444-v2\" as const;\n/**\n * C(u,x) = cos((2x+1)·u·π/16) for u,x in 0..7, row-major by u.\n *\n * Each entry is the correctly rounded IEEE-754 double of the exact cosine. They were computed offline with\n * 130-digit decimal arithmetic and checked so that no neighbouring double is closer (see\n * docs/PHOTOGRAPHIC_EXPORT.md). Hard-coding them makes the DCT independent of each JavaScript engine's\n * `Math.cos`, which differs in the last bit between engines. These are mathematical facts, not third-party data.\n */\nexport const JPEG_DCT_COSINES: Readonly<Float64Array> = Float64Array.of(\n 1,1,1,1,1,1,1,1,\n 0.9807852804032304,0.8314696123025452,0.5555702330196022,0.19509032201612828,-0.19509032201612828,-0.5555702330196022,-0.8314696123025452,-0.9807852804032304,\n 0.9238795325112867,0.3826834323650898,-0.3826834323650898,-0.9238795325112867,-0.9238795325112867,-0.3826834323650898,0.3826834323650898,0.9238795325112867,\n 0.8314696123025452,-0.19509032201612828,-0.9807852804032304,-0.5555702330196022,0.5555702330196022,0.9807852804032304,0.19509032201612828,-0.8314696123025452,\n 0.7071067811865476,-0.7071067811865476,-0.7071067811865476,0.7071067811865476,0.7071067811865476,-0.7071067811865476,-0.7071067811865476,0.7071067811865476,\n 0.5555702330196022,-0.9807852804032304,0.19509032201612828,0.8314696123025452,-0.8314696123025452,-0.19509032201612828,0.9807852804032304,-0.5555702330196022,\n 0.3826834323650898,-0.9238795325112867,0.9238795325112867,-0.3826834323650898,-0.3826834323650898,0.9238795325112867,-0.9238795325112867,0.3826834323650898,\n 0.19509032201612828,-0.5555702330196022,0.8314696123025452,-0.9807852804032304,0.9807852804032304,-0.8314696123025452,0.5555702330196022,-0.19509032201612828\n);\n/**\n * Rounds a quantized coefficient to the nearest integer, with ties going away from zero (−2.5 → −3,\n * 2.5 → 3), so positive and negative values round the same way. `Math.round` alone sends −2.5 to −2.\n */\nexport function roundJpegCoefficient(value: number): number {\n return value<0 ? -Math.round(-value) : Math.round(value);\n}\n\n/** Internal baseline JPEG settings: 8-bit YCbCr 4:4:4, one explicit constant quantization step, and uniform Huffman tables written for this project. */\nexport interface ExportJpegInput { width: number; height: number; samples: readonly number[]; quantizationStep: number; exif: Uint8Array; xmp: Uint8Array }\n/** Encodes a baseline JPEG with a directly computed DCT. Uses no third-party encoder source, default quantization tables or Huffman tables. */\nexport function encodeExportJpeg(input: ExportJpegInput): Uint8Array {\n const { width, height, samples, quantizationStep: q } = input;\n if (![width,height,q].every(Number.isSafeInteger) || width < 1 || height < 1 || width*height > 4096 ||\n q < 1 || q > 255 || samples.length !== width*height*3 || Array.from(samples).some((v) => !Number.isInteger(v) || v < 0 || v > 255)) {\n throw new InvalidConfigurationError(\"Unsupported baseline JPEG input.\");\n }\n const encoder=createExportJpegEncoder({...input,maximumOutputBytes:1_000_000});\n for(let by=0;by<height;by+=8)for(let bx=0;bx<width;bx+=8){\n const block:number[]=[];\n for(let y=0;y<8;y++)for(let x=0;x<8;x++){\n const p=(Math.min(by+y,height-1)*width+Math.min(bx+x,width-1))*3;\n block.push(samples[p]!,samples[p+1]!,samples[p+2]!);\n }\n encoder.writeRgbBlock(block);\n }\n return encoder.finish();\n}\n/**\n * Largest byte budget the internal writer accepts: the Print JPEG budget ceiling at the largest opt-in source\n * (256 000 000 bytes per 24 000 000 pixels at 2^27 pixels). Each caller enforces its own smaller budget.\n */\nconst EXPORT_JPEG_MAXIMUM_OUTPUT_BYTES=scaleBudgetCeiling(256_000_000,FULL_RESOLUTION_CEILING_PIXELS);\n/**\n * Internal size-limited JPEG writer. The reference export and the scalable export use exactly the same\n * DCT and entropy-coding equations.\n *\n * When `densityDotsPerInch` is supplied, it is written to the JFIF 1.02 APP0 segment as units=1 (dots\n * per inch) with equal X and Y density. JFIF density fields are 16-bit unsigned, so it must be an integer\n * in 1..65535; callers round any fractional density and store the exact value in EXIF. When omitted,\n * APP0 keeps units=0 with a 1:1 pixel aspect ratio and states no physical size.\n */\nexport function createExportJpegEncoder(input: Omit<ExportJpegInput,\"samples\"> & {maximumOutputBytes:number;icc?:Uint8Array;densityDotsPerInch?:number}): {\n writeRgbBlock(samples:readonly number[]):void; finish():Uint8Array; dispose():void;\n} {\n const {width,height,quantizationStep:q}=input;\n if(![width,height,q,input.maximumOutputBytes].every(Number.isSafeInteger)||width<1||height<1||width>65535||height>65535||q<1||q>255||input.maximumOutputBytes<1||input.maximumOutputBytes>EXPORT_JPEG_MAXIMUM_OUTPUT_BYTES)\n throw new InvalidConfigurationError(\"Invalid bounded JPEG encoding plan.\");\n const density=input.densityDotsPerInch;\n if(density!==undefined&&(!Number.isSafeInteger(density)||density<1||density>65535))\n throw new InvalidConfigurationError(\"JFIF density requires an integer from 1 to 65535 dots per inch.\");\n // Growable output. Capacity starts at min(budget, 64 KiB) and doubles, but never beyond the budget. The budget\n // check is unchanged (offset + n > maximumOutputBytes fails), so the output bytes and failures are the same as\n // with a buffer allocated at full budget up front. An old buffer is zeroed after its bytes are copied over.\n const budget=input.maximumOutputBytes;\n let storage=new Uint8Array(Math.min(budget,65_536)),offset=0,disposed=false,blocks=0;\n const reserve=(count:number):void=>{\n if(disposed||offset+count>budget)throw new InvalidConfigurationError(\"JPEG output exceeds admitted byte budget or is disposed.\");\n if(offset+count<=storage.length)return;\n const grown=new Uint8Array(Math.min(budget,Math.max(storage.length*2,offset+count)));\n grown.set(storage.subarray(0,offset));storage.fill(0,0,offset);storage=grown;\n };\n const push=(...values:number[]):void=>{reserve(values.length);storage.set(values,offset);offset+=values.length;};\n // Fast path for one entropy-coded byte: the same budget/disposal check and byte order as push(), without an array per byte.\n const pushByte=(value:number):void=>{if(disposed||offset>=storage.length)reserve(1);storage[offset++]=value;};\n push(255,216);\n const marker = (id: number, payload: readonly number[]): void => {\n if (payload.length > 65533) throw new InvalidConfigurationError(\"JPEG metadata segment exceeds the standard APP limit.\");\n push(255,id,(payload.length+2)>>8,(payload.length+2)&255,...payload);\n };\n // JFIF 1.02 APP0: identifier, version 1.02, units (0 aspect ratio only, 1 dots per inch), Xdensity, Ydensity, no thumbnail.\n marker(224,density===undefined ? [74,70,73,70,0,1,2,0,0,1,0,1,0,0] : [74,70,73,70,0,1,2,1,density>>8,density&255,density>>8,density&255,0,0]);\n marker(225,[69,120,105,102,0,0,...input.exif]);\n marker(225,[...new TextEncoder().encode(\"http://ns.adobe.com/xap/1.0/\\0\"),...input.xmp]);\n if(input.icc){\n if(input.icc.length>65519)throw new InvalidConfigurationError(\"ICC profile exceeds the single-segment encoding envelope.\");\n marker(226,[...new TextEncoder().encode(\"ICC_PROFILE\\0\"),1,1,...input.icc]);\n }\n marker(219,[0,...Array<number>(64).fill(q)]);\n marker(192,[8,height>>8,height&255,width>>8,width&255,3,1,17,0,2,17,0,3,17,0]);\n // DC categories 0..11 use 4-bit codes; AC EOB/ZRL and all baseline run/category pairs use 8-bit codes.\n // Incomplete canonical Huffman trees reserve the all-ones code, so JPEG bit padding (1-bits) can never be read as a symbol.\n const dc = Array.from({length:12},(_,i) => i), ac = [0,240];\n for (let run=0;run<16;run++) for (let size=1;size<=10;size++) ac.push(run*16+size);\n marker(196,[0,...Array.from({length:16},(_,i) => i===3 ? 12 : 0),...dc,\n 16,...Array.from({length:16},(_,i) => i===7 ? ac.length : 0),...ac]);\n marker(218,[3,1,0,2,0,3,0,0,63,0]);\n let accumulator=0, bits=0;\n // Appends the low `count` bits (count <= 16) most-significant first, emitting each completed byte with 0xFF stuffing.\n const emit = (value: number, count: number): void => {\n accumulator=(accumulator<<count)|(value&((1<<count)-1)); bits+=count;\n while (bits>=8) { bits-=8; const byte=(accumulator>>>bits)&255; pushByte(byte); if (byte===255) pushByte(0); }\n accumulator&=(1<<bits)-1;\n };\n const amplitude = (value: number, max: number): number => {\n const size=value===0 ? 0 : Math.floor(Math.log2(Math.abs(value)))+1;\n if (size>max) throw new InvalidConfigurationError(\"JPEG coefficient exceeds baseline entropy range.\");\n return size;\n };\n const coefficient = (value: number, size: number): void => { if (size) emit(value>=0 ? value : value+(1<<size)-1,size); };\n const zigzag=new Int32Array(64);\n for (let diagonal=0,i=0;diagonal<=14;diagonal++) {\n const points: number[] = [];\n for (let y=0;y<8;y++) { const x=diagonal-y; if (x>=0 && x<8) points.push(y*8+x); }\n for (const point of diagonal%2===0 ? points.reverse() : points) zigzag[i++]=point;\n }\n // Huffman symbol index of each AC (run, size) pair; equals ac.indexOf(symbol) for every emitted symbol.\n const acIndex=new Int16Array(256).fill(-1);\n for (let i=ac.length-1;i>=0;i--) acIndex[ac[i]!]=i;\n/**\n * The 8×8 DCT, computed directly, with orthonormal DC factors and one chosen quantization step.\n *\n * This is a deliberately simple 4:4:4 reference encoder. Lossy quantization means exact RGB pixels are\n * not preserved. Decoding the JPEG and comparing is an interoperability check, not an exact scientific\n * check of pixel values.\n *\n * Each coefficient is the direct 64-term sum Σ_y Σ_x (f(x,y)·C(u,x))·C(v,y), summed over y, then x.\n * The row products f(x,y)·C(u,x) are computed once per u and reused for all eight v. This reuses the\n * identical IEEE-754 products and keeps the exact order of multiplication and summation. A factored\n * row/column DCT would reorder the sum, so it is deliberately not used.\n *\n * C(u,x) comes from the fixed table `JPEG_DCT_COSINES` (correctly rounded constants), not `Math.cos`,\n * so the coefficients do not depend on the JavaScript engine's maths library. Each quantized coefficient\n * is rounded half away from zero (`roundJpegCoefficient`).\n */\n const cosine=JPEG_DCT_COSINES;\n const previous=[0,0,0];\n const planes=[new Float64Array(64),new Float64Array(64),new Float64Array(64)];\n const rowProducts=new Float64Array(64), transformed=new Float64Array(64);\n const blueScale=2*(1-.114), redScale=2*(1-.299);\n return {\n writeRgbBlock(samples:readonly number[]):void {\n let valid=!disposed&&blocks<Math.ceil(width/8)*Math.ceil(height/8)&&samples.length===192;\n for(let i=0;valid&&i<192;i++){const v=samples[i]!;valid=Number.isInteger(v)&&v>=0&&v<=255;}\n if(!valid)throw new InvalidConfigurationError(\"JPEG requires complete bounded RGB coding blocks.\");\n const [luma,blueDifference,redDifference]=planes as [Float64Array,Float64Array,Float64Array];\n for (let i=0;i<64;i++) {\n // JPEG pads only partial 8x8 blocks (MCUs, minimum coded units). The declared image and crop size never change.\n const p=i*3;\n const red=samples[p]!, green=samples[p+1]!, blue=samples[p+2]!, luminance=.299*red+.587*green+.114*blue;\n luma[i]=luminance-128; blueDifference[i]=(blue-luminance)/blueScale; redDifference[i]=(red-luminance)/redScale;\n }\n for (let channel=0;channel<3;channel++) {\n const block=planes[channel]!;\n for (let u=0;u<8;u++) {\n const uOffset=u*8;\n for (let y=0;y<8;y++) for (let x=0;x<8;x++) rowProducts[y*8+x]=block[y*8+x]!*cosine[uOffset+x]!;\n const uFactor=u===0 ? Math.SQRT1_2 : 1;\n for (let v=0;v<8;v++) {\n const vOffset=v*8;\n let sum=0; for (let y=0;y<8;y++) { const cv=cosine[vOffset+y]!; for (let x=0;x<8;x++) sum+=rowProducts[y*8+x]!*cv; }\n transformed[vOffset+u]=roundJpegCoefficient(sum*.25*uFactor*(v===0 ? Math.SQRT1_2 : 1)/q);\n }\n }\n const difference=transformed[0]!-previous[channel]!, size=amplitude(difference,11); emit(size,4); coefficient(difference,size); previous[channel]=transformed[0]!;\n let zeroes=0;\n for (let i=1;i<64;i++) {\n const value=transformed[zigzag[i]!]!;\n if (value===0) { zeroes++; continue; }\n while (zeroes>=16) { emit(1,8); zeroes-=16; }\n const length=amplitude(value,10), symbol=zeroes*16+length; emit(acIndex[symbol]!,8); coefficient(value,length); zeroes=0;\n }\n if (zeroes) emit(0,8);\n }\n blocks++;\n },\n finish():Uint8Array {\n if(disposed||blocks!==Math.ceil(width/8)*Math.ceil(height/8))throw new InvalidConfigurationError(\"JPEG cannot finish incomplete or disposed encoding.\");\n if(bits)emit((1<<(8-bits))-1,8-bits);\n // Only the written prefix can hold data; zero it after the copy instead of filling the whole capacity.\n push(255,217);const output=storage.slice(0,offset);storage.fill(0,0,offset);storage=new Uint8Array(0);disposed=true;return output;\n },\n dispose():void {storage.fill(0,0,offset);storage=new Uint8Array(0);disposed=true;}\n };\n}\n"]}
1
+ {"version":3,"file":"export-jpeg.js","sourceRoot":"","sources":["../../src/capture/export-jpeg.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Baseline JPEG encoder written for this project. It computes the DCT (discrete cosine transform)\n * directly and uses no third-party encoder source, default quantization tables or Huffman tables.\n * @see docs/SIMULATED_CAPTURE.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { FULL_RESOLUTION_CEILING_PIXELS, scaleBudgetCeiling } from \"../core/pixel-limits.js\";\n\n/**\n * Identifies the exact byte output of the baseline encoder.\n *\n * Version 2 (engine 2.0.0) uses the fixed cosine table below instead of runtime `Math.cos`, and rounds\n * quantized coefficients half away from zero. Compared with version 1, bytes can differ only where a\n * coefficient before rounding was within about one ULP (unit in the last place) of a half-integer, or\n * was exactly a negative half-integer. The repository's test images are unchanged.\n */\nexport const EXPORT_JPEG_ENCODER_ID = \"photivra-baseline-jpeg-444-v2\" as const;\n/**\n * C(u,x) = cos((2x+1)·u·π/16) for u,x in 0..7, row-major by u.\n *\n * Each entry is the correctly rounded IEEE-754 double of the exact cosine. They were computed offline with\n * 130-digit decimal arithmetic and checked so that no neighbouring double is closer (see\n * docs/PHOTOGRAPHIC_EXPORT.md). Hard-coding them makes the DCT independent of each JavaScript engine's\n * `Math.cos`, which differs in the last bit between engines. These are mathematical facts, not third-party data.\n */\nexport const JPEG_DCT_COSINES: Readonly<Float64Array> = Float64Array.of(\n 1,1,1,1,1,1,1,1,\n 0.9807852804032304,0.8314696123025452,0.5555702330196022,0.19509032201612828,-0.19509032201612828,-0.5555702330196022,-0.8314696123025452,-0.9807852804032304,\n 0.9238795325112867,0.3826834323650898,-0.3826834323650898,-0.9238795325112867,-0.9238795325112867,-0.3826834323650898,0.3826834323650898,0.9238795325112867,\n 0.8314696123025452,-0.19509032201612828,-0.9807852804032304,-0.5555702330196022,0.5555702330196022,0.9807852804032304,0.19509032201612828,-0.8314696123025452,\n 0.7071067811865476,-0.7071067811865476,-0.7071067811865476,0.7071067811865476,0.7071067811865476,-0.7071067811865476,-0.7071067811865476,0.7071067811865476,\n 0.5555702330196022,-0.9807852804032304,0.19509032201612828,0.8314696123025452,-0.8314696123025452,-0.19509032201612828,0.9807852804032304,-0.5555702330196022,\n 0.3826834323650898,-0.9238795325112867,0.9238795325112867,-0.3826834323650898,-0.3826834323650898,0.9238795325112867,-0.9238795325112867,0.3826834323650898,\n 0.19509032201612828,-0.5555702330196022,0.8314696123025452,-0.9807852804032304,0.9807852804032304,-0.8314696123025452,0.5555702330196022,-0.19509032201612828\n);\n/**\n * Rounds a quantized coefficient to the nearest integer, with ties going away from zero (−2.5 → −3,\n * 2.5 → 3), so positive and negative values round the same way. `Math.round` alone sends −2.5 to −2.\n */\nexport function roundJpegCoefficient(value: number): number {\n return value<0 ? -Math.round(-value) : Math.round(value);\n}\n\n/** Internal baseline JPEG settings: 8-bit YCbCr 4:4:4, one explicit constant quantization step, and uniform Huffman tables written for this project. */\nexport interface ExportJpegInput { width: number; height: number; samples: readonly number[]; quantizationStep: number; exif: Uint8Array; xmp: Uint8Array }\n/** Encodes a baseline JPEG with a directly computed DCT. Uses no third-party encoder source, default quantization tables or Huffman tables. */\nexport function encodeExportJpeg(input: ExportJpegInput): Uint8Array {\n const { width, height, samples, quantizationStep: q } = input;\n if (![width,height,q].every(Number.isSafeInteger) || width < 1 || height < 1 || width*height > 4096 ||\n q < 1 || q > 255 || samples.length !== width*height*3 || Array.from(samples).some((v) => !Number.isInteger(v) || v < 0 || v > 255)) {\n throw new InvalidConfigurationError(\"Unsupported baseline JPEG input.\");\n }\n const encoder=createExportJpegEncoder({...input,maximumOutputBytes:1_000_000});\n for(let by=0;by<height;by+=8)for(let bx=0;bx<width;bx+=8){\n const block:number[]=[];\n for(let y=0;y<8;y++)for(let x=0;x<8;x++){\n const p=(Math.min(by+y,height-1)*width+Math.min(bx+x,width-1))*3;\n block.push(samples[p]!,samples[p+1]!,samples[p+2]!);\n }\n encoder.writeRgbBlock(block);\n }\n return encoder.finish();\n}\n/**\n * Largest byte budget the internal writer accepts: the Print JPEG budget ceiling at the largest opt-in source\n * (256 000 000 bytes per 24 000 000 pixels at 2^27 pixels). Each caller enforces its own smaller budget.\n */\nconst EXPORT_JPEG_MAXIMUM_OUTPUT_BYTES=scaleBudgetCeiling(256_000_000,FULL_RESOLUTION_CEILING_PIXELS);\n/**\n * Internal size-limited JPEG writer. The reference export and the scalable export use exactly the same\n * DCT and entropy-coding equations.\n *\n * When `densityDotsPerInch` is supplied, it is written to the JFIF 1.02 APP0 segment as units=1 (dots\n * per inch) with equal X and Y density. JFIF density fields are 16-bit unsigned, so it must be an integer\n * in 1..65535; callers round any fractional density and store the exact value in EXIF. When omitted,\n * APP0 keeps units=0 with a 1:1 pixel aspect ratio and states no physical size.\n */\nexport function createExportJpegEncoder(input: Omit<ExportJpegInput,\"samples\"> & {maximumOutputBytes:number;icc?:Uint8Array;densityDotsPerInch?:number}): {\n writeRgbBlock(samples:readonly number[]):void; finish():Uint8Array; dispose():void;\n} {\n const {width,height,quantizationStep:q}=input;\n if(![width,height,q,input.maximumOutputBytes].every(Number.isSafeInteger)||width<1||height<1||width>65535||height>65535||q<1||q>255||input.maximumOutputBytes<1||input.maximumOutputBytes>EXPORT_JPEG_MAXIMUM_OUTPUT_BYTES)\n throw new InvalidConfigurationError(\"Invalid bounded JPEG encoding plan.\");\n const density=input.densityDotsPerInch;\n if(density!==undefined&&(!Number.isSafeInteger(density)||density<1||density>65535))\n throw new InvalidConfigurationError(\"JFIF density requires an integer from 1 to 65535 dots per inch.\");\n // Growable output. Capacity starts at min(budget, 64 KiB) and doubles, but never beyond the budget. The budget\n // check is unchanged (offset + n > maximumOutputBytes fails), so the output bytes and failures are the same as\n // with a buffer allocated at full budget up front. An old buffer is zeroed after its bytes are copied over.\n const budget=input.maximumOutputBytes;\n let storage=new Uint8Array(Math.min(budget,65_536)),offset=0,disposed=false,blocks=0;\n const reserve=(count:number):void=>{\n if(disposed||offset+count>budget)throw new InvalidConfigurationError(\"JPEG output exceeds admitted byte budget or is disposed.\");\n if(offset+count<=storage.length)return;\n const grown=new Uint8Array(Math.min(budget,Math.max(storage.length*2,offset+count)));\n grown.set(storage.subarray(0,offset));storage.fill(0,0,offset);storage=grown;\n };\n const push=(...values:number[]):void=>{reserve(values.length);storage.set(values,offset);offset+=values.length;};\n // Fast path for one entropy-coded byte: the same budget/disposal check and byte order as push(), without an array per byte.\n const pushByte=(value:number):void=>{if(disposed||offset>=storage.length)reserve(1);storage[offset++]=value;};\n push(255,216);\n const marker = (id: number, payload: readonly number[]): void => {\n if (payload.length > 65533) throw new InvalidConfigurationError(\"JPEG metadata segment exceeds the standard APP limit.\");\n push(255,id,(payload.length+2)>>8,(payload.length+2)&255,...payload);\n };\n // JFIF 1.02 APP0: identifier, version 1.02, units (0 aspect ratio only, 1 dots per inch), Xdensity, Ydensity, no thumbnail.\n marker(224,density===undefined ? [74,70,73,70,0,1,2,0,0,1,0,1,0,0] : [74,70,73,70,0,1,2,1,density>>8,density&255,density>>8,density&255,0,0]);\n marker(225,[69,120,105,102,0,0,...input.exif]);\n marker(225,[...new TextEncoder().encode(\"http://ns.adobe.com/xap/1.0/\\0\"),...input.xmp]);\n if(input.icc){\n if(input.icc.length>65519)throw new InvalidConfigurationError(\"ICC profile exceeds the single-segment encoding envelope.\");\n marker(226,[...new TextEncoder().encode(\"ICC_PROFILE\\0\"),1,1,...input.icc]);\n }\n marker(219,[0,...Array<number>(64).fill(q)]);\n marker(192,[8,height>>8,height&255,width>>8,width&255,3,1,17,0,2,17,0,3,17,0]);\n // DC categories 0..11 use 4-bit codes; AC EOB/ZRL and all baseline run/category pairs use 8-bit codes.\n // Incomplete canonical Huffman trees reserve the all-ones code, so JPEG bit padding (1-bits) can never be read as a symbol.\n const dc = Array.from({length:12},(_,i) => i), ac = [0,240];\n for (let run=0;run<16;run++) for (let size=1;size<=10;size++) ac.push(run*16+size);\n marker(196,[0,...Array.from({length:16},(_,i) => i===3 ? 12 : 0),...dc,\n 16,...Array.from({length:16},(_,i) => i===7 ? ac.length : 0),...ac]);\n marker(218,[3,1,0,2,0,3,0,0,63,0]);\n let accumulator=0, bits=0;\n // Appends the low `count` bits (count <= 16) most-significant first, emitting each completed byte with 0xFF stuffing.\n const emit = (value: number, count: number): void => {\n accumulator=(accumulator<<count)|(value&((1<<count)-1)); bits+=count;\n while (bits>=8) { bits-=8; const byte=(accumulator>>>bits)&255; pushByte(byte); if (byte===255) pushByte(0); }\n accumulator&=(1<<bits)-1;\n };\n const amplitude = (value: number, max: number): number => {\n const size=value===0 ? 0 : Math.floor(Math.log2(Math.abs(value)))+1;\n if (size>max) throw new InvalidConfigurationError(\"JPEG coefficient exceeds baseline entropy range.\");\n return size;\n };\n const coefficient = (value: number, size: number): void => { if (size) emit(value>=0 ? value : value+(1<<size)-1,size); };\n const zigzag=new Int32Array(64);\n for (let diagonal=0,i=0;diagonal<=14;diagonal++) {\n const points: number[] = [];\n for (let y=0;y<8;y++) { const x=diagonal-y; if (x>=0 && x<8) points.push(y*8+x); }\n for (const point of diagonal%2===0 ? points.reverse() : points) zigzag[i++]=point;\n }\n // Huffman symbol index of each AC (run, size) pair; equals ac.indexOf(symbol) for every emitted symbol.\n const acIndex=new Int16Array(256).fill(-1);\n for (let i=ac.length-1;i>=0;i--) acIndex[ac[i]!]=i;\n/**\n * The 8×8 DCT, computed directly, with orthonormal DC factors and one chosen quantization step.\n *\n * This is a deliberately simple 4:4:4 reference encoder. Lossy quantization means exact RGB pixels are\n * not preserved. Decoding the JPEG and comparing is an interoperability check, not an exact scientific\n * check of pixel values.\n *\n * Each coefficient is the direct 64-term sum Σ_y Σ_x (f(x,y)·C(u,x))·C(v,y), summed over y, then x.\n * The row products f(x,y)·C(u,x) are computed once per u and reused for all eight v. This reuses the\n * identical IEEE-754 products and keeps the exact order of multiplication and summation. A factored\n * row/column DCT would reorder the sum, so it is deliberately not used.\n *\n * C(u,x) comes from the fixed table `JPEG_DCT_COSINES` (correctly rounded constants), not `Math.cos`,\n * so the coefficients do not depend on the JavaScript engine's maths library. Each quantized coefficient\n * is rounded half away from zero (`roundJpegCoefficient`).\n */\n const cosine=JPEG_DCT_COSINES;\n const previous=[0,0,0];\n const planes=[new Float64Array(64),new Float64Array(64),new Float64Array(64)];\n const transformed=new Float64Array(64);\n const blueScale=2*(1-.114), redScale=2*(1-.299);\n return {\n writeRgbBlock(samples:readonly number[]):void {\n let valid=!disposed&&blocks<Math.ceil(width/8)*Math.ceil(height/8)&&samples.length===192;\n for(let i=0;valid&&i<192;i++){const v=samples[i]!;valid=Number.isInteger(v)&&v>=0&&v<=255;}\n if(!valid)throw new InvalidConfigurationError(\"JPEG requires complete bounded RGB coding blocks.\");\n const [luma,blueDifference,redDifference]=planes as [Float64Array,Float64Array,Float64Array];\n for (let i=0;i<64;i++) {\n // JPEG pads only partial 8x8 blocks (MCUs, minimum coded units). The declared image and crop size never change.\n const p=i*3;\n const red=samples[p]!, green=samples[p+1]!, blue=samples[p+2]!, luminance=.299*red+.587*green+.114*blue;\n luma[i]=luminance-128; blueDifference[i]=(blue-luminance)/blueScale; redDifference[i]=(red-luminance)/redScale;\n }\n for (let channel=0;channel<3;channel++) {\n const block=planes[channel]!;\n for (let u=0;u<8;u++) {\n const uOffset=u*8;\n const uFactor=u===0 ? Math.SQRT1_2 : 1;\n // The eight v sums in one pass over (y, x): each sum still adds the same products f(x,y)·C(u,x)·C(v,y) in the same\n // order (y, then x), and each row product is the same IEEE product, formed once per (x, y) instead of stored.\n let s0=0, s1=0, s2=0, s3=0, s4=0, s5=0, s6=0, s7=0;\n for (let y=0;y<8;y++) {\n const c0=cosine[y]!, c1=cosine[8+y]!, c2=cosine[16+y]!, c3=cosine[24+y]!, c4=cosine[32+y]!, c5=cosine[40+y]!, c6=cosine[48+y]!, c7=cosine[56+y]!;\n for (let x=0;x<8;x++) {\n const r=block[y*8+x]!*cosine[uOffset+x]!;\n s0+=r*c0; s1+=r*c1; s2+=r*c2; s3+=r*c3; s4+=r*c4; s5+=r*c5; s6+=r*c6; s7+=r*c7;\n }\n }\n // Scaled as before, left to right: sum·0.25·(u factor)·(v factor)/q.\n transformed[u]=roundJpegCoefficient(s0*.25*uFactor*Math.SQRT1_2/q); transformed[8+u]=roundJpegCoefficient(s1*.25*uFactor*1/q);\n transformed[16+u]=roundJpegCoefficient(s2*.25*uFactor*1/q); transformed[24+u]=roundJpegCoefficient(s3*.25*uFactor*1/q);\n transformed[32+u]=roundJpegCoefficient(s4*.25*uFactor*1/q); transformed[40+u]=roundJpegCoefficient(s5*.25*uFactor*1/q);\n transformed[48+u]=roundJpegCoefficient(s6*.25*uFactor*1/q); transformed[56+u]=roundJpegCoefficient(s7*.25*uFactor*1/q);\n }\n const difference=transformed[0]!-previous[channel]!, size=amplitude(difference,11); emit(size,4); coefficient(difference,size); previous[channel]=transformed[0]!;\n let zeroes=0;\n for (let i=1;i<64;i++) {\n const value=transformed[zigzag[i]!]!;\n if (value===0) { zeroes++; continue; }\n while (zeroes>=16) { emit(1,8); zeroes-=16; }\n const length=amplitude(value,10), symbol=zeroes*16+length; emit(acIndex[symbol]!,8); coefficient(value,length); zeroes=0;\n }\n if (zeroes) emit(0,8);\n }\n blocks++;\n },\n finish():Uint8Array {\n if(disposed||blocks!==Math.ceil(width/8)*Math.ceil(height/8))throw new InvalidConfigurationError(\"JPEG cannot finish incomplete or disposed encoding.\");\n if(bits)emit((1<<(8-bits))-1,8-bits);\n // Only the written prefix can hold data; zero it after the copy instead of filling the whole capacity.\n push(255,217);const output=storage.slice(0,offset);storage.fill(0,0,offset);storage=new Uint8Array(0);disposed=true;return output;\n },\n dispose():void {storage.fill(0,0,offset);storage=new Uint8Array(0);disposed=true;}\n };\n}\n"]}
@@ -32,14 +32,15 @@
32
32
  import type { SimulateEnvironmentSensorRawFrameInput } from "./environment-raw-producer.js";
33
33
  import type { planEnvironmentSensorPhotoSignal, executeEnvironmentSensorPhotoSignal, EnvironmentRadianceEvaluator, EnvironmentApertureRadianceEvaluator } from "../sensor/environment-photo-signal.js";
34
34
  import type { SensorEqeTemporalPhotoSignal } from "../sensor/temporal-photo-signal.js";
35
+ import { type InternalRadianceAccess } from "../core/internal-radiance-evaluator.js";
35
36
  import { type SceneRadianceEvaluationRequest } from "../schema/scene-radiance.js";
36
37
  import { type PreparedEnvironmentLensFieldMapping } from "../optics/sensor-environment-query.js";
37
38
  import { type ResolveLensSampledPsfInput } from "../optics/lens-psf-profile.js";
38
39
  import { type SensorApertureRay } from "../optics/sensor-aperture-rays.js";
39
40
  import { parseFocusPlane } from "../optics/focus-state.js";
40
41
  import { prepareCaptureExposureWindowSampler } from "../sensor/exposure-window.js";
41
- import { type NativeRawProducerFrameContext } from "./sensor-raw-producer.js";
42
- import type { NativeRawLeanSensorHook } from "./native-raw.js";
42
+ import { calculateNativeRawSite, type NativeRawProducerFrameContext, type SensorRawProducerSiteInput } from "./sensor-raw-producer.js";
43
+ import type { NativeRawLeanOutput, NativeRawLeanSensorHook } from "./native-raw.js";
43
44
  type Site = SimulateEnvironmentSensorRawFrameInput["sites"][number];
44
45
  type Env = Site["environment"];
45
46
  type FullPlan = ReturnType<typeof planEnvironmentSensorPhotoSignal>;
@@ -51,6 +52,7 @@ export declare const nativeEnvironmentLeanStatistics: {
51
52
  executedSites: number;
52
53
  sensorSites: number;
53
54
  psfFallbacks: number;
55
+ keySites: number;
54
56
  };
55
57
  export declare function setNativeEnvironmentLeanExecutionEnabled(enabled: boolean): void;
56
58
  interface SpatialNode {
@@ -86,6 +88,13 @@ interface Template {
86
88
  execution?: ExecutionConstants;
87
89
  /** undefined until the template site executed through the full path. */
88
90
  executionVerified?: boolean;
91
+ /**
92
+ * #394: true when an engine-built evaluator's key lookup reproduced every radiance of the template's own full execution; its
93
+ * sites are then planned as key taps and their radiances taken from the lookup instead of the evaluator (tabulated keys, chart
94
+ * materials, from `radiance`: per key, per spectral node, NaN until first needed).
95
+ */
96
+ keyMode?: boolean;
97
+ radiance?: Float64Array[];
89
98
  }
90
99
  interface PlanConstants {
91
100
  spectralCount: number;
@@ -131,7 +140,10 @@ interface PlanConstants {
131
140
  sceneBindings: FullPlan["sceneBindings"];
132
141
  }
133
142
  interface LeanTap {
134
- request: SceneRadianceEvaluationRequest;
143
+ /** The provider request; absent for a key tap (#394), whose radiance comes from the evaluator's key lookup. */
144
+ request?: SceneRadianceEvaluationRequest;
145
+ /** #394: the key (chart material or lattice site) the request would read (key taps only). */
146
+ key?: number;
135
147
  sourcePointImagePlaneMm: {
136
148
  x: number;
137
149
  y: number;
@@ -167,6 +179,8 @@ export interface LeanEnvironmentPlan {
167
179
  }[];
168
180
  /** Per group: the resolved PSF kernel weights. Present only with a sampled PSF. */
169
181
  readonly kernels?: readonly (readonly number[])[];
182
+ /** #394: true when the taps are key taps (no requests). */
183
+ readonly keyTaps?: true;
170
184
  }
171
185
  export declare function isLeanEnvironmentPlan(plan: unknown): plan is LeanEnvironmentPlan;
172
186
  export interface NativeEnvironmentLeanFrame {
@@ -197,9 +211,25 @@ export interface NativeEnvironmentLeanExecutor {
197
211
  * caller then plans and executes this site in full. `fullPlan` builds the site's full plan without callbacks;
198
212
  * it is called only when a sampled-PSF check fails and the full PSF quadrature must decide.
199
213
  */
200
- execute(plan: LeanEnvironmentPlan, evaluate: EnvironmentRadianceEvaluator, aperture: EnvironmentApertureRadianceEvaluator | undefined, fullPlan: () => FullPlan, internalEvaluator?: boolean): SensorEqeTemporalPhotoSignal | undefined;
214
+ execute(plan: LeanEnvironmentPlan, evaluate: EnvironmentRadianceEvaluator, aperture: EnvironmentApertureRadianceEvaluator | undefined, fullPlan: () => FullPlan, internalEvaluator?: boolean, countEvaluations?: (count: number) => void): SensorEqeTemporalPhotoSignal | undefined;
201
215
  }
202
- export declare function createNativeEnvironmentLeanExecutor(frame: NativeEnvironmentLeanFrame): NativeEnvironmentLeanExecutor;
216
+ /**
217
+ * Creates the per-event lean executor. With `access` (#394: the key lookup of an engine-built evaluator), a template whose own
218
+ * site verifies it plans its sites as key taps and takes their radiances from the lookup.
219
+ */
220
+ export declare function createNativeEnvironmentLeanExecutor(frame: NativeEnvironmentLeanFrame, access?: InternalRadianceAccess): NativeEnvironmentLeanExecutor;
221
+ /** #394: the sensor stage of one CFA phase for given expected photo electrons, verified against a full site computation. */
222
+ export interface NativeSiteReadout {
223
+ /** The site's RAW outputs for expected photo electrons `photoE` over a local exposure of `duration` seconds; undefined where a check of the full path would throw. */
224
+ compute(photoE: number, duration: number, x: number, y: number, index: number, seed: number): NativeRawLeanOutput | undefined;
225
+ }
226
+ /**
227
+ * #394: a sensor template from one fully parsed and computed site (`calculateNativeRawSite`), as the lean sensor stage builds
228
+ * its templates; undefined unless the lean arithmetic reproduces that site's dark and total charge, both charge realizations,
229
+ * its codes and its flags bit for bit. Its `compute` then gives `calculateNativeRawSite`'s outputs for any site of the same
230
+ * readout inputs (same CFA channel, dark current, capacity, readout regime and fixed-pattern profile).
231
+ */
232
+ export declare function createVerifiedNativeSiteReadout(parsed: SensorRawProducerSiteInput, result: ReturnType<typeof calculateNativeRawSite>, whiteCode: number, photo: SensorEqeTemporalPhotoSignal, x: number, y: number, index: number, seed: number): NativeSiteReadout | undefined;
203
233
  /**
204
234
  * Lean sensor stage for createNativeEnvironmentRawTask.
205
235
  *