mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-08 22:28:09 +00:00
Merge pull request #6187 from dotCooCoo/fix/generic-cmyk-solver-converges
Color: Generic CMYK separates nearby colors alike when an ink sits at 0 or 100%
This commit is contained in:
@@ -231,6 +231,21 @@ impl GenericCmyk {
|
||||
}
|
||||
g[p] = -(0..3).map(|i| j[i][p] * r[i]).sum::<f32>();
|
||||
}
|
||||
// An ink at a bound whose descent direction points out of the box stays where it is; the step moves the other inks.
|
||||
for p in 0..3 {
|
||||
if (x[p] <= 0.0 && g[p] < 0.0) || (x[p] >= 1.0 && g[p] > 0.0) {
|
||||
a[p] = [0.0; 3];
|
||||
for row in &mut a {
|
||||
row[p] = 0.0;
|
||||
}
|
||||
a[p][p] = 1.0;
|
||||
g[p] = 0.0;
|
||||
}
|
||||
}
|
||||
// Every ink is held at a bound, or the gradient is zero: no step can lower the error.
|
||||
if g == [0.0; 3] {
|
||||
break;
|
||||
}
|
||||
let mut improved = false;
|
||||
for _ in 0..8 {
|
||||
let mut al = a;
|
||||
|
||||
@@ -259,6 +259,46 @@ fn generic_cmyk_black_round_trips_with_bpc() {
|
||||
assert!(raw.iter().all(|v| *v > 0.05), "{raw:?}");
|
||||
}
|
||||
|
||||
/// A color whose solve holds cyan at 0 separates to the same inks when its values change by a few
|
||||
/// float steps: hex ed1e79 with its red stepped one `f32` at a time over 4,001 values (about 0.00024
|
||||
/// in all) stays within 0.001 on every ink.
|
||||
#[test]
|
||||
fn generic_cmyk_separates_nearby_colors_at_the_gamut_edge_alike() {
|
||||
let c = cms();
|
||||
let pink = [237.0f32 / 255.0, 30.0 / 255.0, 121.0 / 255.0];
|
||||
let (mut lo, mut hi) = ([f32::MAX; 4], [f32::MIN; 4]);
|
||||
for step in -2000i32..=2000 {
|
||||
let mut rgb = pink;
|
||||
rgb[0] = f32::from_bits(pink[0].to_bits().wrapping_add_signed(step));
|
||||
let ink = c.srgb_to_cmyk(rgb, Intent::RelativeColorimetric);
|
||||
for i in 0..4 {
|
||||
lo[i] = lo[i].min(ink[i]);
|
||||
hi[i] = hi[i].max(ink[i]);
|
||||
}
|
||||
}
|
||||
assert!((0..4).all(|i| hi[i] - lo[i] < 1e-3), "{lo:?} to {hi:?}");
|
||||
assert_eq!(lo[0], 0.0, "{lo:?}");
|
||||
}
|
||||
|
||||
/// A color whose solve holds cyan and magenta at 1 separates to the same inks when its values change
|
||||
/// by a few float steps: sRGB (0, 0, 0.25) with its blue stepped one `f32` at a time over 121 values
|
||||
/// stays within 0.001 on every ink.
|
||||
#[test]
|
||||
fn generic_cmyk_separates_nearby_colors_with_inks_at_full_alike() {
|
||||
let c = cms();
|
||||
let (mut lo, mut hi) = ([f32::MAX; 4], [f32::MIN; 4]);
|
||||
for step in -60i32..=60 {
|
||||
let blue = f32::from_bits(0.25f32.to_bits().wrapping_add_signed(step));
|
||||
let ink = c.srgb_to_cmyk([0.0, 0.0, blue], Intent::RelativeColorimetric);
|
||||
for i in 0..4 {
|
||||
lo[i] = lo[i].min(ink[i]);
|
||||
hi[i] = hi[i].max(ink[i]);
|
||||
}
|
||||
}
|
||||
assert!((0..4).all(|i| hi[i] - lo[i] < 1e-3), "{lo:?} to {hi:?}");
|
||||
assert_eq!((lo[0], lo[1]), (1.0, 1.0), "{lo:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lab_colours_round_trip_through_the_cms() {
|
||||
let c = cms_no_bpc();
|
||||
|
||||
@@ -503,7 +503,7 @@ fn a_cmyk_document_starts_with_cmyk_swatches_and_stores_new_colours_as_cmyk() {
|
||||
// A swatch made from a hex colour is CMYK as well; the document round-trips natively.
|
||||
let name = run(&mut s, "swatch.new", json!({"color": "#00ff00"}))["name"].as_str().unwrap().to_string();
|
||||
assert_eq!(doc(&s).swatch(&name).and_then(|w| w.paint.color()), Some(managed(Color::rgb(0.0, 1.0, 0.0))));
|
||||
assert_eq!(name, "C=62 M=0 Y=100 K=0");
|
||||
assert_eq!(name, "C=61 M=0 Y=100 K=0");
|
||||
let json = serde_json::to_string(doc(&s)).unwrap();
|
||||
assert_eq!(serde_json::from_str::<vectorcraft_doc::Document>(&json).unwrap(), *doc(&s));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user