1use std::{
2 borrow::Cow,
3 fmt,
4 ops::{Deref, DerefMut},
5};
6
7use crate::json::ToJson;
8use pathfinder_color::{ColorF, ColorU};
9use schemars::JsonSchema;
10use serde::{
11 de::{self, Unexpected},
12 Deserialize, Deserializer,
13};
14use serde_json::json;
15
16#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord, JsonSchema)]
17#[repr(transparent)]
18pub struct Color(#[schemars(with = "String")] ColorU);
19
20impl Color {
21 pub fn transparent_black() -> Self {
22 Self(ColorU::transparent_black())
23 }
24
25 pub fn black() -> Self {
26 Self(ColorU::black())
27 }
28
29 pub fn white() -> Self {
30 Self(ColorU::white())
31 }
32
33 pub fn red() -> Self {
34 Self(ColorU::from_u32(0xff0000ff))
35 }
36
37 pub fn green() -> Self {
38 Self(ColorU::from_u32(0x00ff00ff))
39 }
40
41 pub fn blue() -> Self {
42 Self(ColorU::from_u32(0x0000ffff))
43 }
44
45 pub fn yellow() -> Self {
46 Self(ColorU::from_u32(0xffff00ff))
47 }
48
49 pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
50 Self(ColorU::new(r, g, b, a))
51 }
52
53 pub fn from_u32(rgba: u32) -> Self {
54 Self(ColorU::from_u32(rgba))
55 }
56
57 pub fn blend(source: Color, dest: Color) -> Color {
58 // Skip blending if we don't need it.
59 if source.a == 255 {
60 return source;
61 } else if source.a == 0 {
62 return dest;
63 }
64
65 let source = source.0.to_f32();
66 let dest = dest.0.to_f32();
67
68 let a = source.a() + (dest.a() * (1. - source.a()));
69 let r = ((source.r() * source.a()) + (dest.r() * dest.a() * (1. - source.a()))) / a;
70 let g = ((source.g() * source.a()) + (dest.g() * dest.a() * (1. - source.a()))) / a;
71 let b = ((source.b() * source.a()) + (dest.b() * dest.a() * (1. - source.a()))) / a;
72
73 Self(ColorF::new(r, g, b, a).to_u8())
74 }
75
76 pub fn fade_out(&mut self, fade: f32) {
77 let fade = fade.clamp(0., 1.);
78 self.0.a = (self.0.a as f32 * (1. - fade)) as u8;
79 }
80}
81
82impl<'de> Deserialize<'de> for Color {
83 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
84 where
85 D: Deserializer<'de>,
86 {
87 let literal: Cow<str> = Deserialize::deserialize(deserializer)?;
88 if let Some(digits) = literal.strip_prefix('#') {
89 if let Ok(value) = u32::from_str_radix(digits, 16) {
90 if digits.len() == 6 {
91 return Ok(Color::from_u32((value << 8) | 0xFF));
92 } else if digits.len() == 8 {
93 return Ok(Color::from_u32(value));
94 }
95 }
96 }
97 Err(de::Error::invalid_value(
98 Unexpected::Str(literal.as_ref()),
99 &"#RRGGBB[AA]",
100 ))
101 }
102}
103
104impl ToJson for Color {
105 fn to_json(&self) -> serde_json::Value {
106 json!(format!(
107 "0x{:x}{:x}{:x}{:x}",
108 self.0.r, self.0.g, self.0.b, self.0.a
109 ))
110 }
111}
112
113impl Deref for Color {
114 type Target = ColorU;
115 fn deref(&self) -> &Self::Target {
116 &self.0
117 }
118}
119
120impl DerefMut for Color {
121 fn deref_mut(&mut self) -> &mut Self::Target {
122 &mut self.0
123 }
124}
125
126impl fmt::Debug for Color {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 self.0.fmt(f)
129 }
130}