| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use anyhow::Error; |
| use fidl_fuchsia_ui_gfx::ColorRgba; |
| |
| fn srgb_to_linear(l: u8) -> f32 { |
| let l = l as f32 * 255.0f32.recip(); |
| |
| if l <= 0.04045 { |
| l * 12.92f32.recip() |
| } else { |
| ((l + 0.055) * 1.055f32.recip()).powf(2.4) |
| } |
| } |
| |
| /// Struct representing an RGBA color value in un-premultiplied non-linear sRGB space |
| #[derive(Hash, Eq, PartialEq, Debug, Clone, Copy)] |
| pub struct Color { |
| /// Red |
| pub r: u8, |
| /// Green |
| pub g: u8, |
| /// Blue |
| pub b: u8, |
| /// Alpha |
| pub a: u8, |
| } |
| |
| impl Color { |
| /// Create a new color set to black with full alpha |
| pub const fn new() -> Color { |
| Color { r: 0, g: 0, b: 0, a: 255 } |
| } |
| |
| /// Create a new color set to white with full alpha |
| pub const fn white() -> Color { |
| Color { r: 255, g: 255, b: 255, a: 255 } |
| } |
| |
| /// Create a new color set to red with full alpha |
| pub const fn red() -> Color { |
| Color { r: 255, g: 0, b: 0, a: 255 } |
| } |
| |
| /// Create a new color set to green with full alpha |
| pub const fn green() -> Color { |
| Color { r: 0, g: 255, b: 0, a: 255 } |
| } |
| |
| /// Create a new color set to blue with full alpha |
| pub const fn blue() -> Color { |
| Color { r: 0, g: 0, b: 255, a: 255 } |
| } |
| |
| /// Create a new color set to Fuchsia with full alpha |
| pub const fn fuchsia() -> Color { |
| Color { r: 255, g: 0, b: 255, a: 255 } |
| } |
| |
| fn extract_hex_slice(hash_code: &str, start_index: usize) -> Result<u8, Error> { |
| Ok(u8::from_str_radix(&hash_code[start_index..start_index + 2], 16)?) |
| } |
| |
| /// Create a color from a six hexadecimal digit string like '#EBD5B3' |
| pub fn from_hash_code(hash_code: &str) -> Result<Color, Error> { |
| let mut new_color = Color::new(); |
| new_color.r = Color::extract_hex_slice(&hash_code, 1)?; |
| new_color.g = Color::extract_hex_slice(&hash_code, 3)?; |
| new_color.b = Color::extract_hex_slice(&hash_code, 5)?; |
| Ok(new_color) |
| } |
| |
| /// Create a Scenic-compatible [`ColorRgba`] |
| pub fn make_color_rgba(&self) -> ColorRgba { |
| ColorRgba { red: self.r, green: self.g, blue: self.b, alpha: self.a } |
| } |
| |
| pub fn to_linear_premult_rgba(&self) -> [f32; 4] { |
| let alpha = self.a as f32 * 255.0f32.recip(); |
| |
| [ |
| srgb_to_linear(self.r) * alpha, |
| srgb_to_linear(self.g) * alpha, |
| srgb_to_linear(self.b) * alpha, |
| alpha, |
| ] |
| } |
| |
| pub fn to_linear_bgra(&self) -> [f32; 4] { |
| [ |
| srgb_to_linear(self.b), |
| srgb_to_linear(self.g), |
| srgb_to_linear(self.r), |
| self.a as f32 * 255.0f32.recip(), |
| ] |
| } |
| |
| pub fn to_srgb_premult_rgba(&self) -> [f32; 4] { |
| let recip = 255.0f32.recip(); |
| let alpha = self.a as f32 * recip; |
| |
| [ |
| self.r as f32 * recip * alpha, |
| self.g as f32 * recip * alpha, |
| self.b as f32 * recip * alpha, |
| alpha, |
| ] |
| } |
| } |
| |
| impl Default for Color { |
| fn default() -> Self { |
| Color::new() |
| } |
| } |