1use crate::palette::CorePalette;
4
5#[derive(Debug, Clone)]
7pub struct MaterialColorScheme {
8 pub primary: u32,
10 pub on_primary: u32,
11 pub primary_container: u32,
12 pub on_primary_container: u32,
13
14 pub secondary: u32,
16 pub on_secondary: u32,
17 pub secondary_container: u32,
18 pub on_secondary_container: u32,
19
20 pub tertiary: u32,
22 pub on_tertiary: u32,
23 pub tertiary_container: u32,
24 pub on_tertiary_container: u32,
25
26 pub error: u32,
28 pub on_error: u32,
29 pub error_container: u32,
30 pub on_error_container: u32,
31
32 pub surface: u32,
34 pub surface_bright: u32,
35 pub surface_dim: u32,
36 pub on_surface: u32,
37 pub on_surface_variant: u32,
38
39 pub surface_container_lowest: u32,
41 pub surface_container_low: u32,
42 pub surface_container: u32,
43 pub surface_container_high: u32,
44 pub surface_container_highest: u32,
45
46 pub outline: u32,
48 pub outline_variant: u32,
49
50 pub inverse_surface: u32,
52 pub inverse_on_surface: u32,
53 pub inverse_primary: u32,
54
55 pub primary_fixed: u32,
57 pub primary_fixed_dim: u32,
58 pub on_primary_fixed: u32,
59 pub on_primary_fixed_variant: u32,
60 pub secondary_fixed: u32,
61 pub secondary_fixed_dim: u32,
62 pub on_secondary_fixed: u32,
63 pub on_secondary_fixed_variant: u32,
64 pub tertiary_fixed: u32,
65 pub tertiary_fixed_dim: u32,
66 pub on_tertiary_fixed: u32,
67 pub on_tertiary_fixed_variant: u32,
68
69 pub scrim: u32,
71 pub shadow: u32,
72}
73
74impl Default for MaterialColorScheme {
75 fn default() -> Self {
76 Self::dark_from_argb(0xFF6750A4)
77 }
78}
79
80impl MaterialColorScheme {
81 pub fn dark_from_argb(seed: u32) -> Self {
82 let mut palette = CorePalette::from_argb(seed);
83 palette.cache_all();
84 Self::dark_from_palette(&mut palette)
85 }
86
87 pub fn light_from_argb(seed: u32) -> Self {
88 let mut palette = CorePalette::from_argb(seed);
89 palette.cache_all();
90 Self::light_from_palette(&mut palette)
91 }
92
93 pub fn dark_from_palette(p: &mut CorePalette) -> Self {
94 Self {
95 primary: p.primary.tone(80),
96 on_primary: p.primary.tone(20),
97 primary_container: p.primary.tone(30),
98 on_primary_container: p.primary.tone(90),
99
100 secondary: p.secondary.tone(80),
101 on_secondary: p.secondary.tone(20),
102 secondary_container: p.secondary.tone(30),
103 on_secondary_container: p.secondary.tone(90),
104
105 tertiary: p.tertiary.tone(80),
106 on_tertiary: p.tertiary.tone(20),
107 tertiary_container: p.tertiary.tone(30),
108 on_tertiary_container: p.tertiary.tone(90),
109
110 error: p.error.tone(80),
111 on_error: p.error.tone(20),
112 error_container: p.error.tone(30),
113 on_error_container: p.error.tone(90),
114
115 surface: p.neutral.tone(6),
116 surface_bright: p.neutral.tone(24),
117 surface_dim: p.neutral.tone(6),
118 on_surface: p.neutral.tone(90),
119 on_surface_variant: p.neutral_variant.tone(80),
120
121 surface_container_lowest: p.neutral.tone(4),
122 surface_container_low: p.neutral.tone(10),
123 surface_container: p.neutral.tone(12),
124 surface_container_high: p.neutral.tone(17),
125 surface_container_highest: p.neutral.tone(22),
126
127 outline: p.neutral_variant.tone(60),
128 outline_variant: p.neutral_variant.tone(30),
129
130 inverse_surface: p.neutral.tone(90),
131 inverse_on_surface: p.neutral.tone(20),
132 inverse_primary: p.primary.tone(40),
133
134 primary_fixed: p.primary.tone(90),
135 primary_fixed_dim: p.primary.tone(80),
136 on_primary_fixed: p.primary.tone(10),
137 on_primary_fixed_variant: p.primary.tone(30),
138 secondary_fixed: p.secondary.tone(90),
139 secondary_fixed_dim: p.secondary.tone(80),
140 on_secondary_fixed: p.secondary.tone(10),
141 on_secondary_fixed_variant: p.secondary.tone(30),
142 tertiary_fixed: p.tertiary.tone(90),
143 tertiary_fixed_dim: p.tertiary.tone(80),
144 on_tertiary_fixed: p.tertiary.tone(10),
145 on_tertiary_fixed_variant: p.tertiary.tone(30),
146
147 scrim: 0xFF000000,
148 shadow: 0xFF000000,
149 }
150 }
151
152 pub fn light_from_palette(p: &mut CorePalette) -> Self {
153 Self {
154 primary: p.primary.tone(40),
155 on_primary: p.primary.tone(100),
156 primary_container: p.primary.tone(90),
157 on_primary_container: p.primary.tone(10),
158
159 secondary: p.secondary.tone(40),
160 on_secondary: p.secondary.tone(100),
161 secondary_container: p.secondary.tone(90),
162 on_secondary_container: p.secondary.tone(10),
163
164 tertiary: p.tertiary.tone(40),
165 on_tertiary: p.tertiary.tone(100),
166 tertiary_container: p.tertiary.tone(90),
167 on_tertiary_container: p.tertiary.tone(10),
168
169 error: p.error.tone(40),
170 on_error: p.error.tone(100),
171 error_container: p.error.tone(90),
172 on_error_container: p.error.tone(10),
173
174 surface: p.neutral.tone(98),
175 surface_bright: p.neutral.tone(98),
176 surface_dim: p.neutral.tone(87),
177 on_surface: p.neutral.tone(10),
178 on_surface_variant: p.neutral_variant.tone(30),
179
180 surface_container_lowest: p.neutral.tone(100),
181 surface_container_low: p.neutral.tone(96),
182 surface_container: p.neutral.tone(94),
183 surface_container_high: p.neutral.tone(92),
184 surface_container_highest: p.neutral.tone(90),
185
186 outline: p.neutral_variant.tone(50),
187 outline_variant: p.neutral_variant.tone(80),
188
189 inverse_surface: p.neutral.tone(20),
190 inverse_on_surface: p.neutral.tone(95),
191 inverse_primary: p.primary.tone(80),
192
193 primary_fixed: p.primary.tone(90),
194 primary_fixed_dim: p.primary.tone(80),
195 on_primary_fixed: p.primary.tone(10),
196 on_primary_fixed_variant: p.primary.tone(30),
197 secondary_fixed: p.secondary.tone(90),
198 secondary_fixed_dim: p.secondary.tone(80),
199 on_secondary_fixed: p.secondary.tone(10),
200 on_secondary_fixed_variant: p.secondary.tone(30),
201 tertiary_fixed: p.tertiary.tone(90),
202 tertiary_fixed_dim: p.tertiary.tone(80),
203 on_tertiary_fixed: p.tertiary.tone(10),
204 on_tertiary_fixed_variant: p.tertiary.tone(30),
205
206 scrim: 0xFF000000,
207 shadow: 0xFF000000,
208 }
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 fn luminance_from_argb(argb: u32) -> f32 {
217 let r = ((argb >> 16) & 0xFF) as f32 / 255.0;
218 let g = ((argb >> 8) & 0xFF) as f32 / 255.0;
219 let b = (argb & 0xFF) as f32 / 255.0;
220 0.299 * r + 0.587 * g + 0.114 * b
221 }
222
223 #[test]
224 fn test_dark_scheme_generation() {
225 let scheme = MaterialColorScheme::dark_from_argb(0xFF6750A4);
226 assert!(luminance_from_argb(scheme.primary) > luminance_from_argb(scheme.on_primary));
227 }
228
229 #[test]
230 fn test_light_scheme_generation() {
231 let scheme = MaterialColorScheme::light_from_argb(0xFF6750A4);
232 assert!(luminance_from_argb(scheme.on_primary) > luminance_from_argb(scheme.primary));
233 }
234
235 #[test]
236 fn test_surface_hierarchy_dark() {
237 let scheme = MaterialColorScheme::dark_from_argb(0xFF6750A4);
238 assert!(
239 luminance_from_argb(scheme.surface_container_lowest)
240 < luminance_from_argb(scheme.surface_container_low)
241 );
242 assert!(
243 luminance_from_argb(scheme.surface_container_low)
244 < luminance_from_argb(scheme.surface_container)
245 );
246 assert!(
247 luminance_from_argb(scheme.surface_container)
248 < luminance_from_argb(scheme.surface_container_high)
249 );
250 assert!(
251 luminance_from_argb(scheme.surface_container_high)
252 < luminance_from_argb(scheme.surface_container_highest)
253 );
254 }
255
256 #[test]
257 fn test_error_colors() {
258 let scheme = MaterialColorScheme::dark_from_argb(0xFF6750A4);
259 let r = (scheme.error >> 16) & 0xFF;
260 let b = scheme.error & 0xFF;
261 assert!(r > b);
262 }
263}