Skip to main content

valo_geometry/
color.rs

1/// Straight (unpremultiplied) sRGB color, components 0..=1.
2///
3/// Premultiplication happens at the GPU boundary (`premultiplied()`), and valo
4/// blends in sRGB space — the CSS/Skia-compatible look. Linear-light blending
5/// and wide gamut are deliberately deferred: when they land, this type stays and
6/// the conversion moves into the uniform-fill path.
7#[derive(Clone, Copy, Debug, Default, PartialEq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct Color {
10    pub r: f32,
11    pub g: f32,
12    pub b: f32,
13    pub a: f32,
14}
15
16impl Color {
17    pub const TRANSPARENT: Color = Color::rgba(0.0, 0.0, 0.0, 0.0);
18    pub const BLACK: Color = Color::rgba(0.0, 0.0, 0.0, 1.0);
19    pub const WHITE: Color = Color::rgba(1.0, 1.0, 1.0, 1.0);
20
21    pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
22        Self { r, g, b, a }
23    }
24
25    pub const fn rgb(r: f32, g: f32, b: f32) -> Self {
26        Self { r, g, b, a: 1.0 }
27    }
28
29    /// From 8-bit sRGB (the CSS `#rrggbbaa` layout).
30    pub fn from_rgba8(r: u8, g: u8, b: u8, a: u8) -> Self {
31        Self {
32            r: r as f32 / 255.0,
33            g: g as f32 / 255.0,
34            b: b as f32 / 255.0,
35            a: a as f32 / 255.0,
36        }
37    }
38
39    pub fn with_alpha(self, a: f32) -> Self {
40        Self { a, ..self }
41    }
42
43    /// Straight components in draw order `[r, g, b, a]`.
44    pub fn components(self) -> [f32; 4] {
45        [self.r, self.g, self.b, self.a]
46    }
47
48    /// Alpha-premultiplied components in draw order `[r, g, b, a]` — what the
49    /// uniform fill hands the blender.
50    pub fn premultiplied(self) -> [f32; 4] {
51        [self.r * self.a, self.g * self.a, self.b * self.a, self.a]
52    }
53
54    pub fn is_opaque(self) -> bool {
55        self.a >= 1.0
56    }
57}