@photivra/engine 2.3.0 → 2.4.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.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,28 @@
1
1
  # Changelog
2
2
 
3
- ## 2.3.0 — unreleased (prepared October 18, 2026)
3
+ ## 2.4.1 — unreleased (prepared October 18, 2026)
4
+
5
+ A republication of 2.4.0. 2.4.0 was tagged and released on GitHub on October 18, 2026 but never reached npm: the Publish npm workflow's verify job hit its 10-minute limit during the coverage gate on both attempts and was cancelled before the publish job ran. 2.4.1 carries the 2.4.0 contents unchanged (caller text metadata in JPEG, PNG and DNG outputs, `output.metadata`, `serializeNativeRawDng` `textMetadata` and `OutputTextMetadata`; see the 2.4.0 entry below) plus the publish-workflow fix. Apart from the engine version that captures record, every request, plan, RAW, image, encoded file and hash lock is byte-identical to 2.4.0, and without `output.metadata` to 2.3.0. Root exports stay at 1,714 symbols; none is added or removed.
6
+
7
+ ### Fixed
8
+
9
+ - **Publish workflow timeout.** The verify job of `.github/workflows/publish.yml` now has 25 minutes instead of 10 (#346). It took 9m20s for 2.2.1 and 9m33s for 2.3.0; the coverage gate alone takes 8–12 minutes. Repository tooling only: nothing in the published package changes.
10
+
11
+ ## 2.4.0 — tagged; not published to npm (prepared October 18, 2026)
12
+
13
+ Tagged `v2.4.0` and released on GitHub, but never published to npm because the publish job timed out; use 2.4.1, which carries the same contents.
14
+
15
+ A minor release for labelling exported files: callers can write their own description, software name, copyright and comment into the JPEG, PNG and DNG files that `simulateCapture` produces (`output.metadata`, also on a PNG `simulateIdealReference`) and into DNGs written directly with `serializeNativeRawDng` (`textMetadata`). It is opt-in: without metadata every request, plan, RAW, image, encoded file and hash lock is byte-identical to 2.3.0 (the engine version recorded in captures aside). Root exports grow from 1,713 to 1,714 symbols (`OutputTextMetadata`); none is removed.
16
+
17
+ ### Added
18
+
19
+ - **Text metadata in encoded files (`output.metadata`).** `simulateCapture` (and a PNG `simulateIdealReference`) takes an optional `output.metadata: { description?, software?, copyright?, comment? }` and writes the caller's text, unchanged, into the file: JPEG as an XMP `rdf:Description` in the existing APP1 packet (`dc:description`, `xmp:CreatorTool`, `dc:rights`, `exif:UserComment`) plus a COM segment for the comment; PNG as `tEXt` chunks (printable Latin-1) or uncompressed `iTXt` chunks (UTF-8) with the keywords Description, Software, Copyright and Comment before the first IDAT; DNG as an XMP packet in tag 700, with the description appended to ImageDescription after the simulated-RAW statement and a Copyright tag when the text is printable ASCII. The engine adds no text of its own beyond the DNG's existing simulated-RAW statement. Each field is 1–2048 UTF-8 bytes; NUL, lone surrogates, U+FFFE/U+FFFF and control characters other than line feed are rejected with an `InvalidConfigurationError` naming the field. Specifications: ITU-T T.81 §B.2.4.5, Adobe XMP Specification Parts 1–3, W3C PNG Third Edition §11.3.3, TIFF 6.0 (see the quick start's "Text metadata (2.4)").
20
+ - **`serializeNativeRawDng` `textMetadata`.** The same fields for direct DNG writing.
21
+ - **`OutputTextMetadata`** type. Root exports grow from 1,713 to 1,714 symbols; none is removed.
22
+
23
+ Defaults are unchanged: without `output.metadata` every request, RAW, image and encoded file is byte-identical to 2.3.0 (new hash locks on the facade's JPEG, PNG, Display P3 PNG, DNG and ideal-reference PNG bytes, recorded on 2.3.0, plus the existing integration locks; like the 2.0.0 lock, the DNG lock writes the Software tag's engine version back to the 2.3.0 name before hashing).
24
+
25
+ ## 2.3.0 — published October 18, 2026
4
26
 
5
27
  A minor release for building test charts and comparing captures: generic chart elements and a `composite` preset to lay them out, physical chart sizes (`chartSize`) with a framing-distance calculator and a generic prime lens family (`lens.prime`), an opt-in box pixel aperture, byte-identical region rendering for tiles and crops, and ideal reference images without camera effects (`simulateIdealReference`). Everything is opt-in: without the new inputs every request, plan, RAW, image, encoded file, scene digest and hash lock is byte-identical to 2.2.1 (the engine version recorded in captures aside). Root exports grow from 1,680 to 1,713 symbols; none is removed.
6
28
 
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.3.0"
12
+ version: "2.4.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.3.0 release record](https://github.com/Photivra/photivra/blob/main/docs/RELEASE_2_3_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.4.1 release record](https://github.com/Photivra/photivra/blob/main/docs/RELEASE_2_4_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.3.0
38
+ npm install @photivra/engine@2.4.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.3.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.4.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.3.0` (release candidate prepared October 18, 2026; publication is separate)
367
- - Engine API contract: `2.3.0`
366
+ - Repository package version: `2.4.1` (release candidate prepared October 18, 2026; publication is separate)
367
+ - Engine API contract: `2.4.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.3.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.4.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.2.1**, published on October 18, 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.3.0**, published on October 18, 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
 
@@ -1,5 +1,6 @@
1
1
  import type { NativeRawOutput } from "./native-raw.js";
2
2
  import { type ExportSensorColorProfile } from "./photographic-export.js";
3
+ import { type OutputTextMetadata } from "../output/text-metadata.js";
3
4
  export declare const NATIVE_RAW_DNG_SCHEMA_VERSION: "1.0.0";
4
5
  /** Output-referred noise parameters of one CFA channel (see the module notes). */
5
6
  export interface NativeRawDngChannelNoise {
@@ -19,6 +20,8 @@ export interface SerializeNativeRawDngInput {
19
20
  baselineExposureEv?: number;
20
21
  /** Optional UTC time of the capture, exactly YYYY-MM-DDTHH:mm:ss.sssZ. If omitted, no date tags are written; the engine never makes up a clock time. */
21
22
  capturedAtUtc?: string;
23
+ /** Optional (2.4) caller text: description, software, copyright, comment (see the module notes). If omitted, no text is added. */
24
+ textMetadata?: OutputTextMetadata;
22
25
  }
23
26
  export interface NativeRawDng {
24
27
  schemaVersion: typeof NATIVE_RAW_DNG_SCHEMA_VERSION;
@@ -25,6 +25,12 @@
25
25
  * covers only Poisson photo and dark shot noise, Gaussian read noise and uniform quantization. It is an
26
26
  * approximation: PRNU (photo-response non-uniformity), DSNU (dark-signal non-uniformity) and other
27
27
  * fixed-pattern noise are not included.
28
+ * - Text (2.4, only with `textMetadata`): the caller's description is appended to ImageDescription after the
29
+ * simulated-RAW statement and the copyright is written as Copyright (33432), each only when it is printable
30
+ * ASCII (TIFF 6.0 §2: ASCII fields hold 7-bit codes). An XMP packet in tag 700 (BYTE; XMP Specification Part 3
31
+ * §1.1.6, Table 13) always carries every given field in UTF-8: `dc:description` (the full ImageDescription text,
32
+ * statement first, as XMP Part 3 §3.2.3 maps tag 270), `dc:rights`, `xmp:CreatorTool` and `exif:UserComment`.
33
+ * The Software tag keeps naming the engine's writer. Without `textMetadata` the bytes are unchanged.
28
34
  * @see docs/NATIVE_RAW_AND_PRINT.md for the tag table, limits and the round trip through parseDngRawImage.
29
35
  */
30
36
  import { InvalidConfigurationError } from "../core/configuration-error.js";
@@ -37,6 +43,7 @@ import { chromaticAdaptationMatrix } from "../color/chromatic-adaptation.js";
37
43
  import { transformOrientedRasterRectToNative } from "../output/capture-geometry.js";
38
44
  import { checkNativeRawIntegrity, validatePackedNativeRaw } from "./native-raw-development.js";
39
45
  import { parseExportSensorColorProfile } from "./photographic-export.js";
46
+ import { parseOutputTextMetadata, xmpTextMetadataPacket } from "../output/text-metadata.js";
40
47
  import { packExportTiff, exportTiffAscii as ascii, exportTiffNumbers as numbers, exportTiffRationals as rationals, exportTiffBytes as rawBytes, exportTiffDoubles as doubles } from "./export-tiff.js";
41
48
  export const NATIVE_RAW_DNG_SCHEMA_VERSION = "1.0.0";
42
49
  function fail(message) { throw new InvalidConfigurationError("Native RAW DNG: " + message); }
@@ -57,7 +64,7 @@ const ORIENTATION = { landscape: 1, "portrait-clockwise": 6, "landscape-inverted
57
64
  * where its value comes from. The input arrays are copied before the first await.
58
65
  */
59
66
  export async function serializeNativeRawDng(input) {
60
- const r = requireAllowlistedRecord(input, ["raw", "colorProfile", "noise", "baselineExposureEv", "capturedAtUtc"], "Invalid native RAW DNG input.");
67
+ const r = requireAllowlistedRecord(input, ["raw", "colorProfile", "noise", "baselineExposureEv", "capturedAtUtc", "textMetadata"], "Invalid native RAW DNG input.");
61
68
  const raw = r.raw, plan = validatePackedNativeRaw(raw);
62
69
  const mosaic = plan.frame.colorSamplingProfile.layout;
63
70
  if (mosaic.kind === "periodic-mosaic")
@@ -74,6 +81,13 @@ export async function serializeNativeRawDng(input) {
74
81
  const capturedAtUtc = r.capturedAtUtc;
75
82
  if (capturedAtUtc !== undefined && (typeof capturedAtUtc !== "string" || !/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{3}Z$/.test(capturedAtUtc) || Number.isNaN(Date.parse(capturedAtUtc))))
76
83
  fail("capturedAtUtc must be YYYY-MM-DDTHH:mm:ss.sssZ.");
84
+ let text;
85
+ try {
86
+ text = parseOutputTextMetadata(r.textMetadata, "textMetadata");
87
+ }
88
+ catch (error) {
89
+ fail(error.message);
90
+ }
77
91
  const native = plan.exposure.geometry.nativeRaster, width = native.pixelWidth, height = native.pixelHeight, g = plan.exposure.resolvedGeometry;
78
92
  const active = g.activeCapture.nativeRect;
79
93
  const crop = transformOrientedRasterRectToNative({ rect: g.output.cropRect, nativeRaster: g.activeCapture.raster, orientation: plan.exposure.geometry.orientation });
@@ -142,8 +156,14 @@ export async function serializeNativeRawDng(input) {
142
156
  const uniqueCameraModel = "Photivra Virtual Camera " + color.profileId + " " + color.profileVersion + " " + colorHash;
143
157
  const orientation = ORIENTATION[plan.exposure.geometry.orientation];
144
158
  const e = plan.exposure.exposure, software = "Photivra " + ENGINE_API_VERSION;
145
- const description = "Simulated capture: packed native RAW from the Photivra engine (" + frame.frameId + "). Source " + plan.exposure.source.kind +
159
+ const statement = "Simulated capture: packed native RAW from the Photivra engine (" + frame.frameId + "). Source " + plan.exposure.source.kind +
146
160
  "; no linear master image or developed rendering is stored.";
161
+ const printableAscii = (value) => value !== undefined && /^[\x20-\x7e]*$/.test(value);
162
+ const description = printableAscii(text?.description) ? statement + " " + text.description : statement;
163
+ const textTags = text === undefined ? [] : [
164
+ ...(printableAscii(text.copyright) ? [ascii(33432, text.copyright)] : []),
165
+ rawBytes(700, 1, xmpTextMetadataPacket({ ...text, description: text.description === undefined ? statement : statement + " " + text.description }))
166
+ ];
147
167
  const exif = [rawBytes(36864, 7, new TextEncoder().encode("0232")), rationals(33434, false, [e.shutterSeconds]), rationals(33437, false, [e.aperture]),
148
168
  ...(Number.isInteger(e.iso) && e.iso >= 1 && e.iso <= 65535 ? [numbers(34855, 3, [e.iso]), numbers(34864, 3, [2]), numbers(34866, 4, [e.iso])] : []),
149
169
  rationals(37386, false, [e.focalLengthMm]), numbers(40961, 3, [65535]), numbers(40962, 4, [crop.width]), numbers(40963, 4, [crop.height]),
@@ -159,7 +179,7 @@ export async function serializeNativeRawDng(input) {
159
179
  rationals(50730, true, [baseline]), numbers(50778, 3, [21]),
160
180
  rawBytes(50781, 1, Uint8Array.from(rawDataUniqueId.match(/../g).map((s) => parseInt(s, 16)))),
161
181
  numbers(50829, 4, [active.y, active.x, active.y + active.height, active.x + active.width]),
162
- rationals(50964, true, forward.flat()), ...(noiseProfile ? [doubles(51041, noiseProfile)] : [])], exif, pixels);
182
+ rationals(50964, true, forward.flat()), ...(noiseProfile ? [doubles(51041, noiseProfile)] : []), ...textTags], exif, pixels);
163
183
  return Object.freeze({ schemaVersion: NATIVE_RAW_DNG_SCHEMA_VERSION, mediaType: "image/dng", bytes, sha256: await sha256(bytes), rawDataUniqueId, uniqueCameraModel,
164
184
  activeArea: { ...active }, defaultCrop: { ...crop }, orientation, blackLevels, whiteLevel, cfaPattern, colorMatrix1: stored(colorMatrixTag), asShotNeutral: stored(neutralTag),
165
185
  noiseProfile, unrepresentedSaturationFlagCount: unrepresented, source: "packed-native-raw", linearMasterDeclared: false });
@@ -1 +1 @@
1
- {"version":3,"file":"native-raw-dng.js","sourceRoot":"","sources":["../../src/capture/native-raw-dng.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n// This product includes DNG technology under license by Adobe.\n\n/**\n * Writes a packed native RAW (`NativeRawOutput`, from the native RAW or native environment RAW tasks) as a\n * lossless, uncompressed 16-bit DNG with a CFA (colour filter array) mosaic.\n *\n * The file describes only what the packed RAW, the caller's declared colour profile and noise model\n * contain. It never claims a linear float master as its source, or a developed image. The tag layout reuses\n * the TIFF/DNG writers of the paired photographic export (`packExportTiff`, DNG 1.4 tags, Adobe DNG\n * Specification 1.7.1.0):\n *\n * - CFA geometry: CFARepeatPatternDim 2×2, CFAPattern and BlackLevel phases relative to the ActiveArea\n * origin (DNG applies them to the stage-2, ActiveArea-cropped image); DefaultCropOrigin/Size are the\n * capture's oriented output crop mapped back to stored (native) coordinates; Orientation from the capture.\n * - Levels: BlackLevel per 2×2 phase (the packed per-site black levels must repeat with the CFA tile).\n * WhiteLevel is the smallest per-phase effective white code, so no channel is reported as unsaturated\n * above its own saturation point. DNG cannot represent a saturation flag on a site whose code is below\n * WhiteLevel; such sites are counted in `unrepresentedSaturationFlagCount`.\n * - Colour: ColorMatrix1 = (camera→XYZ)⁻¹ with CalibrationIlluminant1 D65 (21), ForwardMatrix1 =\n * Bradford(D65→D50)·M·diag(w), AnalogBalance 1, AsShotNeutral from the capture's white-balance intent\n * (the same operator native development uses), BaselineExposure as declared (default 0).\n * - Noise: NoiseProfile (51041) per CFA plane, var(x) = S·x + O for x = (code − black)/(WhiteLevel − black),\n * S = 1/(G·span), O = (σ_read²/G² + 1/12)/span², written only when the caller supplies each channel's\n * output-referred conversion gain G (electrons per code) and read noise σ_read (electrons). The model\n * covers only Poisson photo and dark shot noise, Gaussian read noise and uniform quantization. It is an\n * approximation: PRNU (photo-response non-uniformity), DSNU (dark-signal non-uniformity) and other\n * fixed-pattern noise are not included.\n * @see docs/NATIVE_RAW_AND_PRINT.md for the tag table, limits and the round trip through parseDngRawImage.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { requireAllowlistedRecord } from \"../core/record-validation.js\";\nimport { ENGINE_API_VERSION } from \"../core/version.js\";\nimport { resolveCaptureColorModel } from \"../color/capture-color.js\";\nimport { invertSensorColorMatrix, prepareSensorColorDevelopment, sensorColorReferenceWhite } from \"../color/sensor-color-development.js\";\nimport { ICC_D50_WHITE_XYZ } from \"../color/srgb-icc.js\";\nimport { chromaticAdaptationMatrix } from \"../color/chromatic-adaptation.js\";\nimport { transformOrientedRasterRectToNative } from \"../output/capture-geometry.js\";\nimport type { NativeRawOutput } from \"./native-raw.js\";\nimport { checkNativeRawIntegrity, validatePackedNativeRaw } from \"./native-raw-development.js\";\nimport { parseExportSensorColorProfile, type ExportSensorColorProfile } from \"./photographic-export.js\";\nimport { packExportTiff, exportTiffAscii as ascii, exportTiffNumbers as numbers, exportTiffRationals as rationals,\n exportTiffBytes as rawBytes, exportTiffDoubles as doubles } from \"./export-tiff.js\";\n\nexport const NATIVE_RAW_DNG_SCHEMA_VERSION = \"1.0.0\" as const;\n\n/** Output-referred noise parameters of one CFA channel (see the module notes). */\nexport interface NativeRawDngChannelNoise {\n /** Electrons per output RAW code (system conversion gain divided by any post-ADC digital gain). */\n conversionGainElectronsPerCode: number;\n /** Combined RMS read noise, electrons. */\n readNoiseRmsElectrons: number;\n}\nexport interface SerializeNativeRawDngInput {\n /** Packed native RAW. It is read, never taken over or modified. */\n raw: NativeRawOutput;\n /** Declared camera→XYZ (D65) colour profile for the RAW's CFA. Native development uses the same profile. */\n colorProfile: ExportSensorColorProfile;\n /** Optional NoiseProfile parameters by channel ID. If omitted, no NoiseProfile tag is written. */\n noise?: Readonly<Record<string, NativeRawDngChannelNoise>>;\n /** BaselineExposure in EV (exposure value stops). Default 0: the RAW white level maps to display white, as in native development. */\n baselineExposureEv?: number;\n /** Optional UTC time of the capture, exactly YYYY-MM-DDTHH:mm:ss.sssZ. If omitted, no date tags are written; the engine never makes up a clock time. */\n capturedAtUtc?: string;\n}\nexport interface NativeRawDng {\n schemaVersion: typeof NATIVE_RAW_DNG_SCHEMA_VERSION;\n mediaType: \"image/dng\";\n bytes: Uint8Array;\n sha256: string;\n /** RawDataUniqueID (32 hex digits): the first 128 bits of SHA-256 over a fixed header and the little-endian codes. */\n rawDataUniqueId: string;\n uniqueCameraModel: string;\n /** Stored geometry, levels and colour values exactly as written (rationals as numerator/denominator). */\n activeArea: { x: number; y: number; width: number; height: number };\n defaultCrop: { x: number; y: number; width: number; height: number };\n orientation: number;\n blackLevels: readonly number[];\n whiteLevel: number;\n cfaPattern: readonly number[];\n colorMatrix1: readonly number[];\n asShotNeutral: readonly number[];\n noiseProfile: readonly number[] | null;\n /** Sites flagged saturated whose code is below WhiteLevel; DNG has no field for them. */\n unrepresentedSaturationFlagCount: number;\n source: \"packed-native-raw\";\n linearMasterDeclared: false;\n}\n\nfunction fail(message: string): never { throw new InvalidConfigurationError(\"Native RAW DNG: \" + message); }\nasync function sha256(bytes: Uint8Array): Promise<string> {\n const subtle = globalThis.crypto?.subtle;\n if (!subtle) fail(\"needs crypto.subtle (a secure browser context or Node 22+).\");\n return Array.from(new Uint8Array(await subtle.digest(\"SHA-256\", bytes as Uint8Array<ArrayBuffer>)), (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n/** Values exactly as stored by a RATIONAL/SRATIONAL tag (numerator / denominator), so reported metadata equals what a reader decodes. */\nfunction stored(tag: ReturnType<typeof rationals>): number[] {\n const view = new DataView(tag.data.buffer, tag.data.byteOffset, tag.data.byteLength);\n return Array.from({ length: tag.count }, (_, i) => (tag.type === 10 ? view.getInt32(i * 8, true) : view.getUint32(i * 8, true)) / view.getUint32(i * 8 + 4, true));\n}\nconst ORIENTATION = { landscape: 1, \"portrait-clockwise\": 6, \"landscape-inverted\": 3, \"portrait-counter-clockwise\": 8 } as const;\n\n/**\n * Writes packed native RAW codes as a lossless, uncompressed CFA DNG. The module notes list every tag and\n * where its value comes from. The input arrays are copied before the first await.\n */\nexport async function serializeNativeRawDng(input: SerializeNativeRawDngInput): Promise<NativeRawDng> {\n const r = requireAllowlistedRecord(input, [\"raw\", \"colorProfile\", \"noise\", \"baselineExposureEv\", \"capturedAtUtc\"], \"Invalid native RAW DNG input.\");\n const raw = r.raw as NativeRawOutput, plan = validatePackedNativeRaw(raw);\n const mosaic = plan.frame.colorSamplingProfile.layout;\n if (mosaic.kind === \"periodic-mosaic\") checkNativeRawIntegrity(raw, plan.exposure.geometry.nativeRaster.pixelWidth, mosaic.repeatWidthSites, mosaic.repeatHeightSites, 0, plan.exposure.geometry.nativeRaster.pixelHeight);\n const color = parseExportSensorColorProfile(r.colorProfile), frame = plan.frame, layout = frame.colorSamplingProfile.layout;\n if (layout.kind !== \"periodic-mosaic\" || layout.repeatWidthSites !== 2 || layout.repeatHeightSites !== 2 ||\n [...layout.siteChannelIds].sort().join() !== \"blue,green,green,red\" || color.channelIds.join() !== \"red,green,blue\")\n fail(\"supports 2×2 RGB Bayer CFAs with red, green, blue colour-profile channels only.\");\n if (color.colorSamplingProfileId !== frame.colorSamplingProfile.profileId) fail(\"colour profile is not bound to the RAW's CFA.\");\n const baseline = r.baselineExposureEv === undefined ? 0 : r.baselineExposureEv;\n if (typeof baseline !== \"number\" || !Number.isFinite(baseline) || Math.abs(baseline) > 16) fail(\"baselineExposureEv must be finite within ±16.\");\n const capturedAtUtc = r.capturedAtUtc as string | undefined;\n if (capturedAtUtc !== undefined && (typeof capturedAtUtc !== \"string\" || !/^\\d{4}-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d\\.\\d{3}Z$/.test(capturedAtUtc) || Number.isNaN(Date.parse(capturedAtUtc))))\n fail(\"capturedAtUtc must be YYYY-MM-DDTHH:mm:ss.sssZ.\");\n\n const native = plan.exposure.geometry.nativeRaster, width = native.pixelWidth, height = native.pixelHeight, g = plan.exposure.resolvedGeometry;\n const active = g.activeCapture.nativeRect;\n const crop = transformOrientedRasterRectToNative({ rect: g.output.cropRect, nativeRaster: g.activeCapture.raster, orientation: plan.exposure.geometry.orientation });\n // Copy before any await: the caller keeps ownership of the packed arrays.\n const codes = raw.codes.slice(), blacks = raw.blackLevels.slice(), flags = raw.saturationFlags.slice(), whiteByPhase = Array.from(raw.effectiveSaturationCodes);\n\n // Levels per CFA phase, relative to the active area. The black-level tile must repeat exactly.\n const blackLevels: number[] = [], cfaPattern: number[] = [], whites: number[] = [], phaseChannel: string[] = [];\n for (let py = 0; py < 2; py++) for (let px = 0; px < 2; px++) {\n const ax = (active.x + px) % 2, ay = (active.y + py) % 2;\n const black = blacks[ay * width + ax]!;\n for (let y = ay; y < height; y += 2) for (let x = ax; x < width; x += 2) if (blacks[y * width + x] !== black) fail(\"per-site black levels do not repeat with the 2×2 CFA tile.\");\n const channel = layout.siteChannelIds[ay * 2 + ax]!;\n blackLevels.push(black); whites.push(whiteByPhase[ay * 2 + ax]!); phaseChannel.push(channel); cfaPattern.push([\"red\", \"green\", \"blue\"].indexOf(channel));\n }\n const whiteLevel = Math.min(...whites);\n if (blackLevels.some((b) => b >= whiteLevel)) fail(\"black level must be below the effective WhiteLevel.\");\n let unrepresented = 0;\n for (let i = 0; i < codes.length; i++) if (flags[i] !== 0 && codes[i]! < whiteLevel) unrepresented++;\n\n // NoiseProfile: one (S, O) pair per CFA plane colour, only when every phase of a channel shares its black level.\n let noiseProfile: number[] | null = null;\n if (r.noise !== undefined) {\n const noise = requireAllowlistedRecord(r.noise, [\"red\", \"green\", \"blue\"], \"Invalid native RAW DNG noise.\") as Record<string, NativeRawDngChannelNoise>;\n noiseProfile = [];\n for (const channel of [\"red\", \"green\", \"blue\"]) {\n const n = noise[channel];\n if (!n) fail(\"noise needs red, green and blue entries.\");\n requireAllowlistedRecord(n, [\"conversionGainElectronsPerCode\", \"readNoiseRmsElectrons\"], \"Invalid native RAW DNG channel noise.\");\n const gain = n.conversionGainElectronsPerCode, read = n.readNoiseRmsElectrons;\n if (typeof gain !== \"number\" || !Number.isFinite(gain) || gain <= 0 || typeof read !== \"number\" || !Number.isFinite(read) || read < 0) fail(\"noise gain must be positive and read noise non-negative.\");\n const phaseBlacks = blackLevels.filter((_, i) => phaseChannel[i] === channel);\n if (phaseBlacks.some((b) => b !== phaseBlacks[0])) { noiseProfile = null; break; }\n const span = whiteLevel - phaseBlacks[0]!;\n noiseProfile.push(1 / (gain * span), (read * read / (gain * gain) + 1 / 12) / (span * span));\n }\n }\n\n // Colour tags from the declared profile and the capture's white-balance intent.\n const m = color.normalizedCameraChannelsToXyz, colorMatrix = invertSensorColorMatrix(m), intent = plan.exposure.whiteBalanceIntent;\n const development = prepareSensorColorDevelopment(m, intent ? [intent.channelGains.red, intent.channelGains.green, intent.channelGains.blue] : null);\n const cameraWhite = sensorColorReferenceWhite(colorMatrix);\n const chad = chromaticAdaptationMatrix({ transform: \"bradford\", sourceWhiteXyz: resolveCaptureColorModel().referenceWhiteXyz,\n destinationWhiteXyz: { x: ICC_D50_WHITE_XYZ[0], y: 1, z: ICC_D50_WHITE_XYZ[2] } }).matrix;\n const forward = chad.map((row) => [0, 1, 2].map((j) => row.reduce((sum, x, k) => sum + x * m[k]![j]! * cameraWhite[j]!, 0)));\n\n const pixels = new Uint8Array(codes.length * 2), view = new DataView(pixels.buffer);\n codes.forEach((c, i) => view.setUint16(i * 2, c, true));\n const header = new TextEncoder().encode(JSON.stringify({ schema: \"photivra-native-raw-data-id-0.1\", width, height, cfa: layout.siteChannelIds, containerBitDepth: 16 }));\n const idInput = new Uint8Array(header.length + pixels.length); idInput.set(header); idInput.set(pixels, header.length);\n const rawDataUniqueId = (await sha256(idInput)).slice(0, 32);\n const colorHash = (await sha256(new TextEncoder().encode(JSON.stringify(color)))).slice(0, 16);\n const uniqueCameraModel = \"Photivra Virtual Camera \" + color.profileId + \" \" + color.profileVersion + \" \" + colorHash;\n const orientation = ORIENTATION[plan.exposure.geometry.orientation];\n const e = plan.exposure.exposure, software = \"Photivra \" + ENGINE_API_VERSION;\n const description = \"Simulated capture: packed native RAW from the Photivra engine (\" + frame.frameId + \"). Source \" + plan.exposure.source.kind +\n \"; no linear master image or developed rendering is stored.\";\n\n const exif = [rawBytes(36864, 7, new TextEncoder().encode(\"0232\")), rationals(33434, false, [e.shutterSeconds]), rationals(33437, false, [e.aperture]),\n ...(Number.isInteger(e.iso) && e.iso >= 1 && e.iso <= 65535 ? [numbers(34855, 3, [e.iso]), numbers(34864, 3, [2]), numbers(34866, 4, [e.iso])] : []),\n rationals(37386, false, [e.focalLengthMm]), numbers(40961, 3, [65535]), numbers(40962, 4, [crop.width]), numbers(40963, 4, [crop.height]),\n ...(capturedAtUtc === undefined ? [] : [ascii(36867, capturedAtUtc.slice(0, 19).replaceAll(\"-\", \":\").replace(\"T\", \" \")), ascii(36881, \"+00:00\"), ascii(37521, capturedAtUtc.slice(20, 23))])];\n const colorMatrixTag = rationals(50721, true, colorMatrix.flat()), neutralTag = rationals(50728, false, [...development.asShotNeutral]);\n const bytes = packExportTiff([ascii(270, description), ascii(271, \"Photivra\"), ascii(272, \"Photivra Virtual Camera\"), ascii(305, software), numbers(274, 3, [orientation]),\n numbers(254, 4, [0]), numbers(256, 4, [width]), numbers(257, 4, [height]), numbers(258, 3, [16]), numbers(259, 3, [1]), numbers(262, 3, [32803]),\n { ...numbers(273, 4, [0]), pointer: \"pixels\" as const }, numbers(277, 3, [1]), numbers(278, 4, [height]), numbers(279, 4, [pixels.length]),\n numbers(284, 3, [1]), numbers(339, 3, [1]), numbers(33421, 3, [2, 2]), numbers(33422, 1, cfaPattern),\n numbers(50706, 1, [1, 4, 0, 0]), numbers(50707, 1, [1, 1, 0, 0]), ascii(50708, uniqueCameraModel), numbers(50710, 1, [0, 1, 2]), numbers(50711, 3, [1]),\n numbers(50713, 3, [2, 2]), rationals(50714, false, blackLevels), numbers(50717, 4, [whiteLevel]), numbers(50719, 4, [crop.x, crop.y]), numbers(50720, 4, [crop.width, crop.height]),\n colorMatrixTag, rationals(50727, false, [1, 1, 1]), neutralTag,\n rationals(50730, true, [baseline]), numbers(50778, 3, [21]),\n rawBytes(50781, 1, Uint8Array.from(rawDataUniqueId.match(/../g)!.map((s) => parseInt(s, 16)))),\n numbers(50829, 4, [active.y, active.x, active.y + active.height, active.x + active.width]),\n rationals(50964, true, forward.flat()), ...(noiseProfile ? [doubles(51041, noiseProfile)] : [])], exif, pixels);\n return Object.freeze({ schemaVersion: NATIVE_RAW_DNG_SCHEMA_VERSION, mediaType: \"image/dng\" as const, bytes, sha256: await sha256(bytes), rawDataUniqueId, uniqueCameraModel,\n activeArea: { ...active }, defaultCrop: { ...crop }, orientation, blackLevels, whiteLevel, cfaPattern, colorMatrix1: stored(colorMatrixTag), asShotNeutral: stored(neutralTag),\n noiseProfile, unrepresentedSaturationFlagCount: unrepresented, source: \"packed-native-raw\" as const, linearMasterDeclared: false as const });\n}\n"]}
1
+ {"version":3,"file":"native-raw-dng.js","sourceRoot":"","sources":["../../src/capture/native-raw-dng.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n// This product includes DNG technology under license by Adobe.\n\n/**\n * Writes a packed native RAW (`NativeRawOutput`, from the native RAW or native environment RAW tasks) as a\n * lossless, uncompressed 16-bit DNG with a CFA (colour filter array) mosaic.\n *\n * The file describes only what the packed RAW, the caller's declared colour profile and noise model\n * contain. It never claims a linear float master as its source, or a developed image. The tag layout reuses\n * the TIFF/DNG writers of the paired photographic export (`packExportTiff`, DNG 1.4 tags, Adobe DNG\n * Specification 1.7.1.0):\n *\n * - CFA geometry: CFARepeatPatternDim 2×2, CFAPattern and BlackLevel phases relative to the ActiveArea\n * origin (DNG applies them to the stage-2, ActiveArea-cropped image); DefaultCropOrigin/Size are the\n * capture's oriented output crop mapped back to stored (native) coordinates; Orientation from the capture.\n * - Levels: BlackLevel per 2×2 phase (the packed per-site black levels must repeat with the CFA tile).\n * WhiteLevel is the smallest per-phase effective white code, so no channel is reported as unsaturated\n * above its own saturation point. DNG cannot represent a saturation flag on a site whose code is below\n * WhiteLevel; such sites are counted in `unrepresentedSaturationFlagCount`.\n * - Colour: ColorMatrix1 = (camera→XYZ)⁻¹ with CalibrationIlluminant1 D65 (21), ForwardMatrix1 =\n * Bradford(D65→D50)·M·diag(w), AnalogBalance 1, AsShotNeutral from the capture's white-balance intent\n * (the same operator native development uses), BaselineExposure as declared (default 0).\n * - Noise: NoiseProfile (51041) per CFA plane, var(x) = S·x + O for x = (code − black)/(WhiteLevel − black),\n * S = 1/(G·span), O = (σ_read²/G² + 1/12)/span², written only when the caller supplies each channel's\n * output-referred conversion gain G (electrons per code) and read noise σ_read (electrons). The model\n * covers only Poisson photo and dark shot noise, Gaussian read noise and uniform quantization. It is an\n * approximation: PRNU (photo-response non-uniformity), DSNU (dark-signal non-uniformity) and other\n * fixed-pattern noise are not included.\n * - Text (2.4, only with `textMetadata`): the caller's description is appended to ImageDescription after the\n * simulated-RAW statement and the copyright is written as Copyright (33432), each only when it is printable\n * ASCII (TIFF 6.0 §2: ASCII fields hold 7-bit codes). An XMP packet in tag 700 (BYTE; XMP Specification Part 3\n * §1.1.6, Table 13) always carries every given field in UTF-8: `dc:description` (the full ImageDescription text,\n * statement first, as XMP Part 3 §3.2.3 maps tag 270), `dc:rights`, `xmp:CreatorTool` and `exif:UserComment`.\n * The Software tag keeps naming the engine's writer. Without `textMetadata` the bytes are unchanged.\n * @see docs/NATIVE_RAW_AND_PRINT.md for the tag table, limits and the round trip through parseDngRawImage.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { requireAllowlistedRecord } from \"../core/record-validation.js\";\nimport { ENGINE_API_VERSION } from \"../core/version.js\";\nimport { resolveCaptureColorModel } from \"../color/capture-color.js\";\nimport { invertSensorColorMatrix, prepareSensorColorDevelopment, sensorColorReferenceWhite } from \"../color/sensor-color-development.js\";\nimport { ICC_D50_WHITE_XYZ } from \"../color/srgb-icc.js\";\nimport { chromaticAdaptationMatrix } from \"../color/chromatic-adaptation.js\";\nimport { transformOrientedRasterRectToNative } from \"../output/capture-geometry.js\";\nimport type { NativeRawOutput } from \"./native-raw.js\";\nimport { checkNativeRawIntegrity, validatePackedNativeRaw } from \"./native-raw-development.js\";\nimport { parseExportSensorColorProfile, type ExportSensorColorProfile } from \"./photographic-export.js\";\nimport { parseOutputTextMetadata, xmpTextMetadataPacket, type OutputTextMetadata } from \"../output/text-metadata.js\";\nimport { packExportTiff, exportTiffAscii as ascii, exportTiffNumbers as numbers, exportTiffRationals as rationals,\n exportTiffBytes as rawBytes, exportTiffDoubles as doubles } from \"./export-tiff.js\";\n\nexport const NATIVE_RAW_DNG_SCHEMA_VERSION = \"1.0.0\" as const;\n\n/** Output-referred noise parameters of one CFA channel (see the module notes). */\nexport interface NativeRawDngChannelNoise {\n /** Electrons per output RAW code (system conversion gain divided by any post-ADC digital gain). */\n conversionGainElectronsPerCode: number;\n /** Combined RMS read noise, electrons. */\n readNoiseRmsElectrons: number;\n}\nexport interface SerializeNativeRawDngInput {\n /** Packed native RAW. It is read, never taken over or modified. */\n raw: NativeRawOutput;\n /** Declared camera→XYZ (D65) colour profile for the RAW's CFA. Native development uses the same profile. */\n colorProfile: ExportSensorColorProfile;\n /** Optional NoiseProfile parameters by channel ID. If omitted, no NoiseProfile tag is written. */\n noise?: Readonly<Record<string, NativeRawDngChannelNoise>>;\n /** BaselineExposure in EV (exposure value stops). Default 0: the RAW white level maps to display white, as in native development. */\n baselineExposureEv?: number;\n /** Optional UTC time of the capture, exactly YYYY-MM-DDTHH:mm:ss.sssZ. If omitted, no date tags are written; the engine never makes up a clock time. */\n capturedAtUtc?: string;\n /** Optional (2.4) caller text: description, software, copyright, comment (see the module notes). If omitted, no text is added. */\n textMetadata?: OutputTextMetadata;\n}\nexport interface NativeRawDng {\n schemaVersion: typeof NATIVE_RAW_DNG_SCHEMA_VERSION;\n mediaType: \"image/dng\";\n bytes: Uint8Array;\n sha256: string;\n /** RawDataUniqueID (32 hex digits): the first 128 bits of SHA-256 over a fixed header and the little-endian codes. */\n rawDataUniqueId: string;\n uniqueCameraModel: string;\n /** Stored geometry, levels and colour values exactly as written (rationals as numerator/denominator). */\n activeArea: { x: number; y: number; width: number; height: number };\n defaultCrop: { x: number; y: number; width: number; height: number };\n orientation: number;\n blackLevels: readonly number[];\n whiteLevel: number;\n cfaPattern: readonly number[];\n colorMatrix1: readonly number[];\n asShotNeutral: readonly number[];\n noiseProfile: readonly number[] | null;\n /** Sites flagged saturated whose code is below WhiteLevel; DNG has no field for them. */\n unrepresentedSaturationFlagCount: number;\n source: \"packed-native-raw\";\n linearMasterDeclared: false;\n}\n\nfunction fail(message: string): never { throw new InvalidConfigurationError(\"Native RAW DNG: \" + message); }\nasync function sha256(bytes: Uint8Array): Promise<string> {\n const subtle = globalThis.crypto?.subtle;\n if (!subtle) fail(\"needs crypto.subtle (a secure browser context or Node 22+).\");\n return Array.from(new Uint8Array(await subtle.digest(\"SHA-256\", bytes as Uint8Array<ArrayBuffer>)), (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n/** Values exactly as stored by a RATIONAL/SRATIONAL tag (numerator / denominator), so reported metadata equals what a reader decodes. */\nfunction stored(tag: ReturnType<typeof rationals>): number[] {\n const view = new DataView(tag.data.buffer, tag.data.byteOffset, tag.data.byteLength);\n return Array.from({ length: tag.count }, (_, i) => (tag.type === 10 ? view.getInt32(i * 8, true) : view.getUint32(i * 8, true)) / view.getUint32(i * 8 + 4, true));\n}\nconst ORIENTATION = { landscape: 1, \"portrait-clockwise\": 6, \"landscape-inverted\": 3, \"portrait-counter-clockwise\": 8 } as const;\n\n/**\n * Writes packed native RAW codes as a lossless, uncompressed CFA DNG. The module notes list every tag and\n * where its value comes from. The input arrays are copied before the first await.\n */\nexport async function serializeNativeRawDng(input: SerializeNativeRawDngInput): Promise<NativeRawDng> {\n const r = requireAllowlistedRecord(input, [\"raw\", \"colorProfile\", \"noise\", \"baselineExposureEv\", \"capturedAtUtc\", \"textMetadata\"], \"Invalid native RAW DNG input.\");\n const raw = r.raw as NativeRawOutput, plan = validatePackedNativeRaw(raw);\n const mosaic = plan.frame.colorSamplingProfile.layout;\n if (mosaic.kind === \"periodic-mosaic\") checkNativeRawIntegrity(raw, plan.exposure.geometry.nativeRaster.pixelWidth, mosaic.repeatWidthSites, mosaic.repeatHeightSites, 0, plan.exposure.geometry.nativeRaster.pixelHeight);\n const color = parseExportSensorColorProfile(r.colorProfile), frame = plan.frame, layout = frame.colorSamplingProfile.layout;\n if (layout.kind !== \"periodic-mosaic\" || layout.repeatWidthSites !== 2 || layout.repeatHeightSites !== 2 ||\n [...layout.siteChannelIds].sort().join() !== \"blue,green,green,red\" || color.channelIds.join() !== \"red,green,blue\")\n fail(\"supports 2×2 RGB Bayer CFAs with red, green, blue colour-profile channels only.\");\n if (color.colorSamplingProfileId !== frame.colorSamplingProfile.profileId) fail(\"colour profile is not bound to the RAW's CFA.\");\n const baseline = r.baselineExposureEv === undefined ? 0 : r.baselineExposureEv;\n if (typeof baseline !== \"number\" || !Number.isFinite(baseline) || Math.abs(baseline) > 16) fail(\"baselineExposureEv must be finite within ±16.\");\n const capturedAtUtc = r.capturedAtUtc as string | undefined;\n if (capturedAtUtc !== undefined && (typeof capturedAtUtc !== \"string\" || !/^\\d{4}-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d\\.\\d{3}Z$/.test(capturedAtUtc) || Number.isNaN(Date.parse(capturedAtUtc))))\n fail(\"capturedAtUtc must be YYYY-MM-DDTHH:mm:ss.sssZ.\");\n let text: Readonly<OutputTextMetadata> | undefined;\n try { text = parseOutputTextMetadata(r.textMetadata, \"textMetadata\"); } catch (error) { fail((error as Error).message); }\n\n const native = plan.exposure.geometry.nativeRaster, width = native.pixelWidth, height = native.pixelHeight, g = plan.exposure.resolvedGeometry;\n const active = g.activeCapture.nativeRect;\n const crop = transformOrientedRasterRectToNative({ rect: g.output.cropRect, nativeRaster: g.activeCapture.raster, orientation: plan.exposure.geometry.orientation });\n // Copy before any await: the caller keeps ownership of the packed arrays.\n const codes = raw.codes.slice(), blacks = raw.blackLevels.slice(), flags = raw.saturationFlags.slice(), whiteByPhase = Array.from(raw.effectiveSaturationCodes);\n\n // Levels per CFA phase, relative to the active area. The black-level tile must repeat exactly.\n const blackLevels: number[] = [], cfaPattern: number[] = [], whites: number[] = [], phaseChannel: string[] = [];\n for (let py = 0; py < 2; py++) for (let px = 0; px < 2; px++) {\n const ax = (active.x + px) % 2, ay = (active.y + py) % 2;\n const black = blacks[ay * width + ax]!;\n for (let y = ay; y < height; y += 2) for (let x = ax; x < width; x += 2) if (blacks[y * width + x] !== black) fail(\"per-site black levels do not repeat with the 2×2 CFA tile.\");\n const channel = layout.siteChannelIds[ay * 2 + ax]!;\n blackLevels.push(black); whites.push(whiteByPhase[ay * 2 + ax]!); phaseChannel.push(channel); cfaPattern.push([\"red\", \"green\", \"blue\"].indexOf(channel));\n }\n const whiteLevel = Math.min(...whites);\n if (blackLevels.some((b) => b >= whiteLevel)) fail(\"black level must be below the effective WhiteLevel.\");\n let unrepresented = 0;\n for (let i = 0; i < codes.length; i++) if (flags[i] !== 0 && codes[i]! < whiteLevel) unrepresented++;\n\n // NoiseProfile: one (S, O) pair per CFA plane colour, only when every phase of a channel shares its black level.\n let noiseProfile: number[] | null = null;\n if (r.noise !== undefined) {\n const noise = requireAllowlistedRecord(r.noise, [\"red\", \"green\", \"blue\"], \"Invalid native RAW DNG noise.\") as Record<string, NativeRawDngChannelNoise>;\n noiseProfile = [];\n for (const channel of [\"red\", \"green\", \"blue\"]) {\n const n = noise[channel];\n if (!n) fail(\"noise needs red, green and blue entries.\");\n requireAllowlistedRecord(n, [\"conversionGainElectronsPerCode\", \"readNoiseRmsElectrons\"], \"Invalid native RAW DNG channel noise.\");\n const gain = n.conversionGainElectronsPerCode, read = n.readNoiseRmsElectrons;\n if (typeof gain !== \"number\" || !Number.isFinite(gain) || gain <= 0 || typeof read !== \"number\" || !Number.isFinite(read) || read < 0) fail(\"noise gain must be positive and read noise non-negative.\");\n const phaseBlacks = blackLevels.filter((_, i) => phaseChannel[i] === channel);\n if (phaseBlacks.some((b) => b !== phaseBlacks[0])) { noiseProfile = null; break; }\n const span = whiteLevel - phaseBlacks[0]!;\n noiseProfile.push(1 / (gain * span), (read * read / (gain * gain) + 1 / 12) / (span * span));\n }\n }\n\n // Colour tags from the declared profile and the capture's white-balance intent.\n const m = color.normalizedCameraChannelsToXyz, colorMatrix = invertSensorColorMatrix(m), intent = plan.exposure.whiteBalanceIntent;\n const development = prepareSensorColorDevelopment(m, intent ? [intent.channelGains.red, intent.channelGains.green, intent.channelGains.blue] : null);\n const cameraWhite = sensorColorReferenceWhite(colorMatrix);\n const chad = chromaticAdaptationMatrix({ transform: \"bradford\", sourceWhiteXyz: resolveCaptureColorModel().referenceWhiteXyz,\n destinationWhiteXyz: { x: ICC_D50_WHITE_XYZ[0], y: 1, z: ICC_D50_WHITE_XYZ[2] } }).matrix;\n const forward = chad.map((row) => [0, 1, 2].map((j) => row.reduce((sum, x, k) => sum + x * m[k]![j]! * cameraWhite[j]!, 0)));\n\n const pixels = new Uint8Array(codes.length * 2), view = new DataView(pixels.buffer);\n codes.forEach((c, i) => view.setUint16(i * 2, c, true));\n const header = new TextEncoder().encode(JSON.stringify({ schema: \"photivra-native-raw-data-id-0.1\", width, height, cfa: layout.siteChannelIds, containerBitDepth: 16 }));\n const idInput = new Uint8Array(header.length + pixels.length); idInput.set(header); idInput.set(pixels, header.length);\n const rawDataUniqueId = (await sha256(idInput)).slice(0, 32);\n const colorHash = (await sha256(new TextEncoder().encode(JSON.stringify(color)))).slice(0, 16);\n const uniqueCameraModel = \"Photivra Virtual Camera \" + color.profileId + \" \" + color.profileVersion + \" \" + colorHash;\n const orientation = ORIENTATION[plan.exposure.geometry.orientation];\n const e = plan.exposure.exposure, software = \"Photivra \" + ENGINE_API_VERSION;\n const statement = \"Simulated capture: packed native RAW from the Photivra engine (\" + frame.frameId + \"). Source \" + plan.exposure.source.kind +\n \"; no linear master image or developed rendering is stored.\";\n const printableAscii = (value: string | undefined): value is string => value !== undefined && /^[\\x20-\\x7e]*$/.test(value);\n const description = printableAscii(text?.description) ? statement + \" \" + text.description : statement;\n const textTags = text === undefined ? [] : [\n ...(printableAscii(text.copyright) ? [ascii(33432, text.copyright)] : []),\n rawBytes(700, 1, xmpTextMetadataPacket({ ...text, description: text.description === undefined ? statement : statement + \" \" + text.description }))];\n\n const exif = [rawBytes(36864, 7, new TextEncoder().encode(\"0232\")), rationals(33434, false, [e.shutterSeconds]), rationals(33437, false, [e.aperture]),\n ...(Number.isInteger(e.iso) && e.iso >= 1 && e.iso <= 65535 ? [numbers(34855, 3, [e.iso]), numbers(34864, 3, [2]), numbers(34866, 4, [e.iso])] : []),\n rationals(37386, false, [e.focalLengthMm]), numbers(40961, 3, [65535]), numbers(40962, 4, [crop.width]), numbers(40963, 4, [crop.height]),\n ...(capturedAtUtc === undefined ? [] : [ascii(36867, capturedAtUtc.slice(0, 19).replaceAll(\"-\", \":\").replace(\"T\", \" \")), ascii(36881, \"+00:00\"), ascii(37521, capturedAtUtc.slice(20, 23))])];\n const colorMatrixTag = rationals(50721, true, colorMatrix.flat()), neutralTag = rationals(50728, false, [...development.asShotNeutral]);\n const bytes = packExportTiff([ascii(270, description), ascii(271, \"Photivra\"), ascii(272, \"Photivra Virtual Camera\"), ascii(305, software), numbers(274, 3, [orientation]),\n numbers(254, 4, [0]), numbers(256, 4, [width]), numbers(257, 4, [height]), numbers(258, 3, [16]), numbers(259, 3, [1]), numbers(262, 3, [32803]),\n { ...numbers(273, 4, [0]), pointer: \"pixels\" as const }, numbers(277, 3, [1]), numbers(278, 4, [height]), numbers(279, 4, [pixels.length]),\n numbers(284, 3, [1]), numbers(339, 3, [1]), numbers(33421, 3, [2, 2]), numbers(33422, 1, cfaPattern),\n numbers(50706, 1, [1, 4, 0, 0]), numbers(50707, 1, [1, 1, 0, 0]), ascii(50708, uniqueCameraModel), numbers(50710, 1, [0, 1, 2]), numbers(50711, 3, [1]),\n numbers(50713, 3, [2, 2]), rationals(50714, false, blackLevels), numbers(50717, 4, [whiteLevel]), numbers(50719, 4, [crop.x, crop.y]), numbers(50720, 4, [crop.width, crop.height]),\n colorMatrixTag, rationals(50727, false, [1, 1, 1]), neutralTag,\n rationals(50730, true, [baseline]), numbers(50778, 3, [21]),\n rawBytes(50781, 1, Uint8Array.from(rawDataUniqueId.match(/../g)!.map((s) => parseInt(s, 16)))),\n numbers(50829, 4, [active.y, active.x, active.y + active.height, active.x + active.width]),\n rationals(50964, true, forward.flat()), ...(noiseProfile ? [doubles(51041, noiseProfile)] : []), ...textTags], exif, pixels);\n return Object.freeze({ schemaVersion: NATIVE_RAW_DNG_SCHEMA_VERSION, mediaType: \"image/dng\" as const, bytes, sha256: await sha256(bytes), rawDataUniqueId, uniqueCameraModel,\n activeArea: { ...active }, defaultCrop: { ...crop }, orientation, blackLevels, whiteLevel, cfaPattern, colorMatrix1: stored(colorMatrixTag), asShotNeutral: stored(neutralTag),\n noiseProfile, unrepresentedSaturationFlagCount: unrepresented, source: \"packed-native-raw\" as const, linearMasterDeclared: false as const });\n}\n"]}
@@ -6,4 +6,4 @@
6
6
  * own versions. Release checks confirm it matches package.json and the packed package.
7
7
  * @see docs/RELEASE_1_0_1.md
8
8
  */
9
- export declare const ENGINE_API_VERSION: "2.3.0";
9
+ export declare const ENGINE_API_VERSION: "2.4.1";
@@ -7,5 +7,5 @@
7
7
  * own versions. Release checks confirm it matches package.json and the packed package.
8
8
  * @see docs/RELEASE_1_0_1.md
9
9
  */
10
- export const ENGINE_API_VERSION = "2.3.0";
10
+ export const ENGINE_API_VERSION = "2.4.1";
11
11
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/core/version.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAgB,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Version of the engine release. Since 1.0.1 it equals the npm package version.\n *\n * New captures and plans record this version as their creator. Archived records keep the\n * version they were made with. Proof-of-concept (POC), schema and model contracts have their\n * own versions. Release checks confirm it matches package.json and the packed package.\n * @see docs/RELEASE_1_0_1.md\n */\nexport const ENGINE_API_VERSION = \"2.3.0\" as const;\n"]}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/core/version.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAgB,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Version of the engine release. Since 1.0.1 it equals the npm package version.\n *\n * New captures and plans record this version as their creator. Archived records keep the\n * version they were made with. Proof-of-concept (POC), schema and model contracts have their\n * own versions. Release checks confirm it matches package.json and the packed package.\n * @see docs/RELEASE_1_0_1.md\n */\nexport const ENGINE_API_VERSION = \"2.4.1\" as const;\n"]}
@@ -7,6 +7,7 @@ import { type NativeRawDevelopmentInput, type RawHighlightDiagnostics } from "..
7
7
  import { type SdrToneCurve, type SdrToneCurveParameters, type SdrRenderingResult } from "../output/sdr-rendering.js";
8
8
  import type { RawHighlightMode } from "../color/sensor-color-development.js";
9
9
  import type { SimulateEnvironmentSensorRawFrameInput } from "../capture/environment-raw-producer.js";
10
+ import { type OutputTextMetadata } from "../output/text-metadata.js";
10
11
  import { type LensProjectionModel } from "../camera/lens-projection.js";
11
12
  import type { RadialDistortionProfile } from "../optics/radial-distortion.js";
12
13
  import type { BrownConradyDistortionProfile } from "../optics/brown-conrady-distortion.js";
@@ -207,6 +208,12 @@ export interface SimulateCaptureOptions {
207
208
  dng?: {
208
209
  capturedAtUtc?: string;
209
210
  };
211
+ /**
212
+ * Opt-in (2.4) text written into the file for formats "jpeg", "png" and "dng": the caller's description,
213
+ * software, copyright and comment, unchanged (JPEG XMP + COM, PNG tEXt/iTXt, DNG TIFF tags + XMP; see the
214
+ * quick start). Each field is 1–2048 UTF-8 bytes with no control characters except line feed. Default: none.
215
+ */
216
+ metadata?: OutputTextMetadata;
210
217
  /**
211
218
  * Opt-in large images: the most native sensor sites (the output plus the demosaic margin on each side) the
212
219
  * capture may allocate, an integer up to `NATIVE_RAW_LIMITS.ceilingPixels` (2^27). With it, `width` and
@@ -24,6 +24,8 @@
24
24
  * - motion: constant camera angular velocity and the electronic rolling-shutter exposure schedule.
25
25
  * The 2.1 options are opt-in in the same way: `lens.diffraction`, `output.demosaic: "ahd"` and
26
26
  * `sensor.antiAliasing` (a birefringent point-splitting low-pass filter in the sensor optical stack).
27
+ * The 2.4 `output.metadata` adds the caller's text to JPEG, PNG and DNG files (see `src/output/text-metadata.ts`);
28
+ * without it every encoded file is byte-identical to 2.3.
27
29
  * @see docs/QUICK_START.md
28
30
  */
29
31
  import { InvalidConfigurationError } from "../core/configuration-error.js";
@@ -59,6 +61,7 @@ import { transformOrientedRasterPointToNative } from "../output/capture-geometry
59
61
  import { resolveCaptureColorModel } from "../color/capture-color.js";
60
62
  import { evaluateBt709Oetf, evaluateSrgbEncoding, resolveRgbColorSpace } from "../color/rgb-color-spaces.js";
61
63
  import { serializePngImage, PNG_ENCODER_LIMITS } from "../output/png-encoder.js";
64
+ import { addJpegTextMetadata, addPngTextMetadata, parseOutputTextMetadata } from "../output/text-metadata.js";
62
65
  import { parseLensProjectionModel } from "../camera/lens-projection.js";
63
66
  import { prepareEnvironmentLensFieldMapping } from "../optics/sensor-environment-query.js";
64
67
  import { createRollingShutterCaptureExposureSchedule, resolveElectronicRollingShutterExposureSchedule } from "../sensor/readout-exposure-linkage.js";
@@ -205,6 +208,12 @@ const XYZ_TO_CAMERA = [[EQE_SCALE.red / EQE_SCALE.green, 0, 0], [0, 1, 0], [0, 0
205
208
  const DARK_CURRENT_ELECTRONS_PER_SECOND = 1;
206
209
  const OPERATING_TEMPERATURE_C = 20;
207
210
  function fail(message) { throw new InvalidConfigurationError(message); }
211
+ /** Where `output.metadata` goes in each file format (reported in the `output.metadata` assumption). */
212
+ const TEXT_METADATA_LAYOUT = Object.freeze({
213
+ jpeg: "XMP packet in APP1 (dc:description, xmp:CreatorTool, dc:rights, exif:UserComment), plus a COM segment holding the comment",
214
+ png: "One tEXt (printable Latin-1) or iTXt (UTF-8) chunk per field, keywords Description, Software, Copyright, Comment, before the first IDAT",
215
+ dng: "ImageDescription (statement, then the description) and Copyright when printable ASCII; an XMP packet in tag 700 with every field"
216
+ });
208
217
  /**
209
218
  * The readout profile with every regime's charge-domain transfer scaled by `areaScale`: system conversion gain
210
219
  * (e⁻ per code) and pre-ADC saturation (e⁻ equivalent) are multiplied by it, so codes per unit focal-plane exposure
@@ -620,7 +629,7 @@ async function runCapture(options, idealReference) {
620
629
  const diffractionObjectSpace = diffractionRadiusOption !== undefined && !rotating && !lensFieldMapping && !vignetting;
621
630
  // Output and sensor
622
631
  const outputOptions = allowKeys(top.output, "output", ["width", "height", "format", "toneCurve", "toneCurveParameters", "highlightMode", "whiteBalance", "jpegQuantizationStep",
623
- "colorEncoding", "bitDepth", "gamutMapping", "demosaic", "borderHandling", "dng", "maximumPixels", "region"]);
632
+ "colorEncoding", "bitDepth", "gamutMapping", "demosaic", "borderHandling", "dng", "maximumPixels", "region", "metadata"]);
624
633
  // Opt-in large images: a caller-raised native pixel limit. Without it the 4094-pixel sides are unchanged.
625
634
  const raisedPixels = outputOptions.maximumPixels === undefined ? undefined : integerIn(outputOptions.maximumPixels, "output.maximumPixels", 1, NATIVE_RAW_LIMITS.ceilingPixels);
626
635
  const maximumSide = raisedPixels === undefined ? 4094 : NATIVE_RAW_LIMITS.maximumDimension;
@@ -638,6 +647,13 @@ async function runCapture(options, idealReference) {
638
647
  }
639
648
  else if (outputOptions.dng !== undefined)
640
649
  fail("output.dng applies only to output.format \"dng\".");
650
+ const textMetadata = parseOutputTextMetadata(outputOptions.metadata, "output.metadata");
651
+ if (textMetadata && format !== "jpeg" && format !== "png" && format !== "dng")
652
+ fail("output.metadata applies only to output.format \"jpeg\", \"png\" or \"dng\".");
653
+ const assumeTextMetadata = (layout) => {
654
+ if (textMetadata)
655
+ assume("output.metadata", "model", { fields: Object.keys(textMetadata), layout }, "The caller's text, written unchanged; the engine adds no text of its own (in the DNG the description follows the existing simulated-RAW statement). Without output.metadata the file is byte-identical to 2.3.");
656
+ };
641
657
  let colorEncoding, pngBitDepth;
642
658
  if (format === "png") {
643
659
  colorEncoding = pick(outputOptions, "colorEncoding", "output.colorEncoding", "srgb", "sRGB (W3C CSS Color 4 srgb) samples signalled with the PNG sRGB chunk (perceptual intent) and its prescribed gAMA/cHRM values.");
@@ -1305,8 +1321,11 @@ async function runCapture(options, idealReference) {
1305
1321
  const mapping = perceptual ? prepareRgbGamutMapping({ colorSpace: colorEncoding, method: "cielab-hue-preserving-chroma-compression", kneeFraction: PERCEPTUAL_GAMUT_KNEE_FRACTION }) : undefined;
1306
1322
  samples = encodeSdrFromLinear(rgb, colorEncoding, pngBitDepth, mapping).samples;
1307
1323
  }
1308
- const png = format === "png" ? serializePngImage({ width: outputWidth, height: outputHeight, bitDepth: pngBitDepth, samples, colourEncoding: pngColourEncoding(colorEncoding),
1324
+ const encodedPng = format === "png" ? serializePngImage({ width: outputWidth, height: outputHeight, bitDepth: pngBitDepth, samples, colourEncoding: pngColourEncoding(colorEncoding),
1309
1325
  ...(raisedPixels === undefined || outputWidth * outputHeight <= PNG_ENCODER_LIMITS.maximumPixels ? {} : { maximumPixels: raisedPixels }) }) : undefined;
1326
+ const png = encodedPng && textMetadata ? addPngTextMetadata(encodedPng, textMetadata) : encodedPng;
1327
+ if (png)
1328
+ assumeTextMetadata(TEXT_METADATA_LAYOUT.png);
1310
1329
  const referenceMs = clockMs() - referenceStarted;
1311
1330
  assume("reference.kind", "model", "ideal-reference", "An ideal reference, not a capture: the same scene, framing, chart size, output size, region and pixel grid as the capture request, with no camera effects.");
1312
1331
  assume("reference.excludes", "model", ["lens PSF, diffraction, defocus and pupil sampling", "projection and distortion (ideal rectilinear mapping)", "vignetting", "anti-aliasing filter",
@@ -1419,8 +1438,10 @@ async function runCapture(options, idealReference) {
1419
1438
  readNoiseRmsElectrons: resolved.combinedReadNoiseRmsElectrons }];
1420
1439
  }));
1421
1440
  const dngStarted = clockMs();
1422
- dng = await serializeNativeRawDng({ raw: rawOutput.raw, colorProfile, noise, ...(dngCapturedAtUtc === undefined ? {} : { capturedAtUtc: dngCapturedAtUtc }) });
1441
+ dng = await serializeNativeRawDng({ raw: rawOutput.raw, colorProfile, noise, ...(dngCapturedAtUtc === undefined ? {} : { capturedAtUtc: dngCapturedAtUtc }),
1442
+ ...(textMetadata ? { textMetadata } : {}) });
1423
1443
  encodeMs = clockMs() - dngStarted;
1444
+ assumeTextMetadata(TEXT_METADATA_LAYOUT.dng);
1424
1445
  checkAbort();
1425
1446
  assume("output.dng", "model", { writer: "serializeNativeRawDng", schemaVersion: dng.schemaVersion, whiteLevel: dng.whiteLevel, blackLevels: dng.blackLevels,
1426
1447
  noiseProfile: dng.noiseProfile, unrepresentedSaturationFlagCount: dng.unrepresentedSaturationFlagCount }, "Lossless uncompressed 16-bit CFA DNG of exactly the packed native RAW codes: per-phase BlackLevel, WhiteLevel = smallest per-phase effective white, ActiveArea and DefaultCrop from the capture geometry, ColorMatrix1/ForwardMatrix1 from the facade's diagonal camera→XYZ profile (D65), AsShotNeutral from the white-balance intent, BaselineExposure 0, and NoiseProfile from each channel's readout regime (conversion gain ÷ digital gain, combined read noise; Poisson + Gaussian + quantization only). No linear master or developed image is declared; saturation flags below WhiteLevel are not representable and are counted.");
@@ -1516,6 +1537,9 @@ async function runCapture(options, idealReference) {
1516
1537
  const jpegEncodeStarted = clockMs();
1517
1538
  await runTask(jpegTask);
1518
1539
  jpeg = jpegTask.takeOutput().bytes;
1540
+ if (textMetadata)
1541
+ jpeg = addJpegTextMetadata(jpeg, textMetadata);
1542
+ assumeTextMetadata(TEXT_METADATA_LAYOUT.jpeg);
1519
1543
  encodeMs = clockMs() - jpegEncodeStarted;
1520
1544
  assume("output.jpeg", "model", { encoder: "photivra-print-jpeg", iccProfile: "photivra-srgb" }, "Baseline JPEG from the existing print JPEG encoder with an embedded sRGB ICC profile; the source digest covers a sample prefix, length and scene digest.");
1521
1545
  }
@@ -1525,6 +1549,9 @@ async function runCapture(options, idealReference) {
1525
1549
  const pngStarted = clockMs();
1526
1550
  png = serializePngImage({ width: outputWidth, height: outputHeight, bitDepth: pngBitDepth, samples: developed.integerSamples, colourEncoding: pngColourEncoding(colorEncoding),
1527
1551
  ...(raisedPixels === undefined || outputWidth * outputHeight <= PNG_ENCODER_LIMITS.maximumPixels ? {} : { maximumPixels: raisedPixels }) });
1552
+ if (textMetadata)
1553
+ png = addPngTextMetadata(png, textMetadata);
1554
+ assumeTextMetadata(TEXT_METADATA_LAYOUT.png);
1528
1555
  encodeMs = clockMs() - pngStarted;
1529
1556
  assume("output.png", "model", { encoder: "serializePngImage", bitDepth: pngBitDepth, colourEncoding: pngColourEncoding(colorEncoding) }, "Deterministic PNG (8/16-bit RGB, in-house zlib) with the colour encoding signalled in the file: the sRGB chunk for sRGB, otherwise a cICP chunk with ITU-T H.273 primaries/transfer code points. No mastering-display (mDCV) or content-light-level (cLLI) metadata is written.");
1530
1557
  }