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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
use num_traits::{clamp, Signed, Zero};
use std::ops::Rem;

#[derive(Copy, Clone, Debug)]
/// Main color structure, holds RGBA but can be constructed
/// using other color spaces.
pub struct Color {
    /// Red value of the color in the range [0.0, 1.0].
    pub r: f32,
    /// Green value of the color in the range [0.0, 1.0].
    pub g: f32,
    /// Blue value of the color in the range [0.0, 1.0].
    pub b: f32,
    /// Alpha value of the color in the range [0.0, 1.0].
    pub a: f32,
}

impl Color {
    /// Shorthand to create a new RGBA color value. Clamps
    /// values to their respective ranges.
    pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
        let r = clamp(r, 0.0, 1.0);
        let g = clamp(g, 0.0, 1.0);
        let b = clamp(b, 0.0, 1.0);
        let a = clamp(a, 0.0, 1.0);

        Self { r, g, b, a }
    }

    /// Construct a color from an 8-digit hexidecimal color
    /// code.
    ///
    /// # Examples
    ///
    /// Basic usage:
    /// ```no_run
    /// use courgette::RGB;
    ///
    /// // FF at the end represents 1.0 alpha.
    /// let red = RGB::hex(0xFF0000FF);
    /// let yellow = RGB::hex(0xFFFF00FF);
    /// let green = RGB::hex(0x00FF00FF);
    /// let cyan = RGB::hex(0x00FFFFFF);
    /// let blue = RGB::hex(0x0000FFFF);
    /// let magenta = RGB::hex(0xFF00FFFF);
    /// ```
    pub fn hex(hex: u32) -> Self {
        Self::new(
            ((hex & 0xFF000000) >> 24) as f32 / 255.0,
            ((hex & 0x00FF0000) >> 16) as f32 / 255.0,
            ((hex & 0x0000FF00) >> 8) as f32 / 255.0,
            (hex & 0x000000FF) as f32 / 255.0,
        )
    }

    /// Construct a color from a 6-digit hexidecimal color
    /// code.
    pub fn hex_rgb(hex: u32) -> Self {
        Self::new(
            ((hex & 0xFF0000) >> 16) as f32 / 255.0,
            ((hex & 0x00FF00) >> 8) as f32 / 255.0,
            (hex & 0x0000FF) as f32 / 255.0,
            1.0,
        )
    }

    /// Construct a color from HSLA values. Applies modulo
    /// 360 to hue.
    pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Self {
        // https://en.wikipedia.org/wiki/HSL_and_HSV#Alternative_HSL_to_RGB
        //
        // Replace `a` with `x` so it doesn't inerfere with alpha.
        let h = modulo(h, 360.0);

        let x = s * l.min(1.0 - l);
        let f = |n: f32| {
            let k = modulo(n + h / 30.0, 12.0);
            l - x * (k - 3.0).min(9.0 - k).min(1.0).max(-1.0)
        };

        Self::new(f(0.0), f(8.0), f(4.0), a)
    }

    /// Construct a color from HSL values. Calls
    /// `Color::hsla` with alpha of 1.0.
    pub fn hsl(h: f32, s: f32, l: f32) -> Self {
        Self::hsla(h, s, l, 1.0)
    }

    /// Construct a color from HSVA values. Applies modulo
    /// 360 to hue.
    pub fn hsva(h: f32, s: f32, v: f32, a: f32) -> Self {
        // https://en.wikipedia.org/wiki/HSL_and_HSV#Alternative_HSL_to_RGB
        //
        // Replace `a` with `x` so it doesn't inerfere with alpha.
        let h = modulo(h, 360.0);

        let f = |n: f32| {
            let k = modulo(n + h / 60.0, 6.0);
            v - v * s * k.min(4.0 - k).min(1.0).max(0.0)
        };

        Self::new(f(5.0), f(3.0), f(1.0), a)

    }

    /// Construct a color from HSV values. Calls
    /// `Color::hsva` with alpha of 1.0.
    pub fn hsv(h: f32, s: f32, v: f32) -> Self {
        Self::hsva(h, s, v, 1.0)
    }

    /// Construct a color from HSBA values. HSB is
    /// equivelant to HSV, therefore this simply calls
    /// `Color::hsva`.
    pub fn hsba(h: f32, s: f32, b: f32, a: f32) -> Self {
        Self::hsva(h, s, b, a)
    }

    /// Construct a color from HSB values. Calls
    /// `Color::hsba` with alpha of 1.0.
    pub fn hsb(h: f32, s: f32, b: f32) -> Self {
        Self::hsva(h, s, b, 1.0)
    }

    /// Change the brightness of the current color, with a
    /// percent of 1.0 being normal brightness.
    ///
    /// # Examples
    ///
    /// Basic usage:
    /// ```no_run
    /// use courgette::Color;
    ///
    /// let color = Color::hex(0xff4488ff);
    ///
    /// let brighter = color.brightness(1.1); // Lighten color by 10%.
    /// let darker = color.brightness(0.9); // Dark color by 10%.
    /// ```
    pub fn brightness(self, percent: f32) -> Self {
        let a = self.a;
        let (h, s, _) = self.get_hsv();

        Self::hsva(h, s, percent, a)
    }

    /// Invert the current color.
    pub fn invert(self) -> Self {
        let Self { r, g, b, a } = self;

        Self::new(1.0 - r, 1.0 - g, 1.0 - b, a)
    }

    /// Get greyscale version of the color.
    pub fn grey(self) -> Self {
        let mean = (self.r + self.g + self.b) / 3.0;

        Self::new(mean, mean, mean, self.a)
    }

    /// Get hue shifted color of the current color. Hue
    /// value has a modulo of 360.0 applied.
    pub fn hue(self, hue: f32) -> Self {
        let hue = modulo(hue, 360.0);

        let a = self.a;
        let (_, s, v) = self.get_hsv();

        Self::hsla(hue, s, v, a)
    }

    /// Hue shift the current color. Changes hue by `dhue`
    /// (delta hue) value.
    pub fn hue_shift(self, dhue: f32) -> Self {
        let (h, _, _) = self.get_hsv();

        self.hue(h + dhue)
    }

    fn get_hsv(self) -> (f32, f32, f32) {
        // https://en.wikipedia.org/wiki/HSL_and_HSV#RGB_to_HSL_and_HSV
        let Self { r, g, b, .. } = self;

        let max = r.max(g.max(b));
        let min = r.min(g.min(b));

        let delta = max - min;

        let h = match max {
            m if m == r => 60.0 * ((g - b) / delta),
            m if m == g => 60.0 * (2.0 + (b - r) / delta),
            m if m == b => 60.0 * (4.0 + (r - g) / delta),
            _ => 0.0,
        };

        let h = if h < 0.0 { h + 360.0 } else { h };
        let s = if max == 0.0 { 0.0 } else { delta / max };
        let v = max;

        (h, s, v)
    }

}

impl Into<[f32; 4]> for Color {
    fn into(self) -> [f32; 4] {
        let Self { r, g, b, a } = self;

        [r, g, b, a]
    }
}

impl Into<[f32; 3]> for Color {
    fn into(self) -> [f32; 3] {
        let Self { r, g, b, .. } = self;

        [r, g, b]
    }
}

fn modulo<T>(num: T, det: T) -> T
where
    T: Rem<Output = T>,
    T: PartialOrd,
    T: Zero,
    T: Signed,
    T: Copy,
{
    let num = num % det;
    if num < T::zero() {
        num + det.abs()
    } else {
        num
    }
}