@photivra/engine 2.21.0 → 2.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +150 -1
- package/CITATION.cff +1 -1
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +11 -0
- package/dist/capture/c2pa-embed.d.ts +116 -0
- package/dist/capture/c2pa-embed.js +493 -0
- package/dist/capture/c2pa-embed.js.map +1 -0
- package/dist/capture/dng-import.d.ts +73 -8
- package/dist/capture/dng-import.js +315 -80
- package/dist/capture/dng-import.js.map +1 -1
- package/dist/capture/dng-opcodes.d.ts +96 -0
- package/dist/capture/dng-opcodes.js +391 -0
- package/dist/capture/dng-opcodes.js.map +1 -0
- package/dist/capture/dng-profile-maps.d.ts +99 -0
- package/dist/capture/dng-profile-maps.js +381 -0
- package/dist/capture/dng-profile-maps.js.map +1 -0
- package/dist/capture/dng-stage-image.d.ts +23 -0
- package/dist/capture/dng-stage-image.js +65 -0
- package/dist/capture/dng-stage-image.js.map +1 -0
- package/dist/capture/jpeg-xl-tiles.d.ts +22 -0
- package/dist/capture/jpeg-xl-tiles.js +151 -0
- package/dist/capture/jpeg-xl-tiles.js.map +1 -0
- package/dist/capture/lossy-jpeg.d.ts +17 -0
- package/dist/capture/lossy-jpeg.js +369 -0
- package/dist/capture/lossy-jpeg.js.map +1 -0
- package/dist/capture/native-raw-development.d.ts +2 -1
- package/dist/capture/native-raw-development.js.map +1 -1
- package/dist/color/neighbor-highlight-reconstruction.d.ts +76 -0
- package/dist/color/neighbor-highlight-reconstruction.js +257 -0
- package/dist/color/neighbor-highlight-reconstruction.js.map +1 -0
- package/dist/color/sensor-color-development.d.ts +5 -0
- package/dist/color/sensor-color-development.js +7 -1
- package/dist/color/sensor-color-development.js.map +1 -1
- package/dist/color/spectral-spikiness.d.ts +11 -0
- package/dist/color/spectral-spikiness.js +23 -0
- package/dist/color/spectral-spikiness.js.map +1 -0
- package/dist/core/execution-error.d.ts +21 -0
- package/dist/core/execution-error.js +12 -0
- package/dist/core/execution-error.js.map +1 -0
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/core/version.js.map +1 -1
- package/dist/facade/catalogue-lens-signal.d.ts +26 -1
- package/dist/facade/catalogue-lens-signal.js +25 -7
- package/dist/facade/catalogue-lens-signal.js.map +1 -1
- package/dist/facade/diffraction-provider.d.ts +1 -1
- package/dist/facade/diffraction-provider.js +4 -4
- package/dist/facade/diffraction-provider.js.map +1 -1
- package/dist/facade/display-view-gpu-session.js +38 -6
- package/dist/facade/display-view-gpu-session.js.map +1 -1
- package/dist/facade/display-view-gpu.d.ts +13 -5
- package/dist/facade/display-view-gpu.js.map +1 -1
- package/dist/facade/display-view-normal.d.ts +15 -0
- package/dist/facade/display-view-normal.js +44 -0
- package/dist/facade/display-view-normal.js.map +1 -0
- package/dist/facade/display-view-rendering.d.ts +102 -0
- package/dist/facade/display-view-rendering.js +633 -0
- package/dist/facade/display-view-rendering.js.map +1 -0
- package/dist/facade/display-view.d.ts +37 -65
- package/dist/facade/display-view.js +147 -476
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/expected-signal-setup.js +2 -2
- package/dist/facade/expected-signal-setup.js.map +1 -1
- package/dist/facade/fast-methods.d.ts +1 -1
- package/dist/facade/fast-methods.js +37 -8
- package/dist/facade/fast-methods.js.map +1 -1
- package/dist/facade/generic-lens-capture.d.ts +1 -1
- package/dist/facade/generic-lens-capture.js +1 -1
- package/dist/facade/generic-lens-capture.js.map +1 -1
- package/dist/facade/image-plane.js +41 -10
- package/dist/facade/image-plane.js.map +1 -1
- package/dist/facade/on-demand-kits.d.ts +14 -0
- package/dist/facade/on-demand-kits.js +36 -0
- package/dist/facade/on-demand-kits.js.map +1 -0
- package/dist/facade/opt-in-kit.d.ts +9 -0
- package/dist/facade/opt-in-kit.js +11 -0
- package/dist/facade/opt-in-kit.js.map +1 -0
- package/dist/facade/quality-kit.d.ts +21 -0
- package/dist/facade/quality-kit.js +23 -0
- package/dist/facade/quality-kit.js.map +1 -0
- package/dist/facade/quality-paths.d.ts +22 -0
- package/dist/facade/quality-paths.js +44 -0
- package/dist/facade/quality-paths.js.map +1 -0
- package/dist/facade/scene-radiance.d.ts +18 -2
- package/dist/facade/scene-radiance.js +6 -4
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +32 -13
- package/dist/facade/simulate-capture.js +162 -69
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.d.ts +10 -12
- package/dist/facade/simulate-display-view.js +57 -48
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/display-view-gpu-renderer.d.ts +43 -0
- package/dist/gpu/display-view-gpu-renderer.js +134 -0
- package/dist/gpu/display-view-gpu-renderer.js.map +1 -0
- package/dist/gpu/display-view-image-material-module.d.ts +3 -2
- package/dist/gpu/display-view-image-material-module.js +25 -3
- package/dist/gpu/display-view-image-material-module.js.map +1 -1
- package/dist/gpu/display-view-image-quality.d.ts +43 -1
- package/dist/gpu/display-view-image-quality.js +256 -11
- package/dist/gpu/display-view-image-quality.js.map +1 -1
- package/dist/gpu/display-view-quality-gpu.d.ts +13 -64
- package/dist/gpu/display-view-quality-gpu.js +55 -240
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/display-view-speed-gpu.d.ts +16 -16
- package/dist/gpu/display-view-speed-gpu.js +62 -19
- package/dist/gpu/display-view-speed-gpu.js.map +1 -1
- package/dist/gpu/image-kernel-accelerator.js +3 -0
- package/dist/gpu/image-kernel-accelerator.js.map +1 -1
- package/dist/gpu/image-kernel-execution.d.ts +2 -13
- package/dist/gpu/image-kernel-execution.js +2 -10
- package/dist/gpu/image-kernel-execution.js.map +1 -1
- package/dist/gpu/wgsl-display-view-coupled-stub.d.ts +13 -0
- package/dist/gpu/wgsl-display-view-coupled-stub.js +29 -0
- package/dist/gpu/wgsl-display-view-coupled-stub.js.map +1 -0
- package/dist/gpu/wgsl-display-view-coupled.d.ts +91 -0
- package/dist/gpu/wgsl-display-view-coupled.js +365 -0
- package/dist/gpu/wgsl-display-view-coupled.js.map +1 -0
- package/dist/gpu/wgsl-display-view-neighbors.d.ts +46 -0
- package/dist/gpu/wgsl-display-view-neighbors.js +114 -0
- package/dist/gpu/wgsl-display-view-neighbors.js.map +1 -0
- package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +6 -9
- package/dist/gpu/wgsl-display-view-quality-kernel.js +113 -113
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +1 -41
- package/dist/gpu/wgsl-display-view-speed-kernel.js +73 -10
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
- package/dist/index.d.ts +11 -3
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/output/camera-channel-response.d.ts +50 -0
- package/dist/output/camera-channel-response.js +69 -0
- package/dist/output/camera-channel-response.js.map +1 -0
- package/dist/output/native-capture-sdr.js +5 -2
- package/dist/output/native-capture-sdr.js.map +1 -1
- package/dist/output/sdr-rendering.d.ts +18 -1
- package/dist/output/sdr-rendering.js +39 -2
- package/dist/output/sdr-rendering.js.map +1 -1
- package/dist/sensor/native-sensor-report.d.ts +10 -0
- package/dist/sensor/native-sensor-report.js +16 -0
- package/dist/sensor/native-sensor-report.js.map +1 -0
- package/dist/sensor/sensor-characteristics.d.ts +81 -13
- package/dist/sensor/sensor-characteristics.js +10 -6
- package/dist/sensor/sensor-characteristics.js.map +1 -1
- package/docs/API_REFERENCE.md +117 -4
- package/docs/QUICK_START.md +37 -8
- package/docs/USAGE.md +105 -22
- package/docs/api/capture-c2pa-embed.md +177 -0
- package/docs/api/capture-dng-import.md +86 -10
- package/docs/api/capture-dng-opcodes.md +93 -0
- package/docs/api/capture-dng-profile-maps.md +141 -0
- package/docs/api/capture-dng-stage-image.md +43 -0
- package/docs/api/capture-jpeg-xl-tiles.md +33 -0
- package/docs/api/capture-lossy-jpeg.md +41 -0
- package/docs/api/capture-native-raw-development.md +2 -1
- package/docs/api/core-execution-error.md +43 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/exports.json +255 -5
- package/docs/api/facade-display-view-gpu.md +5 -2
- package/docs/api/facade-display-view.md +11 -15
- package/docs/api/facade-fast-methods.md +38 -9
- package/docs/api/facade-quality-paths.md +24 -0
- package/docs/api/facade-scene-radiance.md +1 -1
- package/docs/api/facade-simulate-capture.md +24 -7
- package/docs/api/facade-simulate-display-view.md +10 -12
- package/docs/api/gpu-image-kernel-execution.md +0 -36
- package/docs/api/output-camera-channel-response.md +55 -0
- package/docs/api/output-sdr-rendering.md +6 -1
- package/docs/api/sensor-sensor-characteristics.md +54 -2
- package/llms.txt +1 -0
- package/package.json +6 -5
package/docs/USAGE.md
CHANGED
|
@@ -3324,6 +3324,18 @@ void develop;
|
|
|
3324
3324
|
value by 2^stops before demosaic and white balance. A site pushed to white clips and counts as saturated; the RAW
|
|
3325
3325
|
codes are not changed. 0 develops byte for byte as before. The WebGPU accelerator rejects a non-zero push.
|
|
3326
3326
|
- `extended-reinhard-luminance` takes `{ whiteLuminance }`, the luminance mapped to output white.
|
|
3327
|
+
- `gamutHandling: "desaturate-to-luminance"` (#601, 2.22.0; opt-in, also `simulateCapture`'s `output.gamutHandling`) moves a
|
|
3328
|
+
pixel whose largest component exceeds 1 after the tone curve toward the gray of its own luminance until that component is 1,
|
|
3329
|
+
keeping its luminance and hue; the default `"clip-components"` clips each component. The WebGPU SDR kernels refuse it.
|
|
3330
|
+
- The display view (`simulateDisplayView`, `prepareDisplayView`) renders the luminance tone curves, `highlightMode: "reconstruct"`
|
|
3331
|
+
and the desaturation since 2.22.0 (#593), with fast rules on screen and full ones for `fast: false`
|
|
3332
|
+
([Display view, Coupled rendering](https://github.com/Photivra/photivra/blob/main/docs/DISPLAY_VIEW.md#coupled-rendering-593-601)). Photivra's Standard look is
|
|
3333
|
+
`{ exponent: 1.5, semiSaturation: 0.5009, whiteLuminance: 8 }` and its Flat look `{ exponent: 1, semiSaturation: 0.8465, whiteLuminance: 32 }`
|
|
3334
|
+
for `naka-rushton-luminance` (18 % gray stays at 0.18; the engine's own looks, not a camera maker's).
|
|
3335
|
+
- `simulateCapture`'s `output.highlightMode: "reconstruct-neighbors"` (#602, 2.22.0; opt-in, an estimate) rebuilds clipped
|
|
3336
|
+
channels from the color ratios around each clipped region: the ring's mean ratios with `fast` (the sRGB default), their harmonic
|
|
3337
|
+
interpolation inward for exports and `fast: false`. Whole-image, so not with `output.region`; the display view renders it on the CPU and (since 2.23.0, #605) in its
|
|
3338
|
+
WebGPU Speed and Quality kernels, fast and full.
|
|
3327
3339
|
- `createPhotographicExportPair(input, { signal })` accepts an optional `AbortSignal`.
|
|
3328
3340
|
|
|
3329
3341
|
## Capture-mode profiles
|
|
@@ -7096,7 +7108,8 @@ See [Local POC Simulation API](https://github.com/Photivra/photivra/blob/main/do
|
|
|
7096
7108
|
|
|
7097
7109
|
## DNG import and PNG output
|
|
7098
7110
|
|
|
7099
|
-
- `parseDngRawImage` reads a DNG or TIFF-EP CFA/LinearRaw file (uncompressed
|
|
7111
|
+
- `parseDngRawImage` reads a DNG or TIFF-EP CFA/LinearRaw file (uncompressed, lossless JPEG or, from 2.22.0, 8-bit lossy JPEG; 8–16 bits). It returns the stored raw codes plus the CFA, black/white levels, active area, default crop, colour matrices, as-shot neutral and orientation, and (2.22.0) the opcode lists, the profile gain table map, RGBTables, semantic masks and an `assumptions` list naming every rendering tag it does not apply. Nothing is applied to the codes. `importDngRawImage` (2.22.0) does the same asynchronously and also reads JPEG XL raw data through a decoder the application supplies (`createJxlOxideDngDecoder` over the jxl-oxide-wasm package, which the app installs and loads only when a JPEG XL DNG is opened; the engine does not depend on it); `size: "half"` gives a half-size LinearRaw preview and `maximumBytes` bounds the memory.
|
|
7112
|
+
- `developDngStageImage` (2.22.0) builds the DNG's linear stage image (linearization, black and white levels, OpcodeList1/2/3); `applyDngProfileGainTableMap` and `applyDngRgbTables` apply the phone rendering tags at their places in a development.
|
|
7100
7113
|
- `serializePngImage` writes an 8- or 16-bit RGB PNG with an sRGB, ICC or cICP (Display P3, BT.2020; sRGB, BT.709 or linear transfer) colour description, and optional mDCV/cLLI display metadata. PQ and HLG are not part of 2.0 (see the patent review in `THIRD_PARTY.md`).
|
|
7101
7114
|
|
|
7102
7115
|
See [DNG import and PNG output](https://github.com/Photivra/photivra/blob/main/docs/IMPORT_AND_PNG.md) for supported subsets, limits and sources.
|
|
@@ -7533,6 +7546,25 @@ for (const p of SENSOR_PROFILES) {
|
|
|
7533
7546
|
}
|
|
7534
7547
|
```
|
|
7535
7548
|
|
|
7549
|
+
### Read noise and native-pixel figures (2.22.0, #595, #599)
|
|
7550
|
+
|
|
7551
|
+
`sensor.readNoise` is the read noise of one **physical photosite**, as a sensor datasheet gives it, on a generic tier, a caller
|
|
7552
|
+
preset and a sensor profile alike (2.22.0; before, a tier took it as the rendered site's). A rendered site that stands for N
|
|
7553
|
+
photosites (pitch above the tier's 6 µm, or above a profile's own pitch) sums their read noise: it takes the value ×√N. Without
|
|
7554
|
+
`sensor.readNoise` a tier's site takes its 6 µm photosite's read noise ×√N in the same way (#599). Below one photosite the value is
|
|
7555
|
+
kept. `result.sensor` reports the rendered site; `result.sensor.native` one photosite (its `readNoiseElectrons` is the value you
|
|
7556
|
+
gave), with the EMVA 1288 and the 8 MP-normalized dynamic range
|
|
7557
|
+
([QUICK_START.md, "Native-pixel dynamic range"](QUICK_START.md#sensor-characteristics-555)).
|
|
7558
|
+
|
|
7559
|
+
```ts
|
|
7560
|
+
import { simulateCapture } from "@photivra/engine";
|
|
7561
|
+
|
|
7562
|
+
const r = await simulateCapture({ scene: { preset: "uniform" }, sensor: { pixelPitchMicrometers: 12, readNoise: { lowGainElectrons: 3 } }, fast: false });
|
|
7563
|
+
r.sensor.native.readNoiseElectrons; // 3, one 6 µm photosite's (the value given)
|
|
7564
|
+
r.sensor.readNoise.lowGainElectrons; // 6, the rendered 12 µm site's: 3 × √4
|
|
7565
|
+
r.sensor.native.emva1288.dynamicRangeStops;
|
|
7566
|
+
```
|
|
7567
|
+
|
|
7536
7568
|
## Generic lens catalogue (#549)
|
|
7537
7569
|
|
|
7538
7570
|
`GENERIC_LENS_CATALOGUE` lists the engine's 30 generic primes (17 professional, 13 consumer, "PV 50 1.2 Pro", "PV 50 1.8"; Photivra's
|
|
@@ -7547,7 +7579,8 @@ correction profile for `resolveLensCorrectionPlan`, which `simulateCapture` appl
|
|
|
7547
7579
|
([quick start](QUICK_START.md#generic-lens-catalogue-549)); `fast: true` (or `lens: { catalogue: { kind, fast: true } }`) takes its
|
|
7548
7580
|
fast method (#568), a labeled approximation that blends the PSF over a field grid, for interactive captures.
|
|
7549
7581
|
`calculateLateralColorFringe({ lensId, activeImagingArea, pixelPitchMicrometers, fieldPosition? })` gives the lens's lateral color as a
|
|
7550
|
-
fringe width: the blue-minus-red image displacement in µm and pixels at a field position (default the corner; #580)
|
|
7582
|
+
fringe width: the blue-minus-red image displacement in µm and pixels at a field position (default the corner; #580); its `channelDispersion` for
|
|
7583
|
+
the capture's own channels comes from `calculateCameraChannelResponse({ illuminant })` (#591, full quality: a 5 nm grid by default). Equations, tables and the
|
|
7551
7584
|
chart measurements: [LENS_CATALOGUE.md](https://github.com/Photivra/photivra/blob/main/docs/LENS_CATALOGUE.md).
|
|
7552
7585
|
|
|
7553
7586
|
```ts
|
|
@@ -7861,6 +7894,27 @@ const print = await simulateCapture({
|
|
|
7861
7894
|
console.log(print.scene.parameters.encoding, print.assumptions.some((a) => a.id === "scene.imagePlane")); // "srgb-8", true
|
|
7862
7895
|
```
|
|
7863
7896
|
|
|
7897
|
+
### Loading the full-quality code ahead of time (2.22, #594)
|
|
7898
|
+
|
|
7899
|
+
The full-quality paths load on demand: a bundler that splits dynamic imports keeps their code out of the first load, and the
|
|
7900
|
+
first request that needs it fetches it: the CPU full-quality code (diffraction, the capture's AHD demosaic, `lens.defocus`
|
|
7901
|
+
bokeh shapes, cat's eye and large discs, lens projection and distortion, the display view's luminance tone curves,
|
|
7902
|
+
reconstruct and desaturation, `highlightMode: "reconstruct-neighbors"`), the opt-in features (`sensor.profile`,
|
|
7903
|
+
`illumination`, `motion.subject`) and the display view's GPU full-quality code (diffraction, AHD, defocus and image-plane
|
|
7904
|
+
prints with `prepareDisplayView(…, { gpu: "auto" | "require" })`). `prepareQualityPaths()` fetches all of it ahead of time,
|
|
7905
|
+
so that request does not wait. It is optional, changes no result, and is safe to call at any time: concurrent calls share one
|
|
7906
|
+
load, and later calls load nothing. If the code does not load, the promise rejects with `ImageKernelExecutionError` reason
|
|
7907
|
+
`quality-code-unavailable` and nothing is remembered; a GPU render in the meantime runs the CPU reference (the same full-quality
|
|
7908
|
+
result, slower) with `diagnostics.fast.execution.fallbackReason: "quality-code-unavailable"`, a CPU request whose code does not
|
|
7909
|
+
load fails with that error, and the next request tries again.
|
|
7910
|
+
|
|
7911
|
+
```ts
|
|
7912
|
+
import { prepareQualityPaths } from "@photivra/engine";
|
|
7913
|
+
|
|
7914
|
+
// For example when the user opens Export, where full-quality renders follow.
|
|
7915
|
+
await prepareQualityPaths().catch(() => undefined); // a failed preload is retried by the request that needs the code
|
|
7916
|
+
```
|
|
7917
|
+
|
|
7864
7918
|
**Representative light sources (2.22).** The engine's own [representative light sources](https://github.com/Photivra/photivra/blob/main/docs/LIGHT_SOURCES.md) are four relative spectra
|
|
7865
7919
|
designed by Photivra: a tri-phosphor fluorescent tube and white LEDs of low and high color rendering at 4000 K and a warm one at
|
|
7866
7920
|
2700 K, each "representative, not a named product". `createRepresentativeLightSourceIlluminant({ sourceId })` returns an
|
|
@@ -8009,14 +8063,13 @@ it: Vite builds workers as `iife` by default, so a worker that calls `prepareDis
|
|
|
8009
8063
|
modules (`worker: { format: "es" }`, with `new Worker(url, { type: "module" })`) to keep it out. Measured sizes:
|
|
8010
8064
|
[Bundle size](https://github.com/Photivra/photivra/blob/main/docs/DISPLAY_VIEW.md#bundle-size).
|
|
8011
8065
|
|
|
8012
|
-
####
|
|
8066
|
+
#### AHD correction states (deprecated in 2.22.0)
|
|
8013
8067
|
|
|
8014
|
-
|
|
8015
|
-
|
|
8016
|
-
|
|
8017
|
-
|
|
8018
|
-
|
|
8019
|
-
runs in its own worker, and the arrays travel with `postMessage(set, [set.gridKeys.buffer, set.values.buffer])`.
|
|
8068
|
+
From 2.10 to 2.21, low-signal AHD display pixels read correction states that workers could exchange. 2.22.0 removes the
|
|
8069
|
+
correction (a labelled approximation remains; see "AHD at low signal" in
|
|
8070
|
+
[DISPLAY_VIEW.md](https://github.com/Photivra/photivra/blob/main/docs/DISPLAY_VIEW.md#ahd-at-low-signal)), so there is nothing
|
|
8071
|
+
to exchange: workers render AHD views with no shared state. The exchange functions stay until 3.0.0 as checked no-ops;
|
|
8072
|
+
remove the calls.
|
|
8020
8073
|
|
|
8021
8074
|
```ts
|
|
8022
8075
|
import { serializeDisplayViewCorrectionStates, bindDisplayViewCorrectionStates, prepareDisplayView, type SimulateCaptureOptions } from "@photivra/engine";
|
|
@@ -8024,19 +8077,13 @@ import { serializeDisplayViewCorrectionStates, bindDisplayViewCorrectionStates,
|
|
|
8024
8077
|
const request: SimulateCaptureOptions = { scene: { preset: "uniform", reflectance: 0.18 }, exposure: { iso: 1600, shutterSeconds: 1 / 2000 },
|
|
8025
8078
|
sensor: { pixelPitchMicrometers: 1.4 }, output: { width: 200, height: 150, demosaic: "ahd" } };
|
|
8026
8079
|
const part = { width: 20, height: 15, region: { x: 0, y: 0, width: 10, height: 8 } };
|
|
8027
|
-
|
|
8028
|
-
|
|
8029
|
-
console.log(
|
|
8030
|
-
|
|
8031
|
-
|
|
8032
|
-
const
|
|
8033
|
-
|
|
8034
|
-
const fromB = await workerB.computeCorrectionStates(gridKeys.slice(half));
|
|
8035
|
-
// Each worker imports the other's share (in one realm, as here, the second import adds nothing new).
|
|
8036
|
-
console.log(bindDisplayViewCorrectionStates([fromB]), bindDisplayViewCorrectionStates([fromA]));
|
|
8037
|
-
// Either worker now renders the part without computing a state; a new worker can start from an export.
|
|
8038
|
-
const view = await workerA.render(part);
|
|
8039
|
-
console.log(view.diagnostics.ahdCorrectedPixelCount, serializeDisplayViewCorrectionStates().map((set) => set.gridKeys.length));
|
|
8080
|
+
const prepared = await prepareDisplayView(request);
|
|
8081
|
+
// Deprecated: null, no grid keys, no states, nothing stored.
|
|
8082
|
+
console.log(prepared.correctionKey(), (await prepared.collectCorrectionStates(part)).gridKeys.length);
|
|
8083
|
+
console.log(serializeDisplayViewCorrectionStates().length, bindDisplayViewCorrectionStates([]));
|
|
8084
|
+
// The view itself needs none of them.
|
|
8085
|
+
const view = await prepared.render(part);
|
|
8086
|
+
console.log(view.diagnostics.ahdCorrectedPixelCount); // always 0 since 2.22.0
|
|
8040
8087
|
```
|
|
8041
8088
|
|
|
8042
8089
|
### Facade 2.0 options: output, demosaic, optics and motion
|
|
@@ -8239,3 +8286,39 @@ console.log(outOfFocus.limitingFactor); // "defocus"
|
|
|
8239
8286
|
- Demosaicing, sharpening, noise and motion are not included.
|
|
8240
8287
|
|
|
8241
8288
|
See [Physics Foundation](https://github.com/Photivra/photivra/blob/main/docs/PHYSICS_FOUNDATION.md#which-limits-detail-the-lens-or-the-sensor).
|
|
8289
|
+
|
|
8290
|
+
## Content Credentials in exports (#165)
|
|
8291
|
+
|
|
8292
|
+
`bindC2paManifestStore`, `calculateC2paDataHash` and `setC2paManifestStore` embed a C2PA Manifest Store in a JPEG, PNG or
|
|
8293
|
+
DNG export, hash the bytes the signature covers and write the signed store in place at the same length (C2PA 2.4 §10.4);
|
|
8294
|
+
`parseC2paManifestStore` finds a store already in a file, without verifying it. The application builds, signs and validates
|
|
8295
|
+
the manifest; the engine holds no keys and makes no network calls. Here a minimal one-IFD TIFF
|
|
8296
|
+
stands in for a DNG export, and the stores are only the C2PA framing around zero or one bytes, not real manifests:
|
|
8297
|
+
|
|
8298
|
+
```ts
|
|
8299
|
+
import { bindC2paManifestStore, calculateC2paDataHash, parseC2paManifestStore, setC2paManifestStore } from '@photivra/engine';
|
|
8300
|
+
|
|
8301
|
+
// A C2PA Manifest Store's outer framing (C2PA 2.4 §11.1.4.2) around `fill` bytes. A real store comes from the app's manifest builder.
|
|
8302
|
+
function framedStore(length: number, fill: number): Uint8Array {
|
|
8303
|
+
const store = new Uint8Array(length).fill(fill), view = new DataView(store.buffer);
|
|
8304
|
+
view.setUint32(0, length); store.set([0x6a, 0x75, 0x6d, 0x62], 4); // 'jumb' superbox
|
|
8305
|
+
view.setUint32(8, 30); store.set([0x6a, 0x75, 0x6d, 0x64], 12); // 'jumd' description box
|
|
8306
|
+
store.set([0x63, 0x32, 0x70, 0x61, 0, 0x11, 0, 0x10, 0x80, 0, 0, 0xaa, 0, 0x38, 0x9b, 0x71, 3, 0x63, 0x32, 0x70, 0x61, 0], 16); // C2PA UUID, label "c2pa"
|
|
8307
|
+
view.setUint32(38, length - 38); store.set([0x6a, 0x75, 0x6d, 0x62], 42); // one child box filling the rest
|
|
8308
|
+
return store;
|
|
8309
|
+
}
|
|
8310
|
+
const tiff = Uint8Array.of(0x49, 0x49, 42, 0, 8, 0, 0, 0, 1, 0, 0, 1, 4, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0);
|
|
8311
|
+
|
|
8312
|
+
const bound = bindC2paManifestStore({ file: tiff, manifestStore: framedStore(1024, 0) });
|
|
8313
|
+
console.log(bound.binding.exclusions); // [{ start: 26, length: 4 }, { start: 40, length: 1024 }]: the entry's count field and the store
|
|
8314
|
+
const { hashHex } = await calculateC2paDataHash({ file: bound.bytes, exclusions: bound.binding.exclusions });
|
|
8315
|
+
// The app writes the exclusions and hashHex into its c2pa.hash.data assertion, signs, and builds the final store at 1,024 bytes.
|
|
8316
|
+
const signed = setC2paManifestStore({ file: bound.bytes, binding: bound.binding, manifestStore: framedStore(1024, 1) });
|
|
8317
|
+
const after = await calculateC2paDataHash({ file: signed, exclusions: bound.binding.exclusions });
|
|
8318
|
+
if (signed.length !== bound.bytes.length || after.hashHex !== hashHex) throw new Error('The signed file must keep its length and data hash');
|
|
8319
|
+
const found = parseC2paManifestStore({ file: signed }); // the store and the same binding, or null for a file without one
|
|
8320
|
+
if (found?.manifestStore[46] !== 1) throw new Error('The signed store must be found');
|
|
8321
|
+
```
|
|
8322
|
+
|
|
8323
|
+
See [Content Credentials](https://github.com/Photivra/photivra/blob/main/docs/CONTENT_CREDENTIALS.md) for the byte layouts,
|
|
8324
|
+
the checks and what is still to be validated externally.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# capture/c2pa-embed.ts public contracts
|
|
2
|
+
|
|
3
|
+
The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
|
|
4
|
+
|
|
5
|
+
## bindC2paManifestStore
|
|
6
|
+
|
|
7
|
+
Embeds a placeholder C2PA Manifest Store in a JPEG, PNG or TIFF/DNG export and returns the new file with its hard binding:
|
|
8
|
+
the exclusions for `c2pa.hash.data` and where the store sits (C2PA 2.4 §A.3.1, §A.3.2, §A.3.6, §18.5, §10.4; see the module
|
|
9
|
+
notes). The input file is not changed. Only the first store of a file can be added; a file that already has one is rejected.
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
export function bindC2paManifestStore(input: BindC2paManifestStoreInput): C2paBoundFile;
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This call is synchronous; any supplied provider must follow its explicit synchronous contract.
|
|
16
|
+
|
|
17
|
+
## BindC2paManifestStoreInput
|
|
18
|
+
|
|
19
|
+
Input of `bindC2paManifestStore`.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
export interface BindC2paManifestStoreInput {
|
|
23
|
+
/** A JPEG, a PNG, or a TIFF-based file such as a DNG, as written by the engine's exporters. It is read, never changed. */
|
|
24
|
+
file: Uint8Array;
|
|
25
|
+
/**
|
|
26
|
+
* The placeholder store (§10.4): a JUMBF C2PA Manifest Store sized for the final signature. For JPEG it may instead be the
|
|
27
|
+
* same store already split into APP11 segments (what the C2PA reference SDK's placeholder call returns), as long as that
|
|
28
|
+
* split is the one described above.
|
|
29
|
+
*/
|
|
30
|
+
manifestStore: Uint8Array;
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## C2PA_ALGORITHMIC_MEDIA_SOURCE_TYPE
|
|
35
|
+
|
|
36
|
+
IPTC digital source type for media created purely by an algorithm (https://cv.iptc.org/newscodes/digitalsourcetype/).
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
C2PA_ALGORITHMIC_MEDIA_SOURCE_TYPE = "http://cv.iptc.org/newscodes/digitalsourcetype/algorithmicMedia" as const
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## C2PA_MANIFEST_STORE_MAXIMUM_BYTES
|
|
43
|
+
|
|
44
|
+
Largest manifest store the engine embeds or reads, bytes (an engine limit, not a C2PA one).
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
C2PA_MANIFEST_STORE_MAXIMUM_BYTES = 33_554_432
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## C2paBoundFile
|
|
51
|
+
|
|
52
|
+
A file with a manifest store embedded, and its binding.
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
export interface C2paBoundFile { bytes: Uint8Array; binding: C2paHardBinding }
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## C2paByteRange
|
|
59
|
+
|
|
60
|
+
A byte range of a file: `start` is a zero-based offset, `length` a byte count.
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
export interface C2paByteRange { start: number; length: number }
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## C2paDataHash
|
|
67
|
+
|
|
68
|
+
SHA-256 over every byte of the file outside the exclusions, in file order (§18.5): the `hash` of `c2pa.hash.data`.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
export interface C2paDataHash { alg: "sha256"; hash: Uint8Array; hashHex: string; hashedByteCount: number }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## C2paFoundManifestStore
|
|
75
|
+
|
|
76
|
+
A manifest store found in a file (`parseC2paManifestStore`): its binding and a copy of the store's JUMBF bytes.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
export interface C2paFoundManifestStore { binding: C2paHardBinding; manifestStore: Uint8Array }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## C2paHardBinding
|
|
83
|
+
|
|
84
|
+
Where the manifest store sits in a file and which bytes the data hash excludes. Plain data: it can be kept, sent to a
|
|
85
|
+
worker or serialized, and must be passed back unchanged to `setC2paManifestStore`.
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
export interface C2paHardBinding {
|
|
89
|
+
/** `image/jpeg`, `image/png`, or for TIFF files `image/dng` when the main IFD has DNGVersion (50706), otherwise `image/tiff`. */
|
|
90
|
+
mediaType: "image/jpeg" | "image/png" | "image/dng" | "image/tiff";
|
|
91
|
+
/** Length of the file with the store embedded, bytes. */
|
|
92
|
+
fileLength: number;
|
|
93
|
+
/** The `exclusions` of the `c2pa.hash.data` assertion, ascending and non-overlapping (§18.5). */
|
|
94
|
+
exclusions: readonly C2paByteRange[];
|
|
95
|
+
/** JPEG: every C2PA APP11 segment, markers and length fields included. PNG: the whole caBX chunk. TIFF/DNG: the store's own bytes. */
|
|
96
|
+
manifestRegion: C2paByteRange;
|
|
97
|
+
/** Length of the JUMBF manifest store itself, bytes. A replacement store must have exactly this length. */
|
|
98
|
+
manifestStoreLength: number;
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## calculateC2paDataHash
|
|
103
|
+
|
|
104
|
+
SHA-256 over every byte of a file outside the given exclusions, in file order: the value of `hash` in a `c2pa.hash.data`
|
|
105
|
+
assertion with `alg` "sha256" (C2PA 2.4 §18.5). It uses Web Crypto (`crypto.subtle`, browsers in secure contexts and
|
|
106
|
+
Node 22+). The bytes outside the exclusions are copied once into one array before hashing.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
export async function calculateC2paDataHash(input: CalculateC2paDataHashInput): Promise<C2paDataHash>;
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
This call is asynchronous; byte hashing uses the caller runtime's Web Crypto.
|
|
113
|
+
|
|
114
|
+
## CalculateC2paDataHashInput
|
|
115
|
+
|
|
116
|
+
Input of `calculateC2paDataHash`.
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
export interface CalculateC2paDataHashInput {
|
|
120
|
+
/** The bound file. */
|
|
121
|
+
file: Uint8Array;
|
|
122
|
+
/** The binding's exclusions (or any ascending, non-overlapping ranges inside the file). */
|
|
123
|
+
exclusions: readonly C2paByteRange[];
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## parseC2paManifestStore
|
|
128
|
+
|
|
129
|
+
Finds the C2PA Manifest Store embedded in a JPEG (APP11), PNG (caBX) or TIFF/DNG (tag 52545) and returns its location as a
|
|
130
|
+
binding (the exclusions the engine's own writer uses for that format) and a copy of the store. It checks the file's structure
|
|
131
|
+
and the store's outer framing only: it does not read the manifests, check the data hash or verify any signature. The
|
|
132
|
+
manifest's own `c2pa.hash.data` assertion is what a validator uses; this binding is what `setC2paManifestStore` needs.
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
export function parseC2paManifestStore(input: ParseC2paManifestStoreInput): C2paFoundManifestStore | null;
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
This call is synchronous; any supplied provider must follow its explicit synchronous contract.
|
|
139
|
+
|
|
140
|
+
## ParseC2paManifestStoreInput
|
|
141
|
+
|
|
142
|
+
Input of `parseC2paManifestStore`.
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
export interface ParseC2paManifestStoreInput {
|
|
146
|
+
/** A JPEG, PNG or TIFF-based file such as a DNG. It is read, never changed. */
|
|
147
|
+
file: Uint8Array;
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## setC2paManifestStore
|
|
152
|
+
|
|
153
|
+
Writes the final, signed manifest store over the placeholder of a bound file (C2PA 2.4 §10.4.4). The store must have
|
|
154
|
+
exactly the placeholder's length, so the file's length, every offset and every byte outside the exclusions stay the same
|
|
155
|
+
and the data hash computed before signing still holds. The binding is checked against the file first. The new store goes
|
|
156
|
+
into the file's existing segments or chunk as they are (for PNG the chunk CRC is recomputed; it is inside the exclusion).
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
export function setC2paManifestStore(input: SetC2paManifestStoreInput): Uint8Array;
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
This call is synchronous; any supplied provider must follow its explicit synchronous contract.
|
|
163
|
+
|
|
164
|
+
## SetC2paManifestStoreInput
|
|
165
|
+
|
|
166
|
+
Input of `setC2paManifestStore`.
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
export interface SetC2paManifestStoreInput {
|
|
170
|
+
/** The bound file (`C2paBoundFile.bytes`). It is read, never changed. */
|
|
171
|
+
file: Uint8Array;
|
|
172
|
+
/** The binding `bindC2paManifestStore` (or `parseC2paManifestStore`) returned for it. */
|
|
173
|
+
binding: C2paHardBinding;
|
|
174
|
+
/** The final, signed store: exactly `binding.manifestStoreLength` bytes, as JUMBF or (JPEG) in the same APP11 split. */
|
|
175
|
+
manifestStore: Uint8Array;
|
|
176
|
+
}
|
|
177
|
+
```
|
|
@@ -4,18 +4,31 @@ The package and root API versions are recorded once in [Navigation](../API_REFER
|
|
|
4
4
|
|
|
5
5
|
## DNG_IMPORT_LIMITS
|
|
6
6
|
|
|
7
|
-
Upper bound for `maximumPixels
|
|
7
|
+
Upper bound for `maximumPixels`: 200 MP (megapixels) of 16-bit codes is 400 MB. `maximumBytes` bounds the estimated peak memory
|
|
8
|
+
of the import (default 1 GiB, at most 4 GiB).
|
|
8
9
|
|
|
9
10
|
```ts
|
|
10
|
-
DNG_IMPORT_LIMITS = Object.freeze({ defaultMaximumPixels: 64_000_000, maximumPixels: 200_000_000, defaultMaximumIfds: 64, maximumIfds: 1024
|
|
11
|
+
DNG_IMPORT_LIMITS = Object.freeze({ defaultMaximumPixels: 64_000_000, maximumPixels: 200_000_000, defaultMaximumIfds: 64, maximumIfds: 1024,
|
|
12
|
+
defaultMaximumBytes: 1_073_741_824, maximumBytes: 4_294_967_296 })
|
|
11
13
|
```
|
|
12
14
|
|
|
13
15
|
## DNG_IMPORT_SCHEMA_VERSION
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
1.1.0 (2.22.0): lossy JPEG and JPEG XL raw data, opcode lists, profile maps, semantic masks, `scale`, `assumptions`.
|
|
16
18
|
|
|
17
19
|
```ts
|
|
18
|
-
DNG_IMPORT_SCHEMA_VERSION = "1.
|
|
20
|
+
DNG_IMPORT_SCHEMA_VERSION = "1.1.0" as const
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## DngImportAsyncInput
|
|
24
|
+
|
|
25
|
+
{@link importDngRawImage}'s input: {@link DngImportInput} plus the JPEG XL decoder a 52546 file needs.
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
export interface DngImportAsyncInput extends DngImportInput {
|
|
29
|
+
/** Decodes one JPEG XL strip or tile to PNG (`createJxlOxideDngDecoder`). Needed only for JPEG XL raw data or masks. */
|
|
30
|
+
jpegXlDecoder?: DngJpegXlDecoder;
|
|
31
|
+
}
|
|
19
32
|
```
|
|
20
33
|
|
|
21
34
|
## DngImportInput
|
|
@@ -29,9 +42,31 @@ export interface DngImportInput {
|
|
|
29
42
|
maximumPixels?: number;
|
|
30
43
|
/** Total IFDs (main chain plus SubIFDs) visited. Default 64, at most 1024. */
|
|
31
44
|
maximumIfds?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Estimated peak memory of the import, bytes (the codes, the largest strip or tile's decode buffers and its decoder's working
|
|
47
|
+
* memory; see docs/IMPORT_AND_PNG.md, "Memory"), checked before any image allocation. Default 1 073 741 824 (1 GiB), at most 4 GiB.
|
|
48
|
+
*/
|
|
49
|
+
maximumBytes?: number;
|
|
50
|
+
/**
|
|
51
|
+
* "half": a half-size preview of a LinearRaw image, each output pixel the rounded mean of a 2 × 2 block of stored codes, decoded
|
|
52
|
+
* strip by strip (a quarter of the codes' memory). Areas, crops and black-level deltas are halved; CFA images are refused.
|
|
53
|
+
* Default "full".
|
|
54
|
+
*/
|
|
55
|
+
size?: "full" | "half";
|
|
32
56
|
}
|
|
33
57
|
```
|
|
34
58
|
|
|
59
|
+
## importDngRawImage
|
|
60
|
+
|
|
61
|
+
{@link parseDngRawImage} for every supported compression, JPEG XL included: each JPEG XL strip or tile goes to `jpegXlDecoder` in
|
|
62
|
+
turn (one at a time, so only one tile's decoder memory is held), and its PNG is read at the DNG's bit depth (jpeg-xl-tiles.ts).
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
export async function importDngRawImage(input: DngImportAsyncInput): Promise<ImportedDngRaw>;
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This call is asynchronous; byte hashing uses the caller runtime's Web Crypto.
|
|
69
|
+
|
|
35
70
|
## ImportedDngBlackLevel
|
|
36
71
|
|
|
37
72
|
Black level (the code for zero light) pattern, from BlackLevel, BlackLevelRepeatDim and BlackLevelDeltaH/V. Origin is the active-area top-left.
|
|
@@ -83,7 +118,14 @@ export interface ImportedDngRaw {
|
|
|
83
118
|
height: number;
|
|
84
119
|
samplesPerPixel: number;
|
|
85
120
|
bitsPerSample: number;
|
|
86
|
-
compression: "uncompressed" | "lossless-jpeg";
|
|
121
|
+
compression: "uncompressed" | "lossless-jpeg" | "lossy-jpeg" | "jpeg-xl";
|
|
122
|
+
/**
|
|
123
|
+
* True when the raw data went through a lossy coder: lossy JPEG always; JPEG XL when JXLDistance (52553) is above 0 or absent
|
|
124
|
+
* (the file then does not say it is lossless).
|
|
125
|
+
*/
|
|
126
|
+
lossySource: boolean;
|
|
127
|
+
/** 1, or 0.5 for a `size: "half"` preview (every geometry field is already halved). */
|
|
128
|
+
scale: 1 | 0.5;
|
|
87
129
|
photometricInterpretation: "cfa" | "linear-raw";
|
|
88
130
|
/** Stored raw codes, row-major then sample, length width x height x samplesPerPixel. */
|
|
89
131
|
codes: Uint16Array;
|
|
@@ -107,6 +149,23 @@ export interface ImportedDngRaw {
|
|
|
107
149
|
asShotNeutral: number[] | null;
|
|
108
150
|
/** RawDataUniqueID as 32 lowercase hex digits, or null. */
|
|
109
151
|
rawDataUniqueId: string | null;
|
|
152
|
+
/** ForwardMatrix1/2 (white-balanced camera → XYZ D50), row-major 3 x colorPlanes, or null. */
|
|
153
|
+
forwardMatrix1: number[] | null;
|
|
154
|
+
forwardMatrix2: number[] | null;
|
|
155
|
+
/** BaselineExposure plus BaselineExposureOffset (stops), or null when neither is present. */
|
|
156
|
+
baselineExposure: number | null;
|
|
157
|
+
/** OpcodeList1/2/3 (raw IFD), parsed but not applied (`developDngStageImage` applies them), or null when absent. */
|
|
158
|
+
opcodeList1: DngOpcode[] | null;
|
|
159
|
+
opcodeList2: DngOpcode[] | null;
|
|
160
|
+
opcodeList3: DngOpcode[] | null;
|
|
161
|
+
/** ProfileGainTableMap2 (IFD 0), else ProfileGainTableMap (raw IFD), or null; applied by `applyDngProfileGainTableMap`. */
|
|
162
|
+
profileGainTableMap: DngProfileGainTableMap | null;
|
|
163
|
+
/** RGBTables (IFD 0), or null; applied by `applyDngRgbTables`. */
|
|
164
|
+
rgbTables: DngRgbTables | null;
|
|
165
|
+
/** Decoded semantic masks, for `applyDngRgbTables`. */
|
|
166
|
+
semanticMasks: ImportedDngSemanticMask[];
|
|
167
|
+
/** What the import did not do: tags present but not returned or applied, masks not decoded, lossy sources, inferred layouts. */
|
|
168
|
+
assumptions: string[];
|
|
110
169
|
}
|
|
111
170
|
```
|
|
112
171
|
|
|
@@ -118,14 +177,31 @@ Half-open raster rectangle in stored-image pixel coordinates (+X right, +Y down)
|
|
|
118
177
|
export interface ImportedDngRect { x: number; y: number; width: number; height: number }
|
|
119
178
|
```
|
|
120
179
|
|
|
180
|
+
## ImportedDngSemanticMask
|
|
181
|
+
|
|
182
|
+
A decoded semantic mask (DNG 1.6 SemanticName, NewSubFileType 0x10004, PhotometricInterpretation 52527).
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
export interface ImportedDngSemanticMask {
|
|
186
|
+
name: string;
|
|
187
|
+
/** SemanticInstanceID, or null. */
|
|
188
|
+
instanceId: string | null;
|
|
189
|
+
width: number;
|
|
190
|
+
height: number;
|
|
191
|
+
/** Mask values 0..1 (code / (2^bits − 1)), row-major. */
|
|
192
|
+
data: Float32Array;
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
121
196
|
## parseDngRawImage
|
|
122
197
|
|
|
123
|
-
Parse one DNG or TIFF-EP file and decode its primary CFA or LinearRaw image (the first IFD with
|
|
124
|
-
|
|
198
|
+
Parse one DNG or TIFF-EP file and decode its primary CFA or LinearRaw image (the first IFD with NewSubFileType 0 and
|
|
199
|
+
PhotometricInterpretation 32803 or 34892, in IFD-chain then SubIFD order), synchronously.
|
|
125
200
|
|
|
126
|
-
Supported raw data: Compression 1 at 8..16 bits/sample (8 bits as bytes, 16 bits in the file byte
|
|
127
|
-
|
|
128
|
-
|
|
201
|
+
Supported raw data: Compression 1 at 8..16 bits/sample (8 bits as bytes, 16 bits in the file byte order, other depths packed
|
|
202
|
+
MSB-first with byte-aligned rows, per DNG BitsPerSample), Compression 7 lossless Huffman JPEG (T.81 Annex H) and Compression 34892
|
|
203
|
+
8-bit lossy JPEG (T.81 sequential DCT), in strips or tiles, chunky planar configuration only. JPEG XL (52546) needs
|
|
204
|
+
{@link importDngRawImage}; a JPEG XL semantic mask is skipped here and named in `assumptions`.
|
|
129
205
|
|
|
130
206
|
```ts
|
|
131
207
|
export function parseDngRawImage(input: DngImportInput): ImportedDngRaw;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# capture/dng-opcodes.ts public contracts
|
|
2
|
+
|
|
3
|
+
The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
|
|
4
|
+
|
|
5
|
+
## applyDngOpcodes
|
|
6
|
+
|
|
7
|
+
Applies an opcode list to a stage image in place (WarpRectilinear returns a new buffer in `image.data`). Returns one assumption
|
|
8
|
+
per opcode: what was applied, or why it was not.
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
export function applyDngOpcodes(image: DngStageImage, opcodes: readonly DngOpcode[], options: ApplyDngOpcodesOptions): string[];
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
This call is synchronous; any supplied provider must follow its explicit synchronous contract.
|
|
15
|
+
|
|
16
|
+
## ApplyDngOpcodesOptions
|
|
17
|
+
|
|
18
|
+
Options of {@link applyDngOpcodes}.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
export interface ApplyDngOpcodesOptions {
|
|
22
|
+
/** 1, 2 or 3: the list's stage (for the report). */
|
|
23
|
+
stage: 1 | 2 | 3;
|
|
24
|
+
/**
|
|
25
|
+
* WarpRectilinear resampling: false (full quality) uses cubic convolution (Keys, a = −0.75, the DNG SDK's kernel); true uses
|
|
26
|
+
* bilinear interpolation (fast). Registered as "dngImport.warpRectilinear" in SIMULATE_CAPTURE_FAST_METHODS.
|
|
27
|
+
*/
|
|
28
|
+
fast: boolean;
|
|
29
|
+
/** 1, or 0.5 for a half-size image: pixel-coordinate parameters are scaled; pitched areas and bad pixels are then not applied. */
|
|
30
|
+
scale?: 1 | 0.5;
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## DngOpcode
|
|
35
|
+
|
|
36
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
export interface DngOpcode {
|
|
40
|
+
id: number;
|
|
41
|
+
/** The specification's name, or "Unknown". */
|
|
42
|
+
name: string;
|
|
43
|
+
dngVersion: [number, number, number, number];
|
|
44
|
+
/** Flag bit 0: a reader may skip it. */
|
|
45
|
+
optional: boolean;
|
|
46
|
+
/** Flag bit 1: may be skipped for preview-quality rendering. */
|
|
47
|
+
previewSkip: boolean;
|
|
48
|
+
/** Decoded parameters of an applied opcode; null for one this reader does not apply. */
|
|
49
|
+
parameters: DngOpcodeParameters | null;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## DngOpcodeArea
|
|
54
|
+
|
|
55
|
+
The rectangle, plane range and pitch an area-based opcode acts on (stage pixel coordinates, bottom and right exclusive).
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
export interface DngOpcodeArea { top: number; left: number; bottom: number; right: number; plane: number; planes: number; rowPitch: number; colPitch: number }
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## DngOpcodeParameters
|
|
62
|
+
|
|
63
|
+
Opcode parameters, by opcode.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
export type DngOpcodeParameters =
|
|
67
|
+
| { opcode: "WarpRectilinear"; planes: { radial: [number, number, number, number]; tangential: [number, number] }[]; centerX: number; centerY: number }
|
|
68
|
+
| { opcode: "FixVignetteRadial"; coefficients: [number, number, number, number, number]; centerX: number; centerY: number }
|
|
69
|
+
| { opcode: "FixBadPixelsConstant"; constant: number; bayerPhase: number }
|
|
70
|
+
| { opcode: "FixBadPixelsList"; bayerPhase: number; points: [number, number][]; rects: [number, number, number, number][] }
|
|
71
|
+
| { opcode: "MapTable"; area: DngOpcodeArea; table: Uint16Array }
|
|
72
|
+
| { opcode: "MapPolynomial"; area: DngOpcodeArea; coefficients: number[] }
|
|
73
|
+
| { opcode: "GainMap"; area: DngOpcodeArea; pointsV: number; pointsH: number; spacingV: number; spacingH: number; originV: number; originH: number; mapPlanes: number; gains: Float32Array }
|
|
74
|
+
| { opcode: "DeltaPerRow" | "DeltaPerColumn" | "ScalePerRow" | "ScalePerColumn"; area: DngOpcodeArea; values: Float32Array };
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## DngStageImage
|
|
78
|
+
|
|
79
|
+
A stage image: interleaved (row, column, plane), normalized values.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
export interface DngStageImage { width: number; height: number; planes: number; data: Float32Array }
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## parseDngOpcodeList
|
|
86
|
+
|
|
87
|
+
Parses an opcode list (the tag's bytes). Every count and length is checked against the bytes before allocation.
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
export function parseDngOpcodeList(bytes: Uint8Array): DngOpcode[];
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
This call is synchronous; any supplied provider must follow its explicit synchronous contract.
|