plotters/style/
color.rs

1use super::palette::Palette;
2use super::ShapeStyle;
3
4use plotters_backend::{BackendColor, BackendStyle};
5
6use std::marker::PhantomData;
7
8/// Any color representation
9pub trait Color {
10    /// Normalize this color representation to the backend color
11    fn to_backend_color(&self) -> BackendColor;
12
13    /// Convert the RGB representation to the standard RGB tuple
14    #[inline(always)]
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)]
21    fn alpha(&self) -> f64 {
22        self.to_backend_color().alpha
23    }
24
25    /// Mix the color with given opacity
26    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
33    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
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
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
56impl<T: Color> Color for &'_ T {
57    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)]
67pub struct RGBAColor(pub u8, pub u8, pub u8, pub f64);
68
69impl Color for RGBAColor {
70    #[inline(always)]
71    fn to_backend_color(&self) -> BackendColor {
72        BackendColor {
73            rgb: (self.0, self.1, self.2),
74            alpha: self.3,
75        }
76    }
77}
78
79impl From<RGBColor> for RGBAColor {
80    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)]
87pub struct PaletteColor<P: Palette>(usize, PhantomData<P>);
88
89impl<P: Palette> PaletteColor<P> {
90    /// Pick a color from the palette
91    pub fn pick(idx: usize) -> PaletteColor<P> {
92        PaletteColor(idx % P::COLORS.len(), PhantomData)
93    }
94}
95
96impl<P: Palette> Color for PaletteColor<P> {
97    #[inline(always)]
98    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)]
108pub struct RGBColor(pub u8, pub u8, pub u8);
109
110impl BackendStyle for RGBAColor {
111    fn color(&self) -> BackendColor {
112        self.to_backend_color()
113    }
114}
115
116impl Color for RGBColor {
117    #[inline(always)]
118    fn to_backend_color(&self) -> BackendColor {
119        BackendColor {
120            rgb: (self.0, self.1, self.2),
121            alpha: 1.0,
122        }
123    }
124}
125impl BackendStyle for RGBColor {
126    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)]
133pub struct HSLColor(pub f64, pub f64, pub f64);
134
135impl Color for HSLColor {
136    #[inline(always)]
137    #[allow(clippy::many_single_char_names)]
138    fn to_backend_color(&self) -> BackendColor {
139        let (h, s, l) = (
140            self.0.clamp(0.0, 1.0),
141            self.1.clamp(0.0, 1.0),
142            self.2.clamp(0.0, 1.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}