Commit Graph
21 Commits
Author SHA1 Message Date
Brandon ThomasandClaude Opus 5.5 c7a1e3e2f6 M5.2a: lightcraft-gpu crate (wgpu compute) with the per-pixel stage on the GPU
New L3 crate `lightcraft-gpu`: a wgpu device (Metal/Vulkan/DX12; none on wasm32),
WGSL kernels compiled once, buffer/dispatch/readback helpers, and a renderer that
runs the per-pixel stage (tone map, highlights/shadows, clarity/texture/sharpen,
dehaze, local mask terms, OkLCh colour ops, grading, vignette, gamut map, sRGB
encode, curves, grain) on the device. Earlier stages still run on the CPU and are
uploaded (per-stage hybrid). Stage results are cached per view next to the CPU
StageCache (new `StageCache::extension`).

The CPU pipeline stays the oracle and shares its resolved parameters with the
kernels: `finish::FinishParams` / `mask_terms`, `pipeline::plan` (stage-cache keys),
`local::plane_sigmas` / `nr_params` / `wb_matrix_for`, exact LUTs and OkLab
matrices exported for the shader prelude. Equivalence tests (skip without an
adapter): mean |d| < 0.5 LSB, max <= 3 LSB; measured max 1 LSB on Apple M4 Pro.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 02:29:54 -04:00
Brandon ThomasandClaude Opus 5.5 179aa90f01 Merge M5 CPU performance (stage cache, full-res source level, parallel planes)
Integrated with M4 optics/Upright: render_impl resolves Upright and uses frame_for (lens + auto CA)
on the cached source; defringe runs in the cached linear stage and optics settings are part of its key.
Stage-cache equivalence test extended with optics/geometry steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 02:05:14 -04:00
Brandon ThomasandClaude Opus 5.5 37ab239b7b M0.10: headless UI rendering — CPU rasterizer for egui, ui.screenshot headless + fallback, lightcraft-cli snapshot
- ui-egui/softpaint: pure-Rust rasterizer for tessellated egui output (gamma-space premultiplied
  blending like egui-wgpu, bilinear texel-centre sampling, scissor clips, tie-break rule so shared
  edges blend once, parallel row bands, deterministic) + CPU texture mirror from TexturesDelta.
- ui-egui/headless: HeadlessView (offscreen context with our fonts/theme) and Headless (windowless
  app session: logic+ui per frame, answers viewport Screenshot/InnerSize/Close, settle loop,
  control requests through the shared handler).
- ui.screenshot {headless: true} in the running app renders from the logic tick (works while the
  window is occluded / display asleep); windowed captures fall back to headless after 2 s.
  Screenshots now wait for in-flight renders, not just queued ones.
- lightcraft-cli snapshot [--demo|--library DIR] [--script f.jsonl] -o out.png --size WxH --scale S.
- Fixes found with it: dialog backdrop was painted over the dialog itself (now an area below the
  window); dialog content padding; crop overlay options use our buttons (registered widget ids).
- Docs: control-protocol.md (headless section), AGENTS.md (prefer headless when unattended).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 01:48:48 -04:00
Brandon ThomasandClaude Opus 5.5 0892148bab M5.3: faster per-pixel stage and row-parallel guided filter
- finish: sRGB encode through an interpolated 4096-entry table (< 2e-5 from the exact curve,
  tested), exp2 instead of powf(2, x).
- guided / guided_fast: coefficient and output passes run row-parallel (were serial loops);
  Image::zip_map.
- blur_bench reports process CPU time next to wall-clock (meaningful on a loaded machine).
- RENDER_CACHE_VERSION 3 (exposure-after-filters and the table change pixels by <= 2 LSB).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 00:57:02 -04:00
Brandon ThomasandClaude Opus 5.5 42d2298118 M5.3: stage cache — slider drags redo only the stages the setting feeds; draft quality during drags
- pipeline: StageCache + render_cached: resampled source, white-balanced/retouched/denoised image
  and each spatial plane (log L, guided base, clarity, texture, dark channel + airlight) are keyed
  by exactly their inputs; keeps a draft-size and a full-size entry warm.
