1use swc_atoms::atom;
2use swc_css_ast::{
3 AbsoluteColorBase, AlphaValue, Angle, ComponentValue, Delimiter, DelimiterValue, FunctionName,
4 Hue, Ident, Number, Percentage,
5};
6use swc_css_utils::{angle_to_deg, hwb_to_rgb, to_rgb255};
7
8use crate::compiler::Compiler;
9
10impl Compiler {
11 fn get_hue(&self, hue: Option<&ComponentValue>) -> Option<f64> {
12 match hue {
13 Some(ComponentValue::Hue(hue)) => {
14 let mut value = match &**hue {
15 Hue::Number(Number { value, .. }) => *value,
16 Hue::Angle(Angle {
17 value: Number { value, .. },
18 unit: Ident { value: unit, .. },
19 ..
20 }) => angle_to_deg(*value, unit),
21 };
22
23 value %= 360.0;
24
25 if value < 0.0 {
26 value += 360.0;
27 }
28
29 Some(value)
30 }
31 Some(ComponentValue::Ident(ident)) if ident.value.eq_ignore_ascii_case("none") => {
32 Some(0.0)
33 }
34 _ => None,
35 }
36 }
37
38 fn get_percentage(&self, percentage: Option<&ComponentValue>) -> Option<f64> {
39 match percentage {
40 Some(ComponentValue::Percentage(percentage)) => {
41 let Number { value, .. } = &percentage.value;
42 if *value > 100.0 {
43 return Some(1.0);
44 } else if *value < 0.0 {
45 return Some(0.0);
46 }
47
48 Some(*value / 100.0)
49 }
50 Some(ComponentValue::Ident(ident)) if ident.value.eq_ignore_ascii_case("none") => {
51 Some(0.0)
52 }
53 _ => None,
54 }
55 }
56
57 fn get_alpha_value(&self, alpha_value: Option<&ComponentValue>) -> Option<f64> {
58 let Some(alpha_value) = alpha_value else {
59 return Some(1.0);
60 };
61
62 match &alpha_value {
63 ComponentValue::AlphaValue(alpha_value) => match &**alpha_value {
64 AlphaValue::Number(Number { value, .. }) => {
65 if *value > 1.0 {
66 return Some(1.0);
67 } else if *value < 0.0 {
68 return Some(0.0);
69 }
70
71 Some(*value)
72 }
73 AlphaValue::Percentage(Percentage {
74 value: Number { value, .. },
75 ..
76 }) => {
77 if *value > 100.0 {
78 return Some(1.0);
79 } else if *value < 0.0 {
80 return Some(0.0);
81 }
82
83 Some(*value / 100.0)
84 }
85 },
86 ComponentValue::Ident(ident) if ident.value.eq_ignore_ascii_case("none") => Some(0.0),
87 _ => None,
88 }
89 }
90
91 pub(crate) fn process_color_hwb(&mut self, n: &mut AbsoluteColorBase) {
92 if let AbsoluteColorBase::Function(function) = n {
93 if function.name != "hwb" {
94 return;
95 }
96
97 let h = match self.get_hue(function.value.first()) {
98 Some(value) => value,
99 _ => return,
100 };
101 let w = match self.get_percentage(function.value.get(1)) {
102 Some(value) => value,
103 _ => return,
104 };
105 let b = match self.get_percentage(function.value.get(2)) {
106 Some(value) => value,
107 _ => return,
108 };
109 let a = match self.get_alpha_value(function.value.get(4)) {
110 Some(value) => value,
111 _ => return,
112 };
113
114 let rgb = to_rgb255(hwb_to_rgb([h, w, b]));
115
116 if a == 1.0 {
117 *n = AbsoluteColorBase::Function(swc_css_ast::Function {
118 name: FunctionName::Ident(Ident {
119 value: atom!("rgb"),
120 span: Default::default(),
121 raw: None,
122 }),
123 value: vec![
124 ComponentValue::Number(Box::new(Number {
125 value: rgb[0].round(),
126 span: Default::default(),
127 raw: None,
128 })),
129 ComponentValue::Delimiter(Box::new(Delimiter {
130 value: DelimiterValue::Comma,
131 span: Default::default(),
132 })),
133 ComponentValue::Number(Box::new(Number {
134 value: rgb[1].round(),
135 span: Default::default(),
136 raw: None,
137 })),
138 ComponentValue::Delimiter(Box::new(Delimiter {
139 value: DelimiterValue::Comma,
140 span: Default::default(),
141 })),
142 ComponentValue::Number(Box::new(Number {
143 value: rgb[2].round(),
144 span: Default::default(),
145 raw: None,
146 })),
147 ],
148 span: Default::default(),
149 });
150 } else {
151 *n = AbsoluteColorBase::Function(swc_css_ast::Function {
152 name: FunctionName::Ident(Ident {
153 value: atom!("rgba"),
154 span: Default::default(),
155 raw: None,
156 }),
157 value: vec![
158 ComponentValue::Number(Box::new(Number {
159 value: rgb[0].round(),
160 span: Default::default(),
161 raw: None,
162 })),
163 ComponentValue::Delimiter(Box::new(Delimiter {
164 value: DelimiterValue::Comma,
165 span: Default::default(),
166 })),
167 ComponentValue::Number(Box::new(Number {
168 value: rgb[1].round(),
169 span: Default::default(),
170 raw: None,
171 })),
172 ComponentValue::Delimiter(Box::new(Delimiter {
173 value: DelimiterValue::Comma,
174 span: Default::default(),
175 })),
176 ComponentValue::Number(Box::new(Number {
177 value: rgb[2].round(),
178 span: Default::default(),
179 raw: None,
180 })),
181 ComponentValue::Delimiter(Box::new(Delimiter {
182 value: DelimiterValue::Comma,
183 span: Default::default(),
184 })),
185 ComponentValue::AlphaValue(Box::new(AlphaValue::Number(Number {
186 value: a,
187 span: Default::default(),
188 raw: None,
189 }))),
190 ],
191 span: Default::default(),
192 });
193 }
194 }
195 }
196}