cichlid/color_util/
mod.rs

1//! Various Functions and traits for colors.
2//!
3//! The majority of these traits are not intended to be implemented by users. Rather, they are
4//! meant for allowing easy ways to fill iterators with color.
5//!
6//! ## Importing
7//!
8//! Importing is done with the following:
9//!
10//! ```
11//! use cichlid::prelude::*;
12//! ```
13//!
14//! It is preferable to import that traits anonymously with `use ... as _`. This is because
15//! these traits are not meant to be implemented directly, so only the types implementing
16//! the traits need to be imported.
17//!
18//! ## Traits
19//!
20//! - [`ColorIterMut`]:
21//!     - General functions applying on iterators over [`ColorRGB`]'s.
22//!     - Examples of functions: `fill()`, `clear()`.
23//!     - Implemented for all Iterators over `&mut ColorRGB`.
24//! - [`ColorSliceMut`]:
25//!     - Special optimized functions implemented for slices / arrays of [`ColorRGB`]'s.
26//!     - Examples of functions: `blur()`, `fade_to_black()`, `blend()`.
27//! - [`GradientFill`]:
28//!     - Fills a Gradient from one [`HSV`] to another using Linear Interpolation.
29//!     - Implemented for any iterators implementing `ExactSizeIter`.
30//!     - Also Generic over `Iterator::Item` for any item implementing `From<HSV>`, meaning
31//!       this method works on itorators over both `HSV` and `ColorRGB`.
32//! - [`GradientFillToInclusive`]:
33//!     - Same as `GradientFill`, but creates a gradient inclusive of the last `HSV`. This means
34//!       that the last item iterated over will be garunteed to be the last `HSV`, rather than
35//!       being the color before.
36//!     - Generally Requires a `DoubleEndedIter` trait be implemented for implementee's type.
37//! - [`GradientFillRGB`]:
38//!     - Works the same as`GradientFill`, but does a linear interpolation between two `ColorRGBs`.
39//!     - This results in a gradient that is a mathematically consistent transition, but isn't
40//!       as visually pleasing compared to doing Linear Interpolation with `HSV`s.
41//! - [`GradientFillRGBToInclusive`]:
42//!     - The `GradientFillToInclusive` to `GradientFillRGB`, as it fills up to and including the
43//!       last color.
44//!     - Also requires that the Iterator implements `DoubleEndedIter`.
45//! - [`RainbowFill`]:
46//!     - Fills an Iterator over `&mut From<HSV>` with a rainbow pattern. This pattern repeats
47//!       forever, starting at a specific hue and taking a user-defined step size for each new
48//!       element.
49//! - [`RainbowFillSingleCycle`]:
50//!     - Fills an Iterator with a full rainbow cycle.
51//!
52//! [`ColorIterMut`]: ./trait.ColorIterMut.html
53//! [`ColorSliceMut`]: ./trait.ColorSliceMut.html
54//! [`GradientFill`]: ./trait.GradientFill.html
55//! [`GradientFillToInclusive`]: ./trait.GradientFillToInclusive.html
56//! [`GradientFillRGB`]: ./trait.GradientFill.html
57//! [`GradientFillRGBToInclusive`]: ./trait.GradientFillRGBToInclusive.html
58//! [`RainbowFill`]: ./trait.RainbowFill.html
59//! [`RainbowFillSingleCycle`]: ./trait.RainbowFillSingleCycle.html
60//! [`ColorRGB`]: ../struct.ColorRGB.html
61//! [`HSV`]: ../struct.HSV.html
62
63pub mod color_impls;
64pub mod gradient;
65
66#[doc(hidden)]
67pub mod rainbow;
68
69use crate::{ColorRGB, HSV};
70
71/// Useful methods when iterating over `ColorRGB`s.
72///
73/// This is `impl`'d for any Iterator over `&mut RGB`. This includes both arrays and slices, the
74/// most common use case for this.
75///
76/// # Examples
77///
78/// Operating Directly on an array of `ColorRGB`s:
79///
80/// ```
81/// use cichlid::{prelude::*, ColorRGB};
82///
83/// let mut colors = [ColorRGB::BlanchedAlmond; 100];
84///
85/// colors.fill(ColorRGB::Yellow);
86/// colors.iter().for_each(|c| assert_eq!(*c, ColorRGB::Yellow));
87/// ```
88///
89/// Operating on slices is supported as well:
90///
91/// ```
92/// use cichlid::{prelude::*, ColorRGB};
93///
94/// let mut colors = [ColorRGB::Purple; 50];
95/// let color_slice = &mut colors[0..40];
96///
97/// color_slice.clear();
98/// color_slice.iter().for_each(|c| assert_eq!(*c, ColorRGB::Black));
99/// ```
100pub trait ColorIterMut: Sized {
101    /// Fills an entire Iterator with the specified color.
102    fn fill(self, color: ColorRGB);
103
104    /// Sets all colors to black.
105    #[inline]
106    fn clear(self) {
107        self.fill(mk_rgb!(0, 0, 0));
108    }
109}
110
111/// Optimized methods for iterating over arrays and slices of `ColorRGB`s.
112///
113/// # Usage
114///
115/// Normally, to scale an array by a fixed amount, something like is done:
116///
117/// ```
118/// use cichlid::ColorRGB;
119/// let mut colors = [ColorRGB::Pink; 50];
120/// colors.iter_mut().for_each(|c| c.fade_to_black_by(200));
121/// ```
122///
123///
124/// But, when operating on arrays, it's often faster to operate on multiple values at once
125/// using custom SIMD functions. `ColorSliceMut::fade_to_black()` implements this internally,
126/// resulting in dimming that is twice as fast as the above method.
127///
128/// ```
129/// use cichlid::{prelude::*, ColorRGB};
130/// let mut colors = [ColorRGB::Pink; 50];
131/// colors.fade_to_black(200); // Much faster!
132/// ```
133///
134/// # Examples
135///
136/// ```
137/// use cichlid::{prelude::*, ColorRGB};
138///
139/// let mut colors = [ColorRGB::Purple; 50];
140/// colors.fade_to_black(50);
141///
142/// let color_slice = &mut colors[0..40];
143/// color_slice.blur(50);
144/// color_slice.blend(ColorRGB::Gold, 120);
145/// ```
146pub trait ColorSliceMut: Sized {
147    /// Blurs colors by `blur_amount`.
148    ///
149    /// A lower `blur_amount` means a less extreme blur. For example, a `blur_amount` of 64
150    /// is a moderate blur, while past 171 the blur is somewhat flickery.
151    ///
152    /// This method does not retain brightness. Blurring will slowly fade all the colors to black.
153    fn blur(self, blur_amount: u8);
154
155    /// Fades all colors to black by a fraction.
156    ///
157    /// The `fade_by` parameter is interpreted as a fraction with a denominator of 255,
158    /// of which itself is the numerator. A higher `fade_by` means the colors a larger fade,
159    /// while a lower `face_by` results in less dimmed colors.
160    fn fade_to_black(self, fade_by: u8);
161
162    /// Applies `ColorRGB::blend()` to the entire slice.
163    fn blend(self, other: ColorRGB, amount_of_other: u8);
164}
165
166/// Fills an iterable object with a gradient from the `HSV` values `start` to `finish`, exclusive of the
167/// `finish`.
168///
169/// # Examples
170///
171/// ```
172/// use cichlid::{prelude::*, ColorRGB, HSV, GradientDirection};
173///
174/// let mut colors = [ColorRGB::Black; 24];
175/// let start = HSV::new(0, 255, 255);
176/// let end = HSV::new(100, 255, 180);
177/// colors.gradient_fill(start, end, GradientDirection::Longest);
178/// ```
179///
180/// Also usable over Iterators for `HSV`:
181///
182/// ```
183/// use cichlid::{prelude::*, HSV, GradientDirection};
184///
185/// let mut colors = [HSV::BLANK; 80];
186/// let start = HSV::new(0, 255, 255);
187/// let end = HSV::new(100, 255, 180);
188/// colors.gradient_fill(start, end, GradientDirection::Longest);
189/// ```
190pub trait GradientFill {
191    /// Fills a gradient from two HSV's using linear interpolation between the two.
192    fn gradient_fill(self, start: HSV, end: HSV, dir: GradientDirection);
193}
194
195/// Fills an iterable object with a gradient from the `HSV` values `start` to `finish`, inclusive of the
196/// `finish`.
197///
198/// # Examples
199///
200/// ```
201/// use cichlid::{prelude::*, ColorRGB, HSV, GradientDirection};
202///
203/// let mut colors = [ColorRGB::Black; 80];
204/// let start = HSV::new(130, 200, 251);
205/// let end = HSV::new(206, 100, 255);
206///
207/// colors.gradient_fill_to_inclusive(start, end, GradientDirection::Shortest);
208/// assert_eq!(*colors.last().unwrap(), ColorRGB::from(end));
209/// ```
210pub trait GradientFillToInclusive {
211    /// Fills a gradient from two HSV's using linear interpolation between the two, inclusive of
212    /// the end HSV.
213    fn gradient_fill_to_inclusive(self, start: HSV, end: HSV, dir: GradientDirection);
214}
215
216/// Fills an iterable object with a gradient from the `ColorRGB` values `start` to `finish`, exclusive of the
217/// `finish`.
218pub trait GradientFillRGB {
219    /// Fills a gradient from two RGBs's using linear interpolation between the two.
220    fn gradient_fill_rgb(self, start: ColorRGB, end: ColorRGB);
221}
222
223/// Fills an iterable object with a gradient from the `ColorRGB` values `start` to `finish`, inclusive of the
224/// `finish`.
225pub trait GradientFillRGBToInclusive {
226    /// Fills a gradient from two RGB's using linear interpolation between the two, inclusive of
227    /// the end RGB.
228    fn gradient_fill_rgb_to_inclusive(self, start: ColorRGB, end: ColorRGB);
229}
230
231/// Fills an iterable object with a rainbow hue of a desired step size.
232///
233/// Step sizes is a `u16`. The Most significant byte of each integer is used to represent the
234/// full number of hues to increment between each iterated value, while the second (LSB)
235/// byte is added as a fractional component.
236///
237/// For example, if one desires to change a single hue between each element, the `hue_delta`
238/// should be set to `0x0100`. If a hue is desired to change every 256 elements, then the
239/// `hue_delta` should be `0x0001`.
240pub trait RainbowFill: Sized {
241    /// Fills an object with a rainbow gradient hue of a desired step size and from a desired
242    /// starting hue.
243    #[inline(always)]
244    fn rainbow_fill(self, start_hue: u8, hue_delta: u16) {
245        self.rainbow_fill_with_sat_val(start_hue, hue_delta, 255, 255);
246    }
247
248    /// Fills an object with a rainbow gradient hue of a desired step size and from a desired
249    /// starting hue and constant additional saturation and value (components of a HSV).
250    fn rainbow_fill_with_sat_val(self, start_hue: u8, hue_delta: u16, sat: u8, val: u8);
251}
252
253/// Fills an iterable object with a single complete rainbow.
254///
255/// If the the rainbow is needed backwards, try calling `iter.rev()` before calling this
256/// method.
257pub trait RainbowFillSingleCycle {
258    fn rainbow_fill_single_cycle(self, start_hue: u8);
259}
260
261/// Possible Directions around the color wheel a hue can go.
262#[derive(Copy, Clone, Eq, PartialEq, Debug)]
263#[repr(u8)]
264pub enum HueDirection {
265    /// Goes around the color wheel clockwise. ala, Hue increases as the gradient progresses,
266    /// including integer wrapping.
267    Forward = 0,
268    /// Goes around the color wheel counter-clockwise. Hue decreases as the gradient progresses,
269    /// including integer wrapping.
270    Backwards = 1,
271}
272
273/// Possible Directions around the color wheel a gradient can go.
274#[derive(Copy, Clone, Eq, PartialEq, Debug)]
275#[repr(u8)]
276pub enum GradientDirection {
277    /// Goes around the color wheel clockwise. ala, Hue increases as the gradient progresses,
278    /// including integer wrapping.
279    Forward = 0,
280    /// Goes around the color wheel counter-clockwise. Hue decreases as the gradient progresses,
281    /// including integer wrapping.
282    Backwards = 1,
283    /// Goes around the color wheel by the shortest direction available.
284    Shortest = 2,
285    /// Goes around the color wheel by longest direction available.
286    Longest = 3,
287}
288
289impl GradientDirection {
290    /// Transforms a `GradientDirection` into a `HueDirection`.
291    ///
292    /// `hue_diff` is the difference between the ending hue and the starting hue. Specifically,
293    /// `hue_diff = end_hue.wrapping_sub(start_hue)`. This is needed in the cases where the
294    /// discriminant is neither forwards or backwards.
295    #[inline]
296    pub fn into_hue_direction(self, hue_diff: u8) -> HueDirection {
297        match self {
298            GradientDirection::Shortest => {
299                if hue_diff > 127 {
300                    HueDirection::Backwards
301                } else {
302                    HueDirection::Forward
303                }
304            }
305            GradientDirection::Longest => {
306                if hue_diff < 128 {
307                    HueDirection::Backwards
308                } else {
309                    HueDirection::Forward
310                }
311            }
312            GradientDirection::Forward => HueDirection::Forward,
313            GradientDirection::Backwards => HueDirection::Backwards,
314        }
315    }
316
317    /// Returns the difference between hues.
318    #[inline(always)]
319    fn into_hue_distance(self, start_hue: u8, end_hue: u8) -> i16 {
320        let hue_diff: u8 = end_hue.wrapping_sub(start_hue);
321        match self.into_hue_direction(hue_diff) {
322            HueDirection::Forward => i16::from(hue_diff) << 7,
323            HueDirection::Backwards => {
324                let hue_diff: u8 = (256u16).wrapping_sub(u16::from(hue_diff)) as u8;
325                let hue_diff: i16 = i16::from(hue_diff) << 7;
326                -hue_diff
327            }
328        }
329    }
330}
331
332impl From<HueDirection> for GradientDirection {
333    fn from(dir: HueDirection) -> GradientDirection {
334        match dir {
335            HueDirection::Forward => GradientDirection::Forward,
336            HueDirection::Backwards => GradientDirection::Backwards,
337        }
338    }
339}
340
341#[cfg(test)]
342mod test {
343    use super::*;
344
345    #[test]
346    fn blur_test() {
347        let mut arr = [
348            ColorRGB::Black,
349            ColorRGB::Red,
350            ColorRGB::BlueViolet,
351            ColorRGB::Yellow,
352        ];
353
354        for _ in 0..4 {
355            arr.blur(64);
356        }
357    }
358
359    #[test]
360    fn slice_color_itermut_test() {
361        let mut colors = [ColorRGB::Purple; 50];
362        let color_slice = &mut colors[0..40];
363        color_slice.blur(20);
364        color_slice.clear();
365        color_slice
366            .iter()
367            .for_each(|c| assert_eq!(*c, ColorRGB::Black));
368    }
369
370    #[test]
371    fn color_itermut_test() {
372        let mut colors = [ColorRGB::Gold; 50];
373        for i in 0..=255 {
374            colors.blur(i);
375        }
376    }
377
378    #[test]
379    fn blur_test_long() {
380        let mut arr = [ColorRGB::BlueViolet; 256];
381        for _ in 0..4 {
382            arr.blur(64);
383        }
384    }
385}