Commit Graph
139 Commits
Author SHA1 Message Date
Brandon Thomas f7204aa8dd Merge PR #1176 (rebased) 2026-10-15 00:52:31 -04:00
Brandon Thomas f613aeba59 Merge PR #1176
# Conflicts:
#	crates/raw/src/vendor/arw.rs
#	docs/parity-checklist.md
#	docs/sony-lens-corrections.md
2026-10-14 23:54:06 -04:00
Brandon ThomasandClaude Opus 5.5 851aedc64d Batch 36: tests and tracker after the merges
- Visualize HDR (#1555) has no default key: ⇧H cycles the Tool Overlay (#1473, issue #224)
- CR3 tests (#1461, #1462) accept adaptive QP 131–175, as #1694 decodes; cr3.md says the R6 Mark II files decode
- English fallbacks in the Czech and Polish catalogs use → for the menu triangle; Polish in the keyword-label test
- canvas handle tests show pins (Tool Overlay Always); a focused handle keeps its pins shown in Auto
- the model-install test and demo deadlock guards wait on deadlines long enough for a busy machine

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 23:25:29 -04:00
Brandon Thomas 60696fe7c1 Merge PR #1462 2026-10-14 22:29:12 -04:00
Brandon Thomas 0f842c8f87 Merge PR #1461
# Conflicts:
#	crates/raw/tests/robust.rs
2026-10-14 22:29:11 -04:00
Brandon Thomas d8b52d9405 Merge PR #1105
# Conflicts:
#	ROADMAP.md
#	docs/gaps.md
#	docs/parity-checklist.md
#	docs/raw-parity.md
2026-10-14 21:59:32 -04:00
Brandon Thomas aba04aaf5c Merge PR #1087
# Conflicts:
#	ROADMAP.md
#	docs/gaps.md
#	docs/parity-checklist.md
#	docs/raw-parity.md
#	docs/raw/coverage.tsv
#	docs/raw/evidence.tsv
#	xtask/src/main.rs
2026-10-14 21:59:31 -04:00
Brandon Thomas d3edca8000 Merge PR #1081
# Conflicts:
#	ROADMAP.md
#	docs/gaps.md
#	docs/parity-checklist.md
#	docs/raw-parity.md
#	docs/raw/coverage.tsv
#	xtask/src/main.rs
2026-10-14 21:59:29 -04:00
Brandon Thomas 6ab5e2b0d4 Merge PR #1108
# Conflicts:
#	ROADMAP.md
#	docs/gaps.md
#	docs/parity-checklist.md
#	docs/raw-parity.md
#	docs/raw/evidence.tsv
#	xtask/src/main.rs
2026-10-14 21:59:28 -04:00
Brandon Thomas 381b57dd0b Merge PR #1096
# Conflicts:
#	ROADMAP.md
#	docs/gaps.md
#	docs/parity-checklist.md
#	docs/raw-parity.md
#	xtask/src/main.rs
2026-10-14 21:59:10 -04:00
Brandon Thomas a19cbdb17b Merge PR #1045
# Conflicts:
#	ROADMAP.md
#	docs/gaps.md
#	docs/parity-checklist.md
#	docs/raw-parity.md
#	docs/target-app-parity.md
2026-10-14 21:59:07 -04:00
Brandon Thomas 89a51be77f Merge PR #1029
# Conflicts:
#	ROADMAP.md
#	docs/gaps.md
#	docs/parity-checklist.md
#	docs/raw-parity.md
#	docs/target-app-parity.md
2026-10-14 21:59:06 -04:00
Brandon Thomas 58dc540cc0 Merge PR #1010
# Conflicts:
#	docs/gaps.md
2026-10-14 21:57:58 -04:00
Brandon Thomas cbf3664e4f Merge PR #824
# Conflicts:
#	crates/engine/src/files.rs
#	docs/gaps.md
#	docs/parity-checklist.md
#	docs/raw-parity.md
2026-10-14 21:56:53 -04:00
Andrew FrahnandClaude Opus 5.5 e5efaea6a8 Merge upstream/main into pr/file-writes
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 19:23:00 +11:00
Andrew FrahnandClaude Opus 5.5 61117e53e1 Merge upstream/main into pr/more-verified-bodies-2
Docs conflicts re-applied on upstream's text; coverage numbers regenerated
(unchanged: 334 verified, 844 unverified, 232 preview); duplicate rows in
the raw-parity summary table removed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 19:21:03 +11:00
Andrew FrahnandClaude Opus 5.5 065818f524 Merge upstream/main into pr/more-verified-bodies
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 19:19:31 +11:00
Andrew FrahnandClaude Opus 5.5 bd2ad63b26 Merge upstream/main into pr/more-verified-bodies-3
Resolve doc conflicts in gaps, parity-checklist and raw-parity; coverage
numbers regenerated with docs/raw/coverage.py, parity summary refreshed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 19:02:15 +11:00
Andrew FrahnandClaude Opus 5.5 befd25f2a3 Merge pr/raw-erf into pr/raw-mef
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 19:00:57 +11:00
Andrew FrahnandClaude Opus 5.5 50cb797872 Merge upstream/main into pr/raw-erf
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 18:48:38 +11:00
Andrew FrahnandClaude Opus 5.5 5f1464170e Merge upstream/main into pr/more-verified-bodies-4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 18:35:33 +11:00
Andrew FrahnandClaude Opus 5.5 1181b89048 Merge upstream/main into pr/cr3-variants
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 18:12:20 +11:00
Alex ChambersandClaude Opus 5.5 8feda7a997 LR-IMP-FORMATS: the TIFF reader's next-IFD pointer no longer overflows on huge BigTIFF entry counts
Pre-existing on main. The reader computed the position of a directory's next-IFD pointer before it
checked that every declared entry had been read: a saturating multiplication of the declared count,
then an unchecked addition. A 44-byte BigTIFF declaring u64::MAX entries therefore panicked with
overflow checks on. The position is now computed only for a directory read whole, with checked
arithmetic.

32-bit note: the same guard compared the counts after narrowing the declared one to usize, so on
wasm32 a BigTIFF declaring 4,294,967,297 entries but holding one passed for a whole directory and its
next pointer was followed. The counts now compare as 64-bit numbers (reads_every_entry). On wasm32 the
old comparison followed all six malformed test chains and the new one follows none; 64-bit results
are unchanged for every directory whose count is read whole.

Regression tests (crates/tiff/src/reader.rs):
a_bigtiff_directory_declaring_u64_max_entries_parses_without_overflow, which panicked before;
a_bigtiff_count_past_u32_max_never_follows_the_next_pointer (the whole chain, on every target); and
only_a_directory_read_whole_has_a_next_pointer (the comparison itself, on every target).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 08:40:42 +02:00
Alex ChambersandClaude Opus 5.5 ac677a2624 LR-EDIT-OPTICS-PROFILE: decode Sony shading, CA and distortion tables with settings and provenance; apply nothing new
Sony ARWs record shading, lateral chromatic aberration and distortion as a camera setting plus a radial
table in the raw image IFD (0x7031-0x7037), with copies in the encrypted SR2SubIFD (0x797c-0x7982).
They are now decoded for every body that records them and carried through RawInfo and RawImage as
`lens_metadata.sony` (types in crates/raw/src/lens_metadata.rs, decoder in
crates/raw/src/vendor/arw_lens.rs):

- Raw-IFD fields are the ones the TIFF reader parsed, which is what the image itself uses. A scan of
  the directory adds diagnostics only: repeated tags, and why an entry the reader skipped was dropped
  (values outside the file, an unknown type, past the reader's limits). SR2SubIFD fields are read
  from their first entry. Only the exact layouts are accepted: SHORT[1] or non-negative SSHORT[1]
  settings, SSHORT[17] headed by 16 for shading and distortion, SSHORT[33] headed by 32 for CA.
  Nothing is allocated from a count in the file.
- The raw IFD's copy wins. The SR2SubIFD copy fills in only for a tag the raw IFD lacks, never for a
  malformed one, and the two copies' agreement is recorded.
- Settings keep their number and their documented meaning, Unknown(value) otherwise (never Off).
- Framing evidence (stored frame, Sony and default crops, reference rectangle, coding, 3:2) and a
  per-component status. Only Applied components become opcodes.

Applied corrections are unchanged: the ILCE-7RM4A distortion conversion receives exactly the table it
received before, and no other body or component gains a correction. The ILCE-7CR distortion misses its
camera JPEG at 24 mm, and shading and CA semantics are not validated on any body; see
docs/sony-lens-corrections.md.

Locating the SR2SubIFD, now done for every ARW that has one, selects the SR2Private offset, length and
key with the TIFF reader's own rules, which lightcraft-tiff now exposes and builds its reader on
(directory_entries, entry_span, ParseOptions::budget_for, decode_value). Only each field's first value
is decoded, so the directory's other entries cost no memory sized by the file: a 16 MiB file whose
SR2Private holds eight fields aliasing it peaks at 37 kB. DirectoryEntries keeps its layout private,
and its positions() iterator uses checked arithmetic.

Tests: synthetic decoder tests; a differential test of the SR2Private selection against the TIFF
reader's in both byte orders (0 to 8,193 filler entries, unreadable and unusual first occurrences of
each field, a used-up value budget); a dhat peak-heap test; public-corpus checks against the values
ExifTool prints, with pinned and anchored SHA-256s (LIGHTCRAFT_CORPUS_REQUIRED=1 makes absent files
fail); and an opt-in local inventory check (crates/raw/tests/sony_lens_inventory.rs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 08:19:21 +02:00
Brandon Thomas 4fe0466942 Merge PR #1666
# Conflicts:
#	docs/whats-new.md
2026-10-14 01:39:02 -04:00
Brandon Thomas 0a68a21e03 Merge PR #1459
# Conflicts:
#	crates/raw/tests/dng.rs
2026-10-14 01:38:59 -04:00
Brandon Thomas b54a2e9793 Merge PR #1541 2026-10-14 01:38:52 -04:00
Brandon Thomas 992e67188b Merge PR #1539
# Conflicts:
#	docs/whats-new.md
2026-10-14 01:38:51 -04:00
Brandon Thomas b4d5b47617 Merge PR #1453 (rebased onto main) 2026-10-14 01:27:32 -04:00
skybluu bcfda31678 Merge current main into DNG identity fix 2026-10-13 21:34:33 -05:00
skybluu ce607d5a49 LR-LIB-CONVERT: avoid duplicated DNG camera make (#840) 2026-10-13 21:13:51 -05:00
Andrew FrahnandClaude Opus 5.5 363a1e2a67 Merge upstream/main into pr/file-writes
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 08:08:53 +11:00
Andrew FrahnandClaude Opus 5.5 9b9676ca85 Merge upstream/main into pr/more-verified-bodies
Coverage regenerated with docs/raw/coverage.py: 354 verified, 824 unverified,
230 preview, 38 unsupported; ~86% / ~58% use-weighted.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 08:06:27 +11:00
Andrew FrahnandClaude Opus 5.5 506566d436 Merge pr/raw-erf (with upstream/main) into pr/raw-mef
Docs recounted with docs/raw/coverage.py: 241 verified, 35 unsupported. The MCP
headless extension list now comes from engine::import::EXTENSIONS upstream, which
already carries mef.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 07:56:09 +11:00
Andrew FrahnandClaude Opus 5.5 a3071fe0f3 Merge upstream/main into pr/raw-erf
Docs conflicts resolved on upstream's newer state (Leica Q3, Hasselblad Lunar);
ERF counts recomputed with docs/raw/coverage.py: 240 verified, 229 preview.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 07:55:22 +11:00
Andrew FrahnandClaude Opus 5.5 37deb70da9 Merge upstream/main into pr/more-verified-bodies-2
Conflicts in ROADMAP.md, docs/gaps.md, docs/parity-checklist.md and docs/raw-parity.md resolved by
taking upstream's text and re-applying this PR's rows; coverage re-measured with docs/raw/coverage.py
(334 verified, 844 unverified, 50% of photographers on a verified body).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 07:53:45 +11:00
Andrew FrahnandClaude Opus 5.5 853f3ddafe Merge upstream/main into pr/more-verified-bodies-3
Coverage regenerated with docs/raw/coverage.py (258 verified, 920 unverified);
doc conflicts resolved on upstream's text with this PR's numbers re-applied.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 07:53:29 +11:00
Andrew FrahnandClaude Opus 5.5 0cb32e384e Merge upstream/main into pr/more-verified-bodies-4
Coverage regenerated with docs/raw/coverage.py: 254 verified (18%), 932 unverified, 224 preview.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 07:43:46 +11:00
Andrew FrahnandClaude Opus 5.5 f2f6f1386f Merge upstream/main into pr/cr3-variants
Keeps upstream's ColorData 66 handling (white level from the data); recounts
coverage (259 verified, 919 unverified, 232 preview); drops the duplicate
raw-parity rows; notes high-QP R6 Mark II C-RAW and unverified R7 C-RAW.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 07:27:04 +11:00
Michael Anderson 853a92e2de LR-EDIT-OPTICS-PROFILE: Apply Nikon's in-file distortion correction for the Z 6II and Z f
Read the polynomial, zoom and optical center offset from NikonNEFInfo.
2026-10-13 10:47:57 -05:00
Michael Anderson 3624a50423 Never crash: Refuse raw data with two samples per pixel (#1511)
The demosaic read past each pixel. DNG decode and validation refuse it now.
2026-10-13 10:45:29 -05:00
Michael Anderson cef6f2d4b8 Never crash: Fix demosaic panic on a NaN black level
Read non-finite DNG black levels as 0 and map non-finite normalized samples to black.
2026-10-13 10:45:18 -05:00
dev-null-undefinedandClaude 8c64c467f5 LR-EDIT-OPTICS-PROFILE: Sony lateral CA planes off by default (the demosaic already hides most of it)
Verified on 54 ILCE-7RM6 photos, the CA planes made the render worse: our
demosaic already hides about 70 % of the lens CA (the render shows 0.29x
the raw-CFA CA) while the post-demosaic warp moves R and B by 0.84x the
table, so the corner R/G and B/G CA went from rms 0.032 / max 0.125 px
(off) to 0.052 / 0.219 px (on) at 1440 px; only the 16-35 mm improved.

warp_opcode takes a lateral_ca flag; lens_opcodes passes false, so a Sony
ARW gets the one-plane distortion warp. The CA conversion stays live and
tested (FF and APS-C examples, both lossless corpus files' tables). The
follow-up is to warp the CFA colour planes before demosaic. The doc,
tracker (docs/parity-checklist.md), docs/gaps.md, docs/raw-parity.md and
whats-new say distortion applied, CA parsed and off
by default, vignetting not applied; the one-plane default costs 24 -> 45 ms
in the sample stage at 1920 px (three planes: 112 ms).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 13:19:44 +02:00
dev-null-undefinedandClaude b677fbac0a LR-EDIT-OPTICS-PROFILE: Sony ARW lateral CA correction from the embedded table
vendor::arw_lens_rm6::warp_opcode (was distortion_opcode) turns the CA table
(0x7035: red half, blue half, on the distortion knots) into the R and B
planes of the WarpRectilinear: a colour's source radius is
g (1 + c(g) / 3.36e6) at the green source radius g. The unit is provisional
(borderline measurement, about +-30 % scene bias). Each colour plane goes
through the same fit gate as G, and a table with a value beyond 2e-3 relative
(about 12 px at the full-frame corner; the 54 photos' tables stay below
5.1e-4, i.e. 1724) is refused; without a CA table, or when it is refused, the
warp keeps its single G plane, and CA alone is never applied. fit_radial_warp
now rounds its gate up to the next sample, capped at 1 (a NaN gates every
sample), so the band between the last sample inside the crop and the crop
corner is checked too (RW2 passes 1.0: unchanged).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 13:09:40 +02:00
dev-null-undefinedandClaude 85cc4273be LR-EDIT-OPTICS-PROFILE: Sony ARW distortion correction from the embedded table
New vendor::arw_lens_rm6 reads the raw IFD's lens tables (0x7037 distortion,
0x7032 vignetting, 0x7035 lateral CA, their flags) and turns the distortion
table into a one-plane WarpRectilinear in OpcodeList3, with the conventions
measured on 54 ILCE-7RM6 photos: knots at i/15 of the full-frame crop half
diagonal, S(r) = r (1 + v(r)/15000), frame-filling zoom, centred on the
default crop. Only the ILCE-7RM6 is enabled; vignetting is read, not applied.
The radial least-squares fit is split out as vendor::fit_radial_warp (RW2
results unchanged); its error gate can be limited to the radii the crop
shows, so a padded active area (lossless files) doesn't refuse a fit over
pixels that are never rendered.

It sits next to main's vendor::arw_lens (#498), which holds the ILCE-7RM4A
conventions measured against Sony's exports (unit 16384, table indexed at the
output radius); the two are selected by camera model (disjoint allowlists).
arw::decode chains this module's opcodes after arw_lens::distortion, with the
same active area and the same crop (the DNG-style default crop when the file
has one, which on the ILCE-7RM6 equals Sony's crop tags), and like it only
for CFA data (not the downsized linear-RGB lossless sizes, where the knot
spacing in full-frame pixels would not hold).

The corpus test checks the conversion on the two lossless RawDB files (CC0).
The APS-C one is pinned in xtask RAW_SAMPLES with its sha256; the full-frame
one is not (at most two pins per PR, and #625 pins two Compressed ARW 6.0
files), so its case runs only where the file is present.

RENDER_CACHE_VERSION 62 -> 63.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 13:09:40 +02:00
eliets2 be592cf2b1 LR-IMP-FORMATS: CI-enforce the unsupported CRX variants, listed in docs/cr3.md
docs/cr3.md gains a variant-status table: every supported and rejected CRX
coding variant, the function that gates it and the exact Unsupported text.

crates/raw/tests/cr3_variants.rs enforces the rejections (19 tests). Each
rejected variant is rebuilt as a small synthetic CR3 (container, CMP1
descriptor, CRX stream) and the test asserts the exact string, and that
probe_info (the import path) and decode return the same error. Control
fixtures decode through the same container, including both edges of the
accepted uniform quantization range (4..=44) and adaptive QP range
(131..=168), so a rejection can only come from the documented gate. The
table itself is prose and is not checked against the tests.

The doc said only QP values 131-167 were accepted; the decoder also accepts
168 (quantization value 44, step 102), so that sentence now says so.

Tests and docs only; no decoder change.
2026-10-13 10:56:29 +03:00
radialmonster e82b5089f7 LR-RAW-DNG: DNGs written by older LightCraft keep their sensor-anchored CFAPattern reading; the writer now marks its spec-anchored files 'LightCraft DNG 2' 2026-10-13 03:44:44 -04:00
eliets2 115b19bdcb LR-IMP-FORMATS: fuzz structurally valid CR3 containers in the robustness tests
crates/raw/tests/robust.rs only mutated DNGs. Add two small valid CR3
fixtures (a 4x4 lossless CRX and a 16x16 three-level C-RAW CRX) and a
property that flips bytes in them or truncates them, then runs the existing
exercise() helper (probe, embedded_preview, decode, develop and the colour
transform) plus probe_info, the header-only description used on import:
errors or clean decodes, never a panic. Unlike random TIFF-like bytes, a
structurally valid file gets past the container walk, so the mutations land
in the CRX marker walker and entropy readers. A fixture test asserts that
both files decode as built, so the mutations hit live decode paths.

Tests only; the existing DNG property keeps its 400 cases and the new one
uses proptest's default (256, PROPTEST_CASES overrides it).
2026-10-13 10:41:11 +03:00
eliets2 f4b7175429 LR-IMP-FORMATS: property tests for the CRX decoder through the public decode path
crates/raw/tests/crx_properties.rs drives lightcraft_raw::decode with
synthetic CR3 containers built around CRX streams that the test encodes
itself: reference entropy coders for the MED, Rice, MELCODE and QP-map
layouts, and the exact algebraic inverse of one 5/3 lifting line. The
tile-halo coefficients are free parameters of that inverse, so synthesis
must reconstruct any encoded plane exactly. Tests only; no decoder change.

- Wavelet round trip: random planes decode exactly across odd plane sizes
  and multi-tile halo configurations (eight geometries, arbitrary halo
  values).
- Quantization: every uniform quant value 4..=44 and every adaptive QP value
  131..=168 dequantizes within the 14-bit range. Overshoot is clamped, and
  an in-range plane lands exactly on level * step, so the step sequence
  itself is observable. Quant 3 and 45 and QP 130 and 169 are rejected by
  decode and probe_info alike. Random QP maps, including odd map heights,
  with random per-band base and gain stay in range.
- Lossless samples outside the bit depth are Corrupt errors instead of being
  clamped; in-range planes decode exactly.
- Adversarial entropy payloads planted in well-formed lossless, C-RAW,
  version 0x200 and QP-map streams either error or decode deterministically
  and never panic. A QP Rice prefix longer than the 23-zero escape is
  Corrupt.
- Random IAD1 geometry either decodes with the active area inside the
  sensor and the crop inside the active area, or is rejected.
- Decoding gives identical results across repeated and four-way parallel
  invocations.
- Odd QP-map heights decode: the last QP row has no partner and is used
  alone, and QP values are averaged before the step lookup. Large QP jumps
  take the escape code.

The case budget is proptest's default (256 per property); set
PROPTEST_CASES to change it.
2026-10-13 10:40:45 +03:00
smposaraandClaude Opus 5.5 26df1c5cdf LR-IMP-CAMERA-COVERAGE: verify Canon EOS RP CR3 pixel for pixel
Four CC0 EOS RP samples from raw.pixls.us (lossless RAW and C-RAW, full
frame and 1.6x crop mode) now have exact full-sensor regressions against
the DNGLab 0.8.0 black-box oracle (Windows binary, checked first against
the documented M50 RAW hash). The decoder already matched; no decoder
change was needed. Crop mode is a single 4032x2656 tile, full frame two
3192-wide tiles.

docs/cr3.md also records that the white level is Canon's specular white,
which at the -1/3-stop ISOs (e.g. 11062 at ISO 5000) is well below the
sensor's saturation point.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 08:59:30 +02:00