diff --git a/README.md b/README.md index b7e2e07..754700d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > Modern, cross-platform native desktop application for printer profiling, powered by ArgyllCMS. -[![Release](https://img.shields.io/badge/version-v0.6.8-blue.svg)](https://git.i3omb.com/gronod/ICCery) +[![Release](https://img.shields.io/badge/version-v0.7.0-blue.svg)](https://git.i3omb.com/gronod/ICCery) [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux-lightgrey.svg)](https://git.i3omb.com/gronod/ICCery) [![Framework](https://img.shields.io/badge/framework-Tauri%20v2%20%2B%20Rust-orange.svg)](https://tauri.app) [![License](https://img.shields.io/badge/license-Proprietary%20%2F%20EULA-blue.svg)](LICENCE.md) diff --git a/ROADMAP.md b/ROADMAP.md index bf1bc75..613a63a 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -79,7 +79,8 @@ ICCery is a native, cross-platform desktop application built with: - [x] **Stage 1 Layout Normalization & Stage 2 Deterministic Target Generation (#162, #163)** (`v0.5.5`): Reorganized Stage 1 Advanced Options into structured 2-column grids with normalized heights; enforced deterministic `-R 1` target generation with custom seed and raster order (`-r`) support in Stage 2. - [x] **macOS Universal Binary Target (#164)** (`v0.5.5`): Added macOS Universal Binary (`universal-apple-darwin`) build target combining Intel (`x86_64`) and Apple Silicon (`arm64`), ArgyllCMS universal sidecar packaging, runtime fallback resolution, and CI release asset automation. -### Milestone 11 — Enterprise Colour Workflow (`v0.6.0` – `v0.6.8`) +### Milestone 11 — Enterprise Colour Workflow (`v0.6.0` – `v0.7.0`) +- [x] **Workflow & Visualizer Enhancements (#176, #177, #178, #179)** (`v0.7.0`): Stage 4 OBA/FWA compensation and viewing conditions UI (`-c`, `-d`), global button standardization, Stage 3 swatch grid diagonal split rendering for CIEDE2000 visual comparison, and robust gamut `.gam` dual-table face parsing with regex fixes for profcheck. - [x] **CGATS Dataset Interoperability (#94)** (`v0.6.0`): Native Rust CGATS and Argyll `.ti3` dataset parser, canonical normalizer (0-255 scaling, field aliasing, metadata synthesis), and direct-jump workflow to Stage 4 (Profile Generation) and Stage 5 (Verification) using imported external datasets. - [x] **Stage 1 Layout Normalization (#162)** (`v0.6.1`): Standardized control heights and structural flexbox auto-margin layout for multi-column Stage 1 advanced settings. - [x] **Overlay Tooltip Rendering (#171)** (`v0.6.2`): Rendered tooltips as absolute overlay popups on hover/focus to prevent layout jitter while preserving in-flow hints in global tooltip toggle mode. diff --git a/package.json b/package.json index 0c14840..838f32a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "iccery", "private": true, - "version": "0.6.8", + "version": "0.7.0", "type": "module", "scripts": { "fetch-argyll": "node scripts/fetch-argyll.mjs", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 79789a8..0d8831f 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iccery" -version = "0.6.8" +version = "0.7.0" description = "Modern Printer Profiling UI frontend for ArgyllCMS" authors = ["Gordon"] edition = "2021" diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 5664000..00b5868 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -968,38 +968,81 @@ pub struct ColprofConfig { pub description: Option, pub basename: String, pub cwd: String, + #[serde(default)] + pub fwa: Option, + #[serde(default)] + pub illuminant: Option, + #[serde(default)] + pub observer: Option, + #[serde(default)] + pub input_viewing_cond: Option, + #[serde(default)] + pub output_viewing_cond: Option, } pub fn build_colprof_args(config: &ColprofConfig) -> Vec { - let mut args = vec![ - "-v".to_string(), - "-a".to_string(), - config.algorithm.clone(), - "-q".to_string(), - config.quality.clone(), - ]; + let mut args = vec!["-v".to_string(), "-a".to_string(), config.algorithm.clone(), "-q".to_string(), config.quality.clone()]; if let Some(ref intent) = config.intent { - if !intent.trim().is_empty() { - args.push("-p".to_string()); + if !intent.is_empty() { + args.push("-t".to_string()); args.push(intent.clone()); } } - if let Some(ref cr) = config.copyright { - if !cr.trim().is_empty() { - args.push("-C".to_string()); - args.push(cr.clone()); + if let Some(ref fwa) = config.fwa { + if fwa.to_lowercase() != "none" { + if fwa.is_empty() { + args.push("-f".to_string()); + } else { + args.push("-f".to_string()); + args.push(fwa.clone()); + } + } + } + + if let Some(ref illuminant) = config.illuminant { + if !illuminant.is_empty() { + args.push("-i".to_string()); + args.push(illuminant.clone()); + } + } + + if let Some(ref observer) = config.observer { + if !observer.is_empty() { + args.push("-o".to_string()); + args.push(observer.clone()); + } + } + + if let Some(ref in_cond) = config.input_viewing_cond { + if !in_cond.is_empty() && in_cond.to_lowercase() != "none" { + args.push("-c".to_string()); + args.push(in_cond.clone()); + } + } + + if let Some(ref out_cond) = config.output_viewing_cond { + if !out_cond.is_empty() && out_cond.to_lowercase() != "none" { + args.push("-d".to_string()); + args.push(out_cond.clone()); } } if let Some(ref desc) = config.description { - if !desc.trim().is_empty() { + if !desc.is_empty() { args.push("-D".to_string()); args.push(desc.clone()); } } + if let Some(ref copyright) = config.copyright { + if !copyright.is_empty() { + args.push("-C".to_string()); + args.push(copyright.clone()); + } + } + args.push(config.basename.clone()); args } @@ -1602,18 +1645,138 @@ mod tests { #[test] fn test_build_colprof_args() { let config = ColprofConfig { - quality: "h".to_string(), algorithm: "l".to_string(), - intent: None, - description: Some("My Profile".to_string()), - copyright: Some("2026 ACME".to_string()), - basename: "my_profile".to_string(), - cwd: "/home/user".to_string(), + quality: "h".to_string(), + intent: Some("p".to_string()), + copyright: Some("Test".to_string()), + description: Some("Test Desc".to_string()), + basename: "basename".to_string(), + cwd: "".to_string(), + fwa: None, + illuminant: None, + observer: None, + input_viewing_cond: None, + output_viewing_cond: None, }; let args = build_colprof_args(&config); assert_eq!( args, - vec!["-v", "-a", "l", "-q", "h", "-C", "2026 ACME", "-D", "My Profile", "my_profile"] + vec!["-v", "-a", "l", "-q", "h", "-t", "p", "-D", "Test Desc", "-C", "Test", "basename"] + ); + } + + #[test] + fn test_build_colprof_args_with_fwa_d50() { + let config = ColprofConfig { + algorithm: "l".to_string(), + quality: "h".to_string(), + intent: None, + copyright: None, + description: None, + basename: "basename".to_string(), + cwd: "".to_string(), + fwa: Some("D50".to_string()), + illuminant: None, + observer: None, + input_viewing_cond: None, + output_viewing_cond: None, + }; + let args = build_colprof_args(&config); + assert_eq!( + args, + vec!["-v", "-a", "l", "-q", "h", "-f", "D50", "basename"] + ); + } + + #[test] + fn test_build_colprof_args_with_fwa_none() { + let config = ColprofConfig { + algorithm: "l".to_string(), + quality: "h".to_string(), + intent: None, + copyright: None, + description: None, + basename: "basename".to_string(), + cwd: "".to_string(), + fwa: Some("none".to_string()), + illuminant: None, + observer: None, + input_viewing_cond: None, + output_viewing_cond: None, + }; + let args = build_colprof_args(&config); + assert_eq!( + args, + vec!["-v", "-a", "l", "-q", "h", "basename"] + ); + } + + #[test] + fn test_build_colprof_args_with_custom_sp() { + let config = ColprofConfig { + algorithm: "l".to_string(), + quality: "h".to_string(), + intent: None, + copyright: None, + description: None, + basename: "basename".to_string(), + cwd: "".to_string(), + fwa: Some("/path/to/viewing_booth.sp".to_string()), + illuminant: None, + observer: None, + input_viewing_cond: None, + output_viewing_cond: None, + }; + let args = build_colprof_args(&config); + assert_eq!( + args, + vec!["-v", "-a", "l", "-q", "h", "-f", "/path/to/viewing_booth.sp", "basename"] + ); + } + + #[test] + fn test_build_colprof_args_with_illuminant_observer() { + let config = ColprofConfig { + algorithm: "l".to_string(), + quality: "h".to_string(), + intent: None, + copyright: None, + description: None, + basename: "basename".to_string(), + cwd: "".to_string(), + fwa: None, + illuminant: Some("D65".to_string()), + observer: Some("1964_10".to_string()), + input_viewing_cond: None, + output_viewing_cond: None, + }; + let args = build_colprof_args(&config); + assert_eq!( + args, + vec!["-v", "-a", "l", "-q", "h", "-i", "D65", "-o", "1964_10", "basename"] + ); + } + + #[test] + fn test_build_colprof_args_with_viewing_conditions() { + let config = ColprofConfig { + algorithm: "l".to_string(), + quality: "h".to_string(), + intent: None, + copyright: None, + description: None, + basename: "basename".to_string(), + cwd: "".to_string(), + fwa: None, + illuminant: None, + observer: None, + input_viewing_cond: Some("pp".to_string()), + output_viewing_cond: Some("mt".to_string()), + }; + let args = build_colprof_args(&config); + assert_eq!( + args, + vec!["-v", "-a", "l", "-q", "h", "-c", "pp", "-d", "mt", "basename"] ); } diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs index 638a9aa..20ab94a 100644 --- a/src-tauri/src/settings.rs +++ b/src-tauri/src/settings.rs @@ -18,6 +18,16 @@ pub struct ProfilingPreset { pub colprof_algorithm: String, pub colprof_quality: String, pub colprof_intent: Option, + #[serde(default)] + pub colprof_fwa: Option, + #[serde(default)] + pub colprof_illuminant: Option, + #[serde(default)] + pub colprof_observer: Option, + #[serde(default)] + pub colprof_input_viewing_cond: Option, + #[serde(default)] + pub colprof_output_viewing_cond: Option, // Advanced Stage 1 fields #[serde(default)] @@ -176,6 +186,11 @@ pub fn get_default_presets() -> Vec { device_power: None, random_seed: Some(1), no_randomize: Some(false), + colprof_fwa: Some("D50".to_string()), + colprof_illuminant: None, + colprof_observer: None, + colprof_input_viewing_cond: None, + colprof_output_viewing_cond: None, }, ] } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 1103fa3..40b6057 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "ICCery", - "version": "0.6.8", + "version": "0.7.0", "identifier": "com.gronod.iccery", "build": { "frontendDist": "../src" diff --git a/src/index.html b/src/index.html index f23a222..7c780f8 100644 --- a/src/index.html +++ b/src/index.html @@ -16,13 +16,13 @@
- - - +
+ +
+ +
+ + +
+ + + +
+ + āš™ļø Advanced Spectral & Viewing Options + ā–¼ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
- +
+ +