# photocraft-cms Pure-Rust ICC colour management for Photocraft. No workspace dependencies (L0, publishable), no C, no `unsafe`. Implemented from the public ICC.1:2010 (v4.3) and ICC.1:2001-04 (v2) specifications, CIE 15, and Adobe's published black point compensation paper. ## API ```rust use photocraft_cms::{Builtin, Intent, Profile, Transform}; let src = Profile::parse(&icc_bytes)?; // v2/v4: matrix/TRC, gray TRC, mft1/mft2/mAB/mBA let dst = Builtin::CoatedCmyk.profile(); let t = Transform::new(&src, dst, Intent::RelativeColorimetric, /*bpc*/ true)?; t.convert_u8(&rgba8, 4, &mut cmyka8, 5, /*copy alpha*/ true); // also convert_u16 / convert_f32 / apply (in place) let mut out = [0.0; 4]; t.eval(&[1.0, 0.0, 0.0], &mut out); // exact single colour ``` * `Transform::proof(src, proof, display, intent, bpc, simulate_paper)`: soft-proof chain. * `GamutCheck::new(src, proof, ΔE)`: out-of-gamut test (gamut warning). * `Lut3d::from_transform(&t, 33).to_rgba16f_bytes()`: display transform as a 3D texture. * `transform::cached(src, dst, opts)`: process-wide transform cache. * `Profile::to_bytes()`: original bytes of parsed profiles (byte-exact), or an ICC v4.3 encoding. ### Evaluation * Exact path: a float stage pipeline (curves, matrices, CLUTs, Lab↔XYZ, PCS encodings, BPC / absolute scaling). Float buffers use it by default (`TransformOptions::precise_float`). * Integer buffers: matrix/TRC chains use per-code input tables + matrix + output tables indexed by `x^(1/4)` (accurate near black even for pure gamma curves); chains containing a CLUT are sampled into a device link (33³ for 3 inputs, 17⁴ for 4, 4096 for 1) evaluated with tetrahedral interpolation (4D: linear on the first input between two tetrahedra); purely analytic chains (e.g. Lab ↔ RGB) are evaluated exactly. Buffers ≥ 16k pixels run on rayon (native only). * Rendering intents: AToB0/1/2 and BToA0/1/2 by intent with AToB0/BToA0 fallback; absolute colorimetric scales by media white (`wtpt`; v2 display profiles treated as D50); BPC uses neutral black points (Y only) mapped linearly in XYZ. ## Built-in profiles (all CC0-1.0, generated by this crate) | id | description | |---|---| | `srgb` | sRGB IEC61966-2.1 (Rec. 709 primaries, D65 Bradford-adapted, sRGB TRC) | | `display-p3` | Display P3 | | `adobe-rgb-compat` | Adobe RGB (1998)-compatible (published primaries, gamma 563/256) | | `prophoto-compat` | ProPhoto-compatible (ROMM primaries, D50, gamma 1.8) | | `linear-srgb` | sRGB primaries, linear | | `rec2020` | Rec. 2020 primaries, Rec. 709 OETF | | `gray-gamma-2.2`, `sgray` | gray gamma 2.2; gray with the sRGB curve (default gray working space) | | `lab-d50` | Lab identity (v4 encoding) | | `coated-cmyk` | **Photocraft Coated CMYK (synthetic, 300% TAC, medium GCR)** — default CMYK | ### The CMYK profile Adobe's CMYK profiles (U.S. Web Coated SWOP, Coated FOGRA39) are proprietary, and the freely downloadable characterisation-based profiles (ECI/FOGRA, colord, Ghostscript) carry redistribution conditions or unclear licences, so Photocraft generates its own (`src/synth.rs`): a Yule–Nielsen modified Neugebauer model (n = 2) over the 16 overprints with ISO 12647-2 coated-paper Lab aims for paper/C/M/Y/RGB overprints, parabolic dot gain (14 % CMY, 17 % K at 50 %), GCR black generation (starts at 20 % gray component, K max 95 %), total ink limit 300 %, perceptual tables with L* black-point scaling and soft chroma compression against a sampled gamut boundary. `profiles/photocraft-coated-cmyk.icc` (237 KB, AToB 11⁴ lut16, BToA 21³ lut16) is that output, dedicated to the public domain (CC0-1.0). Regenerate with ```sh PHOTOCRAFT_REGEN_PROFILES=1 cargo test -p photocraft-cms --release --test regen ``` It is a plausible coated-offset profile, not a measured characterisation: use your printer's profile for production print. ## Not implemented / approximate * v4.4 `mpet` (multiProcessElements, D2Bx/B2Dx) tags, named colour, device link and abstract profiles as transform endpoints, `chad`-based adaptation of v2 absolute colorimetry. * BPC destination black uses the BToA/AToB round trip directly (Adobe's curve fit for ill-behaved LUT profiles is not implemented); v2/v4 perceptual reference black mixing is not modelled beyond the PRMG black for v4 LUT profiles. * Profile ID (MD5) is not computed when writing (left zero, which the spec allows).