Commit Graph
155 Commits
Author SHA1 Message Date
Brandon ThomasandClaude Opus 5.5 4a7eb6b338 LR-IMP-FORMATS: #573's centred crop and #498's DefaultCrop helper side by side (merge put the flag on the wrong function)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 03:57:57 -04:00
Brandon Thomas 4a25abdfa8 Merge PR #480 2026-10-10 03:47:10 -04:00
Brandon Thomas dd2e916842 Merge PR #555
# Conflicts:
#	crates/engine/src/media.rs
#	docs/parity.md
2026-10-10 03:47:10 -04:00
Brandon Thomas eb2a0fde05 Merge PR #560
# Conflicts:
#	crates/engine/src/media.rs
2026-10-10 03:47:10 -04:00
Brandon ThomasandClaude Opus 5.5 fb6dda44ae LR-IMP-FORMATS: merge main; reconcile camera-TIFF detection with the Samsung/Pentax rules (#281)
Merge origin/main into #480 and apply the review:
- the Samsung-with-a-Pentax-maker-note -> PEF rule now also needs raw evidence; its test fixture
  gets a CFA photometric, and the same RGB file without it is not a raw;
- for the Pentax family the Exif CFAPattern (0xa302) counts as raw evidence, as main's PEF reader
  takes its layout from it (uncompressed / PackBits PEFs);
- Samsung's private raw compressions 32769..=32773 count for SRW (NX1 / NX500 use 32773);
- the engine fallback in files.rs is dropped (main's version kept): a corrupt raw without a
  preview stays an import error; the engine regression test for camera-authored TIFFs is kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 03:46:23 -04:00
Brandon ThomasandClaude Opus 5.5 7987783a57 LR-IMP-FORMATS: merge main; record the selection-value-8 rule's provenance
Merge origin/main into #555. The decode_hasselblad doc comment records how the coding rule was
established (first hypothesis recalled by the AI author from training data that includes other
converters, corrected for byte order and confirmed by a black-box search over 17,280 layout
hypotheses on the CC0 files; the code is original). The LR-IMP-FORMATS row on main's text now
lists the decoded FFF and lossless-JPEG 3FR files instead of preview-only. RENDER_CACHE_VERSION
37 -> 38.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 03:39:41 -04:00
Brandon Thomas cf7b55ace8 Merge PR #602
# Conflicts:
#	crates/engine/src/media.rs
2026-10-10 03:38:38 -04:00
Brandon ThomasandClaude Opus 5.5 ec39ca1ad6 LR-EDIT-LIGHT-HIGHLIGHTS: merge main; binned previews' clip mask on top of #607's reconstruction (#548)
Main's reconstruction (#607) replaces this branch's rewrite of highlight::reconstruct. Kept from the
branch: develop_binned_masked (clipping decided per sample before averaging) and the denoised twin
using the same mask. New: highlight::reconstruct_masked, #607's reconstruction with the clipped
channels told by the mask (block means as lower bounds), shared with reconstruct_with through a
generic clip test (no slowdown on full-size images), and CameraColourModel::rebuild_highlights_masked
so binned previews keep the colour model's clipped white. RENDER_CACHE_VERSION 38.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 03:37:51 -04:00
Brandon ThomasandClaude Opus 5.5 9f73f086c9 M11.4: merge main; spectral matrices only replace the neutral fallback
Merge origin/main into #560 (fit_preview / camera-look refactors of #528, #583, #568, #616).
`starting_colour` now tries the per-file camera-JPEG fit (with the camera profile when there is
one) first and uses the spectral-sensitivity matrices only when that fit fails, before the neutral
fallback, so no photo with an accepted JPEG fit changes. Tests pin the order through an injectable
fit; docs, parity, ROADMAP and what's new describe it. RENDER_CACHE_VERSION 37 -> 38.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 03:27:47 -04:00
Brandon Thomas 4539014a42 Merge PR #498
# Conflicts:
#	crates/raw/src/vendor/arw.rs
2026-10-10 03:16:18 -04:00
Brandon ThomasandClaude Opus 5.5 7962c78bc5 LR-EDIT-OPTICS-PROFILE: merge main; keep main's Sony crop precedence
Main (#575, #580) rewrote default_crop. The image crop now keeps main's
order for every Sony body (Sony's crop tags, then DefaultCrop, then the
recorded-size crop), so framing does not change. Only the ILCE-7RM4A
distortion warp takes its centre and normalization from DefaultCrop when
the file has one, which keeps the geometry validated against Sony's
exports (centre x 4783.5 on the A7R IVA sample). The DNG round-trip test
now checks both: Sony's crop on the image, the warp centred on the
default crop. RENDER_CACHE_VERSION 38.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 03:15:16 -04:00
Brandon ThomasandClaude Opus 5.5 20d7d9e981 LR-IMP-FORMATS: merge main into the DSLR-A900 packed 12-bit ARW decode
Main's SR2SubIFD white balance (#575) already reads the A900's applied
levels (2688 / 1024 / 1024 / 1516, the same 2.625 / 1.480 gains), so the
hard-coded maker-note 0x0020 block at offset 188 is dropped. The centred
crop now goes through #580's recorded_size_crop (a `centred` flag, only
for the packed 12-bit layout); every other body keeps main's framing.
Adds the CC0 A900 sample to the corpus with checks for its white balance,
black level, bit depth and crop. RENDER_CACHE_VERSION 38.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 02:53:10 -04:00
Alex ChambersandClaude Opus 5.5 aa6dfe3b30 LR-EDIT-LIGHT-HIGHLIGHTS: pale partly clipped skies turn white, not teal (#523)
Testing the first version on ILCE-7RM4 photos found a partly clipped sky between
bare branches leaning cyan to teal under #523's edit, where Lightroom shows white.
Where only green clipped, red and blue say such a sky is a pale cyan-blue; the
rebuild kept that colour until halfway to full clipping, and darkening and a camera
profile showed it. Colour borrowed from another surface (bright foliage below the
sky) could also push green up, with a small step where green first clips.

- highlight::reconstruct_with: the colour of a pale partly clipped pixel
  (saturation relative to the colour model's white below 0.2, easing off by 0.4)
  fades to the clipped neutral from the first clip on instead of from halfway;
  brightness and strongly coloured pixels keep the old schedule.
- Borrowed colour counts less and less as both the pixel's own channels and the
  rim's colour contradict it (0.15 to 0.4 stop); either alone can be wrong.
- Right past the rim every estimate continues the rim's colour (it used to hold
  the clipped channels at their clip, for the neutral estimate only), so the first
  clip has no step whatever lent the estimate. The rim is built whenever there is
  clipping.
- Nothing jumps where a later channel clips: in the rim distance and the
  contradiction, a clipped channel counts as the lower bound it is, and a channel
  goes over to that from 0.75 of its clip; a channel about to clip is a floor
  capped like the clip floor it becomes.
- Tests: a pale sky above yellow-green foliage with thin dark branches in it (no
  step where green clips, no green from the foliage, the same beside the branches,
  neutral well past the clip); the fringe test compares the sky beside the branch
  with the sky away from it, on its way to neutral; a band whose red clips second
  next to a rim of another colour renders without a step, in six layouts.
- docs/parity.md: the Highlights note. Render cache version 28.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 02:02:00 -04:00
Brandon Thomas d267a7acd5 Merge PR #640
# Conflicts:
#	crates/engine/src/media.rs
#	docs/parity.md
2026-10-10 01:14:26 -04:00
Brandon Thomas 37e95dd2fa Merge PR #651
# Conflicts:
#	crates/engine/src/media.rs
#	crates/raw/src/lib.rs
#	crates/raw/src/vendor/orf.rs
#	docs/parity.md
2026-10-10 01:14:25 -04:00
Brandon Thomas f35663590f Merge PR #633
# Conflicts:
#	crates/engine/src/media.rs
#	crates/raw/src/vendor/nef.rs
#	docs/parity.md
2026-10-10 01:14:24 -04:00
Brandon ThomasandClaude Opus 5.5 e7da679cb3 LR-IMP-FORMATS: word16_order reads sample pairs with as_chunks instead of indexing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 01:10:36 -04:00
Brandon ThomasandClaude Opus 5.5 82566817e6 LR-IMP-FORMATS: drop references to analysis files that aren't in the repo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 01:08:10 -04:00
Brandon ThomasandClaude Opus 5.5 4a3f9e9b6d Apply #580's updated head (drops the DSC-R1 SR2 refusal)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 01:02:09 -04:00
radialmonster d138236291 LR-IMP-FORMATS: Sony DSC-R1 .SR2 decodes (big-endian 16-bit words in a little-endian TIFF)
Uncompressed 16-bit words are read in the other byte order only when the
file's order leaves more than 1% of sampled words at or above
1 << max(bits, 14) and the other order leaves none. Measured on the CC0
DSC-R1 (raw.pixls.us 3221). Crop and white balance (SR2SubIFD) are not
addressed. RENDER_CACHE_VERSION 31 (adjust at merge).
2026-10-09 22:01:36 -04:00
radialmonster 44b126274d LR-IMP-FORMATS: decode 12.8 bpp block-packed ORF (E-300, E-500, E-330, high-res E-M5 II / PEN-F)
Single-strip ORFs at 12.8 bits per pixel are 16-byte blocks: ten little-endian
12-bit samples in bytes 0..14 plus a zero pad byte. Main routed them through the
XZ-2 12-bit word path (stripes, magenta cast). New branch before it; the exactly
12.0 bpp XZ-2 branch is unchanged. Layout measured on CC0 files (log and scripts in
data/testing/retry-olympus-e300 and tools/retry-olympus-e300).

RENDER_CACHE_VERSION bumped to 30 (open PRs use up to 29; adjust at merge).
2026-10-09 21:42:15 -04:00
Brandon Thomas 15850a55b2 Merge PR #580
# Conflicts:
#	crates/engine/src/media.rs
#	crates/raw/src/vendor/arw.rs
#	crates/raw/tests/corpus.rs
#	docs/parity.md
#	xtask/src/main.rs
2026-10-09 20:53:21 -04:00
Brandon Thomas 4a8241dde4 Merge PR #575
# Conflicts:
#	crates/engine/src/media.rs
#	crates/raw/tests/corpus.rs
#	docs/parity.md
#	xtask/src/main.rs
2026-10-09 20:51:50 -04:00
Brandon Thomas 2c5121ae3b Merge PR #568
# Conflicts:
#	crates/engine/src/camera_preview.rs
#	crates/engine/src/media.rs
#	docs/camera-preview-colour.md
#	docs/parity.md
2026-10-09 20:51:08 -04:00
Brandon Thomas 6bf2722209 Merge PR #607
# Conflicts:
#	crates/engine/src/media.rs
#	xtask/src/main.rs
2026-10-09 20:50:23 -04:00
Brandon Thomas 41cdec5daf Merge PR #586
# Conflicts:
#	crates/engine/src/media.rs
#	docs/parity.md
2026-10-09 20:50:08 -04:00
Brandon ThomasandClaude Opus 5.5 010d9bb5da LR-IMP-FORMATS: saturation-point search refuses NaN, negative or inverted levels and overflowing active areas (underflowed on a negative black level)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:43:04 -04:00
Brandon Thomas bd32d82a1f Merge PR #524
# Conflicts:
#	docs/parity.md
2026-10-09 20:36:07 -04:00
Brandon Thomas f41a6357eb Merge PR #600
# Conflicts:
#	crates/engine/src/media.rs
2026-10-09 20:36:07 -04:00
Brandon Thomas a1993c7093 Merge PR #615
# Conflicts:
#	docs/parity.md
2026-10-09 20:35:30 -04:00
Brandon Thomas edb3f29351 Merge PR #603
# Conflicts:
#	docs/parity.md
2026-10-09 20:35:29 -04:00
Brandon Thomas 1d3b69fe63 Merge PR #513 2026-10-09 20:34:54 -04:00
Brandon Thomas 89dc78b3bf Merge PR #508 2026-10-09 20:34:54 -04:00
Exalted5373andClaude Opus 5.5 b5440f167f LR-IMP-CAMERA-COVERAGE: CR2 black level from the masked columns only
Canon's SensorInfo left border is not where light starts: on every corpus
body 10-36 columns left of it already hold image (EOS 6D: masked columns
0-70, border 84), and the black level was averaged over all of them. The
6D read 2591 instead of 2047 (4 % of the range), so CR2s rendered dark with
crushed shadows; 40D +141, 5DS R +131, 5D Mark II +73. The masked run is
now measured from the data: columns whose mean stays within 1/256 of the
range of the leftmost columns', minus a two-column margin.

Against Lightroom Classic (EOS 6D still life, 2000 px, ΔE00): base 7.63 ->
5.23 (p95 24.7 -> 11.3); mean over 86 edit variants abs 9.13 -> 6.19,
effect 3.09 -> 2.75.
RENDER_CACHE_VERSION 23.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:04:31 -07:00
Alex ChambersandClaude Opus 5.5 9abf0d4210 LR-EDIT-LIGHT-HIGHLIGHTS: clipped highlights stay neutral when darkened (#523)
Sensor-clipped pixels are rebuilt at raw decode. The tone map rolls them off to
white at Neutral, so any colour they carry shows only when Highlights, a negative
Exposure or Whites darkens them. They took colour from the nearest unclipped pixel
(dark foliage, lens fringes along branches, pixels demosaiced from clipped samples),
left two clipped channels at their white-balance-scaled clip levels (red or pink),
and were neutral only in camera RGB, not after the colour model (a look fitted to
the camera's JPEG, a camera profile).

- highlight::reconstruct_with: chromaticity only from bright unclipped content away
  from edges and from the clip, through a normalised push-pull pyramid; a neutral
  estimate blended in where reliable content is scarce at every scale up to a
  quarter of the long edge, applied only as far as the pixel lies past the nearest
  near-clip unclipped pixels (so a surface still runs into clipping without a
  step); clipped pairs keep the estimate's ratio at their clip floors; partly
  clipped pixels fade into the clipped neutral on their way from the first channel
  clipping to the last; fully clipped pixels become the colour model's neutral.
  Pixels without a clipped channel are left exactly as they were.
- engine: the decode colour step as CameraColourModel, with clipped_white, which
  keeps the camera's neutral for nearly singular or implausible models.
- Tests in raw (continuity along a ramp through every clip, also as lines 1 to 16
  rows thick with no reliable colour, on canvases 32 to 1024 rows), engine
  (pipeline renders at Highlights -100, #523's edit and Exposure -2 through the
  first clip, over the same sweep; a CC0 ILCE-7RM4 corpus file added to
  RAW_SAMPLES) and the GPU equivalence suite.
- Render cache version 27.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:39:09 +02:00
Claude cf9d8f7ebb M11: name Nikon High Efficiency NEFs instead of blaming a missing table (#193)
High Efficiency NEFs (HE / HE★, Z 8, Z 9, Z 6III, Z f) keep compression
34713 but carry a JPEG XS-based codestream (SOC + CAP, FF10 FF50) and no
maker-note 0x0096 table. They fell through to "Nikon compressed NEF
without a linearization table", so the Preview only notice gave Z f users
no hint why their raw wasn't decoded or that Lossless compressed NEFs work.

Detect the codestream before asking for the table and report "Nikon High
Efficiency NEF" with the workaround. Add a synthetic regression test, CC0
Z f HE + Lossless samples to the corpus (raw.pixls.us) with a corpus test,
and list HE NEF among the format gaps in parity.md and ROADMAP.md.

No decoder: the format has no public specification and the existing
decoders (LibRaw, rawler) are LGPL and derived from Nikon's binaries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011bbmJTxYJyYqKfNH8uXRC6
2026-10-09 16:21:11 -06:00
radialmonster ac93118911 LR-EDIT-LIGHT-HIGHLIGHTS: partly clipped highlights take the colour of bright neighbours, binned previews clip before averaging (#548)
Highlight reconstruction rebuilds clipped channels from the chromaticity of
unclipped pixels nearby. Three changes to that colour:
- an unclipped pixel counts by its brightness ((m / 0.8 clip)^4, at most 1),
  filled coarse to fine by push-pull, so dark wires across a bright cloud no
  longer tint it (the iPhone frame of #548 turned cyan at Highlights -100);
- pixels within 6 px of clipping (partly demosaiced from clipped samples, 25 %
  greener than the sky on that frame) give no colour unless nothing else does;
- the rebuilt colour is drawn toward neutral at its brightest channel as more
  channels clip ((n - 1 + s) / 2, s rising as the next channel nears its clip).

Binned previews keep each channel the mean of its block and mark blocks with a
clipped sample in a mask (develop_binned_masked, reconstruct_masked), instead
of taking the block maximum: one clipped specular sample no longer becomes a
green speckle at 500 px (HUAWEI PRA-LX1, WAS-LX1A). The denoised twin uses the
same mask. Camera-colour fitting keeps the old binning.

RENDER_CACHE_VERSION 26 (adjust at merge).
2026-10-09 18:13:20 -04:00
radialmonster 4ece03a5df LR-IMP-FORMATS: decode Nikon "lossy after split" NEF
Rows below the split row use the existing lossy Huffman path; from the split
row on every pixel is a (bits - 4)-bit word: a complete prefix code for the
difference category followed by the top bits of the T.81 extra bits, dequantised
to the centre of the kept bin, with the usual two-left predictor and an
unclamped running value. The rule was derived by black-box analysis of the 11
CC0 samples (all decode to the same samples as the analysis decoder).

The decoder checks that the fixed-rate section ends within the last byte of the
strip (whole rows plus 0..7 padding bits) and otherwise keeps refusing the file
(preview only), so a file that does not follow the rule is never decoded wrongly.

RENDER_CACHE_VERSION 28 (main is 22, open PRs use 23-27): adjust at merge.
2026-10-09 17:48:15 -04:00
radialmonster 37d3621505 LR-IMP-FORMATS: read the two-field 12-bit ORF layout of old Olympus compacts
C5060WZ, C7070WZ, SP-510UZ, SP-550UZ, SP-565UZ and SP-570UZ files say
uncompressed 12-bit with many strips whose byte counts sum to exactly
width x height x 1.5. main refused them because the 12-bit branch wanted a
single strip, so they fell to "Olympus compressed ORF" and opened
preview-only. The samples are packed MSB-first, the strips hold two fields
(even output rows, then odd ones) with one gap at the seam, and the last row
of the second field sits right after the last strip. Accepted only when all of
that holds exactly. CFA comes from the Exif tag, white balance from maker-note
entries 0x1017/0x1018, black 0. RENDER_CACHE_VERSION 27 (adjust at merge).
2026-10-09 17:23:50 -04:00
radialmonster b4c4a436cb LR-IMP-FORMATS: treat DNG samples piled up below the stated white level as clipped (#548)
A DNG can state a white level the sensor never reaches (an iPhone 15 Pro Max Bayer DNG states 4095 but clips at 3567), so clipped highlights were not recognised and white balance tinted them magenta. On decode, samples that form a plateau clearly below the white level, with nothing above it, are raised to the white level. RENDER_CACHE_VERSION 25 (needs adjusting at merge).
2026-10-09 15:54:18 -04:00
Alex ChambersandClaude Opus 5.5 f1fa336ab1 LR-IMP-CAMERA-COVERAGE: Sony 16:9 crop on pre-crop-tag bodies, refuse the encrypted DSC-R1 SR2
Part of #535 (items 4 and 5).

- Pre-2017 bodies without Sony crop tags shot in the camera's 16:9 mode now get
  the 16:9 crop of their camera JPEG: width from FullImageSize, anchored left as
  before (drops the ILCE-7SM2's 32 padding columns); height from FullImageSize
  (ILCE-7SM2) or from an Exif image size of the same width (DSLR-A580, whose
  FullImageSize still says 3:2); centred vertically at an even offset, as
  measured by registering each camera JPEG on its raw. The aspect now matches
  the JPEG, so the camera-look fit runs: ILCE-7SM2 dE 15.2 -> 4.8,
  DSLR-A580 dE 26.7 -> 3.9 (vs main 88ea1a8).
- The DSC-R1's SR2 (maker-note FileFormat 1 0 0 0) stores encrypted samples and
  decoded to noise; it is now Unsupported in full and header mode and imports
  preview-only with that reason.
- Only these 3 of 140 Sony decodes change; render cache version 26 (23-25 are
  taken by #568, #542, #575).
- Tests: crop helper (3:2 unchanged, both 16:9 sources, even offsets, hostile
  sizes), synthetic Sony file with FileFormat SR2 vs ARW, corpus test of the
  two crops and the SR2 refusal; three samples added to the corpus list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:10:16 +02:00
Alex ChambersandClaude Opus 5.5 179910c020 LR-IMP-CAMERA-COVERAGE: Sony ARW white balance and black level from one SR2SubIFD decryptor
Part of #535 (item 1). Stacked on #542 (SR2 black levels); supersedes its
positional keystream tables.

- White balance is WB_RGGBLevels (0x7313), the gains the camera applied:
  the raw IFD's plain copy when present, else the copy in the encrypted
  SR2SubIFD, which every ARW from the DSLR-A200 on carries (identical to
  the plain one on all 57 CC0 files that have both). The maker note's
  Tag2010 gains stay only as a last resort.
- 14 bodies had no white balance and rendered green whenever the camera
  look was rejected (A450 ... A700, NEX-3/5/C3, SLT-A33/A35/A55,
  ILCE-6001, UMC-R10C). On others Tag2010 missed the applied gains by up
  to 100 % (ILCE-3500 at 5600 K: R -31 / B +101 % against the camera
  JPEG's neutral pixels, now +4 / +0 %; ILCE-7S on Shade -9 / +15 ->
  -1 / +1 %; SLT-A57 -25 / +26 -> -6 / -3 %).
- New vendor/sr2.rs decrypts the whole SR2SubIFD, found black-box:
  exiftool -v5's decrypted dump as known plaintext gives the keystream
  (key 11 22 33 44, the same at every position in 126 CC0 ARWs from 97
  bodies, 15 layouts); an exhaustive search over lag pairs found
  k[n] = k[n-63] ^ k[n-127] on 32-bit words, so the first 127 words
  generate the rest, matching all 126 files (5.2 MB of keystream).
- The SR2 black level (0x7310) is now read through the same decryptor,
  replacing #542's head and per-position keystream tables: one SR2
  decryption path. Identical results on all 131 Sony files (every
  value #542 decoded; the 51 it could not are the files' plain 0x7310).
  Unit tests pin the keystream against answers recorded without the
  generator: every byte #542 and #148 had recovered, the SHA-256 of the
  whole 62112-byte keystream recovered from the CC0 DSLR-A700 sample, and
  the encrypted WB_RGGBLevels of four CC0 files (one per layout) with the
  levels ExifTool decrypts.
- Sweep (4f350c4 -> this): gains equal ExifTool's decrypted
  WB_RGGBLevels on all 100 decoded CC0 CFA files; on 83 files with
  >= 200 JPEG-neutral pixels, within 10 % of the camera's applied gains
  71 -> 83, over 10 % 5 -> 0. Accepted fits unchanged (max 0.15 dE);
  forced fallback median dE 18.50 -> 18.06, neutral-area cast on the 22
  changed files 8.8 -> 1.2.
- Corpus test for five CC0 samples (A500, A33, A700, ILCE-3500 5600 K,
  ILCE-7S Shade; three added to RAW_SAMPLES); the pre-2017 test's gains
  move to WB_RGGBLevels (RX100 III, RX100, ILCE-7RM2: within 2.6 % of
  the old Tag2010 values in Auto). Synthetic tests for decryption,
  directory parsing, source precedence, black level through the full
  DNGPrivateData chain, and hostile input; #542's layout tests (incl.
  the A700 trap) now run through the decryptor.
- Render cache version 25.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:58:44 +02:00
Alex ChambersandClaude Opus 5.5 3100cc1a5c LR-IMP-CAMERA-COVERAGE: Sony black levels from the SR2 layout and the lossless M/S pedestal
Part of #535 (items 2 and 3).

- SR2SubIFD black level: decrypt the directory's first entry (always the
  black level, 0x7310) to find where its value sits, and decrypt it with the
  keystream known for that position (1638, 2166, 2418, 2510). The fixed read at
  2510 gave 354-365 on the DSLR-A450/A500/A550 and 975 on the DSLR-A700, where
  ExifTool and the data floor say 512 (A700 render: dE 20.1 -> 8.8,
  dL* -9.8 -> 0.0). Keystreams recovered black-box (plain 0x7310 and exiftool -v3
  as known plaintext), consistent over 115 ARWs from 97 bodies, 15 layouts.
- Downsized lossless M/S ARWs (linear YCbCr): black 1024 and white 16895, i.e.
  the recorded 512 plus 512, measured against same-scene full-size codings on
  12 files from 4 bodies (offset 1015-1052, scale 0.98-1.02). The 512 pedestal
  lifted shadows by ~3 % of white; M/S renders now match their full-size twins
  (7RM5 M dE 6.7 -> 3.1).
- Only these 16 of 140 Sony files change; render cache version 24.
- Tests: synthetic SR2 blocks for every known layout (incl. the A700 trap),
  YCbCr black/white; corpus test of black levels against the data floor;
  A500 and A700 samples added to the corpus list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:55:03 +02:00
Alex ChambersandClaude Opus 5.5 f48122992b LR-IMP-CAMERA-COVERAGE: ILCE-7CR test pins the black level of Bayer codings only
The YCbCr M coding's pedestal is corrected separately (#535, item 3), so
this test no longer depends on which change lands first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:50:32 +02:00
Alex ChambersandClaude Opus 5.5 ea080e58bd LR-IMP-CAMERA-COVERAGE: Sony ILCE-7CR samples in the corpus
Three CC0 raw.pixls.us files of one ILCE-7CR (61 MP) scene: lossless
compressed L and M (subsampled YCbCr) and compressed ARW2. A raw corpus
test pins model, colour-filter layout, black level, white level, as-shot
gains and default crop, and checks that the lossless and compressed
mosaics agree inside the crop (green block correlation 0.9976). An engine
corpus test checks each gets a fitted camera look, not the neutral
fallback. All nine public codings decode and fit (mean dE76 1.59 against
their camera JPEGs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:50:32 +02:00
Alex ChambersandClaude Opus 5.5 5a61fc5f67 LR-PROF-CAMERACOLOR: Sony ARWs shot with DRO start without the camera JPEG's lifted shadows (#244)
Sony's Dynamic Range Optimizer (factory default Auto) brightens the darker
regions of the camera JPEG, not the raw. The tone curve fitted to that JPEG
carried the brightening into the raw's starting look: on 8 ILCE-7CR photos
with DRO on, LightCraft rendered +6.4 L* from Lightroom's default (median
dE76 7.5), against -0.2 L* (3.6) for the 16 with DRO off.

- lightcraft_raw::embedded_preview_dynamic_range_optimized reads Sony
  maker-note tag 0xb025 (every documented value: 0 off; 1-3, 8-12 and
  16-23, i.e. up to Lv8, on). It was present on all 264 Sony raws checked
  (98 bodies); 0xb04f only says on/off and is missing on older bodies.
- With DRO on, the look keeps the colour fitted to the JPEG and its tone
  curve is lowered, knot by knot, to Sony's curve without DRO wherever that
  is darker (assets/camera-tone/sony-dro-off.json, pooled from 12 CC0
  raw.pixls.us ARWs of 10 interchangeable-lens bodies; sources and
  derivation in the file and assets/ATTRIBUTION.md). The second-last knot
  is lowered only as far as keeps the shoulder past the last knot at or
  below the fitted curve, so the result is never brighter anywhere.
  Compacts that map raw values darker keep their own curve.
- DRO off, unknown and other makers: unchanged (byte-identical renders on
  71 + 35 photos). Render cache version 23.

DRO-on photos now land at +0.8 L* from Lightroom (median dE76 3.3), band by
band like the DRO-off ones. Docs: camera-preview-colour.md, parity row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 20:09:16 +02:00
radialmonster a33570ca73 Merge main into sony-a900-packed 2026-10-09 13:57:54 -04:00
radialmonster 5ed59252e8 LR-IMP-FORMATS: decode packed 12-bit Sony ARW (DSLR-A900) from its exact 1.5 bytes per pixel strip
Compression 32767 with BitsPerSample 12 and one strip of exactly width x height x 1.5 bytes is plain packed 12-bit data: two pixels per three bytes, least-significant-bit first (measured on the DSLR-A900 sample by same-colour neighbour smoothness, 12x smoother than the other orders). RGGB per the file's pattern tag (checked against the camera JPEG), black 128, crop = FullImageSize centred in the frame, white balance from the plain maker-note block. Any other strip size stays Unsupported. RENDER_CACHE_VERSION 22 -> 24 (adjust at merge).
2026-10-09 13:57:06 -04:00
ROSSAandClaude Opus 5.5 82ff68d4f3 M11.4: raws from 52 camera models start from matrices fitted to measured spectral sensitivities
A raw without colour matrices of its own now gets DNG ColorMatrix1/2 and
ForwardMatrix1/2 for its model when the model is one of the 52 cameras
in rawtoaces-data (Academy Software Foundation, Apache-2.0): 22 Canon,
11 Nikon, 11 Sony, 3 Fujifilm and 5 others, plus the other names its
alias list gives (Rebel T3i and Kiss X5 for the EOS 600D; bodies it
lists as sharing a sensor). tools/spectral_camera_table.py fits, under
Standard Illuminant A and D65, a white-preserving 3x3 to the measured
spectral sensitivities over rawtoaces-data's 190 training reflectances
and writes crates/raw/src/spectral_table.rs. No number comes from Adobe
or from a raw decoder. lightcraft_raw::spectral looks a camera up by
Exif make and model; the DNG colour model does the rest.

camera_preview::starting_colour takes the first available of: the
file's own matrices, a camera profile for the model (fitted to the
JPEG for tone), the spectral matrices, the file-local JPEG fit, the
neutral fallback. A profiled model whose profile fit fails takes its
spectral matrices. White balance stays relative to the as-shot look for
every raw without matrices of its own, so Photo::relative_wb and the
controls are unchanged; calibrate still pools covered models.

Tests: one per precedence step (synthetic), the table's lookup, aliases
and DNG shape, and a corpus test of the order on ILCE-7M4 (profile),
EOS 5D Mark III and D5100 (spectral), EOS 7D (own fit) and a DNG. The
ILCE-7RM2 and GFX100S corpus tests now expect the spectral matrices.
Attribution in assets/ATTRIBUTION.md and NOTICE. RENDER_CACHE_VERSION 23.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:56:06 -05:00
Elliot Graham 92de4c1bbc LR-EDIT-OPTICS-PROFILE: merge main and resolve validation conflicts 2026-10-09 17:37:17 +01:00