Commit Graph

22 Commits

Author SHA1 Message Date
Иван Лапочкин f96f732baa M11.1: add CR3 decoding
Decode lossless CRX and version 0x100/0x200 C-RAW in portable Rust,
including sensor geometry, timed white balance and black/white levels.
Verify all sensor samples against six independent M50/R100/R8 references.

Any CR3 decode error falls back to the embedded JPEG, as CR3 files did
before this decoder. The full sensor buffer is allocated only after the
first plane decodes, and plane buffers grow row by row. Tile planes
decode in parallel with a windowed bit reader (24 MP: 424 -> 89 ms
lossless, 519 -> 138 ms C-RAW on an M4).

Add guarded CR3 camera-JPEG colour fitting with independent framing
validation, relative white balance and cache invalidation.

Validation: cargo xtask ci.
2026-10-08 22:22:13 +03:00
Brandon Thomas 3793e8fc33 LR-LIB-PEOPLE: clip XMP face regions to the photo, drop off-photo ones, read Rotation π
A region parsed from mwg-rs:Regions is now intersected with the 0..1 frame and dropped when it
misses the photo, so a hostile or stale sidecar (x=1e5, a 1e5-wide box) can't park a box far
outside the image. Lightroom's mwg-rs:Rotation of ±π (Exif orientation 3) is mapped to the upright
frame like the quarter turns; mirrored orientations can't be expressed in MWG and are taken as
written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:12:49 -04:00
Brandon Thomas 1b22a0e53b Merge origin/main into land-170 2026-10-07 06:09:24 -04:00
Maurice Rogers 13a3eff2e8 LR-IMP-FORMATS: CR3 full-size preview and metadata from the ISO-BMFF container
CR3 files imported as preview-only with the 1620x1080 PRVW JPEG and no metadata
(no camera, capture time, exposure, lens or GPS): nothing read the container.

- crates/meta/src/cr3.rs: bounded ISO base media box parser for CR3 (Canon uuid
  box with CMT1-4 and THMB, tracks with their sample entries, CMP1 coding header
  and sample location, XMP uuid box). Every size/offset checked; depth and box
  count bounded.
- Metadata: CMT1 (IFD0), CMT2 (Exif) and CMT4 (GPS) merged into one TIFF Exif
  block, so CR3 reads like any TIFF-based raw; XMP from its uuid box.
- Preview: the full-size JPEG track (6960x4640 on an EOS R6 Mark III) before
  PRVW/THMB; imports report the full image size.
- Tests: synthetic CR3 (boxes, tracks, merged Exif), truncation/corruption sweep,
  JPEG track preferred over PRVW with fallback.

