1 use super::palette::Palette;
2 use super::ShapeStyle;
3 
4 use plotters_backend::{BackendColor, BackendStyle};
5 
6 use std::marker::PhantomData;
7 
8 /// Any color representation
9 pub trait Color {
10     /// Normalize this color representation to the backend color
to_backend_color(&self) -> BackendColor11     fn to_backend_color(&self) -> BackendColor;
12 
13     /// Convert the RGB representation to the standard RGB tuple
14     #[inline(always)]
rgb(&self) -> (u8, u8, u8)15     fn rgb(&self) -> (u8, u8, u8) {
16         self.to_backend_color().rgb
17     }
18 
19     /// Get the alpha channel of the color
20     #[inline(always)]
alpha(&self) -> f6421     fn alpha(&self) -> f64 {
22         self.to_backend_color().alpha
23     }
24 
25     /// Mix the color with given opacity
mix(&self, value: f64) -> RGBAColor26     fn mix(&self, value: f64) -> RGBAColor {
27         let (r, g, b) = self.rgb();
28         let a = self.alpha() * value;
29         RGBAColor(r, g, b, a)
30     }
31 
32     /// Convert the color into the RGBA color which is internally used by Plotters
to_rgba(&self) -> RGBAColor33     fn to_rgba(&self) -> RGBAColor {
34         let (r, g, b) = self.rgb();
35         let a = self.alpha();
36         RGBAColor(r, g, b, a)
37     }
38 
39     /// Make a filled style form the color
filled(&self) -> ShapeStyle where Self: Sized,40     fn filled(&self) -> ShapeStyle
41     where
42         Self: Sized,
43     {
44         Into::<ShapeStyle>::into(self).filled()
45     }
46 
47     /// Make a shape style with stroke width from a color
stroke_width(&self, width: u32) -> ShapeStyle where Self: Sized,48     fn stroke_width(&self, width: u32) -> ShapeStyle
49     where
50         Self: Sized,
51     {
52         Into::<ShapeStyle>::into(self).stroke_width(width)
53     }
54 }
55 
56 impl<T: Color> Color for &'_ T {
to_backend_color(&self) -> BackendColor57     fn to_backend_color(&self) -> BackendColor {
58         <T as Color>::to_backend_color(*self)
59     }
60 }
61 
62 /// The RGBA representation of the color, Plotters use RGBA as the internal representation
63 /// of color
64 ///
65 /// If you want to directly create a RGB color with transparency use [RGBColor::mix]
66 #[derive(Copy, Clone, PartialEq, Debug, Default)]
67 pub struct RGBAColor(pub u8, pub u8, pub u8, pub f64);
68 
69 impl Color for RGBAColor {
70     #[inline(always)]
to_backend_color(&self) -> BackendColor71     fn to_backend_color(&self) -> BackendColor {
72         BackendColor {
73             rgb: (self.0, self.1, self.2),
74             alpha: self.3,
75         }
76     }
77 }
78 
79 impl From<RGBColor> for RGBAColor {
from(rgb: RGBColor) -> Self80     fn from(rgb: RGBColor) -> Self {
81         Self(rgb.0, rgb.1, rgb.2, 1.0)
82     }
83 }
84 
85 /// A color in the given palette
86 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
87 pub struct PaletteColor<P: Palette>(usize, PhantomData<P>);
88 
89 impl<P: Palette> PaletteColor<P> {
90     /// Pick a color from the palette
pick(idx: usize) -> PaletteColor<P>91     pub fn pick(idx: usize) -> PaletteColor<P> {
92         PaletteColor(idx % P::COLORS.len(), PhantomData)
93     }
94 }
95 
96 impl<P: Palette> Color for PaletteColor<P> {
97     #[inline(always)]
to_backend_color(&self) -> BackendColor98     fn to_backend_color(&self) -> BackendColor {
99         BackendColor {
100             rgb: P::COLORS[self.0],
101             alpha: 1.0,
102         }
103     }
104 }
105 
106 /// The color described by its RGB value
107 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
108 pub struct RGBColor(pub u8, pub u8, pub u8);
109 
110 impl BackendStyle for RGBAColor {
color(&self) -> BackendColor111     fn color(&self) -> BackendColor {
112         self.to_backend_color()
113     }
114 }
115 
116 impl Color for RGBColor {
117     #[inline(always)]
to_backend_color(&self) -> BackendColor118     fn to_backend_color(&self) -> BackendColor {
119         BackendColor {
120             rgb: (self.0, self.1, self.2),
121             alpha: 1.0,
122         }
123     }
124 }
125 impl BackendStyle for RGBColor {
color(&self) -> BackendColor126     fn color(&self) -> BackendColor {
127         self.to_backend_color()
128     }
129 }
130 
131 /// The color described by HSL color space
132 #[derive(Copy, Clone, PartialEq, Debug, Default)]
133 pub struct HSLColor(pub f64, pub f64, pub f64);
134 
135 impl Color for HSLColor {
136     #[inline(always)]
137     #[allow(clippy::many_single_char_names)]
to_backend_color(&self) -> BackendColor138     fn to_backend_color(&self) -> BackendColor {
139         let (h, s, l) = (
140             self.0.min(1.0).max(0.0),
141             self.1.min(1.0).max(0.0),
142             self.2.min(1.0).max(0.0),
143         );
144 
145         if s == 0.0 {
146             let value = (l * 255.0).round() as u8;
147             return BackendColor {
148                 rgb: (value, value, value),
149                 alpha: 1.0,
150             };
151         }
152 
153         let q = if l < 0.5 {
154             l * (1.0 + s)
155         } else {
156             l + s - l * s
157         };
158         let p = 2.0 * l - q;
159 
160         let cvt = |mut t| {
161             if t < 0.0 {
162                 t += 1.0;
163             }
164             if t > 1.0 {
165                 t -= 1.0;
166             }
167             let value = if t < 1.0 / 6.0 {
168                 p + (q - p) * 6.0 * t
169             } else if t < 1.0 / 2.0 {
170                 q
171             } else if t < 2.0 / 3.0 {
172                 p + (q - p) * (2.0 / 3.0 - t) * 6.0
173             } else {
174                 p
175             };
176             (value * 255.0).round() as u8
177         };
178 
179         BackendColor {
180             rgb: (cvt(h + 1.0 / 3.0), cvt(h), cvt(h - 1.0 / 3.0)),
181             alpha: 1.0,
182         }
183     }
184 }
185