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