Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // TODO: fix clippy and add tests
- use cgmath::{Vector3, Vector4};
- use serde::{Deserialize, Serialize};
- use std::{
- convert::TryFrom,
- ops::{Add, AddAssign, Mul, MulAssign},
- };
- #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
- pub struct Color {
- pub r: u8,
- pub g: u8,
- pub b: u8,
- pub a: u8
- }
- impl Color {
- pub const BLACK: Self = Self::new(0, 0, 0, 255);
- pub const BLUE: Self = Self::new(0, 0, 255, 255);
- pub const CYAN: Self = Self::new(0, 255, 255, 255);
- pub const GREEN: Self = Self::new(0, 255, 0, 255);
- pub const MAGENTA: Self = Self::new(255, 0, 255, 255);
- pub const ORANGE: Self = Self::new(255, 165, 0, 255);
- pub const RED: Self = Self::new(255, 0, 0, 255);
- pub const YELLOW: Self = Self::new(255, 255, 0, 255);
- pub const WHITE: Self = Self::new(255, 255, 255, 255);
- #[inline]
- pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
- Self { r, g, b, a }
- }
- #[inline]
- pub const fn from_lightness(l: u8) -> Self {
- Self::new(l, l, l, 255)
- }
- #[inline]
- pub const fn is_opaque(self) -> bool {
- self.a == 255
- }
- #[inline]
- pub fn from_cmy(cyan: f32, magenta: f32, yellow: f32) -> Self {
- Self::new(
- ((1.0 - cyan) * 255.0) as u8,
- ((1.0 - magenta) * 255.0) as u8,
- ((1.0 - yellow) * 255.0) as u8,
- 255,
- )
- }
- #[inline]
- pub fn from_cmyk(cyan: f32, magenta: f32, yellow: f32, black: f32) -> Self {
- Self::from_cmy(
- cyan * (1.0 - black) + black,
- magenta * (1.0 - black) + black,
- yellow * (1.0 - black) + black,
- )
- }
- #[inline]
- pub fn from_hsl(hue: f32, saturation: f32, lightness: f32) -> Self {
- if saturation == 0.0 {
- return Self::from_lightness((lightness * 255.0) as u8);
- }
- let v2 = if lightness < 0.5 {
- lightness * (1.0 + saturation)
- } else {
- (lightness + saturation) - (saturation * lightness)
- };
- let v1 = 2.0 * lightness - v2;
- let h = hue / 360.0;
- Self::new(
- (255.0 * Self::hue_to_rgb(v1, v2, h + 1.0 / 3.0)) as u8,
- (255.0 * Self::hue_to_rgb(v1, v2, h)) as u8,
- (255.0 * Self::hue_to_rgb(v1, v2, h - 1.0 / 3.0)) as u8,
- 255,
- )
- }
- #[inline]
- pub fn from_hsv(hue: f32, saturation: f32, value: f32) -> Self {
- if saturation == 0.0 {
- return Self::from_lightness((value * 255.0) as u8);
- }
- let mut h = hue / 360.0 * 6.0; // pourquoi pas / 60 ?
- if h == 6.0 {
- h = 0.0 // h must be < 1
- }
- let i = h as isize;
- let v1 = value * (1.0 - saturation);
- let v2 = value * (1.0 - saturation * (h - i as f32));
- let v3 = value * (1.0 - saturation * (1.0 - (h - i as f32)));
- let (r, g, b) = match i {
- 0 => (value, v3, v1),
- 1 => (v2, value, v1),
- 2 => (v1, value, v3),
- 3 => (v1, v2, value),
- 4 => (v3, v1, value),
- _ => (value, v1, v2),
- };
- Self::new((r * 255.0) as u8, (g * 255.0) as u8, (b * 255.0) as u8, 255)
- }
- #[inline]
- pub fn from_xyz(mut x: f32, mut y: f32, mut z: f32) -> Self {
- x /= 100.0; // X from 0 to 95.047
- y /= 100.0; // Y from 0 to 100.000
- z /= 100.0; // Z from 0 to 108.883
- let mut r = x * 3.2406 + y * -1.5372 + z * -0.4986;
- let mut g = x * -0.9689 + y * 1.8758 + z * 0.0415;
- let mut b = x * 0.0557 + y * -0.2040 + z * 1.0570;
- if r > 0.0031308 {
- r = 1.055 * r.powf(1.0/2.4) - 0.055;
- }
- else {
- r *= 12.92;
- }
- if g > 0.0031308 {
- g = 1.055 * g.powf(1.0/2.4) - 0.055;
- } else {
- g *= 12.92;
- }
- if b > 0.0031308 {
- b = 1.055 * b.powf(1.0/2.4) - 0.055;
- } else {
- b *= 12.92;
- }
- Color::new(
- (r * 255.0) as u8,
- (g * 255.0) as u8,
- (b * 255.0) as u8,
- 255
- )
- }
- #[inline]
- pub fn to_cmy(&self) -> (f32, f32, f32) {
- (
- 1.0 - self.r as f32 / 255.0,
- 1.0 - self.g as f32 / 255.0,
- 1.0 - self.b as f32 / 255.0,
- )
- }
- #[inline]
- pub fn to_cmyk(&self) -> (f32, f32, f32, f32) {
- let (cyan, magenta, yellow) = self.to_cmy();
- let key = 1.0f32.min(cyan).min(magenta).min(yellow);
- if key == 1.0 {
- // Black
- (0.0, 0.0, 0.0, key)
- } else {
- (
- (cyan - key) / (1.0 - key),
- (magenta - key) / (1.0 - key),
- (yellow - key) / (1.0 - key),
- key
- )
- }
- }
- #[inline]
- pub fn to_hsl(&self) -> (f32, f32, f32) {
- let red = self.r as f32 / 255.0;
- let green = self.g as f32 / 255.0;
- let blue = self.b as f32 / 255.0;
- let min = red.min(green).min(blue); // Min. value of RGB
- let max = red.max(green).max(blue); // Max. value of RGB
- let delta_max = max - min; // Delta RGB value
- let lightness = (max + min)/2.0;
- if delta_max == 0.0 {
- // This is gray, no chroma...
- return (0.0, 0.0, lightness);
- }
- let saturation = if lightness <= 0.5 {
- delta_max / (max + min)
- } else {
- delta_max / (2.0 - max - min)
- };
- let delta_red = ((max - red) / 6.0 + delta_max / 2.0) / delta_max;
- let delta_green = ((max - green) / 6.0 + delta_max / 2.0) / delta_max;
- let delta_blue = ((max - blue) / 6.0 + delta_max / 2.0) / delta_max;
- let hue = {
- let h = {
- if red == max {
- delta_blue - delta_green
- } else if green == max {
- 1.0 / 3.0 + delta_red - delta_blue
- } else {
- 2.0 / 3.0 + delta_green - delta_red
- }
- };
- if h < 0.0 {
- h + 1.0
- } else {
- h - 1.0
- }
- } * 360.0;
- (hue, saturation, lightness)
- }
- #[inline]
- pub fn to_hsv(&self) -> (f32, f32, f32) {
- let red = self.r as f32 / 255.0;
- let green = self.g as f32 / 255.0;
- let blue = self.b as f32 / 255.0;
- let min = red.min(green).min(blue); // Min. value of RGB
- let max = red.max(green).max(blue); // Max. value of RGB
- let delta_max = max - min; // Delta RGB value
- let value = max;
- if delta_max == 0.0 {
- // This is gray, no chroma...
- return (0.0, 0.0, value);
- }
- // Chromatic data...
- let saturation = delta_max / max;
- let delta_red = ((max - red) / 6.0 + delta_max / 2.0) / delta_max;
- let delta_green = ((max - green) / 6.0 + delta_max / 2.0) / delta_max;
- let delta_blue = ((max - blue) / 6.0 + delta_max / 2.0) / delta_max;
- let hue = {
- let h = {
- if red == max {
- delta_blue - delta_green
- } else if green == max {
- 1.0 / 3.0 + delta_red - delta_blue
- } else {
- 2.0 / 3.0 + delta_green - delta_red
- }
- };
- if h < 0.0 {
- h + 1.0
- } else {
- h - 1.0
- }
- } * 360.0;
- (hue, saturation, value)
- }
- #[inline]
- pub fn to_xyz(&self) -> (f32, f32, f32) {
- let mut red = self.r as f32 / 255.0;
- let mut green = self.g as f32 / 255.0;
- let mut blue = self.b as f32 / 255.0;
- if red > 0.04045 {
- red = ((red + 0.055) / 1.055).powf(2.4);
- } else {
- red /= 12.92;
- }
- if green > 0.04045 {
- green = ((green + 0.055) / 1.055).powf(2.4);
- } else {
- green /= 12.92;
- }
- if blue > 0.04045 {
- blue = ((blue + 0.055) / 1.055).powf(2.4);
- } else {
- blue /= 12.92;
- }
- red *= 100.0;
- blue *= 100.0;
- green *= 100.0;
- // Observer. = 2, Illuminant = D65
- (
- red * 0.4124 + green * 0.3576 + blue * 0.1805,
- red * 0.2126 + green * 0.7152 + blue * 0.0722,
- red * 0.0193 + green * 0.1192 + blue * 0.9505,
- )
- }
- #[allow(non_snake_case)]
- #[inline]
- fn hue_to_rgb(v1: f32, v2: f32, mut v_h: f32) -> f32 {
- if v_h < 0.0 {
- v_h += 1.0;
- }
- if v_h > 1.0 {
- v_h -= 1.0;
- }
- if (6.0 * v_h) < 1.0 {
- return v1 + (v2 - v1) * 6.0 * v_h;
- }
- if (2.0 * v_h) < 1.0 {
- return v2;
- }
- if (3.0 * v_h) < 2.0 {
- return v1 + (v2 - v1) * (2.0 / 3.0 - v_h) * 6.0;
- }
- v1
- }
- }
- impl Add for Color {
- type Output = Self;
- #[inline]
- fn add(self, other: Self) -> Self::Output {
- Self::new(
- (self.r as usize + other.r as usize).min(255) as u8,
- (self.g as usize + other.g as usize).min(255) as u8,
- (self.b as usize + other.b as usize).min(255) as u8,
- (self.a as usize + other.a as usize).min(255) as u8,
- )
- }
- }
- impl AddAssign for Color {
- #[inline]
- fn add_assign(&mut self, other: Self) {
- *self = *self + other
- }
- }
- impl Mul for Color {
- type Output = Self;
- #[inline]
- fn mul(self, other: Self) -> Self::Output {
- Self::new(
- ((self.r as usize * other.r as usize) / 255) as u8,
- ((self.g as usize * other.g as usize) / 255) as u8,
- ((self.b as usize * other.b as usize) / 255) as u8,
- ((self.a as usize * other.a as usize) / 255) as u8,
- )
- }
- }
- impl MulAssign for Color {
- #[inline]
- fn mul_assign(&mut self, other: Self) {
- *self = *self * other
- }
- }
- impl From<(u8, u8, u8)> for Color {
- #[inline]
- fn from(color: (u8, u8, u8)) -> Self {
- Self::new(color.0, color.1, color.2, 255)
- }
- }
- impl From<[u8; 3]> for Color {
- #[inline]
- fn from(color: [u8; 3]) -> Self {
- Self::new(color[0], color[1], color[2], 255)
- }
- }
- impl From<Vector3<u8>> for Color {
- #[inline]
- fn from(color: Vector3<u8>) -> Self {
- Self::new(color[0], color[1], color[2], 255)
- }
- }
- impl From<(u8, u8, u8, u8)> for Color {
- #[inline]
- fn from(color: (u8, u8, u8, u8)) -> Self {
- Self::new(color.0, color.1, color.2, color.3)
- }
- }
- impl From<([u8; 3], u8)> for Color {
- #[inline]
- fn from(color: ([u8; 3], u8)) -> Self {
- Self::new(color.0[0], color.0[1], color.0[2], color.1)
- }
- }
- impl From<[u8; 4]> for Color {
- #[inline]
- fn from(color: [u8; 4]) -> Self {
- Self::new(color[0], color[1], color[2], color[3])
- }
- }
- impl From<Vector4<u8>> for Color {
- #[inline]
- fn from(color: Vector4<u8>) -> Self {
- Self::new(color[0], color[1], color[2], color[3])
- }
- }
- impl From<Color> for (u8, u8, u8) {
- #[inline]
- fn from(color: Color) -> Self {
- (color.r, color.g, color.b)
- }
- }
- impl From<Color> for [u8; 3] {
- #[inline]
- fn from(color: Color) -> Self {
- [color.r, color.g, color.b]
- }
- }
- impl From<Color> for Vector3<u8> {
- #[inline]
- fn from(color: Color) -> Self {
- Vector3::new(color.r, color.g, color.b)
- }
- }
- impl From<Color> for (u8, u8, u8, u8) {
- #[inline]
- fn from(color: Color) -> Self {
- (color.r, color.g, color.b, color.a)
- }
- }
- impl From<Color> for [u8; 4] {
- #[inline]
- fn from(color: Color) -> Self {
- [color.r, color.g, color.b, color.a]
- }
- }
- impl From<Color> for ([u8; 3], u8) {
- #[inline]
- fn from(color: Color) -> Self {
- ([color.r, color.g, color.b], color.a)
- }
- }
- impl From<Color> for Vector4<u8> {
- #[inline]
- fn from(color: Color) -> Self {
- Vector4::new(color.r, color.g, color.b, color.a)
- }
- }
- impl TryFrom<&[u8]> for Color {
- type Error = Result<(Self, String), String>;
- fn try_from(color: &[u8]) -> Result<Self, Self::Error> {
- match color.len() {
- n if n < 3 => Err(Err(format!(
- "len should be at least 3, not {}",
- color.len()
- ))),
- 3 => Err(Ok((
- Color::new(color[0], color[1], color[2], 255),
- String::from("fail to create a color with alpha component"),
- ))),
- _ => Ok(Color::new(color[0], color[1], color[2], color[3])),
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement