carnelian/
color.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::Error;
6
7pub(crate) fn srgb_to_linear(l: u8) -> f32 {
8    let l = l as f32 * 255.0f32.recip();
9
10    if l <= 0.04045 {
11        l * 12.92f32.recip()
12    } else {
13        ((l + 0.055) * 1.055f32.recip()).powf(2.4)
14    }
15}
16
17/// Struct representing an RGBA color value in un-premultiplied non-linear sRGB space
18#[derive(Hash, Eq, PartialEq, Debug, Clone, Copy)]
19pub struct Color {
20    /// Red
21    pub r: u8,
22    /// Green
23    pub g: u8,
24    /// Blue
25    pub b: u8,
26    /// Alpha
27    pub a: u8,
28}
29
30impl Color {
31    /// Create a new color set to black with full alpha
32    pub const fn new() -> Color {
33        Color { r: 0, g: 0, b: 0, a: 255 }
34    }
35
36    /// Create a new color set to white with full alpha
37    pub const fn white() -> Color {
38        Color { r: 255, g: 255, b: 255, a: 255 }
39    }
40
41    /// Create a new color set to red with full alpha
42    pub const fn red() -> Color {
43        Color { r: 255, g: 0, b: 0, a: 255 }
44    }
45
46    /// Create a new color set to green with full alpha
47    pub const fn green() -> Color {
48        Color { r: 0, g: 255, b: 0, a: 255 }
49    }
50
51    /// Create a new color set to blue with full alpha
52    pub const fn blue() -> Color {
53        Color { r: 0, g: 0, b: 255, a: 255 }
54    }
55
56    /// Create a new color set to Fuchsia with full alpha
57    pub const fn fuchsia() -> Color {
58        Color { r: 255, g: 0, b: 255, a: 255 }
59    }
60
61    fn extract_hex_slice(hash_code: &str, start_index: usize) -> Result<u8, Error> {
62        Ok(u8::from_str_radix(&hash_code[start_index..start_index + 2], 16)?)
63    }
64
65    /// Create a color from a six or height hexadecimal digit string like '#EBD5B3'
66    pub fn from_hash_code(hash_code: &str) -> Result<Color, Error> {
67        let mut new_color = Color::new();
68        new_color.r = Color::extract_hex_slice(&hash_code, 1)?;
69        new_color.g = Color::extract_hex_slice(&hash_code, 3)?;
70        new_color.b = Color::extract_hex_slice(&hash_code, 5)?;
71        if hash_code.len() > 8 {
72            new_color.a = Color::extract_hex_slice(&hash_code, 7)?;
73        }
74        Ok(new_color)
75    }
76
77    /// Create a linear, premultiplied RGBA representation
78    pub fn to_linear_premult_rgba(&self) -> [f32; 4] {
79        let alpha = self.a as f32 * 255.0f32.recip();
80
81        [
82            srgb_to_linear(self.r) * alpha,
83            srgb_to_linear(self.g) * alpha,
84            srgb_to_linear(self.b) * alpha,
85            alpha,
86        ]
87    }
88
89    /// Create a linear BGRA representation
90    pub fn to_linear_bgra(&self) -> [f32; 4] {
91        [
92            srgb_to_linear(self.b),
93            srgb_to_linear(self.g),
94            srgb_to_linear(self.r),
95            self.a as f32 * 255.0f32.recip(),
96        ]
97    }
98
99    /// Create a premultiplied SRGB representation
100    pub fn to_srgb_premult_rgba(&self) -> [f32; 4] {
101        let recip = 255.0f32.recip();
102        let alpha = self.a as f32 * recip;
103
104        [
105            self.r as f32 * recip * alpha,
106            self.g as f32 * recip * alpha,
107            self.b as f32 * recip * alpha,
108            alpha,
109        ]
110    }
111}
112
113impl Default for Color {
114    fn default() -> Self {
115        Color::new()
116    }
117}