1use super::*;
4
5#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
11pub struct Percent(pub Numeric);
12
13impl Display for Percent {
14 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15 write!(f, "{}%", self.0)
16 }
17}
18
19impl Interpolatable for Percent {
20 fn interpolate(&self, other: &Self, t: f64) -> Self {
21 Self(self.0.interpolate(&other.0, t))
22 }
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
27pub enum Opacity {
28 Alpha(Numeric),
30 Percent(Numeric),
32}
33
34impl std::hash::Hash for Opacity {
35 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
36 match self {
37 Self::Alpha(v) => {
38 0u8.hash(state);
39 v.hash(state);
40 }
41 Self::Percent(v) => {
42 1u8.hash(state);
43 v.hash(state);
44 }
45 }
46 }
47}
48
49impl Display for Opacity {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 match self {
52 Opacity::Alpha(value) => write!(f, "{}", value),
53 Opacity::Percent(value) => write!(f, "{}%", value),
54 }
55 }
56}
57
58impl Default for Opacity {
59 fn default() -> Self {
60 Self::Alpha(Numeric::F64(1.0))
61 }
62}
63
64impl Interpolatable for Opacity {
65 fn interpolate(&self, other: &Self, t: f64) -> Self {
66 Opacity::Alpha(Numeric::F64(
67 self.to_float_0_1().interpolate(&other.to_float_0_1(), t),
68 ))
69 }
70}
71
72impl From<f64> for Opacity {
73 fn from(value: f64) -> Self {
74 Numeric::F64(value).into()
75 }
76}
77
78impl From<i32> for Opacity {
79 fn from(value: i32) -> Self {
80 Numeric::from(value).into()
81 }
82}
83
84impl From<Numeric> for Opacity {
85 fn from(value: Numeric) -> Self {
86 Opacity::Alpha(value)
87 }
88}
89
90impl From<Percent> for Opacity {
91 fn from(value: Percent) -> Self {
92 Opacity::Percent(value.0)
93 }
94}
95
96impl Opacity {
97 pub fn to_float_0_1(&self) -> f64 {
99 let alpha = match self {
100 Opacity::Alpha(value) => value.to_float(),
101 Opacity::Percent(value) => value.to_float() / 100.0,
102 };
103 if alpha.is_finite() {
104 alpha.clamp(0.0, 1.0)
105 } else {
106 log::warn!("Opacity must be finite; rendering transparent");
107 0.0
108 }
109 }
110}
111
112impl From<f64> for ColorChannel {
113 fn from(value: f64) -> Self {
114 Numeric::F64(value).into()
115 }
116}
117
118impl From<i32> for ColorChannel {
119 fn from(value: i32) -> Self {
120 Numeric::from(value).into()
121 }
122}
123
124impl Into<ColorChannel> for Percent {
125 fn into(self) -> ColorChannel {
126 ColorChannel::Percent(self.0)
127 }
128}
129
130impl Into<Size> for Percent {
131 fn into(self) -> Size {
132 Size::Percent(self.0)
133 }
134}
135
136impl Into<Rotation> for Percent {
137 fn into(self) -> Rotation {
138 Rotation::Percent(self.0)
139 }
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
144pub enum ColorChannel {
145 Rotation(Rotation),
147 Integer(u8),
149 Percent(Numeric),
151}
152
153impl Display for ColorChannel {
154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155 match self {
156 ColorChannel::Rotation(rot) => write!(f, "{}", rot),
157 ColorChannel::Integer(int) => write!(f, "{}", int),
158 ColorChannel::Percent(per) => write!(f, "{}%", per),
159 }
160 }
161}
162
163impl Default for ColorChannel {
164 fn default() -> Self {
165 Self::Percent(Numeric::F64(50.0))
166 }
167}
168
169impl From<Numeric> for Rotation {
170 fn from(value: Numeric) -> Self {
171 Rotation::Degrees(value)
172 }
173}
174
175impl From<Numeric> for ColorChannel {
176 fn from(value: Numeric) -> Self {
177 Self::Integer(value.to_int().clamp(0, 255) as u8)
178 }
179}
180
181impl ColorChannel {
182 pub fn to_float_0_1(&self) -> f64 {
184 match self {
185 Self::Percent(per) => (per.to_float() / 100.0).clamp(0_f64, 1_f64),
186 Self::Integer(zero_to_255) => {
187 let f_zero = (*zero_to_255) as f64;
188 (f_zero / 255.0_f64).clamp(0_f64, 1_f64)
189 }
190 Self::Rotation(rot) => rot.to_float_0_1(),
191 }
192 }
193}
194
195#[allow(non_camel_case_types)]
197#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq)]
198pub enum Color {
199 rgb(ColorChannel, ColorChannel, ColorChannel),
201 rgba(ColorChannel, ColorChannel, ColorChannel, ColorChannel),
203 hsl(Rotation, ColorChannel, ColorChannel),
205 hsla(Rotation, ColorChannel, ColorChannel, ColorChannel),
207
208 #[default]
210 SLATE,
211 GRAY,
213 ZINC,
215 NEUTRAL,
217 STONE,
219 RED,
221 ORANGE,
223 AMBER,
225 YELLOW,
227 LIME,
229 GREEN,
231 EMERALD,
233 TEAL,
235 CYAN,
237 SKY,
239 BLUE,
241 INDIGO,
243 VIOLET,
245 PURPLE,
247 FUCHSIA,
249 PINK,
251 ROSE,
253 BLACK,
255 WHITE,
257 TRANSPARENT,
259 NONE,
261}
262
263impl Hash for Color {
264 fn hash<H: Hasher>(&self, state: &mut H) {
265 core::mem::discriminant(self).hash(state);
266 }
267}
268
269impl Display for Color {
271 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272 match self {
273 Self::rgb(r, g, b) => write!(f, "rgb({}, {}, {})", r, g, b),
274 Self::rgba(r, g, b, a) => write!(f, "rgba({}, {}, {}, {})", r, g, b, a),
275 Self::hsl(h, s, l) => write!(f, "hsl({}, {}, {})", h, s, l),
276 Self::hsla(h, s, l, a) => write!(f, "hsla({}, {}, {}, {})", h, s, l, a),
277 Self::SLATE => write!(f, "SLATE"),
278 Self::GRAY => write!(f, "GRAY"),
279 Self::ZINC => write!(f, "ZINC"),
280 Self::NEUTRAL => write!(f, "NEUTRAL"),
281 Self::STONE => write!(f, "STONE"),
282 Self::RED => write!(f, "RED"),
283 Self::ORANGE => write!(f, "ORANGE"),
284 Self::AMBER => write!(f, "AMBER"),
285 Self::YELLOW => write!(f, "YELLOW"),
286 Self::LIME => write!(f, "LIME"),
287 Self::GREEN => write!(f, "GREEN"),
288 Self::EMERALD => write!(f, "EMERALD"),
289 Self::TEAL => write!(f, "TEAL"),
290 Self::CYAN => write!(f, "CYAN"),
291 Self::SKY => write!(f, "SKY"),
292 Self::BLUE => write!(f, "BLUE"),
293 Self::INDIGO => write!(f, "INDIGO"),
294 Self::VIOLET => write!(f, "VIOLET"),
295 Self::PURPLE => write!(f, "PURPLE"),
296 Self::FUCHSIA => write!(f, "FUCHSIA"),
297 Self::PINK => write!(f, "PINK"),
298 Self::ROSE => write!(f, "ROSE"),
299 Self::BLACK => write!(f, "BLACK"),
300 Self::WHITE => write!(f, "WHITE"),
301 Self::TRANSPARENT => write!(f, "TRANSPARENT"),
302 Self::NONE => write!(f, "NONE"),
303 }
304 }
305}
306
307impl Color {
308 pub fn rgb(r: ColorChannel, g: ColorChannel, b: ColorChannel) -> Self {
313 Self::rgb(r, g, b)
314 }
315
316 pub fn rgba(r: ColorChannel, g: ColorChannel, b: ColorChannel, a: ColorChannel) -> Self {
318 Self::rgba(r, g, b, a)
319 }
320
321 pub fn hsl(h: Rotation, s: ColorChannel, l: ColorChannel) -> Self {
323 Self::hsl(h, s, l)
324 }
325
326 pub fn hsla(h: Rotation, s: ColorChannel, l: ColorChannel, a: ColorChannel) -> Self {
328 Self::hsla(h, s, l, a)
329 }
330
331 pub fn to_piet_color(&self) -> piet::Color {
333 let rgba = self.to_rgba_0_1();
334 piet::Color::rgba(rgba[0], rgba[1], rgba[2], rgba[3])
335 }
336
337 pub fn from_rgba_0_1(rgba_0_1: [f64; 4]) -> Self {
339 Self::rgba(
340 ColorChannel::Percent(Numeric::F64(rgba_0_1[0] * 100.0)),
341 ColorChannel::Percent(Numeric::F64(rgba_0_1[1] * 100.0)),
342 ColorChannel::Percent(Numeric::F64(rgba_0_1[2] * 100.0)),
343 ColorChannel::Percent(Numeric::F64(rgba_0_1[3] * 100.0)),
344 )
345 }
346
347 pub fn from_hex(hex: &str) -> Self {
349 let r = u8::from_str_radix(&hex[0..2], 16).unwrap() as f64 / 255.0;
350 let g = u8::from_str_radix(&hex[2..4], 16).unwrap() as f64 / 255.0;
351 let b = u8::from_str_radix(&hex[4..6], 16).unwrap() as f64 / 255.0;
352 let a = if hex.len() == 8 {
353 u8::from_str_radix(&hex[6..8], 16).unwrap() as f64 / 255.0
354 } else {
355 1.0
356 };
357 Self::rgba(
358 ColorChannel::Percent(Numeric::F64(r * 100.0)),
359 ColorChannel::Percent(Numeric::F64(g * 100.0)),
360 ColorChannel::Percent(Numeric::F64(b * 100.0)),
361 ColorChannel::Percent(Numeric::F64(a * 100.0)),
362 )
363 }
364
365 pub fn alpha_0_1(&self) -> f64 {
367 self.to_rgba_0_1()[3]
368 }
369
370 pub fn blend_premultiplied(colors: impl IntoIterator<Item = (Color, f64)>) -> Self {
374 let mut sum = [0.0; 4];
375 for (color, weight) in colors {
376 let rgba = color.to_rgba_0_1();
377 let alpha = rgba[3] * weight;
378 for i in 0..3 {
379 sum[i] += rgba[i] * alpha;
380 }
381 sum[3] += alpha;
382 }
383 if sum[3] > 0.0 {
384 for i in 0..3 {
385 sum[i] /= sum[3];
386 }
387 }
388 Self::from_rgba_0_1(sum)
389 }
390
391 pub fn with_alpha_factor(&self, factor: f64) -> Self {
393 let mut rgba = self.to_rgba_0_1();
394 rgba[3] = (rgba[3] * factor.clamp(0.0, 1.0)).clamp(0.0, 1.0);
395 Self::from_rgba_0_1(rgba)
396 }
397
398 pub fn to_rgba_0_1(&self) -> [f64; 4] {
400 match self {
401 Self::hsla(h, s, l, a) => {
402 let rgb = hsl_to_rgb(h.to_float_0_1(), s.to_float_0_1(), l.to_float_0_1());
403 [rgb[0], rgb[1], rgb[2], a.to_float_0_1()]
404 }
405 Self::hsl(h, s, l) => {
406 let rgb = hsl_to_rgb(h.to_float_0_1(), s.to_float_0_1(), l.to_float_0_1());
407 [rgb[0], rgb[1], rgb[2], 1.0]
408 }
409 Self::rgba(r, g, b, a) => [
410 r.to_float_0_1(),
411 g.to_float_0_1(),
412 b.to_float_0_1(),
413 a.to_float_0_1(),
414 ],
415 Self::rgb(r, g, b) => [r.to_float_0_1(), g.to_float_0_1(), b.to_float_0_1(), 1.0],
416
417 Self::SLATE => Self::rgb(
419 Numeric::from(0x64).into(),
420 Numeric::from(0x74).into(),
421 Numeric::from(0x8b).into(),
422 )
423 .to_rgba_0_1(),
424 Self::GRAY => Self::rgb(
425 Numeric::from(0x6b).into(),
426 Numeric::from(0x72).into(),
427 Numeric::from(0x80).into(),
428 )
429 .to_rgba_0_1(),
430 Self::ZINC => Self::rgb(
431 Numeric::from(0x71).into(),
432 Numeric::from(0x71).into(),
433 Numeric::from(0x7a).into(),
434 )
435 .to_rgba_0_1(),
436 Self::NEUTRAL => Self::rgb(
437 Numeric::from(0x73).into(),
438 Numeric::from(0x73).into(),
439 Numeric::from(0x73).into(),
440 )
441 .to_rgba_0_1(),
442 Self::STONE => Self::rgb(
443 Numeric::from(0x78).into(),
444 Numeric::from(0x71).into(),
445 Numeric::from(0x6c).into(),
446 )
447 .to_rgba_0_1(),
448 Self::RED => Self::rgb(
449 Numeric::from(0xeF).into(),
450 Numeric::from(0x44).into(),
451 Numeric::from(0x44).into(),
452 )
453 .to_rgba_0_1(),
454 Self::ORANGE => Self::rgb(
455 Numeric::from(0xf9).into(),
456 Numeric::from(0x73).into(),
457 Numeric::from(0x16).into(),
458 )
459 .to_rgba_0_1(),
460 Self::AMBER => Self::rgb(
461 Numeric::from(0xf5).into(),
462 Numeric::from(0x9e).into(),
463 Numeric::from(0x0b).into(),
464 )
465 .to_rgba_0_1(),
466 Self::YELLOW => Self::rgb(
467 Numeric::from(0xea).into(),
468 Numeric::from(0xb3).into(),
469 Numeric::from(0x08).into(),
470 )
471 .to_rgba_0_1(),
472 Self::LIME => Self::rgb(
473 Numeric::from(0x84).into(),
474 Numeric::from(0xcc).into(),
475 Numeric::from(0x16).into(),
476 )
477 .to_rgba_0_1(),
478 Self::GREEN => Self::rgb(
479 Numeric::from(0x22).into(),
480 Numeric::from(0xc5).into(),
481 Numeric::from(0x5e).into(),
482 )
483 .to_rgba_0_1(),
484 Self::EMERALD => Self::rgb(
485 Numeric::from(0x10).into(),
486 Numeric::from(0xb9).into(),
487 Numeric::from(0x81).into(),
488 )
489 .to_rgba_0_1(),
490 Self::TEAL => Self::rgb(
491 Numeric::from(0x14).into(),
492 Numeric::from(0xb8).into(),
493 Numeric::from(0xa6).into(),
494 )
495 .to_rgba_0_1(),
496 Self::CYAN => Self::rgb(
497 Numeric::from(0x06).into(),
498 Numeric::from(0xb6).into(),
499 Numeric::from(0xd4).into(),
500 )
501 .to_rgba_0_1(),
502 Self::SKY => Self::rgb(
503 Numeric::from(0x0e).into(),
504 Numeric::from(0xa5).into(),
505 Numeric::from(0xe9).into(),
506 )
507 .to_rgba_0_1(),
508 Self::BLUE => Self::rgb(
509 Numeric::from(0x3b).into(),
510 Numeric::from(0x82).into(),
511 Numeric::from(0xf6).into(),
512 )
513 .to_rgba_0_1(),
514 Self::INDIGO => Self::rgb(
515 Numeric::from(0x63).into(),
516 Numeric::from(0x66).into(),
517 Numeric::from(0xf1).into(),
518 )
519 .to_rgba_0_1(),
520 Self::VIOLET => Self::rgb(
521 Numeric::from(0x8b).into(),
522 Numeric::from(0x5c).into(),
523 Numeric::from(0xf6).into(),
524 )
525 .to_rgba_0_1(),
526 Self::PURPLE => Self::rgb(
527 Numeric::from(0xa8).into(),
528 Numeric::from(0x55).into(),
529 Numeric::from(0xf7).into(),
530 )
531 .to_rgba_0_1(),
532 Self::FUCHSIA => Self::rgb(
533 Numeric::from(0xd9).into(),
534 Numeric::from(0x46).into(),
535 Numeric::from(0xef).into(),
536 )
537 .to_rgba_0_1(),
538 Self::PINK => Self::rgb(
539 Numeric::from(0xec).into(),
540 Numeric::from(0x48).into(),
541 Numeric::from(0x99).into(),
542 )
543 .to_rgba_0_1(),
544 Self::ROSE => Self::rgb(
545 Numeric::from(0xf4).into(),
546 Numeric::from(0x3f).into(),
547 Numeric::from(0x5e).into(),
548 )
549 .to_rgba_0_1(),
550 Self::BLACK => Self::rgb(
551 Numeric::from(0x00).into(),
552 Numeric::from(0x00).into(),
553 Numeric::from(0x00).into(),
554 )
555 .to_rgba_0_1(),
556 Self::WHITE => Self::rgb(
557 Numeric::from(0xff).into(),
558 Numeric::from(0xff).into(),
559 Numeric::from(0xff).into(),
560 )
561 .to_rgba_0_1(),
562 Self::TRANSPARENT | Self::NONE => Self::rgba(
563 Numeric::from(0xff).into(),
564 Numeric::from(0xff).into(),
565 Numeric::from(0xFF).into(),
566 Numeric::from(0x00).into(),
567 )
568 .to_rgba_0_1(),
569 }
570 }
571
572 pub fn to_hsla_0_1(&self) -> [f64; 4] {
575 let [r, g, b, a] = self.to_rgba_0_1();
576
577 let max = r.max(g).max(b);
578 let min = r.min(g).min(b);
579 let chroma = max - min;
580
581 let h = if chroma == 0.0 {
582 0.0
583 } else if max == r {
584 ((g - b) / chroma + 6.0) % 6.0 / 6.0
585 } else if max == g {
586 ((b - r) / chroma + 2.0) / 6.0
587 } else {
588 ((r - g) / chroma + 4.0) / 6.0
589 };
590
591 let l = (max + min) / 2.0;
592
593 let s = if l == 0.0 || l == 1.0 {
594 0.0
595 } else {
596 (max - l) / l.min(1.0 - l)
597 };
598
599 [h, s, l, a]
600 }
601}
602
603const RGB_UNIT_MAX: f64 = 255.0;
605fn hsl_to_rgb(h: f64, s: f64, l: f64) -> [f64; 3] {
606 if s == 0.0 {
607 let unit = RGB_UNIT_MAX * l;
608 return [
609 unit / RGB_UNIT_MAX,
610 unit / RGB_UNIT_MAX,
611 unit / RGB_UNIT_MAX,
612 ];
613 }
614
615 let temp1 = if l < 0.5 {
616 l * (1.0 + s)
617 } else {
618 l + s - l * s
619 };
620
621 let temp2 = 2.0 * l - temp1;
622 let hue = h;
623
624 let temp_r = bound(hue + (1.0 / 3.0), 1.0);
625 let temp_g = bound(hue, 1.0);
626 let temp_b = bound(hue - (1.0 / 3.0), 1.0);
627
628 let r = calc_rgb_unit(temp_r, temp1, temp2);
629 let g = calc_rgb_unit(temp_g, temp1, temp2);
630 let b = calc_rgb_unit(temp_b, temp1, temp2);
631 [r / RGB_UNIT_MAX, g / RGB_UNIT_MAX, b / RGB_UNIT_MAX]
632}
633
634fn calc_rgb_unit(unit: f64, temp1: f64, temp2: f64) -> f64 {
635 let mut result = temp2;
636 if 6.0 * unit < 1.0 {
637 result = temp2 + (temp1 - temp2) * 6.0 * unit
638 } else if 2.0 * unit < 1.0 {
639 result = temp1
640 } else if 3.0 * unit < 2.0 {
641 result = temp2 + (temp1 - temp2) * ((2.0 / 3.0) - unit) * 6.0
642 }
643 result * RGB_UNIT_MAX
644}
645
646pub fn bound(r: f64, entire: f64) -> f64 {
648 let mut n = r;
649 loop {
650 let less = n < 0.0;
651 let bigger = n > entire;
652 if !less && !bigger {
653 break n;
654 }
655 if less {
656 n += entire;
657 } else {
658 n -= entire;
659 }
660 }
661}
662
663impl Into<ColorMessage> for &Color {
664 fn into(self) -> ColorMessage {
665 let rgba = self.to_rgba_0_1();
666 ColorMessage::Rgba(rgba)
667 }
668}
669impl PartialEq<ColorMessage> for Color {
670 fn eq(&self, other: &ColorMessage) -> bool {
671 let self_rgba = self.to_rgba_0_1();
672
673 match other {
674 ColorMessage::Rgb(other_rgba) => {
675 self_rgba[0] == other_rgba[0]
676 && self_rgba[1] == other_rgba[1]
677 && self_rgba[2] == other_rgba[2]
678 && self_rgba[3] == 1.0
679 }
680 ColorMessage::Rgba(other_rgba) => {
681 self_rgba[0] == other_rgba[0]
682 && self_rgba[1] == other_rgba[1]
683 && self_rgba[2] == other_rgba[2]
684 && self_rgba[3] == other_rgba[3]
685 }
686 }
687 }
688}
689impl Interpolatable for Color {
690 fn interpolate(&self, other: &Self, t: f64) -> Self {
691 let rgba_s = self.to_rgba_0_1();
692 let rgba_o = other.to_rgba_0_1();
693 let rgba_i = [
694 rgba_s[0].interpolate(&rgba_o[0], t),
695 rgba_s[1].interpolate(&rgba_o[1], t),
696 rgba_s[2].interpolate(&rgba_o[2], t),
697 rgba_s[3].interpolate(&rgba_o[3], t),
698 ];
699 Color::from_rgba_0_1(rgba_i)
700 }
701}