material_color_utilities/
blend.rs

1use crate::{
2  hct::{Cam16, Hct},
3  utils,
4};
5
6/// Blend the design color's HCT hue towards the key color's HCT
7/// hue, in a way that leaves the original color recognizable and
8/// recognizably shifted towards the key color.
9pub fn harmonize(design_color: u32, source_color: u32) -> u32 {
10  let from_hct = Hct::from_int(design_color);
11  let to_hct = Hct::from_int(source_color);
12  let difference_degrees = utils::math::difference_degrees(from_hct.hue(), to_hct.hue());
13  let rotation_degrees = f64::min(difference_degrees * 0.5, 15.0);
14  let output_hue = utils::math::sanitize_degrees(
15    from_hct.hue()
16      + rotation_degrees * utils::math::rotation_direction(from_hct.hue(), to_hct.hue()),
17  );
18  Hct::from(output_hue, from_hct.chroma(), from_hct.tone()).to_int()
19}
20
21/// Blends hue from one color into another. The chroma and tone of
22/// the original color are maintained.
23pub fn hct_hue(from: u32, to: u32, amount: f64) -> u32 {
24  let ucs = cam16_ucs(from, to, amount);
25  let ucs_cam = Cam16::from_int(ucs);
26  let from_cam = Cam16::from_int(from);
27  let blended = Hct::from(
28    ucs_cam.hue(),
29    from_cam.chroma(),
30    utils::color::lstar_from_argb(from),
31  );
32  blended.to_int()
33}
34
35/// Blend in CAM16-UCS space.
36pub fn cam16_ucs(from: u32, to: u32, amount: f64) -> u32 {
37  let from_cam = Cam16::from_int(from);
38  let to_cam = Cam16::from_int(to);
39  let from_j = from_cam.jstar();
40  let from_a = from_cam.astar();
41  let from_b = from_cam.bstar();
42  let to_j = to_cam.j();
43  let to_a = to_cam.astar();
44  let to_b = to_cam.bstar();
45  let jstar = from_j + (to_j - from_j) * amount;
46  let astar = from_a + (to_a - from_a) * amount;
47  let bstar = from_b + (to_b - from_b) * amount;
48  Cam16::from_ucs(jstar, astar, bstar).to_int()
49}
50
51#[cfg(test)]
52mod tests {
53  use super::*;
54
55  const RED: u32 = 0xffff0000;
56  const BLUE: u32 = 0xff0000ff;
57  const GREEN: u32 = 0xff00ff00;
58  const YELLOW: u32 = 0xffffff00;
59
60  #[test]
61  fn harmonize_red_to_blue() {
62    let answer = harmonize(RED, BLUE);
63    assert_eq!(answer, 0xfffb0057);
64  }
65
66  #[test]
67  fn harmonize_red_to_green() {
68    let answer = harmonize(RED, GREEN);
69    assert_eq!(answer, 0xffd85600);
70  }
71
72  #[test]
73  fn harmonize_red_to_yellow() {
74    let answer = harmonize(RED, YELLOW);
75    assert_eq!(answer, 0xffd85600);
76  }
77
78  #[test]
79  fn harmonize_blue_to_green() {
80    let answer = harmonize(BLUE, GREEN);
81    assert_eq!(answer, 0xff0047a3);
82  }
83
84  #[test]
85  fn harmonize_blue_to_red() {
86    let answer = harmonize(BLUE, RED);
87    assert_eq!(answer, 0xff5700dc);
88  }
89
90  #[test]
91  fn harmonize_blue_to_yellow() {
92    let answer = harmonize(BLUE, YELLOW);
93    assert_eq!(answer, 0xff0047a3);
94  }
95
96  #[test]
97  fn harmonize_green_to_blue() {
98    let answer = harmonize(GREEN, BLUE);
99    assert_eq!(answer, 0xff00fc94);
100  }
101
102  #[test]
103  fn harmonize_green_to_red() {
104    let answer = harmonize(GREEN, RED);
105    assert_eq!(answer, 0xffb1f000);
106  }
107
108  #[test]
109  fn harmonize_green_to_yellow() {
110    let answer = harmonize(GREEN, YELLOW);
111    assert_eq!(answer, 0xffb1f000);
112  }
113
114  #[test]
115  fn harmonize_yellow_to_blue() {
116    let answer = harmonize(YELLOW, BLUE);
117    assert_eq!(answer, 0xffebffba);
118  }
119
120  #[test]
121  fn harmonize_yellow_to_green() {
122    let answer = harmonize(YELLOW, GREEN);
123    assert_eq!(answer, 0xffebffba);
124  }
125
126  #[test]
127  fn harmonize_yellow_to_red() {
128    let answer = harmonize(YELLOW, RED);
129    assert_eq!(answer, 0xfffff6e3);
130  }
131}