color.rs (4885B)
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 5 use peek_poke::PeekPoke; 6 use std::cmp; 7 use 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)] 18 pub struct PremultipliedColorF { 19 pub r: f32, 20 pub g: f32, 21 pub b: f32, 22 pub a: f32, 23 } 24 25 #[allow(missing_docs)] 26 impl 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)] 42 pub struct ColorF { 43 pub r: f32, 44 pub g: f32, 45 pub b: f32, 46 pub a: f32, 47 } 48 49 #[allow(missing_docs)] 50 impl 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... 92 impl Eq for PremultipliedColorF {} 93 impl 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)] 100 impl 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)] 116 pub struct ColorU { 117 pub r: u8, 118 pub g: u8, 119 pub b: u8, 120 pub a: u8, 121 } 122 123 impl 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 130 fn 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. 141 impl 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 152 impl 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 }