pixel_canvas/
color.rs

1//! Types and utilities to represent colors.
2
3use std::ops::{Add, Mul, Sub};
4
5// @Todo: Explain colors.
6
7/// A single RGB-888 color.
8// This must be repr(C) in order to directly upload to the GPU.
9#[repr(C)]
10#[derive(Copy, Clone, Default)]
11pub struct Color {
12    /// The red component.
13    pub r: u8,
14    /// The green component.
15    pub g: u8,
16    /// The blue component.
17    pub b: u8,
18}
19
20impl Color {
21    /// The color black.
22    pub const BLACK: Color = Color { r: 0, g: 0, b: 0 };
23    /// The color white.
24    pub const WHITE: Color = Color {
25        r: 255,
26        g: 255,
27        b: 255,
28    };
29
30    /// A convenience constructor for a color.
31    pub fn rgb(r: u8, g: u8, b: u8) -> Color {
32        Color { r, g, b }
33    }
34}
35
36/// A trait to blend between two values by some factor.
37pub trait Blend<T> {
38    /// Blend between two values.
39    /// ```rust
40    /// # use pixel_canvas::prelude::*;
41    /// // Blend entirely in integer math.
42    /// assert_eq!(100.blend(200, 0), 100);
43    /// assert_eq!(100.blend(200, 128), 150);
44    /// assert_eq!(100.blend(200, 255), 200);
45    /// // Blend with a floating point factor.
46    /// assert_eq!(100.blend(200, 0.0), 100);
47    /// assert_eq!(100.blend(200, 0.5), 150);
48    /// assert_eq!(100.blend(200, 1.0), 200);
49    /// ```
50    fn blend(self, other: Self, factor: T) -> Self;
51}
52
53impl Blend<u8> for u8 {
54    fn blend(self, other: u8, factor: u8) -> u8 {
55        (self as i16 + (((other as i16 - self as i16) * (factor as i16) + 127) / 255)) as u8
56    }
57}
58
59impl Blend<u8> for Color {
60    fn blend(self, other: Color, factor: u8) -> Color {
61        Color {
62            r: self.r.blend(other.r, factor),
63            g: self.g.blend(other.g, factor),
64            b: self.b.blend(other.b, factor),
65        }
66    }
67}
68
69impl Blend<f32> for u8 {
70    fn blend(self, other: u8, factor: f32) -> u8 {
71        (self as f32 * (1.0 - factor) + other as f32 * factor) as u8
72    }
73}
74
75impl Blend<f32> for Color {
76    fn blend(self, other: Color, factor: f32) -> Color {
77        Color {
78            r: self.r.blend(other.r, factor),
79            g: self.g.blend(other.g, factor),
80            b: self.b.blend(other.b, factor),
81        }
82    }
83}
84
85impl Add<Color> for Color {
86    type Output = Color;
87    fn add(self, rhs: Color) -> Color {
88        Color {
89            r: self.r.saturating_add(rhs.r),
90            g: self.g.saturating_add(rhs.g),
91            b: self.b.saturating_add(rhs.b),
92        }
93    }
94}
95
96impl Sub<Color> for Color {
97    type Output = Color;
98    fn sub(self, rhs: Color) -> Color {
99        Color {
100            r: self.r.saturating_sub(rhs.r),
101            g: self.g.saturating_sub(rhs.g),
102            b: self.b.saturating_sub(rhs.b),
103        }
104    }
105}
106
107impl Mul<Color> for Color {
108    type Output = Color;
109    fn mul(self, rhs: Color) -> Color {
110        Color {
111            r: ((self.r as u16 * rhs.r as u16) >> 8) as u8,
112            g: ((self.g as u16 * rhs.g as u16) >> 8) as u8,
113            b: ((self.b as u16 * rhs.b as u16) >> 8) as u8,
114        }
115    }
116}
117
118impl Mul<u8> for Color {
119    type Output = Color;
120    fn mul(self, rhs: u8) -> Color {
121        Color::BLACK.blend(self, rhs)
122    }
123}
124
125impl Mul<f32> for Color {
126    type Output = Color;
127    fn mul(self, rhs: f32) -> Color {
128        Color::BLACK.blend(self, rhs)
129    }
130}