gpui_kit_material_color/dynamic/
palette.rs1use super::hct::Hct;
10
11#[derive(Clone, Copy, Debug, PartialEq)]
13pub struct TonalPalette {
14 hue: f64,
15 chroma: f64,
16 key_color: Hct,
17}
18
19impl TonalPalette {
20 pub fn from_argb(argb: u32) -> Self {
22 Self::from_hct(Hct::from_argb(argb))
23 }
24
25 pub fn from_hct(hct: Hct) -> Self {
27 Self {
28 hue: hct.hue(),
29 chroma: hct.chroma(),
30 key_color: hct,
31 }
32 }
33
34 pub fn from_hue_and_chroma(hue: f64, chroma: f64) -> Self {
36 Self {
37 hue,
38 chroma,
39 key_color: key_color(hue, chroma),
40 }
41 }
42
43 pub fn hue(&self) -> f64 {
45 self.hue
46 }
47
48 pub fn chroma(&self) -> f64 {
50 self.chroma
51 }
52
53 pub fn key_color(&self) -> Hct {
56 self.key_color
57 }
58
59 pub fn tone(&self, tone: f64) -> u32 {
61 if tone == 99. && Hct::is_yellow(self.hue) {
63 average_argb(self.tone(98.), self.tone(100.))
64 } else {
65 Hct::from_hct(self.hue, self.chroma, tone).argb()
66 }
67 }
68
69 pub fn hct(&self, tone: f64) -> Hct {
71 Hct::from_argb(self.tone(tone))
72 }
73}
74
75fn average_argb(a: u32, b: u32) -> u32 {
76 let channel = |shift: u32| {
77 let sum = ((a >> shift) & 0xff) + ((b >> shift) & 0xff);
78 sum.div_ceil(2) << shift
80 };
81 0xff00_0000 | channel(16) | channel(8) | channel(0)
82}
83
84fn key_color(hue: f64, requested_chroma: f64) -> Hct {
87 const MAX_CHROMA: f64 = 200.;
88 const EPSILON: f64 = 0.01;
89 const PIVOT_TONE: i32 = 50;
90 let mut cache = [f64::NAN; 102];
93 let mut max_chroma = |tone: i32| {
94 let slot = &mut cache[tone.clamp(0, 101) as usize];
95 if slot.is_nan() {
96 *slot = Hct::from_hct(hue, MAX_CHROMA, f64::from(tone)).chroma();
97 }
98 *slot
99 };
100 let (mut lower, mut upper) = (0, 100);
101 while lower < upper {
102 let mid = (lower + upper) / 2;
103 let is_ascending = max_chroma(mid) < max_chroma(mid + 1);
104 if max_chroma(mid) >= requested_chroma - EPSILON {
105 if (lower - PIVOT_TONE).abs() < (upper - PIVOT_TONE).abs() {
107 upper = mid;
108 } else {
109 if lower == mid {
110 return Hct::from_hct(hue, requested_chroma, f64::from(lower));
111 }
112 lower = mid;
113 }
114 } else if is_ascending {
115 lower = mid + 1;
117 } else {
118 upper = mid;
120 }
121 }
122 Hct::from_hct(hue, requested_chroma, f64::from(lower))
123}