Commit Graph
26 Commits
Author SHA1 Message Date
Dylan M. Taylor 2c7d98b5e2 refactor(raw): separate DNG metadata and opcode parsing 2026-10-28 03:24:18 +00:00
Dylan M. Taylor d00df6319c security/raw: bound hostile-memory paths in raw decoders
Sparse walkers, truncated MEF/lossy-JPEG strips, embedded JPEG sizes, the
CR3 preview marker walk, OpcodeList3 full-image passes and caller-supplied
Deflate tile edges each let a small file or one bad argument multiply
allocations or CPU work thousands of times. Each path now is bounded,
every fix with a regression test that failed (or blew up) before it, plus
a tiff::image single_chunk helper so preview extraction never builds a
descriptor vector for a hostile multi-strip layout.
2026-10-25 15:42:19 -04:00
Brandon Thomas 51a784cd95 Merge PR #1475 (rebased)
# Conflicts:
#	crates/raw/examples/rawinfo.rs
#	crates/raw/src/lib.rs
#	crates/raw/src/vendor/arw.rs
#	crates/raw/src/vendor/erf.rs
#	crates/raw/src/vendor/mef.rs
#	crates/raw/src/vendor/nef.rs
#	crates/raw/tests/lossy_dng.rs
#	crates/raw/tests/robust.rs
2026-10-15 01:13:36 -04:00
Brandon Thomas 66a0cd826c Merge PR #1475
# Conflicts:
#	crates/merge/src/output.rs
#	crates/raw/src/vendor/nef.rs
2026-10-15 01:05: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 Thomas 141aec6ddf Merge PR #1731 2026-10-14 22:29:36 -04:00
dev-null-undefinedandClaude Opus 5.5 1e55d36e36 Perf: DNG Deflate tiles at level 1, 4.8–7× faster writes for 3–12 % more bytes
Deflate-compressed DNG tiles are written at zlib level 1 instead of 6. Measured on
Sony A7R VI files (67 MP), 4 threads, best of 3:
- Photo Merge DNG (half floats, 3 channels): 3.88 → 0.55 s, 265.7 → 274.8 MB (+3.4 %)
- raw export with Deflate (16-bit CFA): 0.89 → 0.18 s, 96.7 → 103.0 MB (+6.5 %)
- rendered DNG: 4.60 → 0.69 s, 239.0 → 267.1 MB (+11.8 %)
Lossless either way; both written files decode back bit-exactly. Lossless-JPEG
DNGs (the default for convert/import) are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 15:58:37 +02: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
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
Brandon Thomas 4fe0466942 Merge PR #1666
# Conflicts:
#	docs/whats-new.md
2026-10-14 01:39:02 -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
radialmonster 2dad18d2ec LR-RAW-NEF: stretch non-square pixels (D1X/D1H) to square
The D1X/D1H raw SubIFD is 4028x1324 with XResolution 600 / YResolution 300:
the pixel is twice as tall as it is wide. Nothing read that, so the
picture came out about 3:1. Add a pixel aspect to RawImage/RawInfo, read
it from the raw IFD resolution ratio (NEF) and from DefaultScale (DNG),
accept only 1.5..4, and resample the rows after demosaic. developed_size()
reports the stretched size, binning is off for non-square pixels, and the
catalog and lens-profile sizing use developed_size().
2026-10-13 06:01:40 -04: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
bflatastic d3c024e4df Merge PR #1047
# Conflicts:
#	crates/pipeline/src/spots.rs
#	docs/gaps.md
2026-10-13 11:03:10 +07:00
radialmonster 67dc806ca9 LR-IMP-FORMATS: DNG CFAPattern is anchored at the ActiveArea, not the sensor (odd active-area offsets rendered magenta, #1086)
The DNG CFAPattern describes the top-left of the ActiveArea, but RawImage::cfa is sensor-anchored (the crop to the active area shifts it by the area's offset). The DNG reader now moves the tag's pattern out to the sensor origin, and the DNG writer states the pattern back at the ActiveArea. A DNG whose area starts on an odd row or column (the Canon EOS 60D DNG of #1086, top 51) no longer reads RGGB as GBRG.
2026-10-11 17:59:54 -04:00
Andrew FrahnandClaude Opus 5.5 a1d7c488f8 LR-EXP-RENDERDNG: Render to DNG export format (linear float DNG of the rendered photo)
New export format `rendereddng` (export dialog: Format DNG, "DNG of: Rendered
photo"; `app.export`; MCP export tool): the rendered photo, with the edit,
crop, size, output sharpening and watermark baked in, written by the new
`lightcraft_raw::write_rendered_dng` as a LinearRaw DNG of 16-bit float Deflate
tiles in the export colour space. ColorMatrix1 (XYZ -> space), ForwardMatrix1
(space -> XYZ D50), a neutral AsShotNeutral and an identity ProfileToneCurve
make raw converters show it as rendered. HDR edits keep highlights above white.
Bad input (wrong sample count, zero or huge sizes, NaN/inf, out of half-float
range) gives an error or is clamped, never a panic.

Also fixes the Rust 1.99 clippy lint chunks_exact_to_as_chunks in
pipeline/spots.rs that fails `cargo xtask ci` on main.

Sources: Adobe DNG Specification 1.4+ (LinearRaw, ColorMatrix/ForwardMatrix,
ProfileToneCurve); own design.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 04:33:12 +11:00
Andrew FrahnandClaude Opus 5.5 cc3eb1c846 LR-EXP-DNGOPT: lossy DNG export (DNG 1.4 compression 34892)
DNG export gains a "Lossy" compression: the raw data is demosaiced to linear
camera RGB (OpcodeList3 and the default crop applied), scaled to the resize long
edge when one is set, and stored as 8-bit baseline JPEG tiles at the export
quality. The codes follow a 1/2.2 power curve that a LinearizationTable maps
back to linear 16-bit values; colour matrices, white balance and the profile
look are kept. Available in the export dialog, `app.export`
(`dngCompression: "lossy"`) and the MCP export tool.

Also fixes a new clippy lint (chunks_exact_to_as_chunks) in pipeline/spots.rs
that fails `cargo xtask ci` on Rust 1.99.

Sources: Adobe DNG Specification 1.4+ (Compression 34892, LinearizationTable);
own design for the code curve.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 02:40:10 +11:00
Exalted5373andClaude Opus 5.5 ce7b8f511e LR-IMP-FORMATS: read DNG BaselineSharpness and keep it in DNG exports
ColorData::baseline_sharpness holds the file's BaselineSharpness (raw IFD
or IFD 0; zero, negative, non-finite or above 16 is ignored) and the DNG
writer writes it back, so a conversion or smart preview keeps it. Merge
outputs leave it unset. Rendering doesn't use it yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:18:54 -07:00
Exalted5373andClaude Opus 5.5 a28dc4dbdf LR-PROF-CAMERACOLOR: read and keep DNG ProfileGainTableMap (not rendered)
Parse ProfileGainTableMap (DNG 1.6) and ProfileGainTableMap2 (DNG 1.7):
both versions, DNG 1.7 precedence (version 2 in IFD 0, then the raw IFD;
version 1 in the raw IFD, then IFD 0), u8/u16/f16/f32 gains, gamma.
Malformed tags (wrong size, zero or huge grids, non-finite or negative
gains, bad spacing or gamma) are ignored, never allocated for.

Byte order: the specification gives this tag no byte order of its own,
unlike opcode lists, so the payload is read in the file's byte order with
a big-endian fallback; a wrong-order read can't pass the size checks.
The module docs quote the DNG 1.6 "Byte Order" rule.

The map is kept in ColorData, written back by the DNG writer (version 1,
or version 2 when its gamma isn't 1) and dropped from merge outputs, whose
geometry no longer matches the capture's active area. It is not applied
to the render: Lightroom Classic renders Apple ProRAW without it.
Supersedes the parser of #273.

The CC0 iPhone 12 Pro ProRAW from raw.pixls.us joins the corpus.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:18:16 -07:00
Gremiger 855d3a7959 Issue #212: Raw: keep vendor white balance when writing DNG
NEF (and other vendor raws without a colour matrix) carry their as-shot
white balance as vendor multipliers, not as a DNG AsShotNeutral. The DNG
writer only copied AsShotNeutral/AsShotWhiteXY, so Convert to DNG and
Copy as DNG produced files with no white balance at all, which develop
dark and green. Write the reciprocal of the vendor multipliers as
AsShotNeutral when the source has no DNG white of its own.
2026-10-07 21:43:23 +00:00
Brandon ThomasandClaude Opus 5.5 71eac70696 Issue #138: DNG: apply the file's own camera-profile look (HueSatMap, LookTable, ToneCurve)
Lightroom-converted DNGs carry their camera profile's ProfileHueSatMapData1/2
and ProfileLookTableData (and sometimes ProfileToneCurve). We used only the
colour matrices, so such DNGs rendered flat and muted.

- raw: new `profile` module reads the tags (DNG spec ch. 6: value-major,
  hue-middle, saturation-minor tables, wrap-around hue, tri-linear lookups,
  sRGB value encoding) from the raw IFD or IFD 0; malformed tags are ignored.
- engine: the raw loader applies the hue/sat map (interpolated by the same
  illuminant weight as the matrices) before BaselineExposure and the look
  table after it, in linear ProPhoto; a ProfileToneCurve becomes the source's
  camera tone. Without one the standard base tone is kept.
- dngwrite keeps the look with the data (conversions, smart previews, merges).
- Render cache version 7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 10:36:30 -04:00
Brandon ThomasandClaude Opus 5.5 b42ca76777 Never crash: raw and tiff decoders free of panicking calls
- ljpeg: a scan naming an undefined Huffman table is an error, not an expect.
- ARW2/RW2 block reads use get() + try_from instead of slice + expect.
- Vendor fallback CFAs use Cfa::bayer_static (literal names, tested)
  instead of Cfa::bayer(..).expect("static").
- dngwrite matches (data, compression) instead of a let-else unreachable!.
- lightcraft-raw and lightcraft-tiff now #![deny] unwrap/expect/panic/
  unimplemented/todo/unreachable; clippy.toml (same as the steering PR)
  keeps them allowed in tests.
- tests/vendor_robust.rs: synthetic NEF, ARW, PEF, SRW, ORF, RW2, CR2 and
  RAF files, truncated at every length and randomly mutated, must decode
  to an error, never panic (no new crash found).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:00:26 -04:00
Brandon ThomasandClaude Opus 5.5 543fdde41e M13.1: lightcraft-merge crate — HDR merge, panorama stitching core
New L3 crate `crates/merge` (no UI deps), clean-room from published papers:
- features: Harris/MOPS-style oriented patch descriptors, ANMS, ratio-test matching
- ransac: normalised DLT homographies, similarity/translation, adaptive RANSAC
- hdr: brightness ordering, auto-align (model selection), robust exposure-ratio
  estimation chained to the reference, saturation-aware weighted radiance merge,
  deghost None/Low/Medium/High with feathered masks + overlay
- pano: pairwise matching, Brown–Lowe verification, focal from homographies,
  rotation+focal bundle adjustment (LM), straightening, spherical/cylindrical/
  perspective + auto, gain compensation, max-weight seams, multi-band blending
  in log space, boundary warp, auto crop, fill edges (push–pull)
- output: LinearRaw DNG carrying source colour tags; raw crate gains Deflate
  tiles (16/32-bit float with FP predictor, 16-bit int with predictor 2)

Tests: synthetic brackets (alignment < 0.3 px, EV < 0.02, radiance error
noise-limited, ghost removal) and synthetic panoramas (relative rotations
< 0.1°, focal < 1 %, error vs ground truth, seam test).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:06:00 -04:00
Brandon ThomasandClaude Opus 5.5 05450c791c M3.2-M3.6: lightcraft-raw DNG, LJ92, demosaic, highlights, DNG colour
- LJ92 (T.81 process 14) decoder + Annex K encoder: predictors 1-7,
  1-4 components, 2-16 bit, point transform, restart intervals
- DNG: uncompressed (packed 1-16 bit, 16/24/32-bit float), LJ92 tiles/
  strips, Deflate with predictors 2/3/34892-34895; CFA + LinearRaw;
  linearization, BlackLevelRepeatDim/Delta, WhiteLevel, ActiveArea,
  DefaultCrop; colour tags; OpcodeList1-3 parse/write/apply
- DNG writer (uncompressed / LJ92 tiles) for export and round trips
- demosaic: bilinear, PPG, AHD (Hirakawa-Parks), X-Trans directional
- highlight clip/reconstruct; DNG colour model with dual illuminant,
  forward matrices, as-shot neutral -> xy, Rec.2020 transform
- embedded preview extraction; malformed-input proptests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 11:33:19 -04:00