- Exposure moved after the spatial filters (applied per pixel; planes shifted by EV): identical up
  to rounding (test: <= 2 LSB vs. exposing the source), so exposure drags hit the cache too.
- engine: RenderJob::draft / with_stages; ui: loupe and before views own a StageCache, loupe
  renders at Draft quality while interacting.
- engine example render_bench (min-of-N wall + process CPU time).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 00:42:50 -04:00
Brandon Thomas fa77faeb6b Merge branch 'worktree-agent-a51e3311b37df5c4e' 2026-10-01 00:13:33 -04:00
Brandon ThomasandClaude Opus 5.5 424e23e6e8 M10.6: text watermark on export (Inter, size relative to image, 9 anchors, opacity, shadow)
ab_glyph (Apache-2.0, pure Rust) rasterizes the already-attributed Inter font.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 00:13:33 -04:00
Brandon ThomasandClaude Opus 5.5 1da0263d56 Merge main into persistence branch (keep JobPool renderer; wasm inline job timing via now_ms)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 00:09:07 -04:00
Brandon ThomasandClaude Opus 5.5 2d2bc2ad8f M0.11: web build (WASM)
apps/lightcraft-web runs the egui UI in the browser (eframe web, glow/WebGL2):
procedural demo library on load; in-memory imports via file picker
(Cmd+Shift+I) and drag-and-drop (MemStore + bytes-based engine hooks);
export goes through app.export and downloads in the browser; `?bench` logs
first-frame and slider latency. `cargo xtask web [--serve [port]] [--dev]`
builds with the matching wasm-bindgen CLI into <target>/web/; the web app is
part of the CI wasm check. Wasm fixes: lazy profiling Instant in pipeline,
web-time clocks in ui-egui (render timings were 0 on wasm). Docs: docs/web.md.
No persistence on web yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 00:01:18 -04:00
Brandon ThomasandClaude Opus 5.5 bcecab7d84 M1.5: thumbnail/preview cache (disk + memory LRU, prioritized job pool)
- new crate lightcraft-preview (L3): 128-bit content hashing (SipHash-1-3),
  cost-bounded Lru, DiskCache (JPEG q90 files keyed by content hash +
  settings hash + size + RENDER_CACHE_VERSION; temp+rename writes; LRU prune
  by mtime to 80% of budget; corrupt files dropped), PreviewCache (memory
  LRU over disk), JobPool (per-slot dedupe, priorities, reprioritize/drop,
  inline mode for wasm).
- engine: Session::thumb_job buckets thumbnails to 128/256/384/512 and serves
  them from the cache without decoding the source; decoded thumbnail
  sources now in a 384 MB LRU (was unbounded); library attaches
  <library>/thumbs; library.info cache stats; library.clearPreviews.
- ui-egui: Renderer runs on JobPool; thumbnails scrolled out of the
  prefetch area are dropped from the queue; on-screen thumbnails get
  re-prioritized; ui.inspect reports in-flight renders + thumb textures.
- Demo library (24 photos): grid fully drawn 4.4 s cold -> 0.21 s warm
  (24/24 disk hits, no source decoded).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:51:47 -04:00
Brandon ThomasandClaude Opus 5.5 c7be09429d M10.4: multithreaded AVIF encoding (ravif threading, speed 8): ~4x faster export
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:45:06 -04:00
Brandon ThomasandClaude Opus 5.5 e7b3724b5f M1.4: crash-safe catalog persistence (op log + snapshot)
- catalog: Store trait (FsStore: temp+fsync+rename+dir fsync; MemStore),
  Journal: CRC-checked JSON-lines op log with sequence numbers + atomic
  snapshots; torn final record dropped and truncated, stale records after a
  snapshot skipped, mid-log damage preserved as catalog.log.damaged-N.
