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: BackendStyle { 10 /// Convert the RGB representation to the standard RGB tuple 11 #[inline(always)] rgb(&self) -> (u8, u8, u8)12 fn rgb(&self) -> (u8, u8, u8) { 13 self.color().rgb 14 } 15 16 /// Get the alpha channel of the color 17 #[inline(always)] alpha(&self) -> f6418 fn alpha(&self) -> f64 { 19 self.color().alpha 20 } 21 22 /// Mix the color with given opacity mix(&self, value: f64) -> RGBAColor23 fn mix(&self, value: f64) -> RGBAColor { 24 let (r, g, b) = self.rgb(); 25 let a = self.alpha() * value; 26 RGBAColor(r, g, b, a) 27 } 28 29 /// Convert the color into the RGBA color which is internally used by Plotters to_rgba(&self) -> RGBAColor30 fn to_rgba(&self) -> RGBAColor { 31 let (r, g, b) = self.rgb(); 32 let a = self.alpha(); 33 RGBAColor(r, g, b, a) 34 } 35 36 /// Make a filled style form the color filled(&self) -> ShapeStyle where Self: Sized,37 fn filled(&self) -> ShapeStyle 38 where 39 Self: Sized, 40 { 41 Into::<ShapeStyle>::into(self).filled() 42 } 43 44 /// Make a shape style with stroke width from a color stroke_width(&self, width: u32) -> ShapeStyle where Self: Sized,45 fn stroke_width(&self, width: u32) -> ShapeStyle 46 where 47 Self: Sized, 48 { 49 Into::<ShapeStyle>::into(self).stroke_width(width) 50 } 51 } 52 53 /// The RGBA representation of the color, Plotters use RGBA as the internal representation 54 /// of color 55 #[derive(Clone, PartialEq, Debug)] 56 pub struct RGBAColor(pub(crate) u8, pub(crate) u8, pub(crate) u8, pub(crate) f64); 57 58 impl BackendStyle for RGBAColor { 59 #[inline(always)] color(&self) -> BackendColor60 fn color(&self) -> BackendColor { 61 BackendColor { 62 rgb: (self.0, self.1, self.2), 63 alpha: self.3, 64 } 65 } 66 } 67 impl Color for RGBAColor {} 68 69 /// A color in the given palette 70 pub struct PaletteColor<P: Palette>(usize, PhantomData<P>); 71 72 impl<P: Palette> PaletteColor<P> { 73 /// Pick a color from the palette pick(idx: usize) -> PaletteColor<P>74 pub fn pick(idx: usize) -> PaletteColor<P> { 75 PaletteColor(idx % P::COLORS.len(), PhantomData) 76 } 77 } 78 79 impl<P: Palette> BackendStyle for PaletteColor<P> { 80 #[inline(always)] color(&self) -> BackendColor81 fn color(&self) -> BackendColor { 82 BackendColor { 83 rgb: P::COLORS[self.0], 84 alpha: 1.0, 85 } 86 } 87 } 88 89 impl<P: Palette> Color for PaletteColor<P> {} 90 91 /// The color described by its RGB value 92 #[derive(Debug)] 93 pub struct RGBColor(pub u8, pub u8, pub u8); 94 95 impl BackendStyle for RGBColor { 96 #[inline(always)] color(&self) -> BackendColor97 fn color(&self) -> BackendColor { 98 BackendColor { 99 rgb: (self.0, self.1, self.2), 100 alpha: 1.0, 101 } 102 } 103 } 104 105 impl Color for RGBColor {} 106 107 /// The color described by HSL color space 108 pub struct HSLColor(pub f64, pub f64, pub f64); 109 110 impl BackendStyle for HSLColor { 111 #[inline(always)] 112 #[allow(clippy::many_single_char_names)] color(&self) -> BackendColor113 fn color(&self) -> BackendColor { 114 let (h, s, l) = ( 115 self.0.min(1.0).max(0.0), 116 self.1.min(1.0).max(0.0), 117 self.2.min(1.0).max(0.0), 118 ); 119 120 if s == 0.0 { 121 let value = (l * 255.0).round() as u8; 122 return BackendColor { 123 rgb: (value, value, value), 124 alpha: 1.0, 125 }; 126 } 127 128 let q = if l < 0.5 { 129 l * (1.0 + s) 130 } else { 131 l + s - l * s 132 }; 133 let p = 2.0 * l - q; 134 135 let cvt = |mut t| { 136 if t < 0.0 { 137 t += 1.0; 138 } 139 if t > 1.0 { 140 t -= 1.0; 141 } 142 let value = if t < 1.0 / 6.0 { 143 p + (q - p) * 6.0 * t 144 } else if t < 1.0 / 2.0 { 145 q 146 } else if t < 2.0 / 3.0 { 147 p + (q - p) * (2.0 / 3.0 - t) * 6.0 148 } else { 149 p 150 }; 151 (value * 255.0).round() as u8 152 }; 153 154 BackendColor { 155 rgb: (cvt(h + 1.0 / 3.0), cvt(h), cvt(h - 1.0 / 3.0)), 156 alpha: 1.0, 157 } 158 } 159 } 160 161 impl Color for HSLColor {} 162