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}