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#[derive(Copy, Clone, PartialEq, Debug, Default)]
65pub struct RGBAColor(pub(crate) u8, pub(crate) u8, pub(crate) u8, pub(crate) f64);
66
67impl Color for RGBAColor {
68    #[inline(always)]
69    fn to_backend_color(&self) -> BackendColor {
70        BackendColor {
71            rgb: (self.0, self.1, self.2),
72            alpha: self.3,
73        }
74    }
75}
76
77/// A color in the given palette
78pub struct PaletteColor<P: Palette>(usize, PhantomData<P>);
79
80impl<P: Palette> PaletteColor<P> {
81    /// Pick a color from the palette
82    pub fn pick(idx: usize) -> PaletteColor<P> {
83        PaletteColor(idx % P::COLORS.len(), PhantomData)
84    }
85}
86
87impl<P: Palette> Color for PaletteColor<P> {
88    #[inline(always)]
89    fn to_backend_color(&self) -> BackendColor {
90        BackendColor {
91            rgb: P::COLORS[self.0],
92            alpha: 1.0,
93        }
94    }
95}
96
97/// The color described by its RGB value
98#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
99pub struct RGBColor(pub u8, pub u8, pub u8);
100
101impl BackendStyle for RGBAColor {
102    fn color(&self) -> BackendColor {
103        self.to_backend_color()
104    }
105}
106
107impl Color for RGBColor {
108    #[inline(always)]
109    fn to_backend_color(&self) -> BackendColor {
110        BackendColor {
111            rgb: (self.0, self.1, self.2),
112            alpha: 1.0,
113        }
114    }
115}
116impl BackendStyle for RGBColor {
117    fn color(&self) -> BackendColor {
118        self.to_backend_color()
119    }
120}
121
122/// The color described by HSL color space
123pub struct HSLColor(pub f64, pub f64, pub f64);
124
125impl Color for HSLColor {
126    #[inline(always)]
127    #[allow(clippy::many_single_char_names)]
128    fn to_backend_color(&self) -> BackendColor {
129        let (h, s, l) = (
130            self.0.min(1.0).max(0.0),
131            self.1.min(1.0).max(0.0),
132            self.2.min(1.0).max(0.0),
133        );
134
135        if s == 0.0 {
136            let value = (l * 255.0).round() as u8;
137            return BackendColor {
138                rgb: (value, value, value),
139                alpha: 1.0,
140            };
141        }
142
143        let q = if l < 0.5 {
144            l * (1.0 + s)
145        } else {
146            l + s - l * s
147        };
148        let p = 2.0 * l - q;
149
150        let cvt = |mut t| {
151            if t < 0.0 {
152                t += 1.0;
153            }
154            if t > 1.0 {
155                t -= 1.0;
156            }
157            let value = if t < 1.0 / 6.0 {
158                p + (q - p) * 6.0 * t
159            } else if t < 1.0 / 2.0 {
160                q
161            } else if t < 2.0 / 3.0 {
162                p + (q - p) * (2.0 / 3.0 - t) * 6.0
163            } else {
164                p
165            };
166            (value * 255.0).round() as u8
167        };
168
169        BackendColor {
170            rgb: (cvt(h + 1.0 / 3.0), cvt(h), cvt(h - 1.0 / 3.0)),
171            alpha: 1.0,
172        }
173    }
174}