holiday_lights/lights/src/rgb.rs

73 lines
1.9 KiB
Rust

//! Utilities for working with standard 24-bit RGB color pixels
/// An RGB color value in some perceptual color space;
/// ideally this will be gamma-corrected before display,
/// but this form is better for doing gradient/fade/etc calculations on.
#[derive(Clone, Copy, PartialEq)]
pub struct Rgb(pub u8, pub u8, pub u8);
impl core::ops::Add for Rgb {
type Output = Rgb;
#[inline]
fn add(self, rhs: Rgb) -> Rgb {
Rgb(
self.0.saturating_add(rhs.0),
self.1.saturating_add(rhs.1),
self.2.saturating_add(rhs.2)
)
}
}
impl core::ops::Mul for Rgb {
type Output = Rgb;
#[inline]
fn mul(self, rhs: Rgb) -> Rgb {
Rgb(
(self.0 as usize * rhs.0 as usize / 255) as u8,
(self.1 as usize * rhs.1 as usize / 255) as u8,
(self.2 as usize * rhs.2 as usize / 255) as u8
)
}
}
impl core::ops::Mul<u8> for Rgb
{
type Output = Rgb;
#[inline]
fn mul(self, rhs: u8) -> Rgb {
Rgb(
self.0.saturating_mul(rhs),
self.1.saturating_mul(rhs),
self.2.saturating_mul(rhs)
)
}
}
/// Construct an [Rgb] from a monochrome value.
#[inline]
pub const fn gray(gray: u8) -> Rgb {
Rgb(gray, gray, gray)
}
/// Interpolate linearly between two colors.
///
/// The "mix" value is RGB to permit blending channels individually,
/// but most often you will probably use [gray] to generate the mix value.
#[inline]
pub fn blend(a: Rgb, b: Rgb, mix: Rgb) -> Rgb {
(a * Rgb(255 - mix.0, 255 - mix.1, 255 - mix.2)) + (b * mix)
}
#[inline]
pub fn blend_steps(from: Rgb, to: Rgb, steps: usize, x: usize) -> Rgb {
blend(from, to, gray((x * 255 / steps) as u8))
}
#[inline]
pub fn linear_gradient(from: Rgb, to: Rgb, steps: usize) -> impl Iterator<Item = Rgb> + DoubleEndedIterator + Clone {
(0..steps).map(move |x| blend_steps(from, to, steps, x))
}