* CMYK documents display and export through their embedded CMYK profile
A thread-local CmykSpace (photocraft_color::convert::with_cmyk_space) replaces the built-in
coated CMYK while the CPU compositor, the GPU compositor's uploads and the PSD merged
composite convert a document's CMYK pixels; GPU residents track the profile they were
converted with.
Refs #46
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* EXR/HDR are linear: tag imports linear sRGB, linearise exports
Refs #46
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Colour-managed canvas: document profile -> monitor profile on GPU and CPU paths
* engine display_color: CanvasDisplay per (document profile, mode, monitor), identity fast
path when they match (sRGB on sRGB costs nothing); linear composites are stored
sRGB-encoded in the 8-bit texture. canvas_lut now always carries the CM transform, with
Proof Colors / Gamut Warning / 32-bit preview on top.
* GPU canvas applies the display LUT whenever the transform isn't the identity (also for
filter/proxy previews); the CPU canvas and Navigator transform the 8-bit composite.
* Edit > Color Settings > Monitor Profile (auto|built-in|.icc). macOS 'auto' reads the main
display's ICC profile at launch via osascript/AppKit (no unsafe); elsewhere sRGB.
* Tests: P3 (hand-computed), Adobe RGB, ProPhoto, Gray Gamma 2.2, synthetic CMYK, linear,
monitor setting; GPU+CPU canvas test via kittest (skips without Rgba32Float).
Fixes#46
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* io: tag EXR/HDR linear in import_flat (adapt to the shared image_to_document)
#65 moved flat decoding into image_to_document(name, img) so camera raws can reuse it; the
linear-EXR tagging needs the file bytes for format detection, so it now runs in import_flat.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* raw: clean-room camera raw decoder and developer (photocraft-raw)
A new standalone crate (no workspace deps, wasm-safe, no unsafe) written
from TIFF 6.0, TIFF/EP, the DNG 1.7 spec, ITU-T T.81 Annex H and published
demosaicing papers:
- Lossless JPEG (LJ92) decoder: predictors 1-7, point transform, restart
intervals, combined Huffman+extra-bits fast table.
- DNG: uncompressed (8-16 bit, packed) and LJ92, strips and tiles, CFA and
LinearRaw, linearization, black level (repeat, deltas), white level,
active area, default crop, ColorMatrix/CameraCalibration/ForwardMatrix
interpolation by CCT, AnalogBalance, AsShotNeutral/WhiteXY,
BaselineExposure, orientation.
- CR2 (LJ92 slices, SensorInfo borders, ColorData as-shot WB, measured
black) and TIFF/EP raws (NEF/ARW/PEF uncompressed or LJ92, incl. Sony
lossless ARW tiles, Nikon/Sony documented black and WB tags).
- Develop: black/white scaling, WB with highlight clipping, bilinear /
Malvar-He-Cutler / AHD demosaic (rayon bands, sequential on wasm),
camera -> XYZ D50 -> ProPhoto, exposure, gamma 1.8, 16-bit, orientation.
- Embedded JPEG preview finder; clear Unsupported errors for CR3, RAF,
ORF, RW2, vendor-compressed NEF/ARW/PEF, lossy/JXL/float DNG, X-Trans.
- Limits checked before every allocation; testgen feature with an LJ92
encoder and DNG/CR2/TIFF-EP writers; round-trip, colour-reference,
truncation and corruption tests.
Registered as a standalone crate in xtask layers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* io: open camera raws through photocraft-raw (16-bit ProPhoto), preview fallback
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* raw: DNG GainMap opcode, fuzz targets, stress tools; open raws from the apps
- Apply DNG OpcodeList2 GainMap (lens shading, e.g. phone DNGs); report other opcodes.
- cargo-fuzz targets (develop, ljpeg) and a mutation stress example.
- Raw extensions in the desktop/web open dialogs and the batch folder list.
- file.open returns the import notes (how a raw was developed, preview fallback).
- codecs: the camera raw refusal points at photocraft-raw.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* raw: clippy 1.99 (is_multiple_of, clamp)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* File open/save: set the path everywhere, Open Recent, show errors and import/export warnings
File › Open, drag-and-drop onto the window and launch paths now share
PhotocraftApp::open_file: the document is named after the file (not the full
path), keeps its path so File › Save writes back, and goes to Open Recent.
Failures are shown as errors (status bar + a non-blocking notice).
Import/export warnings from photocraft-io are no longer thrown away: they show
in the status bar and in a dismissable notice (native and web), and the
control protocol returns them from app.open/app.save/file.open/file.save/
file.saveAs/file.saveACopy. .pcraft is in the Open and Save filters, and
File › Save writes .pcraft documents in place.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* macOS: open files from Finder, Open With and the Dock (Apple events)
Finder double-click, Open With, drops on the Dock icon and open -a deliver
files as kAEOpenDocuments Apple events, not argv, and winit 0.30 ignores them.
Register an NSAppleEventManager handler through the small fmv-macos-events
crate (MIT OR Apache-2.0, 168 lines, audited, pinned =0.1.0; it keeps the
Objective-C unsafe out of our forbid(unsafe_code) workspace) before the event
loop starts, so both the launching event and later ones reach the UI as
OsEvent::Open; the window is focused. The quit Apple event goes through the
normal close path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Session::execute wraps a command's run in catch_unwind: a panic that
escapes anyway becomes an EngineError ("`id` failed with an internal
error (logged); the document is unchanged"). Session::edit only commits
after its closure returns, so documents stay intact. This covers the
UI, CLI, MCP and control channels.
- apps/photocraft: a new crash_guard module installs a logging panic hook
(keeping the default one) and guards the native import/export services,
so a panic while opening or exporting a file is reported as an error
and the open documents stay. Wired in with two lines in main.rs.
- services::import_flat returns an error instead of unwrap; photocraft
and photocraft-web deny unwrap/expect/panic/unimplemented/todo/
unreachable.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- rpc::respond (the JSON-lines server behind `photocraft-cli run`/TCP)
catches a panicking request and answers it with an error, and keeps
using a poisoned session lock (as the MCP server already does) instead
of failing every later call with "session lock poisoned". Regression
test poisoned_session_keeps_serving.
- MCP server: the eight bridge_client().expect("bridge") and the
headless_op(..).expect("headless") calls return a tool error instead.
- bridge: the connection is taken with let-else, not expect.
- automation and photocraft-cli deny unwrap/expect/panic/unimplemented/
todo/unreachable.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
CI's `cargo fmt --all -- --check` has failed on every run because the
repo had no rustfmt config and was never formatted. Use the same settings
as EffectCraft/FilmCraft (max_width 160, use_small_heuristics Max), which
also keeps the churn small. Formatting only; no code changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ctx.open_url is unreliable on native (notably Windows), so the Help menu,
the Discord buttons, and the start-page links did nothing. Added an open_url
Service hook wired to the `open` crate on native (ShellExecute on Windows);
links::open prefers it and falls back to ctx.open_url (web). link_row now
routes clicks through it instead of ui.hyperlink_to. Test drives every Help
link id + the open() helper through a mock service.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
- Lowercase reverse-DNS id ai.storyteller.photocraft across icons, Linux
desktop/metainfo/mime/flatpak, Windows WiX, macOS Info.plist and workflows.
- PhotoCraft display name in the title bar, start screen, web loader,
Windows exe metadata, README and docs.
- .gitignore target/, roadmap refresh.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>