1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use super::Rgb;
use crate::converters::{as_rounded_hsl_tuple, hsl_to_rgb, invert_hue};
use crate::error::ParseError;
use crate::normalize::{normalize_hsl, normalize_hue, normalize_percent, normalize_ratio};
use crate::{from_str, AlphaColor, Color, ColorTuple, ColorTupleA, RgbUnit};

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Hsl {
  h: f32,
  s: f32,
  l: f32,
  a: Option<f32>,
}

impl Hsl {
  pub fn from(h: f32, s: f32, l: f32) -> Hsl {
    Hsl::from_tuple(&(h, s, l))
  }

  pub fn grayscale(&self) -> Hsl {
    Hsl { h: 0.0, s: 0.0, l: self.l, a: self.a }
  }
}

impl std::str::FromStr for Hsl {
  type Err = ParseError;

  fn from_str(s: &str) -> Result<Hsl, ParseError> {
    match from_str::hsl(s) {
      Ok(hsl_tuple) => Ok(Hsl::from_tuple(&hsl_tuple)),
      Err(err) => Err(err),
    }
  }
}

impl Color for Hsl {
  type Tuple = ColorTuple;
  type TupleA = ColorTupleA;

  fn new() -> Hsl {
    Hsl { h: 0.0, s: 0.0, l: 0.0, a: None }
  }

  fn get_hue(&self) -> f32 {
    self.h
  }
  fn get_saturation(&self) -> f32 {
    self.s
  }
  fn get_lightness(&self) -> f32 {
    self.l
  }
  fn set_hue(&self, val: f32) -> Hsl {
    Hsl { h: normalize_hue(val), s: self.s, l: self.l, a: self.a }
  }
  fn set_saturation(&self, val: f32) -> Hsl {
    Hsl { h: self.h, s: normalize_percent(val), l: self.l, a: self.a }
  }
  fn set_lightness(&self, val: f32) -> Hsl {
    Hsl { h: self.h, s: self.s, l: normalize_percent(val), a: self.a }
  }

  fn get_red(&self) -> f32 {
    self.to_rgb().get_red()
  }
  fn get_green(&self) -> f32 {
    self.to_rgb().get_green()
  }
  fn get_blue(&self) -> f32 {
    self.to_rgb().get_blue()
  }
  fn set_red(&self, val: f32) -> Hsl {
    self.to_rgb().set_red(val).to_hsl()
  }
  fn set_green(&self, val: f32) -> Hsl {
    self.to_rgb().set_green(val).to_hsl()
  }
  fn set_blue(&self, val: f32) -> Hsl {
    self.to_rgb().set_blue(val).to_hsl()
  }

  fn to_rgb(&self) -> Rgb {
    Rgb::from_tuple(&hsl_to_rgb(&self.as_tuple()))
  }
  fn to_hsl(&self) -> Hsl {
    *self
  }
  fn to_css_string(&self) -> String {
    let (h, s, l) = as_rounded_hsl_tuple(&self.as_tuple());
    format!("hsl({},{}%,{}%)", h, s, l)
  }
  fn from_tuple(t: &ColorTuple) -> Hsl {
    let (h, s, l) = normalize_hsl(&t);
    Hsl { h, s, l, a: None }
  }
  fn from_tuple_with_alpha(t: &ColorTupleA) -> Hsl {
    let (h, s, l, a) = *t;
    let (h, s, l) = normalize_hsl(&(h, s, l));
    Hsl { h, s, l, a: Some(normalize_ratio(a)) }
  }
  fn as_tuple(&self) -> ColorTuple {
    (self.h, self.s, self.l)
  }
  fn lighten(&self, val: f32) -> Hsl {
    self.set_lightness(self.l + val)
  }
  fn saturate(&self, val: f32) -> Hsl {
    self.set_saturation(self.s + val)
  }
  fn adjust_hue(&self, hue: f32) -> Hsl {
    self.set_hue(self.h + hue)
  }
  fn adjust_color(&self, name: RgbUnit, val: f32) -> Hsl {
    self.to_rgb().adjust_color(name, val).to_hsl()
  }

  fn invert(&self) -> Hsl {
    Hsl { h: invert_hue(self.h), s: self.s, l: self.l, a: self.a }
  }
}

impl AlphaColor for Hsl {
  fn get_alpha(&self) -> f32 {
    self.a.unwrap_or(1.0)
  }
  fn set_alpha(&self, a: f32) -> Hsl {
    Hsl { h: self.h, s: self.s, l: self.l, a: Some(normalize_ratio(a)) }
  }
  fn opacify(&self, a: f32) -> Hsl {
    self.set_alpha(self.get_alpha() + a)
  }
}