- PushHistory op so each edit logs one history step (not the full history).
- engine: Session::open_library/persist/close_library; every top-level
  command is fsynced before returning; presets.json + view.json;
  library.info / library.compact commands.
- app: default library ~/Pictures/LightCraft Library (LIGHTCRAFT_LIBRARY or
  --library DIR override), --memory for throwaway sessions, snapshot on quit.
- tests: proptest replay(log)==state with random snapshots/reopens, every
  truncation of the last record, stale log, mid-log damage, FsStore, engine
  restart without close.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:40:40 -04:00
Brandon ThomasandClaude Opus 5.5 50307e59db M0.9: MCP server + lightcraft-cli
crates/mcp (lightcraft-mcp, L5): hand-written MCP over stdio (JSON-RPC 2.0,
protocol 2025-06-18 with 2025-03-26/2024-11-05 negotiation): initialize,
ping, tools/list, tools/call, resources/list/read. Two backends behind one
trait: Headless (in-process engine Session; answers engine.execute,
engine.commands, ui.render, app.export) and Remote (the desktop app's
loopback JSON-lines control channel; lazy connect + reconnect).

Tools: helpers (list_commands, run_command, import, query_photos,
select_photos, list_controls, get/set_develop, apply_preset, crop,
render_photo -> image content, export), UI tools when connected
(screenshot -> image content, inspect_ui, set_ui, list_widgets, click,
press_key, type_text, pointer_gesture), plus one cmd_<id> tool generated
per command in the registry (--compact omits them).

apps/lightcraft-cli: `mcp [--connect [ADDR]] [--demo] [--compact] [FILES]`,
`render <in> -o <out> [--set control=value]... [--settings] [--preset]
[--size] [--quality]`, `commands`, `controls`.

Acceptance e2e: crates/mcp/tests/e2e.rs sets exposure over MCP and renders
(headless and through the TCP transport); apps/lightcraft-cli/tests/cli.rs
does the same through the real binary's stdio. Docs: docs/mcp.md,
docs/control-protocol.md. Also rustfmt of one line in pipeline/finish.rs
(main was not fmt-clean).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:37:08 -04:00
Brandon ThomasandClaude Opus 5.5 a7ea39573b M0.7/M0.8: Lightroom-style egui UI (top bar, My Photos, photo/square grid, loupe + filmstrip, Edit panel with histogram/curves/mixer/grading, crop/masking/remove/presets/info/keywords/versions/history, dialogs, shortcuts) + control channel + desktop app; engine file loading via codecs/raw
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 22:34:03 -04:00
Brandon Thomas b0d435f223 continued work 2026-09-30 22:21:57 -04:00
Brandon ThomasandClaude Opus 5.5 1c073db30f Merge agent-codecs: lightcraft-codecs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 11:46:03 -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
Brandon ThomasandClaude Opus 5.5 304180a500 M1.1: lightcraft-codecs decode/encode, ICC, sniffing
Decoders (JPEG via zune-jpeg + jpeg-decoder for DCT scaling/CMYK, PNG,
TIFF, WebP, GIF, BMP, PSD composite, JPEG XL via jxl-oxide) produce linear
Rgb32f in source primaries with SourceSpace; ICC matrix/TRC applied exactly,
LUT/CMYK profiles through moxcms; to_working() to linear Rec.2020 D65.
Encoders: JPEG, PNG 8/16, TIFF 8/16/32f, lossless WebP, AVIF (native).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 11:30:26 -04:00
Brandon ThomasandClaude Opus 5.5 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 ThomasandClaude Opus 5.5 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
Brandon ThomasandClaude Opus 5.5 3f1ffeb690 M0.1/M0.2: workspace, xtask (layers/wasm/ci/stats), geom, color, raster
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 10:04:12 -04:00