Raw (CRX) data is still not decoded; CR3 stays preview-only.
2026-10-06 20:00:48 -06:00
Daniel Voshart 9715c90413 Meta: map Lightroom's quarter-turn region Rotation to the upright frame
Lightroom Classic stores the box of a photo with Exif orientation 6 / 8 in
the sensor's own frame and records the orientation as mwg-rs:Rotation
(-pi/2 for 6, +pi/2 for 8, 0 for 1) while AppliedToDimensions names the
upright size. Ignoring Rotation put every portrait photo's face box in the
wrong place. Convert such regions into the upright frame, with a synthetic
regression test for -pi/2, +pi/2 and 0.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-06 19:42:32 -04:00
Daniel Voshart f29436a1f6 Meta: read MWG-RS face/pet/focus regions from XMP (read-only)
Adds Metadata::regions: Vec<Region>, populated by parse_xmp from mwg-rs:Regions
(MWG Region Guidelines v2.0) — the format Lightroom, digiKam and Picasa write
for face/pet/focus/barcode regions. Read-only: LightCraft does not write
regions yet (blocked on #92, which would otherwise clobber existing sidecars).

- Canonicalises the mwg-rs/stArea/stDim namespaces so any prefix a writer
  picks resolves consistently, reusing the existing generic struct/array XMP
  machinery (handles both the element and attribute encodings transparently).
- Area center-coordinates are converted to lightcraft_geom::Rect via
  Rect::from_center. Normalized units (the default, and the only unit we've
  seen in practice) need no AppliedToDimensions cross-reference; the rare
  pixel-unit region is converted when AppliedToDimensions is present, and
  dropped (not stored as a nonsense fraction) when it isn't.
- Hostile/malformed regions (missing Area, NaN/absurd/zero/negative extents)
  are skipped individually rather than failing the whole list or panicking.
- regions merges like keywords in fill_missing/overlay_user_fields, since it
  only ever comes from XMP today.

Not included: nothing yet reads Metadata::regions (no catalog field, no UI,
no command) — this is plumbing for a later PR, with no user-visible effect
and no docs/parity.md row to update.

cargo xtask ci passes (fmt, clippy -D warnings, test, parity, layers, assets,
wasm).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-06 19:42:31 -04:00
Brandon Thomas 882a059fac Issue #92: XMP: merge into an existing sidecar instead of replacing it
Saving (Cmd+S or auto-write) now merges LightCraft's properties into an
existing sidecar (lightcraft_meta::merge_xmp): owned properties (rating,
label, dc/IPTC/rights fields, lc:*) are replaced or removed, capture time
and GPS only when LightCraft has a value; everything else (crs:,
xmpMM:History, unknown namespaces, packet wrapper) is kept byte for byte.
The result is re-parsed and checked before it is written (temp + fsync +
rename). A sidecar that isn't valid XMP is copied to .xmp.bak-<time>
(never overwriting) before it is replaced; one that can't be read at all
is left alone and the save fails.

Shared stems: when several catalogued files map to one stem sidecar
(IMG.CR3 + IMG.JPG), it belongs to the raw (else the first by name) and
the others write and read <file>.xmp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 17:08:18 -04:00
storybored379 35e38661a7 Metadata: gracefully reject invalid Unicode timezone offsets
Co-Authored-By: OpenAI Codex <noreply@openai.com>
2026-10-04 21:11:55 -07:00
Brandon Thomas f7361a7d01 Merge main into feat/gpx-tracklog
# Conflicts:
#	docs/parity.md
#	docs/whats-new.md
2026-10-04 15:31:45 -04:00
storybored379 1901a756bb LRC-MAP-TRACKLOG: Auto-Tag from Tracklog — GPS from a GPX track log by capture time
GPX 1.0/1.1 reader in lightcraft-meta (timed track points per segment; interpolation only within a
segment, nearest point within maxGap otherwise). Engine command photo.autoTagTracklog (path or gpx
text, camera time-zone offset, maxGap, replace, dryRun; one undo step) and Photo ▸ Auto-Tag from
Tracklog… with a time-zone prompt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 10:52:12 -07:00
storybored379 18153682f2 LRC-LIB-METADATA: Copyright Status, Rights Usage Terms and Copyright Info URL
Catalog Meta gets copyright_status (unknown / copyrighted / publicDomain), usage_terms and
copyright_url, read and written as XMP Rights Management fields (xmpRights:Marked, UsageTerms,
WebStatement) on import, sidecars and exports (kept by "copyright only"). photo.setMeta,
copy/paste metadata, metadata presets, a Copyright Status smart-album rule and the Info panel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 08:01:00 -07:00
Brandon Thomas def15b0346 LR-META-EXIF-FULL: All Metadata — every EXIF / GPS / XMP field of the file
lightcraft-meta gains tags.rs: every tag of IFD0 and the EXIF, GPS and interoperability IFDs
as (group, name, value) rows, named per the public TIFF / EXIF / DNG specifications, with
common values spelled out (exposure as 1/250 s, f/2.8, 35 mm, orientation, metering, flash,
compression…) and blobs summarised. photo.allMetadata adds the XMP fields (sidecar, else the
file's packet). Info ▸ All Metadata… opens a searchable dialog grouped by TIFF / EXIF / GPS /
XMP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 22:20:43 -04:00
Brandon Thomas b7d497d049 LR-IMP-PROFILES: carry masks over from presets, .lrtemplate and XMP sidecars
Local corrections stored as crs: structures (MaskGroupBasedCorrections and the older
gradient / circular gradient / paint containers) become our masks: linear and radial
gradients, brushes (dab runs → strokes), Subject / Sky, luminance and depth ranges, with
add / subtract / intersect and invert, and all local sliders. lightcraft-meta's XmpData gains
`values`, a structured view of every property (arrays of structs survive); .lrtemplate Lua
tables convert into the same shape.
Applying a preset adds its masks to the photo's own (once; Amount scales them); sidecar masks
replace them and radial masks are fitted to the photo's aspect. Unsupported components
(colour range, other AI selections) are reported as "Mask: <kind>".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 20:31:40 -04:00
Brandon Thomas 096aefe698 LR-META-INFO: Info panel in Lightroom's layout with accessibility and place fields
- Metadata gains alt text, extended description, sublocation, city, state/province, country:
  catalog, photo.setMeta, copy/paste metadata, XMP read/write (Iptc4xmpCore AltText /
  ExtDescrAccessibility / Location, photoshop City / State / Country), sidecars and export
  (place fields dropped with "remove location").
- Info panel: camera card (camera, lens, size · file size, format badge, focal length,
  shutter, aperture, ISO), then title, caption, alt text, extended description, copyright,
  creator, file name (rename), file path (reveal), captured (edit), place fields, GPS.
- Text fields keep what is typed across frames and save on focus loss (they were rebound to
  the stored value every frame). New pencil icon (drawn in code); dates::display_time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 07:58:00 -04:00
Brandon Thomas 4dd57efcac Merge branch 'worktree-agent-af78b5603e6882a3f' 2026-10-01 01:06:29 -04:00
Brandon Thomas b6c9f66857 M10.8: keep lc:settings JSON readable in sidecars (escape only <, >, & in element content)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 01:04:00 -04:00
Brandon Thomas 8af27c11b1 M10.8: XMP sidecars — save/read metadata + develop settings, auto-write, crs: interchange
- crates/engine/src/sidecar.rs: <stem>.xmp (default) or <file>.xmp next to originals with rating,
  label, title, caption, copyright, creator, keywords, capture time/GPS, pick flag + full develop
  settings in lc:settings; atomic writes; read on import (sidecar wins for metadata, edits restored)
  and via photo.readMetadataFromFile (one undo step); raw/DNG embedded XMP used without a sidecar.
- Commands: photo.saveMetadataToFile (Cmd+S), photo.readMetadataFromFile, library.xmpPreferences
  {autoWrite, naming}, library.toggleAutoWriteXmp; prefs persisted in the library's prefs.json.
  Auto-write rewrites sidecars after each committed change (not mid-drag; undo/redo too).
- crates/engine/src/crs.rs: crs: develop fields (PV2012 light, WB abs/incremental, presence, HSL,
  B&W mix, parametric + point curves, colour grading, sharpening, NR, vignette, grain, optics,
  geometry, crop) → partial settings; documented in docs/xmp-interop.md.
- meta: write_xmp_lc for extra lc: properties; crs namespace canonicalised on read (never written).
- Tests: packet round trip, crs mapping with hand-written snippets, save → delete library →
  re-import restores edits, auto-write + prefs persistence, foreign sidecar, DNG-embedded XMP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 00:36:19 -04:00
Brandon Thomas daa32c3e4a Merge main into raw formats branch (nrw/raf/orf/rw2/pef/cr3 in the open dialog, nrw import)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 00:31:23 -04:00
Brandon Thomas c6954d5bd9 M10.5: export metadata policy (all / all except camera / copyright / none, remove location) with EXIF + XMP embedding
New lightcraft_meta::write_exif (IFD0 + Exif + GPS IFDs; round-trip tested).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 00:03:38 -04:00
Brandon Thomas f7b502309c M11.2: Fujifilm RAF decoding (uncompressed Bayer and X-Trans)
RAF header/record parsing, raw IFD (0xf000) with 16-bit, 12-bit LSB and
32-bit-word MSB packings found by black-box analysis of CC0 samples,
X-Trans layout (reversed record, anchored at 0,0), crop, black levels,
WB_GRB levels, metadata and preview from the embedded JPEG. Compressed
RAF reports Unsupported. Colour uses the documented neutral fallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:44:09 -04:00
Brandon Thomas cbe9d8989a M3.1: lightcraft-meta EXIF/XMP/IPTC
Metadata model; EXIF from TIFF/raw, JPEG APP1, PNG eXIf, WebP EXIF; XMP
read (namespace-canonicalised, arrays, structs) and write (dc/xmp/
photoshop/exif/exifEX/tiff/lr + lc:settings JSON); IPTC-IIM record 2;
container walkers for JPEG (incl. ICC and extended XMP), PNG, WebP.
Adds quick-xml and miniz_oxide to workspace deps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 11:08:42 -04:00
Brandon Thomas 44d97a4104 M3.1: lightcraft-tiff reader/writer
Classic + BigTIFF, both byte orders, all field types, SubIFDs and Exif/GPS/
Interop pointer IFDs, strips/tiles layout, maker-note offset-base helpers,
IFD writer; hardened against malformed input (proptests). Placeholder
manifests for lightcraft-meta and lightcraft-raw.

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