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 #[derive(Copy, Clone, PartialEq, Debug, Default)] 65 pub struct RGBAColor(pub(crate) u8, pub(crate) u8, pub(crate) u8, pub(crate) f64); 66 67 impl Color for RGBAColor { 68 #[inline(always)] to_backend_color(&self) -> BackendColor69 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 78 pub struct PaletteColor<P: Palette>(usize, PhantomData<P>); 79 80 impl<P: Palette> PaletteColor<P> { 81 /// Pick a color from the palette pick(idx: usize) -> PaletteColor<P>82 pub fn pick(idx: usize) -> PaletteColor<P> { 83 PaletteColor(idx % P::COLORS.len(), PhantomData) 84 } 85 } 86 87 impl<P: Palette> Color for PaletteColor<P> { 88 #[inline(always)] to_backend_color(&self) -> BackendColor89 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)] 99 pub struct RGBColor(pub u8, pub u8, pub u8); 100 101 impl BackendStyle for RGBAColor { color(&self) -> BackendColor102 fn color(&self) -> BackendColor { 103 self.to_backend_color() 104 } 105 } 106 107 impl Color for RGBColor { 108 #[inline(always)] to_backend_color(&self) -> BackendColor109 fn to_backend_color(&self) -> BackendColor { 110 BackendColor { 111 rgb: (self.0, self.1, self.2), 112 alpha: 1.0, 113 } 114 } 115 } 116 impl BackendStyle for RGBColor { color(&self) -> BackendColor117 fn color(&self) -> BackendColor { 118 self.to_backend_color() 119 } 120 } 121 122 /// The color described by HSL color space 123 pub struct HSLColor(pub f64, pub f64, pub f64); 124 125 impl Color for HSLColor { 126 #[inline(always)] 127 #[allow(clippy::many_single_char_names)] to_backend_color(&self) -> BackendColor128 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 } 175