zng_webrender_api/
color.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use peek_poke::PeekPoke;
6use std::cmp;
7use std::hash::{Hash, Hasher};
8
9/// Represents pre-multiplied RGBA colors with floating point numbers.
10///
11/// All components must be between 0.0 and 1.0.
12/// An alpha value of 1.0 is opaque while 0.0 is fully transparent.
13///
14/// In premultiplied colors transitions to transparent always look "nice"
15/// therefore they are used in CSS gradients.
16#[repr(C)]
17#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
18pub struct PremultipliedColorF {
19    pub r: f32,
20    pub g: f32,
21    pub b: f32,
22    pub a: f32,
23}
24
25#[allow(missing_docs)]
26impl PremultipliedColorF {
27    pub const BLACK: PremultipliedColorF = PremultipliedColorF { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
28    pub const TRANSPARENT: PremultipliedColorF = PremultipliedColorF { r: 0.0, g: 0.0, b: 0.0, a: 0.0 };
29    pub const WHITE: PremultipliedColorF = PremultipliedColorF { r: 1.0, g: 1.0, b: 1.0, a: 1.0 };
30
31    pub fn to_array(&self) -> [f32; 4] {
32        [self.r, self.g, self.b, self.a]
33    }
34}
35
36/// Represents RGBA screen colors with floating point numbers.
37///
38/// All components must be between 0.0 and 1.0.
39/// An alpha value of 1.0 is opaque while 0.0 is fully transparent.
40#[repr(C)]
41#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
42pub struct ColorF {
43    pub r: f32,
44    pub g: f32,
45    pub b: f32,
46    pub a: f32,
47}
48
49#[allow(missing_docs)]
50impl ColorF {
51    pub const BLACK: ColorF = ColorF { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
52    pub const TRANSPARENT: ColorF = ColorF { r: 0.0, g: 0.0, b: 0.0, a: 0.0 };
53    pub const WHITE: ColorF = ColorF { r: 1.0, g: 1.0, b: 1.0, a: 1.0 };
54
55    /// Constructs a new `ColorF` from its components.
56    pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
57        ColorF { r, g, b, a }
58    }
59
60    /// Multiply the RGB channels (but not alpha) with a given factor.
61    pub fn scale_rgb(&self, scale: f32) -> Self {
62        ColorF {
63            r: self.r * scale,
64            g: self.g * scale,
65            b: self.b * scale,
66            a: self.a,
67        }
68    }
69
70    // Scale the alpha by a given factor.
71    pub fn scale_alpha(&self, scale: f32) -> Self {
72        ColorF {
73            r: self.r,
74            g: self.g,
75            b: self.b,
76            a: self.a * scale,
77        }
78    }
79
80    pub fn to_array(&self) -> [f32; 4] {
81        [self.r, self.g, self.b, self.a]
82    }
83
84    /// Multiply the RGB components with the alpha channel.
85    pub fn premultiplied(&self) -> PremultipliedColorF {
86        let c = self.scale_rgb(self.a);
87        PremultipliedColorF { r: c.r, g: c.g, b: c.b, a: c.a }
88    }
89}
90
91// Floats don't impl Hash/Eq/Ord...
92impl Eq for PremultipliedColorF {}
93impl Ord for PremultipliedColorF {
94    fn cmp(&self, other: &Self) -> cmp::Ordering {
95        self.partial_cmp(other).unwrap_or(cmp::Ordering::Equal)
96    }
97}
98
99#[allow(clippy::derived_hash_with_manual_eq)]
100impl Hash for PremultipliedColorF {
101    fn hash<H: Hasher>(&self, state: &mut H) {
102        // Note: this is inconsistent with the Eq impl for -0.0 (don't care).
103        self.r.to_bits().hash(state);
104        self.g.to_bits().hash(state);
105        self.b.to_bits().hash(state);
106        self.a.to_bits().hash(state);
107    }
108}
109
110/// Represents RGBA screen colors with one byte per channel.
111///
112/// If the alpha value `a` is 255 the color is opaque.
113#[repr(C)]
114#[derive(Clone, Copy, Hash, Eq, Debug, Deserialize, MallocSizeOf, PartialEq)]
115#[derive(PartialOrd, Ord, Serialize, PeekPoke, Default)]
116pub struct ColorU {
117    pub r: u8,
118    pub g: u8,
119    pub b: u8,
120    pub a: u8,
121}
122
123impl ColorU {
124    /// Constructs a new additive `ColorU` from its components.
125    pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
126        ColorU { r, g, b, a }
127    }
128}
129
130fn round_to_int(x: f32) -> u8 {
131    debug_assert!((0.0 <= x) && (x <= 1.0), "{} should be between 0 and 1", x);
132    let f = (255.0 * x) + 0.5;
133    let val = f.floor();
134    debug_assert!(val <= 255.0);
135    val as u8
136}
137
138// TODO: We shouldn't really convert back to `ColorU` ever,
139// since it's lossy. One of the blockers is that all of our debug colors
140// are specified in `ColorF`. Changing it to `ColorU` would be nice.
141impl From<ColorF> for ColorU {
142    fn from(color: ColorF) -> Self {
143        ColorU {
144            r: round_to_int(color.r),
145            g: round_to_int(color.g),
146            b: round_to_int(color.b),
147            a: round_to_int(color.a),
148        }
149    }
150}
151
152impl From<ColorU> for ColorF {
153    fn from(color: ColorU) -> Self {
154        ColorF {
155            r: color.r as f32 / 255.0,
156            g: color.g as f32 / 255.0,
157            b: color.b as f32 / 255.0,
158            a: color.a as f32 / 255.0,
159        }
160    }
161}