colorsys-0.6.5/.cargo_vcs_info.json0000644000000001120000000000100127130ustar { "git": { "sha1": "b5d1fe27b9b8f7d8c9975b12ec80b8d69bce2ed8" } } colorsys-0.6.5/.gitignore000064400000000000000000000000540072674642500135300ustar 00000000000000/target **/*.rs.bk Cargo.lock .vscode .idea colorsys-0.6.5/Cargo.toml0000644000000017700000000000100107240ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "colorsys" version = "0.6.5" authors = ["mz "] description = "A module for color conversion and mutation. Works with RGB(a)( as hexadecimal too), HSL(a), CMYK color models and with ANSI color codes" homepage = "https://github.com/emgyrz/colorsys.rs" readme = "README.md" keywords = ["colors", "converter", "rgb", "hsl", "cmyk"] categories = ["graphics"] license = "MIT" repository = "https://github.com/emgyrz/colorsys.rs.git" resolver = "2" [dependencies] [features] default = ["std"] std = [] colorsys-0.6.5/Cargo.toml.orig000064400000000000000000000010320072674642500144240ustar 00000000000000[package] name = "colorsys" version = "0.6.5" description = "A module for color conversion and mutation. Works with RGB(a)( as hexadecimal too), HSL(a), CMYK color models and with ANSI color codes" authors = ["mz "] license = "MIT" homepage = "https://github.com/emgyrz/colorsys.rs" repository = "https://github.com/emgyrz/colorsys.rs.git" keywords = ["colors", "converter", "rgb", "hsl", "cmyk"] categories = [ "graphics"] edition = "2021" readme = "README.md" [dependencies] [features] default = [ "std" ] std = [] colorsys-0.6.5/LICENSE000064400000000000000000000020720072674642500125470ustar 00000000000000The MIT License Copyright (c) 2019 mz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. colorsys-0.6.5/README.md000064400000000000000000000074220072674642500130250ustar 00000000000000# colorsys [![Crates.io](https://img.shields.io/crates/v/colorsys.svg)](https://crates.io/crates/colorsys/) A module for color conversion and mutation written in Rust. For now works with next color models: - RGB(a)( as hexadecimal too) - HSL(a) - CMYK(a) - ANSI256 codes [Documentation](https://docs.rs/colorsys) ## What It Can Do #### getters & setters ```rust use colorsys::{Rgb, Hsl, ColorAlpha}; let rgb = Rgb::from((57.3, 12.7, 53.0)); let r = rgb.red(); // 57.3 let mut hsl = Hsl::default(); // Hsl { h: 0, s: 0, l: 0, a: 1 } hsl.set_saturation(13.98); hsl.set_saturation(305.71); hsl.set_alpha(0.75); // Hsl { h: 305.71, s: 13.98, l: 0, a: 0.75 } ``` #### conversion See `From/FromStr/Into` traits implementation in docs for more info ```rust use colorsys::{Rgb, Hsl}; let rbga_tuple = (57.3, 12.7, 53.0, 0.33); let rgba = Rgb::from(&rbga_tuple); let hsla: Hsl = rgba.as_ref().into(); // ~Hsl { h: 305.78, s: 63.71, l: 13.73, a: 0.33 } let rgb_arr: [u8; 3] = Rgb::from(&hsla).into(); // ~[57, 13, 53] let hsla_tuple: (f64,f64,f64,f64) = Hsl::from( Rgb::from(rgb_tuple) ).into(); // ~Hsl { h: 305.78, s: 63.71, l: 13.73, a: 1 } let hex: String = rgba.to_hex_string(); // #390d35 // // From/Into // let rgb1 = Rgb::from_hex_str("37ea4c").unwrap(); let rgb2 = Rgb::from( Into::<[f32; 4]>::into(Rgb::from( Into::<[u8; 3]>::into( Rgb::from( Into::<(i32,i32,i32)>::into( Rgb::from( Into::<[i64; 3]>::into(&rgb1) ) ) ) ) )) ); assert_eq!(rgb1, rgb2); // // Ratio // use colorsys::{RgbRatio, ApproxEq}; let blue = Rgb::from([34, 111, 235]); let ratio: [f32; 4] = blue.as_ratio().into(); // ~[0.133, 0.435, 0.922, 1.0] let converted: Rgb = RgbRatio::from(&ratio).into(); assert!(blue.approx_eq_clarify(&converted, 0.0001)); ``` #### modification See `ColorTransform/Add*/Sub*..` traits in docs for more ```rust use colorsys::{Hsl, Rgb, ColorTransform,ColorAlpha, SaturationInSpace}; let mut rgb: Rgb = (245.0,152.0,53.0).into(); rgb.lighten(20.1); // ~Rgb { r: 249.83, g: 201.80, b: 150.67 } rgb.opacify(-0.7); rgb.saturate( SaturationInSpace::Hsl(-35.7) ); // ~Rgb { r: 230.29, g: 201.19, b: 170.21, a: 0.3 } rgb.grayscale_simple(); // ~Rgb { r: 200.255, g: 200.255, b: 200.255, a: 0.3 } let mut hsl = Hsl::from(&rgb); hsl.opacify(1.0); // ~Hsl { h: 0.0, s: 0.0, l: 78.53 } hsl.adjust_hue(231.99); hsl.saturate(SaturationInSpace::Hsl(55.7)); let mut rgb2: Rgb = hsl.as_ref().into(); // ~Rgb { r: 169.76, g: 177.9, b: 230.75} rgb2.invert(); // ~Rgb { r: 85.24, g: 77.09, b: 24.25 } let rgb3 = rgb - rgb2; // ~Rgb { r: 115.01, g: 123.16, b: 176.0 } let hsl2 = hsl + rgb3.into(); // ~Hsl { h: 0.0, s: 83.55, l: 100.0 } ``` #### parsing from string & css string representation ```rust use colorsys::{Hsl, Rgb}; use std::str::FromStr; let s = "rgb(177, 255, 176)"; let rgb: Rgb = s.parse().unwrap(); rgb.to_css_string(); // String: "rgb(177,255,176)" rgb.to_hex_string(); // #b1ffb0 Hsl::from_str("hsl(168, 52%, 42%)").unwrap().to_css_string(); // String: hsl(168,52%,42%) ``` ## `no_std` Crate has a Cargo feature named `"std"` that is enabled by default. In order to use `colorsys` in a `no_std` context this feature needs to be disabled. Modify your dependency to opt out of enabled-by-default features. ```toml [dependencies] colorsys = { version = "*", default-features = false } ``` ## Color unit ranges All color units is f64. Here are their ranges: - red: 0.0 - 255.0 - green: 0.0 - 255.0 - blue: 0.0 - 255.0 - hue: 0.0 - 360.0 - saturation: 0.0 - 100.0 - lightness: 0.0 - 100.0 - all in cmyk are: 0.0 - 100.0 - alpha: 0.0 - 1.0 - ansi256 code is `u8` If you specify a value that does not fit within these ranges, they are replaced with a minimum or maximum value. ##### Enjoy using! ### License This module is [MIT licensed](./LICENSE). colorsys-0.6.5/rustfmt.toml000064400000000000000000000001140072674642500141360ustar 00000000000000use_small_heuristics = "Max" edition= "2018" tab_spaces = 2 max_width = 80 colorsys-0.6.5/src/ansi/mod.rs000064400000000000000000000057470072674642500144240ustar 00000000000000use crate::Rgb; /// Predefined set of 256 colors to use with ANSI escape sequences, /// e.g. in terminal emulators /// /// # Example /// ``` /// use colorsys::{Ansi256, Rgb}; /// /// let rgb = Rgb::from_hex_str("#875fff").unwrap(); /// let ansi256: Ansi256 = rgb.into(); /// assert_eq!(ansi256.code(),99); /// /// let green_yellow = Ansi256::new(154); /// let rgb2 = green_yellow.as_rgb(); /// assert_eq!(rgb2.to_hex_string(), "#afff00"); /// /// let txt = format!( /// "\x1b[38;5;{ansi_code}m{text}\x1b[0m", /// ansi_code=green_yellow.code(), /// text="my colored text" /// ); /// println!("{}", txt); /// /// ``` #[derive(Debug, Copy, Clone)] pub struct Ansi256(pub(crate) u8); impl Ansi256 { pub fn new(ansi_code: u8) -> Self { Ansi256(ansi_code) } pub fn code(&self) -> u8 { self.0 } pub fn set_code(&mut self, v: u8) { self.0 = v; } pub fn as_rgb(&self) -> Rgb { (*self).into() } } impl From<&Rgb> for Ansi256 { fn from(rgb: &Rgb) -> Self { let [r, g, b]: [u8; 3] = rgb.into(); let red = if r < 75 { 0 } else { (r - 35) / 40 }; let green = if g < 75 { 0 } else { (g - 35) / 40 }; let blue = if b < 75 { 0 } else { (b - 35) / 40 }; Ansi256(red * 6 * 6 + green * 6 + blue + 16) } } impl From for Ansi256 { fn from(rgb: Rgb) -> Self { rgb.as_ref().into() } } impl From for Rgb { fn from(ansi256: Ansi256) -> Self { let code = ansi256.0; if code < 16 { let mut base = code; let mut mul = 128; if code == 7 { mul = 192; } else if code == 8 { base = 7; } else if code > 8 { mul = 255; } let r = (base & 1) * mul; let g = ((base & 2) >> 1) * mul; let b = ((base & 4) >> 2) * mul; Rgb::from((r, g, b)) } else if code > 231 { let gray = (code - 232) * 10 + 8; Rgb::from((gray, gray, gray)) } else { let b = (code - 16) % 6; let b = if b == 0 { 0 } else { b * 40 + 55 }; let g = ((code - 16) / 6) % 6; let g = if g == 0 { 0 } else { g * 40 + 55 }; let r = (code - 16) / 36; let r = if r == 0 { 0 } else { r * 40 + 55 }; Rgb::from((r, g, b)) } } } #[cfg(test)] mod test { use crate::ansi::Ansi256; use crate::Rgb; #[test] fn rgb_to_ansi_test() { let test_data = [ ([95u8,0,175], 55u8), ([135,95,255], 99), ([135,255,255], 123), ([215,175,95], 179), ([255,215,255], 225), ]; for (rgb_arr, ansi_code) in &test_data { let ansi: Ansi256 = Rgb::from(rgb_arr).as_ref().into(); assert_eq!(ansi.code(), *ansi_code); } } #[test] fn ansi_to_rgb_test() { let test_data = [ (9u8, [255u8, 0, 0]), (211, [255,135,175]), (187, [215,215,175]), (171, [215,95,255]), (77, [95,215,95]), (0, [0,0,0]), (13, [255,0,255]), (249, [178,178,178]), ]; for (ansi_code, rgb_arr) in &test_data { let rgb: Rgb = Ansi256(*ansi_code).into(); let arr: [u8;3] = rgb.into(); assert_eq!(&arr, rgb_arr); } } } colorsys-0.6.5/src/cmyk/from.rs000064400000000000000000000036210072674642500146060ustar 00000000000000use crate::{Cmyk, Rgb}; use crate::cmyk::CmykRatio; use crate::consts::PERCENT_MAX; use crate::converters::{cmyk_to_rgb, rgb_to_cmyk}; fn cmyk_from_ratio(r: &CmykRatio) -> Cmyk { let mut u = r.units.clone(); for v in &mut u.list { v.turn_into_whole(&PERCENT_MAX); } Cmyk::from_units(u) } impl From<&CmykRatio> for Cmyk { fn from(r: &CmykRatio) -> Self { cmyk_from_ratio(r) } } impl From<&mut CmykRatio> for Cmyk { fn from(r: &mut CmykRatio) -> Self { cmyk_from_ratio(r) } } impl From for Cmyk { fn from(r: CmykRatio) -> Self { cmyk_from_ratio(&r) } } fn cmyk_to_ratio(cmyk: &Cmyk) -> CmykRatio { CmykRatio::from_units(cmyk.units.as_ratio()) } impl From<&Cmyk> for CmykRatio { fn from(r: &Cmyk) -> Self { cmyk_to_ratio(r) } } impl From<&mut Cmyk> for CmykRatio { fn from(r: &mut Cmyk) -> Self { cmyk_to_ratio(r) } } impl From for CmykRatio { fn from(r: Cmyk) -> Self { cmyk_to_ratio(&r) } } impl From<&mut Rgb> for Cmyk { fn from(rgb: &mut Rgb) -> Self { rgb_to_cmyk(rgb) } } impl From<&Rgb> for Cmyk { fn from(rgb: &Rgb) -> Self { rgb_to_cmyk(rgb) } } impl From for Cmyk { fn from(rgb: Rgb) -> Self { rgb_to_cmyk(&rgb) } } impl From<&mut Cmyk> for Rgb { fn from(cmyk: &mut Cmyk) -> Self { cmyk_to_rgb(cmyk) } } impl From<&Cmyk> for Rgb { fn from(cmyk: &Cmyk) -> Self { cmyk_to_rgb(cmyk) } } impl From for Rgb { fn from(cmyk: Cmyk) -> Self { cmyk_to_rgb(&cmyk) } } impl From<[f64; 4]> for Cmyk { fn from(a: [f64; 4]) -> Self { Cmyk::new(a[0], a[1], a[2], a[3], None) } } impl<'a> From<&'a [f64; 4]> for Cmyk { fn from(a: &[f64; 4]) -> Self { Cmyk::new(a[0], a[1], a[2], a[3], None) } } impl Into<[f64; 4]> for Cmyk { fn into(self: Cmyk) -> [f64; 4] { self.units.into() } } impl<'a> Into<[f64; 4]> for &'a Cmyk { fn into(self) -> [f64; 4] { self.units.clone().into() } }colorsys-0.6.5/src/cmyk/mod.rs000064400000000000000000000047340072674642500144300ustar 00000000000000use core::ops::{Add, AddAssign, Sub, SubAssign}; pub use ratio::CmykRatio; use crate::consts::PERCENT_MAX; use crate::Rgb; use crate::units::{Alpha, GetColorUnits, Unit, Units}; mod ratio; mod from; /// The CMYK color model. /// /// Has cyan, magenta, yellow, key (0.0..100.0) and optional `alpha` channel (0.0..1.0). /// /// # Example /// ``` /// use colorsys::Cmyk; /// /// let mut cmyk = Cmyk::new(33.1, 999.9, 11.0, 0.0, None); /// /// assert_eq!(cmyk.cyan(), 33.1); /// assert_eq!(cmyk.magenta(), 100.0); /// /// cmyk.set_yellow(73.0); /// assert_eq!(cmyk.yellow(), 73.0); /// /// let doubled_cmyk = &cmyk + &cmyk; /// /// let units: [f64;4] = doubled_cmyk.into(); /// assert_eq!(units, [66.2,100.0,100.0,0.0]); /// /// ``` #[derive(Debug, PartialEq, Clone)] pub struct Cmyk { pub(crate) units: Units, } ops_def!(Cmyk); pub(crate) fn new_cmyk_units(c: f64, m: f64, y: f64, k: f64) -> Units { let p = |v: f64| Unit::new_percent(v); let ul = [p(c), p(m), p(y), p(k)]; Units { len: 4, list: ul, alpha: Alpha::default() } } impl Cmyk { pub fn new(c: f64, m: f64, y: f64, k: f64, a: Option) -> Self { let mut u = new_cmyk_units(c, m, y, k); u.alpha.set_opt(a); u.restrict(); Cmyk::from_units(u) } pub fn cyan(&self) -> f64 { self.units[0] } pub fn magenta(&self) -> f64 { self.units[1] } pub fn yellow(&self) -> f64 { self.units[2] } pub fn key(&self) -> f64 { self.units[3] } pub fn set_cyan(&mut self, c: f64) { self.units.list[0].set(c); } pub fn set_magenta(&mut self, m: f64) { self.units.list[1].set(m); } pub fn set_yellow(&mut self, y: f64) { self.units.list[2].set(y); } pub fn set_key(&mut self, k: f64) { self.units.list[3].set(k); } /// Returns same color in RGB color model /// # Example /// ``` /// use colorsys::{Cmyk, Rgb, ApproxEq}; /// /// let cmyk = Cmyk::from(&[0.0,0.0,100.0,0.0]); /// let rgb_from_cmyk = cmyk.as_rgb(); /// assert!(rgb_from_cmyk.approx_eq(&Rgb::from([255,255,0]))); /// /// ``` pub fn as_rgb(&self) -> Rgb { self.into() } pub fn as_ratio(&self) -> CmykRatio { self.into() } pub(crate) fn from_units(u: Units) -> Cmyk { Cmyk { units: u } } } impl GetColorUnits for Cmyk { fn get_units(&self) -> &Units { &self.units } fn get_units_mut(&mut self) -> &mut Units { &mut self.units } } impl AsRef for Cmyk { fn as_ref(&self) -> &Cmyk { self } } impl Default for Cmyk { fn default() -> Cmyk { Cmyk::from_units(new_cmyk_units(0.0, 0.0, 0.0, PERCENT_MAX)) } } colorsys-0.6.5/src/cmyk/ratio.rs000064400000000000000000000032630072674642500147630ustar 00000000000000use crate::units::Units; #[derive(Debug, PartialEq, Clone)] pub struct CmykRatio { pub(crate) units: Units, } impl CmykRatio { pub fn new(c: f64, m: f64, y: f64, k: f64, a: f64) -> Self { let mut u = Units::new_ratios(&[c, m, y, k]); u.alpha.set(a); CmykRatio::from_units(u) } pub fn cyan(&self) -> f64 { self.units[0] } pub fn magenta(&self) -> f64 { self.units[1] } pub fn yellow(&self) -> f64 { self.units[2] } pub fn key(&self) -> f64 { self.units[3] } pub fn set_cyan(&mut self, c: f64) { self.units.list[0].set(c); } pub fn set_magenta(&mut self, m: f64) { self.units.list[1].set(m); } pub fn set_yellow(&mut self, y: f64) { self.units.list[2].set(y); } pub fn set_key(&mut self, k: f64) { self.units.list[3].set(k); } pub(crate) fn from_units(u: Units) -> Self { CmykRatio { units: u } } } impl AsRef for CmykRatio { fn as_ref(&self) -> &CmykRatio { self } } impl Default for CmykRatio { fn default() -> CmykRatio { CmykRatio::new(0.0, 0.0, 0.0, 1.0, 1.0) } } impl From<[f64; 4]> for CmykRatio { fn from(a: [f64; 4]) -> Self { CmykRatio::new(a[0], a[1], a[2], a[3], 1.0) } } impl<'a> From<&'a [f64; 4]> for CmykRatio { fn from(a: &[f64; 4]) -> Self { CmykRatio::new(a[0], a[1], a[2], a[3], 1.0) } } impl Into<[f64; 4]> for CmykRatio { fn into(self: CmykRatio) -> [f64; 4] { self.units.into() } } impl<'a> Into<[f64; 4]> for &'a CmykRatio { fn into(self) -> [f64; 4] { self.units.clone().into() } } #[cfg(test)] mod test { use crate::{Cmyk, Rgb}; use crate::converters::cmyk_to_rgb; #[test] fn cmyk_to_rbg_test() { let cmyk = Cmyk::new(30.0, 30.0, 30.0, 30.0, None); let _rgb: Rgb = cmyk_to_rgb(&cmyk); } } colorsys-0.6.5/src/common/alpha.rs000064400000000000000000000025430072674642500152570ustar 00000000000000use crate::units::GetColorUnits; /// Methods to work with alpha channel in color. pub trait ColorAlpha { /// Returns alpha channel. If it not set will returns 1.0 fn alpha(&self) -> f64; /// Returns alpha channel. If it not set will returns 1.0 #[deprecated(since = "0.7.0", note = "Please use `alpha` instead")] fn get_alpha(&self) -> f64; /// Sets alpha channel /// ``` /// use colorsys::{Hsl,ColorAlpha}; /// let mut hsl = Hsl::default(); // Hsl { a: None, .. } /// hsl.set_alpha(0.45); // Hsl { a: 0.45, .. } /// hsl.set_alpha(123.015); // Hsl { a: 1.0, .. } /// hsl.set_alpha(-123.3); // Hsl { a: 0.0, .. } /// ``` fn set_alpha(&mut self, val: f64); /// Increase/decrease color alpha channel with specified value. Value can be negative. /// # Example /// ``` /// use colorsys::{Hsl,ColorAlpha}; /// let mut hsl = Hsl::default(); // Hsl { a: None, .. } /// hsl.opacify(-0.3); // Hsl { a: 0.7, .. } /// hsl.opacify(0.015); // Hsl { a: 0.715, .. } /// ``` fn opacify(&mut self, val: f64); } impl ColorAlpha for T where T: GetColorUnits { fn alpha(&self) -> f64 { self.get_units().alpha.get_f64() } fn get_alpha(&self) -> f64 { self.alpha() } fn set_alpha(&mut self, val: f64) { self.get_units_mut().alpha.set(val); } fn opacify(&mut self, val: f64) { self.get_units_mut().alpha.opacify(val); } } colorsys-0.6.5/src/common/approx.rs000064400000000000000000000013410072674642500154760ustar 00000000000000use crate::common::f64_abs; use crate::units::GetColorUnits; /// Default precision used for color comparison. /// It is `0.000_000_001` pub static DEFAULT_APPROX_EQ_PRECISION: f64 = 1e-9; /// Methods to compare two colors pub trait ApproxEq { fn approx_eq(&self, other: &T) -> bool; fn approx_eq_clarify(&self, other: &T, precision: f64) -> bool; } pub fn approx(x: f64, y: f64, precision: f64) -> bool { f64_abs(x - y) < precision } impl ApproxEq for T where T: GetColorUnits { fn approx_eq(&self, other: &T) -> bool { self.get_units().approx_eq(other.get_units()) } fn approx_eq_clarify(&self, other: &T, precision: f64) -> bool { self.get_units().approx_eq_clarify(other.get_units(), precision) } } colorsys-0.6.5/src/common/from_str.rs000064400000000000000000000034000072674642500160160ustar 00000000000000// static RGB_START: &[u8; 3] = b"rgb"; // static HSL_START: &[u8; 3] = b"rgb"; // static COMMA: u8 = b','; // static DOT: u8 = b'.'; // // fn is_delimiter(ch: u8) -> bool { // ch.is_ascii_whitespace() || ch == COMMA // } // fn is_val_char(ch: u8) -> bool { // ch.is_ascii_digit() || ch == DOT // } // // fn from_bytes(bytes: &[u8]) -> Result<([f64; 4], usize), ()> { // let bytes_len = bytes.len(); // if bytes_len == 0 { return Err(()) } // let mut buff = [0.0; 4]; // let mut buff_len = 0; // // let mut i = 0; // while i < bytes_len { // let ch = bytes[i]; // if !is_val_char(ch) { // i += 1; // continue // } // // let mut chi = i + 1; // while chi < bytes_len && is_val_char(bytes[chi]) { // chi += 1; // } // let s = core::str::from_utf8(&bytes[i..chi]).map_err(|_| ())?; // if buff_len == 4 { return Err(()) } // buff[buff_len] = s.parse().map_err(|_| ())?; // buff_len += 1; // i = chi + 1; // } // // if buff_len == 0 { return Err(()) } // // Ok((buff, buff_len)) // } // // // #[allow(clippy::float_cmp)] // #[cfg(test)] // mod test { // use crate::common::from_str::from_bytes; // // #[test] // fn collect_digits_from_str_test() { // let valid = [ // ("10,30,40", [10.0f64, 30.0, 40.0, 0.0]), // ("10.5,30, 400.1, 22", [10.5, 30.0, 400.1, 22.0]), // ("rgb( 0,30, 400.1, 22) ", [0.0, 30.0, 400.1, 22.0]), // ("1.33 ", [1.33, 0.0, 0.0, 0.0]), // ]; // let invalid = [ // "", // "asd", // "rgb( 0,30, 400.1, 22, 1) " // ]; // // for (s, arr) in &valid { // assert_eq!(&from_bytes(s.as_bytes()).unwrap().0, arr); // } // // for s in &invalid { // assert!(&from_bytes(s.as_bytes()).is_err()); // } // } // }colorsys-0.6.5/src/common/hsv_hsl_from_str.rs000064400000000000000000000034770072674642500175620ustar 00000000000000 #[cfg(not(feature = "std"))] use alloc::string::String; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use super::Hs; use crate::err::{make_parse_err, ParseError}; use crate::{consts, ColorTuple}; use consts::{ALL_MIN, HUE_MAX, PERCENT_MAX, RATIO_MAX}; fn get_max_by_ind(ind: usize) -> f64 { match ind { 0 => HUE_MAX, 3 => RATIO_MAX, _ => PERCENT_MAX, } } fn strings_from_name(space: Hs) -> (String, String, String) { let mut x = String::with_capacity(1); match space { Hs::Hsl => { x.push('l'); } Hs::Hsv => { x.push('v'); } } (format!("hs{}(", x), format!("hs{}a(", x), format!("hs{} or hs{}a", x, x)) } pub fn hsl_hsv_from_str( s: &str, col_space: Hs, ) -> Result<(ColorTuple, Option), ParseError> { let (start, start_a, err_name) = strings_from_name(col_space); let make_err = || Err(make_parse_err(s, &err_name)); let s = s.trim().to_lowercase().replace(" ", "").replace("%", ""); let is_hsl = s.starts_with(&start); let is_hsla = s.starts_with(&start_a); let is_ends_with_bracket = s.ends_with(')'); if (!is_hsl && !is_hsla) || !is_ends_with_bracket { return make_err(); } let start_ind = if is_hsl { 4 } else { 5 }; let s = &s[start_ind..s.len() - 1]; let nums_str = s.split(',').collect::>(); let len = nums_str.len(); if (is_hsl && len != 3) || (is_hsla && len != 4) { return make_err(); } let mut nums = Vec::with_capacity(len); for (ind, n) in nums_str.iter().enumerate() { if let Ok(num) = n.parse::() { let max = get_max_by_ind(ind); if num < ALL_MIN || num > max { return make_err(); } nums.push(num) } else { return make_err(); } } let hsl = (nums[0], nums[1], nums[2]); let alpha = if is_hsla { Some(nums[len - 1]) } else { None }; Ok((hsl, alpha)) } colorsys-0.6.5/src/common/mod.rs000064400000000000000000000012120072674642500147410ustar 00000000000000pub use alpha::ColorAlpha; pub use hsv_hsl_from_str::hsl_hsv_from_str; pub use tuple_to_string::tuple_to_string; pub use crate::units::iter::ColorUnitsIter; mod hsv_hsl_from_str; mod tuple_to_string; mod alpha; mod from_str; pub mod approx; pub enum Hs { #[allow(dead_code)] Hsv, Hsl, } #[cfg(feature = "std")] pub(crate) fn f64_abs(n: f64) -> f64 { n.abs() } #[cfg(not(feature = "std"))] pub(crate) fn f64_abs(n: f64) -> f64 { if n < 0.0 { -n } else { n } } #[cfg(feature = "std")] pub(crate) fn f64_round(n: f64) -> f64 { n.round() } #[cfg(not(feature = "std"))] pub(crate) fn f64_round(n: f64) -> f64 { f64::from(n as i32) } colorsys-0.6.5/src/common/tuple_to_string.rs000064400000000000000000000015470072674642500174160ustar 00000000000000#[cfg(not(feature = "std"))] use alloc::string::String; use crate::{normalize::round_ratio, ColorTupleA}; use crate::common::{f64_abs, f64_round}; pub fn tuple_to_string(tuple: &ColorTupleA, prefix: &str) -> String { let (x, y, z, a) = tuple; let mut start = String::from(prefix); let a = if f64_abs(a - 1.0) < core::f64::EPSILON { String::from("1") } else { format!("{}", round_ratio(*a)) }; let is_hsl = prefix == "hsl"; let mut result = String::new(); [x, y, z] .iter() .enumerate() .for_each(|(ind, u)| { result.push_str(&format!("{}", f64_round(**u))); if is_hsl && (ind == 1 || ind == 2) { result.push('%'); } if ind != 2 { result.push(','); } }); if a != "1" { start.push('a'); result.push(','); result.push_str(&a); } format!("{}({})", start, result) } colorsys-0.6.5/src/consts.rs000064400000000000000000000002540072674642500142100ustar 00000000000000pub static RGB_UNIT_MAX: f64 = 255.0; pub static HUE_MAX: f64 = 360.0; pub static PERCENT_MAX: f64 = 100.0; pub static RATIO_MAX: f64 = 1.0; pub static ALL_MIN: f64 = 0.0; colorsys-0.6.5/src/converters/hex_to_rgb.rs000064400000000000000000000034560072674642500172200ustar 00000000000000#[cfg(not(feature = "std"))] use alloc::string::String; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use err::{make_parse_err, ParseError}; use crate::err; const HASH: u8 = b'#'; pub(crate) fn hex_to_rgb(s: &str) -> Result<[u32; 3], ParseError> { from_hex(s.as_bytes()).map_err(|_| make_parse_err(s, "hex")) } pub(crate) fn from_hex(s: &[u8]) -> Result<[u32; 3], ()> { let mut buff: [u8; 6] = [0; 6]; let mut buff_len = 0; for b in s { if !b.is_ascii() || buff_len == 6 { return Err(()); } let bl = b.to_ascii_lowercase(); if bl == HASH { continue } if bl.is_ascii_hexdigit() { buff[buff_len] = bl; buff_len += 1; } } if buff_len == 3 { buff = [buff[0], buff[0], buff[1], buff[1], buff[2], buff[2]]; } let hex_str = core::str::from_utf8(&buff).map_err(|_| ())?; let hex_digit = u32::from_str_radix(hex_str, 16).map_err(|_| ())?; Ok(hex_digit_to_rgb(hex_digit)) } fn hex_digit_to_rgb(num: u32) -> [u32; 3] { let r = num >> 16; let g = (num >> 8) & 0x00FF; let b = num & 0x0000_00FF; [r, g, b] } #[cfg(test)] mod test { use crate::converters::hex_to_rgb::{from_hex, hex_to_rgb}; #[test] fn from_hex_test() { let valid = [ ("000", [0u32; 3]), ("#000", [0; 3]), ("#000000", [0; 3]), ("fFf", [255; 3]), ("#fff", [255; 3]), ("#ffFfff", [255; 3]), ("#ffffff", [255; 3]), ("#777", [119; 3]), ("F7b3aA", [247, 179, 170]) ]; let invalid = [ "0000", "#0000f221", "#000000a", "ั‚ะตัั‚", "ffccfg", "", ]; for (s, t) in valid.iter() { let rgb = from_hex(s.as_bytes()).unwrap(); assert_eq!(&rgb, t); } for s in invalid.iter() { let result = from_hex(s.as_bytes()); assert!(result.is_err()); } } } colorsys-0.6.5/src/converters/hsl_to_rgb.rs000064400000000000000000000034000072674642500172070ustar 00000000000000use crate::consts::RGB_UNIT_MAX; use crate::Hsl; use crate::normalize::bound_ratio; use crate::rgb::new_rgb_units; use crate::units::Units; static ONE: f64 = 1.0; static TWO: f64 = 2.0; static SIX: f64 = 6.0; static ONE_THIRD: f64 = ONE / 3.0; static TWO_THIRD: f64 = TWO / 3.0; pub(crate) fn hsl_to_rgb(hsl: &Hsl) -> Units { let [h, s, l]: [f64; 3] = hsl.units.as_ratio().into(); if s == 0.0 { let unit = RGB_UNIT_MAX * l; return new_rgb_units(unit, unit, unit); } let temp1 = if l < 0.5 { l * (ONE + s) } else { l + s - l * s }; let temp2 = TWO * l - temp1; let hue = h; let temp_r = bound_ratio(hue + ONE_THIRD); let temp_g = bound_ratio(hue); let temp_b = bound_ratio(hue - ONE_THIRD); let r = calc_rgb_unit(temp_r, temp1, temp2); let g = calc_rgb_unit(temp_g, temp1, temp2); let b = calc_rgb_unit(temp_b, temp1, temp2); new_rgb_units(r, g, b) } fn calc_rgb_unit(unit: f64, temp1: f64, temp2: f64) -> f64 { let mut result = temp2; if SIX * unit < ONE { result = temp2 + (temp1 - temp2) * SIX * unit } else if TWO * unit < ONE { result = temp1 } else if 3.0 * unit < TWO { result = temp2 + (temp1 - temp2) * (TWO_THIRD - unit) * SIX } result * RGB_UNIT_MAX } #[cfg(test)] mod test { use crate::{ApproxEq, ColorTuple, Hsl, Rgb}; #[test] fn hsl_to_rgb_tst() { use crate::converters::hsl_to_rgb; fn a(x: ColorTuple, y: ColorTuple) -> bool { let hsl = Hsl::from(x); let rgb = Rgb::from(y); hsl_to_rgb(&hsl).approx_eq_clarify(&rgb.units, 0.5) } assert!(a((200.0, 100.0, 30.0), (0.0, 102.0, 153.0))); assert!(a((192.0, 67.0, 28.0), (24.0, 100.0, 119.0))); assert!(a((48.0, 70.0, 50.0), (217.0, 181.0, 38.0))); assert!(a((359.0, 33.0, 77.0), (216.0, 177.0, 178.0))); } } colorsys-0.6.5/src/converters/mod.rs000064400000000000000000000004400072674642500156450ustar 00000000000000mod hex_to_rgb; mod hsl_to_rgb; mod rgb_to_hex; mod rgb_to_hsl; mod rgb_cmyk; pub(crate) use hex_to_rgb::hex_to_rgb; pub(crate) use hsl_to_rgb::hsl_to_rgb; pub(crate) use rgb_to_hex::rgb_to_hex; pub(crate) use rgb_to_hsl::rgb_to_hsl; pub(crate) use rgb_cmyk::{rgb_to_cmyk, cmyk_to_rgb}; colorsys-0.6.5/src/converters/rgb_cmyk.rs000064400000000000000000000030370072674642500166700ustar 00000000000000use crate::{Cmyk, Rgb}; use crate::cmyk::CmykRatio; use crate::consts::{RATIO_MAX, RGB_UNIT_MAX}; pub(crate) fn rgb_to_cmyk(rgb: &Rgb) -> Cmyk { let rgb_r = rgb.units.as_ratio(); let [ r, g, b ]: [f64; 3] = (&rgb_r).into(); let k = RATIO_MAX - rgb_r.max().0; let x = RATIO_MAX - k; let c = (x - r) / x; let m = (x - g) / x; let y = (x - b) / x; CmykRatio::new(c, m, y, k, rgb.units.alpha.get_f64()).into() } pub(crate) fn cmyk_to_rgb(cmyk: &Cmyk) -> Rgb { let [ c, m, y, k ]: [f64;4] = cmyk.units.as_ratio().into(); let x = RGB_UNIT_MAX * (1.0 - k); Rgb::new((1.0 - c) * x, (1.0 - m) * x, (1.0 - y) * x, cmyk.units.alpha.get()) } #[allow(clippy::float_cmp)] #[cfg(test)] mod test { use crate::{Cmyk, Rgb}; use crate::converters::{cmyk_to_rgb, rgb_to_cmyk}; #[test] fn cmyk_to_rbg_test() { let cmyk = Cmyk::new(70.0, 23.0, 11.0, 55.0, None); let rgb: Rgb = cmyk_to_rgb(&cmyk); assert_eq!(rgb.units[0].round(), 34.0); assert_eq!(rgb.units[1].round(), 88.0); assert_eq!(rgb.units[2].round(), 102.0); } #[test] fn rbg_to_cmyk_test() { // let assert_data = [ // ([35, 75, 89], [61, 16, 0, 65]), // ([0, 0, 255], [100, 100, 0, 0]), // ([0, 0, 0], [0, 0, 0, 100]), // ([255, 255, 255], [0, 0, 0, 0]), // ]; let rgb = Rgb::new(230.0, 19.0, 70.0, None); let cmyk: Cmyk = rgb_to_cmyk(&rgb); assert_eq!(cmyk.cyan().round(), 0.0); assert_eq!(cmyk.magenta().round(), 92.0); assert_eq!(cmyk.yellow().round(), 70.0); assert_eq!(cmyk.key().round(), 10.0); } } colorsys-0.6.5/src/converters/rgb_to_hex.rs000064400000000000000000000006020072674642500172060ustar 00000000000000 #[cfg(not(feature = "std"))] use alloc::string::String; use crate::ColorTuple; use crate::common::f64_round; fn to_hex(n: f64) -> String { let s = format!("{:x}", f64_round(n) as u32); if s.len() == 1 { String::from("0") + &s } else { s } } pub fn rgb_to_hex(t: &ColorTuple) -> String { let (r, g, b) = *t; format!("#{}{}{}", to_hex(r), to_hex(g), to_hex(b)) } colorsys-0.6.5/src/converters/rgb_to_hsl.rs000064400000000000000000000035030072674642500172130ustar 00000000000000use crate::Rgb; use crate::consts::{ALL_MIN, PERCENT_MAX}; use crate::hsl::new_hsl_units; use crate::units::Units; pub fn rgb_to_hsl(rgb: &Rgb) -> Units { let rgb_r = rgb.units.as_ratio(); let (max, max_unit) = rgb_r.max(); let (min, _) = rgb_r.min(); let max_plus_min = max + min; let luminance = (max_plus_min) / 2.0; if max.eq(&min) { return new_hsl_units(ALL_MIN, ALL_MIN, luminance * PERCENT_MAX); } let max_min_delta = max - min; let saturation = if luminance > 0.5 { max_min_delta / (2.0 - max_plus_min) } else { max_min_delta / (max_plus_min) }; let [red, green, blue]: [f64; 3] = rgb_r.into(); let hue = match max_unit { // red 0 => { let x = if green < blue { 6.0 } else { ALL_MIN }; (green - blue) / max_min_delta + x } // green 1 => (blue - red) / max_min_delta + 2.0, // blue 2 => (red - green) / max_min_delta + 4.0, _ => { unreachable!() } }; new_hsl_units(hue * 60.0, saturation * PERCENT_MAX, luminance * PERCENT_MAX) } #[test] fn rgb_to_hsl_tst() { use crate::{ColorTuple, Rgb, Hsl, ApproxEq}; fn a(x: ColorTuple, y: ColorTuple) -> bool { let from_rgb_u = rgb_to_hsl(&Rgb::from(x)); let hsl_u = new_hsl_units(y.0, y.1, y.2); println!("rgb {:?}\n\n", Into::::into(Rgb::from(x))); from_rgb_u.approx_eq_clarify(&hsl_u, 0.5) // Rgb::from(x).approx_eq_clarify(&Hsl::from(y), 0.5) // approx_tuple(&rgb_to_hsl(&Rgb::from(x)), &y, 0.5) } let asserts = [ ((255.0, 255.0, 255.0), (0.0, 0.0, 100.0)), ((0.0, 0.0, 0.0), (0.0, 0.0, 0.0)), ((215.0, 231.0, 236.0), (194.0, 36.0, 88.0)), ((108.0, 225.0, 36.0), (97.0, 76.0, 51.0)), ((215.0, 0.0, 99.0), (332.0, 100.0, 42.0)), ((10.0, 10.0, 10.0), (0.0, 0.0, 4.0)), ]; asserts.iter().for_each(|tuples| { assert!(a(tuples.0, tuples.1)); }); } colorsys-0.6.5/src/err.rs000064400000000000000000000013150072674642500134660ustar 00000000000000use core::fmt; #[cfg(not(feature = "std"))] use alloc::string::String; #[derive(Clone)] pub struct ParseError { pub message: String, } fn fmt(f: &mut fmt::Formatter, msg: &str) -> Result<(), fmt::Error> { write!(f, "{}", msg) } impl fmt::Debug for ParseError { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt(f, &self.message) } } impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt(f, &self.message) } } pub fn make_parse_err(s: &str, col_type: &str) -> ParseError { ParseError { message: format!("cannot parse string `{}` as {}", s, col_type) } } #[cfg(feature = "std")] impl std::error::Error for ParseError {} colorsys-0.6.5/src/hsl/from.rs000064400000000000000000000073600072674642500144350ustar 00000000000000use crate::{ ColorAlpha, converters::*, ratio_converters::ratio_to_hsla, }; use super::{Hsl, HslRatio, Rgb}; macro_rules! from_for_hsl { ($from_type: ty, $val: ident, $conv: block) => { impl From<&$from_type> for Hsl { fn from($val: &$from_type) -> Hsl { ($conv) } } impl From<$from_type> for Hsl { fn from($val: $from_type) -> Hsl { Hsl::from(&$val) } } }; } macro_rules! from_for_hsl_all { ($t: ty) => { from_for_hsl!(($t, $t, $t), v, { let (h, s, l) = *v; Hsl::new(h as f64, s as f64, l as f64, None) }); from_for_hsl!([$t; 3], v, { let [h, s, l] = *v; Hsl::new(h as f64, s as f64, l as f64, None) }); }; } macro_rules! from_for_hsl_all_with_alpha { ($t: ty) => { from_for_hsl!(($t, $t, $t, $t), v, { let (h, s, l, a) = *v; Hsl::new(h as f64, s as f64, l as f64, Some(a as f64)) }); from_for_hsl!([$t; 4], v, { let [h, s, l, a] = *v; Hsl::new(h as f64, s as f64, l as f64, Some(a as f64)) }); }; } from_for_hsl_all!(f32); from_for_hsl_all!(f64); from_for_hsl_all_with_alpha!(f32); from_for_hsl_all_with_alpha!(f64); from_for_hsl_all!(i16); from_for_hsl_all!(i32); from_for_hsl_all!(i64); from_for_hsl_all!(u16); from_for_hsl_all!(u32); from_for_hsl_all!(u64); fn from_rgb(rgb: &Rgb) -> Hsl { let a = rgb.alpha(); let mut hsl = Hsl::from_units(rgb_to_hsl(rgb)); hsl.set_alpha(a); hsl } impl From<&Rgb> for Hsl { /// # Example /// ``` /// use colorsys::{Rgb,Hsl,prelude::*}; /// let rgb = Rgb::from(&(215.0, 231.0, 236.0)); /// let hsl = Hsl::from(&rgb); /// assert_eq!(hsl.to_css_string(), "hsl(194,36%,88%)"); /// ``` fn from(rgb: &Rgb) -> Self { from_rgb(rgb) } } impl From<&mut Rgb> for Hsl { /// # Example /// ``` /// use colorsys::{Rgb,Hsl,prelude::*}; /// let mut rgb = Rgb::from(&(0.0, 0.0, 0.0)); /// let hsl_string = Hsl::from(&mut rgb).to_css_string(); /// assert_eq!(hsl_string, "hsl(0,0%,0%)"); /// ``` fn from(rgb: &mut Rgb) -> Self { from_rgb(rgb) } } impl From for Hsl { /// # Example /// ``` /// use colorsys::{Rgb,Hsl,prelude::*}; /// let rgb = Rgb::from(&(255.0, 255.0, 255.0)); /// let hsl_string = Hsl::from(rgb).to_css_string(); /// assert_eq!(hsl_string, "hsl(0,0%,100%)"); /// ``` fn from(rgb: Rgb) -> Self { from_rgb(&rgb) } } fn from_hsl_ratio(ratio: &HslRatio) -> Hsl { let ru = &ratio.units; let t = ratio_to_hsla(&(ru[0], ru[1], ru[2], ru.alpha.get_f64())); Hsl::new(t.0, t.1, t.2, Some(t.3)) } impl From<&HslRatio> for Hsl { fn from(r: &HslRatio) -> Self { from_hsl_ratio(r) } } impl From<&mut HslRatio> for Hsl { fn from(r: &mut HslRatio) -> Self { from_hsl_ratio(r) } } impl From for Hsl { fn from(r: HslRatio) -> Self { from_hsl_ratio(&r) } } // // // // INTO // macro_rules! into_for_hsl_all { ($t: ty) => { into_for_some!(($t, $t, $t), Hsl, self, { let u = &self.units; (u[0] as $t, u[1] as $t, u[2] as $t) }); into_for_some!([$t; 3], Hsl, self, { let u = &self.units; [u[0] as $t, u[1] as $t, u[2] as $t] }); }; } macro_rules! into_for_hsl_all_with_alpha { ($t: ty) => { into_for_some!(($t, $t, $t, $t), Hsl, self, { let u = &self.units; (u[0] as $t, u[1] as $t, u[2] as $t, self.units.alpha.get_f64() as $t) }); into_for_some!([$t; 4], Hsl, self, { let u = &self.units; [u[0] as $t, u[1] as $t, u[2] as $t, self.units.alpha.get_f64() as $t] }); }; } into_for_hsl_all!(f32); into_for_hsl_all!(f64); into_for_hsl_all_with_alpha!(f32); into_for_hsl_all_with_alpha!(f64); into_for_hsl_all!(i16); into_for_hsl_all!(i32); into_for_hsl_all!(i64); into_for_hsl_all!(u16); into_for_hsl_all!(u32); into_for_hsl_all!(u64); colorsys-0.6.5/src/hsl/mod.rs000064400000000000000000000057240072674642500142530ustar 00000000000000#[cfg(not(feature = "std"))] use alloc::string::String; pub use ratio::HslRatio; use crate::{ColorAlpha, ColorTupleA, ColorUnitsIter, ParseError, Rgb}; use crate::common::{Hs, hsl_hsv_from_str, tuple_to_string, }; use crate::units::{Alpha, GetColorUnits, Unit, Units}; #[cfg(test)] mod tests; mod from; mod ops; mod ratio; mod transform; /// The HSL or HSI (hue, saturation, lightness (intensity)) color model /// /// Ranges: /// * hue: 0.0 - 360.0 /// * saturation: 0.0 - 100.0 /// * saturation: 0.0 - 100.0 /// * alpha: 0.0 - 1.0 #[derive(Debug, PartialEq, Clone)] pub struct Hsl { pub(crate) units: Units, } iter_def!(Hsl); pub(crate) fn new_hsl_units(h: f64, s: f64, l: f64) -> Units { let ul = [Unit::new_hue(h), Unit::new_percent(s), Unit::new_percent(l), Unit::default()]; Units { len: 3, list: ul, alpha: Alpha::default() } } impl Hsl { pub fn new(h: f64, s: f64, l: f64, a: Option) -> Hsl { let mut units = new_hsl_units(h, s, l); units.alpha.set_opt(a); units.restrict(); Hsl { units } } pub(crate) fn from_units(u: Units) -> Self { Hsl { units: u } } pub fn to_css_string(&self) -> String { let t: ColorTupleA = self.into(); tuple_to_string(&t, "hsl") } pub fn hue(&self) -> f64 { self.units[0] } pub fn saturation(&self) -> f64 { self.units[1] } pub fn lightness(&self) -> f64 { self.units[2] } #[deprecated(since = "0.7.0", note = "Please use `hue` instead")] pub fn get_hue(&self) -> f64 { self.hue() } #[deprecated(since = "0.7.0", note = "Please use `saturation` instead")] pub fn get_saturation(&self) -> f64 { self.saturation() } #[deprecated(since = "0.7.0", note = "Please use `lightness` instead")] pub fn get_lightness(&self) -> f64 { self.lightness() } pub fn set_hue(&mut self, val: f64) { self.units.list[0].set(val); } pub fn set_saturation(&mut self, val: f64) { self.units.list[1].set(val); } pub fn set_lightness(&mut self, val: f64) { self.units.list[2].set(val); } /// Returns an iterator over three color units and the possibly alpha value. pub fn iter(&self) -> ColorUnitsIter { ColorUnitsIter::from_units(&self.units) } /// Returns an HSL representation with values converted to floar from 0.0 to 1.0 pub fn as_ratio(&self) -> HslRatio { HslRatio::from_units(self.units.as_ratio()) } } // // // // Default // impl Default for Hsl { fn default() -> Hsl { Hsl::from_units(new_hsl_units(0.0, 0.0, 0.0)) } } // // // // AsRef // impl AsRef for Hsl { fn as_ref(&self) -> &Hsl { self } } // // // // FromStr // impl core::str::FromStr for Hsl { type Err = ParseError; fn from_str(s: &str) -> Result { let (tuple, alpha) = hsl_hsv_from_str(s, Hs::Hsl)?; let mut hsl = Hsl::from(&tuple); if let Some(a) = alpha { hsl.set_alpha(a); } Ok(hsl) } } impl GetColorUnits for Hsl { fn get_units(&self) -> &Units { &self.units } fn get_units_mut(&mut self) -> &mut Units { &mut self.units } } colorsys-0.6.5/src/hsl/ops.rs000064400000000000000000000007710072674642500142720ustar 00000000000000use core::ops::{Add, AddAssign, Sub, SubAssign}; use crate::{ApproxEq, DEFAULT_APPROX_EQ_PRECISION, Rgb}; use super::Hsl; ops_def!(Hsl); impl ApproxEq for Hsl { fn approx_eq(&self, rgb: &Rgb) -> bool { self.approx_eq_clarify(rgb, DEFAULT_APPROX_EQ_PRECISION) } fn approx_eq_clarify(&self, rgb: &Rgb, precision: f64) -> bool { let hsl_from_rgb: Hsl = rgb.into(); self.units.approx_eq_clarify(&hsl_from_rgb.units, precision) } } #[test] fn hsl_add() {} #[test] fn hsl_eq() {} colorsys-0.6.5/src/hsl/ratio.rs000064400000000000000000000062540072674642500146110ustar 00000000000000use crate::units::{Units, GetColorUnits}; /// /// Hsl representation as ratio (from `0.0` to `1.0`). /// Cannot be modified, added, subtracted, etc. Can be converted to `Hsl` and vice versa. /// Used for compatibility in various libraries. /// /// # Example /// ```rust /// use colorsys::{ApproxEq, Hsl, HslRatio}; /// /// let origin = Hsl::from((126.0, 43.0, 52.0)); /// /// let ratio_f32: [f32; 3] = origin.as_ratio().into(); /// let ratio_f64: [f64; 4] = origin.as_ratio().into(); /// // ~[0.35, 0.43, 0.52, 1.0] /// /// let converted_f32: Hsl = HslRatio::from(&ratio_f32).into(); /// let converted_f64: Hsl = HslRatio::from(&ratio_f64).into(); /// /// assert!(origin.approx_eq_clarify(&converted_f32, 0.0001)); /// assert!(origin.approx_eq(&converted_f64)); /// ``` /// #[derive(Clone)] pub struct HslRatio { pub(super) units: Units, } impl HslRatio { pub fn new(h: f64, s: f64, l: f64, a: f64) -> Self { let mut units = Units::new_ratios(&[h, s, l]); units.alpha.set(a); HslRatio::from_units(units) } pub fn h(&self) -> f64 { self.units[0] } pub fn s(&self) -> f64 { self.units[1] } pub fn l(&self) -> f64 { self.units[2] } pub fn a(&self) -> f64 { self.units.alpha.get_f64() } pub(crate) fn from_units(u: Units) -> Self { HslRatio { units: u } } } impl AsRef for HslRatio { fn as_ref(&self) -> &HslRatio { self } } impl GetColorUnits for HslRatio { fn get_units(&self) -> &Units { &self.units } fn get_units_mut(&mut self) -> &mut Units { &mut self.units } } macro_rules! from_for_hsl_ratio { ($from_type: ty, $val: ident, $conv: block) => { impl From<&$from_type> for HslRatio { fn from($val: &$from_type) -> HslRatio { ($conv) } } impl From<$from_type> for HslRatio { fn from($val: $from_type) -> HslRatio { HslRatio::from(&$val) } } }; } macro_rules! from_for_hsl_ratio_all { ($t: ty) => { from_for_hsl_ratio!(($t, $t, $t), v, { let (h, s, l) = *v; HslRatio::new(h as f64, s as f64, l as f64, 1.0) }); from_for_hsl_ratio!(($t, $t, $t, $t), v, { let (h, s, l, a) = *v; HslRatio::new(h as f64, s as f64, l as f64, a as f64) }); from_for_hsl_ratio!([$t; 3], v, { let [h, s, l] = *v; HslRatio::new(h as f64, s as f64, l as f64, 1.0) }); from_for_hsl_ratio!([$t; 4], v, { let [h, s, l, a] = *v; HslRatio::new(h as f64, s as f64, l as f64, a as f64) }); }; } from_for_hsl_ratio_all!(f32); from_for_hsl_ratio_all!(f64); macro_rules! into_for_hsl_ratio_all { ($t: ty) => { into_for_some!(($t, $t, $t), HslRatio, self, { let u = &self.get_units(); (u[0] as $t, u[1] as $t, u[2] as $t) }); into_for_some!(($t, $t, $t, $t), HslRatio, self, { let u = &self.get_units(); (u[0] as $t, u[1] as $t, u[2] as $t, u.alpha.get_f64() as $t) }); into_for_some!([$t; 3], HslRatio, self, { let u = &self.get_units(); [u[0] as $t, u[1] as $t, u[2] as $t] }); into_for_some!([$t; 4], HslRatio, self, { let u = &self.get_units(); [u[0] as $t, u[1] as $t, u[2] as $t, u.alpha.get_f64() as $t] }); }; } into_for_hsl_ratio_all!(f32); into_for_hsl_ratio_all!(f64); colorsys-0.6.5/src/hsl/tests.rs000064400000000000000000000016570072674642500146370ustar 00000000000000use crate::{ApproxEq, ColorTuple, Hsl, Rgb}; use crate::common::f64_round; fn round(n: f64) -> u32 { f64_round( n ) as u32 } fn round_tuple(t: &ColorTuple) -> (u32, u32, u32) { let (x, y, z) = *t; (round(x), round(y), round(z)) } #[test] fn hsl_to_rgb() { let hsl = Hsl::from((126.0, 43.0, 52.0)); let rgb = Rgb::from(&hsl); #[cfg(feature = "std")] assert_eq!(round_tuple(&rgb.as_ref().into()), (80, 185, 90)); #[cfg(not(feature = "std"))] assert_eq!(round_tuple(&rgb.as_ref().into()), (79, 185, 90)); let hsl_new = Hsl::from(&rgb); assert!(hsl_new.approx_eq(&hsl)); // let x: ColorTuple = hsl.into(); // let y = Hsl::from(&x); // println!("{:?}", x); // println!("{:?}", y); // let a = [1.0_f64, 1.0, 12.0, 2.0]; // let hsl = Hsl::from(&a[..]); // let v = vec![1, 2, 3]; // for a in &v {} // let rgb = Rgb::from_hex_str("ffcc00").unwrap(); // for u in rgb { // println!("{:?}", u); // } } colorsys-0.6.5/src/hsl/transform.rs000064400000000000000000000014710072674642500155020ustar 00000000000000use crate::{ColorTransform, SaturationInSpace}; use crate::consts::{ALL_MIN, HUE_MAX}; use crate::normalize::bound_hue; use super::Hsl; impl ColorTransform for Hsl { fn lighten(&mut self, amt: f64) { self.units.list[2].increase(amt); } fn saturate(&mut self, sat: SaturationInSpace) { match sat { SaturationInSpace::Hsl(s) => self.units.list[1].increase(s), SaturationInSpace::Hsv(s) => { unimplemented!("{}", s); } } } fn adjust_hue(&mut self, hue: f64) { let h = bound_hue(self.units[0] + hue); self.units.list[0].set(h); } fn grayscale_simple(&mut self) { self.units.list[0].value = ALL_MIN; self.units.list[1].value = ALL_MIN; } fn invert(&mut self) { let h = (self.units[0] + HUE_MAX * 0.5) % HUE_MAX; self.units.list[0].set(h); } } colorsys-0.6.5/src/lib.rs000064400000000000000000000135050072674642500134500ustar 00000000000000//! A module for color conversion and mutation written in Rust. //! For now works with RGB(a)( as hexadecimal too), HSL(a) color models //! //! ## What It Can Do //! //! #### getters & setters //! ``` //! use colorsys::{Rgb, Hsl, ColorAlpha}; //! //! let rgb = Rgb::from((57.3, 12.7, 53.0)); //! let r = rgb.red(); //! // 57.3 //! //! let mut hsl = Hsl::default(); //! // Hsl { h: 0, s: 0, l: 0, a: 1 } //! hsl.set_saturation(13.98); //! hsl.set_saturation(305.71); //! hsl.set_alpha(0.75); //! // Hsl { h: 305.71, s: 13.98, l: 0, a: 0.75 } //! ``` //! //! #### conversion //! See `From/FromStr/Into` traits implementation in docs for more info //! ``` //! use colorsys::{Rgb, Hsl}; //! //! let rbga_tuple = (57.3, 12.7, 53.0, 0.33); //! let rgba = Rgb::from(&rbga_tuple); //! let hsla: Hsl = rgba.as_ref().into(); //! // ~Hsl { h: 305.78, s: 63.71, l: 13.73, a: 0.33 } //! //! let rgb_arr: [u8; 3] = Rgb::from(&hsla).into(); //! // ~[57, 13, 53] //! //! let hsla_tuple: (f64,f64,f64,f64) = Hsl::from( Rgb::from(rgb_arr) ).into(); //! // ~Hsl { h: 305.78, s: 63.71, l: 13.73, a: 1 } //! //! let hex: String = rgba.to_hex_string(); //! // #390d35 //! //! //! // From/Into //! //! let rgb1 = Rgb::from_hex_str("37ea4c").unwrap(); //! //! let rgb2 = Rgb::from( //! Into::<[f32; 4]>::into(Rgb::from( //! Into::<[u16; 3]>::into( //! Rgb::from( //! Into::<(i32,i32,i32)>::into( //! Rgb::from( //! Into::<[i64; 3]>::into(&rgb1) //! ) //! ) //! ) //! ) //! )) //! ); //! //! assert_eq!(rgb1, rgb2); //! // //! // Ratio //! // //! use colorsys::{RgbRatio, ApproxEq}; //! let blue = Rgb::from([34, 111, 235]); //! //! let ratio: [f32; 4] = blue.as_ratio().into(); //! // ~[0.133, 0.435, 0.922, 1.0] //! //! let converted: Rgb = RgbRatio::from(&ratio).into(); //! assert!(blue.approx_eq_clarify(&converted, 0.0001)); //! ``` //! //! //! #### modification //! See `ColorTransform/Add*/Sub*..` traits in docs for more //! ``` //! use colorsys::{Hsl, Rgb, ColorTransform,ColorAlpha, SaturationInSpace}; //! //! let mut rgb: Rgb = (245.0,152.0,53.0).into(); //! //! rgb.lighten(20.1); //! // ~Rgb { r: 249.83, g: 201.80, b: 150.67 } //! //! rgb.opacify(-0.7); //! rgb.saturate( SaturationInSpace::Hsl(-35.7) ); //! // ~Rgb { r: 230.29, g: 201.19, b: 170.21, a: 0.3 } //! //! rgb.grayscale_simple(); //! // ~Rgb { r: 200.255, g: 200.255, b: 200.255, a: 0.3 } //! //! //! let mut hsl = Hsl::from(&rgb); //! hsl.opacify(1.0); //! // ~Hsl { h: 0.0, s: 0.0, l: 78.53 } //! //! hsl.adjust_hue(231.99); //! hsl.saturate(SaturationInSpace::Hsl(55.7)); //! //! let mut rgb2: Rgb = hsl.as_ref().into(); //! // ~Rgb { r: 169.76, g: 177.9, b: 230.75} //! //! rgb2.invert(); //! // ~Rgb { r: 85.24, g: 77.09, b: 24.25 } //! //! let rgb3 = rgb - rgb2; //! // ~Rgb { r: 115.01, g: 123.16, b: 176.0 } //! //! let hsl2 = hsl + rgb3.into(); //! // ~Hsl { h: 0.0, s: 83.55, l: 100.0 } //! //! ``` //! //! #### parsing from string & css string representation //! ``` //! use colorsys::{Hsl, Rgb}; //! use std::str::FromStr; //! //! let s = "rgb(177, 255, 176)"; //! //! let rgb: Rgb = s.parse().unwrap(); //! //! rgb.to_css_string(); //! // String: "rgb(177,255,176)" //! //! rgb.to_hex_string(); //! // #b1ffb0 //! //! Hsl::from_str("hsl(168, 52%, 42%)").unwrap().to_css_string(); //! // String: hsl(168,52%,42%) //! //! ``` //! //! ## `no_std` //! Crate has a Cargo feature named `"std"` that is enabled by default. //! In order to use `colorsys` in a `no_std` context this feature needs to be disabled. //! Modify your dependency to opt out of enabled-by-default features. //! ```toml //! [dependencies] //! colorsys = { version = "*", default-features = false } //! ``` //! //! ## Color unit ranges //! All color units is f64. Here are their ranges: //! - red - 0.0 .. 255.0 //! - green - 0.0 .. 255.0 //! - blue - 0.0 .. 255.0 //! - hue - 0.0 .. 360.0 //! - saturation - 0.0 .. 100.0 //! - lightness - 0.0 .. 100.0 //! - alpha - 0.0 .. 1.0 //! //! If you specify a value that does not fit within these ranges, they are replaced with a minimum or maximum value. //! //! #![allow(clippy::many_single_char_names)] #![allow(clippy::from_over_into)] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(not(feature = "std"))] #[macro_use] extern crate alloc; mod macros; mod common; mod consts; mod converters; mod err; mod hsl; mod normalize; mod rgb; mod ansi; mod cmyk; mod units; pub mod prelude; pub mod ratio_converters; pub use common::approx::{ApproxEq, DEFAULT_APPROX_EQ_PRECISION}; pub use common::{ColorUnitsIter, ColorAlpha}; pub use err::ParseError; pub use hsl::{Hsl, HslRatio}; pub use rgb::{GrayScaleMethod, Rgb, RgbRatio}; pub use cmyk::{Cmyk,CmykRatio}; pub use ansi::{Ansi256}; /// Use to transfer and collect color values. /// May be for example `($red,$green,$blue)` or `($hue,$saturation,$value)` pub type ColorTuple = (f64, f64, f64); /// For example `($hue,$saturation,$lightness,$alpha)` pub type ColorTupleA = (f64, f64, f64, f64); pub enum SaturationInSpace { Hsl(f64), Hsv(f64), } /// A collection of methods to some special modification of color. /// Some methods (like saturate, lighten, etc.) requires (inside implementation) /// converting to another color space and vice versa. pub trait ColorTransform { /// Makes color lighter or (if amt is negative) darker. /// Amt is percent - `1..100` to make color lighter; `-100..-1` for blacking-out fn lighten(&mut self, amt: f64); /// Saturate/desaturate color. /// Value is percent: `-100..100`. /// You need specify in what color space you want to increase/decrease saturation. fn saturate(&mut self, sat: SaturationInSpace); /// increase/decrease color tone. Value is degree - `-360..360`. fn adjust_hue(&mut self, hue: f64); /// Brings color to a shade of gray. For more specific grayscale methods see `Rgb.grayscale` fn grayscale_simple(&mut self); /// Just inverts color fn invert(&mut self); } colorsys-0.6.5/src/macros.rs000064400000000000000000000036210072674642500141640ustar 00000000000000#![macro_use] macro_rules! into_for_some { ($into: ty, $some: ty, $sel: ident, $conv: block) => { impl<'a> Into<$into> for &'a $some { fn into($sel) -> $into { ($conv) } } impl<'a> Into<$into> for &'a mut $some { fn into($sel) -> $into { ($conv) } } impl Into<$into> for $some { fn into($sel) -> $into { $sel.as_ref().into() } } }; } macro_rules! ops_def { ($name: ident) => { impl<'a> Add for &'a $name { type Output = $name; fn add(self, rhs: &$name) -> $name { $name::from_units(&self.units + &rhs.units) } } impl<'a> Add for &'a mut $name { type Output = $name; fn add(self, rhs: &'a mut $name) -> $name { $name::from_units(&self.units + &rhs.units) } } impl Add for $name { type Output = $name; fn add(self, rhs: Self) -> Self { $name::from_units(&self.units + &rhs.units) } } impl AddAssign for $name { fn add_assign(&mut self, rhs: Self) { *self = $name::from_units(&self.units + &rhs.units); } } impl Sub for $name { type Output = $name; fn sub(self, rhs: Self) -> Self { $name::from_units(&self.units - &rhs.units) } } impl<'a> Sub for &'a $name { type Output = $name; fn sub(self, rhs: Self) -> $name { $name::from_units(&self.units - &rhs.units) } } impl<'a> Sub for &'a mut $name { type Output = $name; fn sub(self, rhs: Self) -> $name { $name::from_units(&self.units - &rhs.units) } } impl SubAssign for $name { fn sub_assign(&mut self, rhs: Self) { *self = $name::from_units(&self.units - &rhs.units); } } }; } macro_rules! iter_def { ($name: ident) => { impl<'a> core::iter::IntoIterator for &'a $name { type Item = f64; type IntoIter = ColorUnitsIter; fn into_iter(self) -> ColorUnitsIter { self.iter() } } impl core::iter::IntoIterator for $name { type Item = f64; type IntoIter = ColorUnitsIter; fn into_iter(self) -> ColorUnitsIter { self.iter() } } }; } colorsys-0.6.5/src/normalize.rs000064400000000000000000000016200072674642500146750ustar 00000000000000 use super::consts::{ALL_MIN, HUE_MAX, PERCENT_MAX, RATIO_MAX, RGB_UNIT_MAX}; use crate::common::{f64_round}; fn normalize(val: f64, max: f64) -> f64 { if val < ALL_MIN { return ALL_MIN; } if val > max { return max; } val } pub fn normalize_percent(val: f64) -> f64 { normalize(val, PERCENT_MAX) } pub fn normalize_rgb_unit(val: f64) -> f64 { normalize(val, RGB_UNIT_MAX) } pub fn normalize_ratio(val: f64) -> f64 { normalize(val, RATIO_MAX) } pub fn bound(r: f64, entire: f64) -> f64 { let mut n = r; loop { let less = n < ALL_MIN; let bigger = n > entire; if !less && !bigger { break n; } if less { n += entire; } else { n -= entire; } } } pub fn bound_ratio(r: f64) -> f64 { bound(r, RATIO_MAX) } pub fn bound_hue(h: f64) -> f64 { bound(h, HUE_MAX) } pub fn round_ratio(r: f64) -> f64 { f64_round(r * 100.0) / 100.0 } colorsys-0.6.5/src/prelude.rs000064400000000000000000000001660072674642500143410ustar 00000000000000pub use super::{ ApproxEq, ColorAlpha, ColorTransform, ColorTuple, ColorTupleA, ParseError, SaturationInSpace, }; colorsys-0.6.5/src/ratio_converters.rs000064400000000000000000000031070072674642500162670ustar 00000000000000use super::consts::{HUE_MAX, PERCENT_MAX, RGB_UNIT_MAX}; use super::normalize::{ bound_hue, bound_ratio, normalize_percent, normalize_ratio, normalize_rgb_unit, }; use super::{ColorTuple, ColorTupleA}; fn rgb_to(n: f64) -> f64 { normalize_ratio(n / RGB_UNIT_MAX) } fn to_rgb(n: f64) -> f64 { normalize_rgb_unit(n * RGB_UNIT_MAX) } fn hue_to(n: f64) -> f64 { bound_hue(n) / HUE_MAX } fn to_hue(n: f64) -> f64 { bound_ratio(n) * HUE_MAX } fn per_to(n: f64) -> f64 { normalize_ratio(n / PERCENT_MAX) } fn to_per(n: f64) -> f64 { normalize_percent(n * PERCENT_MAX) } pub fn rgb_to_ratio(t: &ColorTuple) -> ColorTuple { (rgb_to(t.0), rgb_to(t.1), rgb_to(t.2)) } pub fn rgba_to_ratio(t: &ColorTupleA) -> ColorTupleA { (rgb_to(t.0), rgb_to(t.1), rgb_to(t.2), normalize_ratio(t.3)) } pub fn ratio_to_rgb(t: &ColorTuple) -> ColorTuple { (to_rgb(t.0), to_rgb(t.1), to_rgb(t.2)) } pub fn ratio_to_rgba(t: &ColorTupleA) -> ColorTupleA { (to_rgb(t.0), to_rgb(t.1), to_rgb(t.2), normalize_ratio(t.3)) } pub fn hsl_to_ratio(t: &ColorTuple) -> ColorTuple { (hue_to(t.0), per_to(t.1), per_to(t.2)) } pub fn hsla_to_ratio(t: &ColorTupleA) -> ColorTupleA { (hue_to(t.0), per_to(t.1), per_to(t.2), normalize_ratio(t.3)) } pub fn ratio_to_hsl(t: &ColorTuple) -> ColorTuple { (to_hue(t.0), to_per(t.1), to_per(t.2)) } pub fn ratio_to_hsla(t: &ColorTupleA) -> ColorTupleA { (to_hue(t.0), to_per(t.1), to_per(t.2), normalize_ratio(t.3)) } pub fn hsv_to_ratio(t: &ColorTuple) -> ColorTuple { hsl_to_ratio(t) } pub fn ratio_to_hsv(t: &ColorTuple) -> ColorTuple { ratio_to_hsl(t) } colorsys-0.6.5/src/rgb/from.rs000064400000000000000000000075550072674642500144270ustar 00000000000000use crate::{ ColorAlpha, Hsl, ratio_converters::ratio_to_rgba, Rgb, }; use crate::converters::*; use crate::rgb::RgbRatio; macro_rules! from_for_rgb { ($from_type: ty, $val: ident, $conv: block) => { impl From<&$from_type> for Rgb { fn from($val: &$from_type) -> Rgb { ($conv) } } impl From<$from_type> for Rgb { fn from($val: $from_type) -> Rgb { Rgb::from(&$val) } } }; } macro_rules! from_for_rgb_all { ($t: ty) => { from_for_rgb!(($t, $t, $t), v, { let (r, g, b) = *v; Rgb::new(r as f64, g as f64, b as f64, None) }); from_for_rgb!([$t; 3], v, { let [r, g, b] = *v; Rgb::new(r as f64, g as f64, b as f64, None) }); }; } macro_rules! from_for_rgb_all_with_alpha { ($t: ty) => { from_for_rgb!(($t, $t, $t, $t), v, { let (r, g, b, a) = *v; Rgb::new(r as f64, g as f64, b as f64, Some(a as f64)) }); from_for_rgb!([$t; 4], v, { let [r, g, b, a] = *v; Rgb::new(r as f64, g as f64, b as f64, Some(a as f64)) }); }; } from_for_rgb_all!(f32); from_for_rgb_all!(f64); from_for_rgb_all_with_alpha!(f32); from_for_rgb_all_with_alpha!(f64); from_for_rgb_all!(i16); from_for_rgb_all!(i32); from_for_rgb_all!(i64); from_for_rgb_all!(u8); from_for_rgb_all!(u16); from_for_rgb_all!(u32); from_for_rgb_all!(u64); fn from_hsl(hsl: &Hsl) -> Rgb { let a = hsl.alpha(); let mut rgb = Rgb::from_units(hsl_to_rgb(hsl)); rgb.set_alpha(a); rgb } impl From<&Hsl> for Rgb { /// # Example /// ``` /// use colorsys::{Rgb,Hsl,prelude::*}; /// let hsl = Hsl::from(&(48.0, 70.0, 50.0)); /// let rgb: Rgb = Rgb::from(&hsl); /// assert_eq!(rgb.to_css_string(), "rgb(217,181,38)"); /// ``` fn from(hsl: &Hsl) -> Self { from_hsl(hsl) } } impl From<&mut Hsl> for Rgb { /// # Example /// ``` /// use colorsys::{Rgb,Hsl,prelude::*}; /// let mut hsl = Hsl::from(&(359.0, 33.0, 77.0)); /// let rgb_string = Rgb::from(&mut hsl).to_css_string(); /// assert_eq!(rgb_string, "rgb(216,177,178)"); /// ``` fn from(hsl: &mut Hsl) -> Self { from_hsl(hsl) } } impl From for Rgb { /// # Example /// ``` /// use colorsys::{Rgb,Hsl,prelude::*}; /// let hsl = Hsl::from(&(192.0, 67.0, 28.0)); /// let rgb_string = Rgb::from(hsl).to_css_string(); /// assert_eq!(rgb_string, "rgb(24,100,119)"); /// ``` fn from(hsl: Hsl) -> Self { from_hsl(&hsl) } } fn from_rgb_ratio(ratio: &RgbRatio) -> Rgb { let ru = &ratio.units; let t = ratio_to_rgba(&(ru[0], ru[1], ru[2], ru.alpha.get_f64())); Rgb::new(t.0, t.1, t.2,Some(t.3)) } impl From<&RgbRatio> for Rgb { fn from(r: &RgbRatio) -> Self { from_rgb_ratio(r) } } impl From<&mut RgbRatio> for Rgb { fn from(r: &mut RgbRatio) -> Self { from_rgb_ratio(r) } } impl From for Rgb { fn from(r: RgbRatio) -> Self { from_rgb_ratio(&r) } } // // // // INTO // into_for_some!(RgbRatio, Rgb, self, { self.as_ratio() }); macro_rules! into_for_rgb_all { ($t: ty) => { into_for_some!(($t, $t, $t), Rgb, self, { let u = &self.units; (u[0] as $t, u[1] as $t, u[2] as $t) }); into_for_some!([$t; 3], Rgb, self, { let u = &self.units; [u[0] as $t, u[1] as $t, u[2] as $t] }); }; } macro_rules! into_for_rgb_all_with_alpha { ($t: ty) => { into_for_some!(($t, $t, $t, $t), Rgb, self, { let u = &self.units; (u[0] as $t, u[1] as $t, u[2] as $t, self.units.alpha.get_f64() as $t) }); into_for_some!([$t; 4], Rgb, self, { let u = &self.units; [u[0] as $t, u[1] as $t, u[2] as $t, self.units.alpha.get_f64() as $t] }); }; } into_for_rgb_all!(f32); into_for_rgb_all!(f64); into_for_rgb_all_with_alpha!(f32); into_for_rgb_all_with_alpha!(f64); into_for_rgb_all!(i16); into_for_rgb_all!(i32); into_for_rgb_all!(i64); into_for_rgb_all!(u8); into_for_rgb_all!(u16); into_for_rgb_all!(u32); into_for_rgb_all!(u64); colorsys-0.6.5/src/rgb/from_str.rs000064400000000000000000000024000072674642500152770ustar 00000000000000#[cfg(not(feature = "std"))] use alloc::vec::Vec; use consts::{ALL_MIN, RATIO_MAX, RGB_UNIT_MAX}; use crate::{ColorTuple, consts}; use crate::err::{make_parse_err, ParseError}; pub fn rgb(s: &str) -> Result<(ColorTuple, Option), ParseError> { let make_err = || Err(make_parse_err(s, "rgb or rgba")); let s = s.trim().to_lowercase().replace(" ", ""); let is_rgb = s.starts_with("rgb("); let is_rgba = s.starts_with("rgba("); let is_ends_with_bracket = s.ends_with(')'); if (!is_rgb && !is_rgba) || !is_ends_with_bracket { return make_err(); } let start_ind = if is_rgb { 4 } else { 5 }; let s = &s[start_ind..s.len() - 1]; let nums_str = s.split(',').collect::>(); let len = nums_str.len(); if (is_rgb && len != 3) || (is_rgba && len != 4) { return make_err(); } let mut nums = Vec::with_capacity(len); for (ind, n) in nums_str.iter().enumerate() { if let Ok(num) = n.parse::() { let max = if ind == 4 { RATIO_MAX } else { RGB_UNIT_MAX }; if num < ALL_MIN || num > max { return make_err(); } nums.push(num) } else { return make_err(); } } let rgb = (nums[0], nums[1], nums[2]); let alpha = if is_rgba { Some(nums[len - 1]) } else { None }; Ok((rgb, alpha)) } colorsys-0.6.5/src/rgb/grayscale.rs000064400000000000000000000037010072674642500154230ustar 00000000000000use crate::ColorTuple; use super::Rgb; pub enum GrayScaleMethod { Average, AverageProminent, Luminance, Rec709, Rec2100, } static R_YUV_FACTOR: f64 = 0.299; static G_YUV_FACTOR: f64 = 0.587; static B_YUV_FACTOR: f64 = 0.114; static YUV_FACTORS: ColorTuple = (R_YUV_FACTOR, G_YUV_FACTOR, B_YUV_FACTOR); static R_REC709_FACTOR: f64 = 0.2126; static G_REC709_FACTOR: f64 = 0.7152; static B_REC709_FACTOR: f64 = 0.0722; static REC709_FACTORS: ColorTuple = (R_REC709_FACTOR, G_REC709_FACTOR, B_REC709_FACTOR); static R_REC2100_FACTOR: f64 = 0.2627; static G_REC2100_FACTOR: f64 = 0.6780; static B_REC2100_FACTOR: f64 = 0.0593; static REC2100_FACTORS: ColorTuple = (R_REC2100_FACTOR, G_REC2100_FACTOR, B_REC2100_FACTOR); fn mul(rgb: &mut Rgb, factors: ColorTuple) { let mut vals = &mut rgb.units.list; vals[0].value *= factors.0; vals[1].value *= factors.1; vals[2].value *= factors.2; } fn rgb_to_grayscale_lum(rgb: &mut Rgb) { mul(rgb, YUV_FACTORS) } fn rgb_to_grayscale_rec709(rgb: &mut Rgb) { mul(rgb, REC709_FACTORS); } fn rgb_to_grayscale_rec2100(rgb: &mut Rgb) { mul(rgb, REC2100_FACTORS); } fn rgb_to_grayscale_avg(rgb: &mut Rgb) { let mut vals = &mut rgb.units.list; let y = (vals[0].value + vals[1].value + vals[2].value) / 3.0; vals[0].value = y; vals[1].value = y; vals[2].value = y; } fn rgb_to_grayscale_avg_prom(rgb: &mut Rgb) { let max = rgb.units.max(); let min = rgb.units.min(); let y = (max.0 + min.0) / 2.0; let mut vals = &mut rgb.units.list; vals[0].value = y; vals[1].value = y; vals[2].value = y; } pub fn rgb_grayscale(rgb: &mut Rgb, method: GrayScaleMethod) { match method { GrayScaleMethod::Average => rgb_to_grayscale_avg(rgb), GrayScaleMethod::AverageProminent => rgb_to_grayscale_avg_prom(rgb), GrayScaleMethod::Luminance => rgb_to_grayscale_lum(rgb), GrayScaleMethod::Rec709 => rgb_to_grayscale_rec709(rgb), GrayScaleMethod::Rec2100 => rgb_to_grayscale_rec2100(rgb), } } colorsys-0.6.5/src/rgb/mod.rs000064400000000000000000000115770072674642500142420ustar 00000000000000#[cfg(not(feature = "std"))] use alloc::string::String; pub use grayscale::GrayScaleMethod; use grayscale::rgb_grayscale; pub use ratio::RgbRatio; use crate::{ColorAlpha, ColorTuple, ColorTupleA, converters, Hsl, ColorUnitsIter}; use crate::common::{tuple_to_string}; use crate::err::ParseError; use crate::units::{Alpha, GetColorUnits, Unit, Units}; #[cfg(test)] mod tests; mod from; mod from_str; mod grayscale; mod ops; mod ratio; mod transform; /// The RGB color model. /// /// Has red, green, blue and optional `alpha` channel fields. /// Red, green, blue values are stored between 0.0 and 255.0, alpha is between 0.0 and 1.0. /// If inputted or received values are exceeds the allowed value, or is less than zero /// it will be equalize to limit. /// /// # Example /// ``` /// use colorsys::{Rgb, Hsl, prelude::*}; /// let mut rgb1 = Rgb::from((100.0, 255.0, 17.0)); /// // Rgb { r: 100.0, g: 255.0, b: 17.0, a: None } /// /// let green = rgb1.green(); /// // 255.0 /// /// rgb1.set_red(108.3); /// // Rgb { r: 108.3, g: 255.0, b: 17.0, .. } /// /// let mut hsl: Hsl = rgb1.into(); /// // ~Hsl { h: 96.98, s: 100.0, l: 53.333, .. } /// /// hsl.saturate( SaturationInSpace::Hsl(-57.901) ); /// // ~Hsl { h: 96.98, s: 42.099, l: 53.333, .. } /// /// let mut rgb2 = Rgb::from(&hsl); /// // ~Rgb { r: 124.34, g: 186.1, b: 85.9, .. } /// /// let rgb2tuple: (f64,f64,f64) = rgb2.as_ref().into(); /// // (124.34, 186.1,85.9) /// /// rgb2 += Rgb::from_hex_str("#35f15b").unwrap();; /// // ~Rgb { r: 177.33, g: 255.0, b: 176.902, .. } /// /// rgb2.set_green(-150.0); /// assert_eq!(rgb2.green(), 0.0); /// /// rgb2.lighten(-13.123); /// // ~Rgb { r: 110.41, g: 0.0, b: 110.1, .. } /// /// rgb2.grayscale_simple(); /// // ~Rgb { r: 55.2, g: 55.2, b: 55.2, .. } /// /// let css_string = rgb2.to_css_string(); /// assert_eq!(css_string, "rgb(55,55,55)"); /// ``` /// #[derive(Debug, PartialEq, Clone)] pub struct Rgb { pub(crate) units: Units, } iter_def!(Rgb); pub(crate) fn new_rgb_units(r: f64, g: f64, b: f64) -> Units { let ul = [Unit::new_rgb(r), Unit::new_rgb(g), Unit::new_rgb(b), Unit::default()]; Units { len: 3, list: ul, alpha: Alpha::default() } } impl Rgb { fn _apply_tuple(&mut self, t: &ColorTuple) { self.units.list[0].value = t.0; self.units.list[1].value = t.1; self.units.list[2].value = t.2; } pub(crate) fn from_units(u: Units) -> Self { Rgb { units: u } } pub fn new(r: f64, g: f64, b: f64, a: Option) -> Rgb { let mut units = new_rgb_units(r, g, b); units.alpha.set_opt(a); units.restrict(); Rgb { units } } pub fn from_hex_str(s: &str) -> Result { let tuple = converters::hex_to_rgb(s)?; Ok(Rgb::from(&tuple)) } pub fn to_hex_string(&self) -> String { converters::rgb_to_hex(&self.into()) } pub fn red(&self) -> f64 { self.units[0] } pub fn green(&self) -> f64 { self.units[1] } pub fn blue(&self) -> f64 { self.units[2] } #[deprecated(since = "0.7.0", note = "Please use `red` instead")] pub fn get_red(&self) -> f64 { self.red() } #[deprecated(since = "0.7.0", note = "Please use `green` instead")] pub fn get_green(&self) -> f64 { self.green() } #[deprecated(since = "0.7.0", note = "Please use `blue` instead")] pub fn get_blue(&self) -> f64 { self.blue() } pub fn set_red(&mut self, val: f64) { self.units.list[0].set(val); } pub fn set_green(&mut self, val: f64) { self.units.list[1].set(val); } pub fn set_blue(&mut self, val: f64) { self.units.list[2].set(val); } /// Returns a String that can be used in CSS. /// # Example /// ``` /// use colorsys::{Rgb}; /// /// let rgb = Rgb::from([55.0,31.1, 201.9]); /// assert_eq!(rgb.to_css_string(), "rgb(55,31,202)"); /// ``` pub fn to_css_string(&self) -> String { let t: ColorTupleA = self.into(); tuple_to_string(&t, "rgb") } pub fn grayscale(&mut self, method: GrayScaleMethod) { rgb_grayscale(self, method); } /// Returns an iterator over three color units and the possibly alpha value. pub fn iter(&self) -> ColorUnitsIter { ColorUnitsIter::from_units(&self.units) } /// Returns an RGB representation with values converted to floar from 0.0 to 1.0 pub fn as_ratio(&self) -> RgbRatio { RgbRatio::from_units(self.units.as_ratio()) } } // // // // Default // impl Default for Rgb { fn default() -> Rgb { Rgb::from_units(new_rgb_units(0.0, 0.0, 0.0)) } } // // // // AsRef // impl AsRef for Rgb { fn as_ref(&self) -> &Rgb { self } } impl GetColorUnits for Rgb { fn get_units(&self) -> &Units { &self.units } fn get_units_mut(&mut self) -> &mut Units { &mut self.units } } // // // // FromStr // impl core::str::FromStr for Rgb { type Err = ParseError; fn from_str(s: &str) -> Result { let (tuple, alpha) = from_str::rgb(s)?; let mut rgb = Rgb::from(&tuple); if let Some(a) = alpha { rgb.set_alpha(a); } Ok(rgb) } } colorsys-0.6.5/src/rgb/ops.rs000064400000000000000000000025270072674642500142570ustar 00000000000000use core::ops::{Add, AddAssign, Sub, SubAssign}; use crate::{ApproxEq, DEFAULT_APPROX_EQ_PRECISION, Hsl, Rgb}; ops_def!(Rgb); impl ApproxEq for Rgb { fn approx_eq(&self, hsl: &Hsl) -> bool { self.approx_eq_clarify(hsl, DEFAULT_APPROX_EQ_PRECISION) } fn approx_eq_clarify(&self, hsl: &Hsl, precision: f64) -> bool { let rgb: Rgb = hsl.into(); self.units.approx_eq_clarify(&rgb.units, precision) } } #[cfg(test)] mod test { use crate::{ApproxEq, ColorTupleA, Hsl, Rgb}; #[test] fn rgb_add() { let rgb1 = Rgb::default(); let rgb2 = Rgb::from_hex_str("ffcc00").unwrap(); let rgb3: ColorTupleA = (rgb1 + rgb2).into(); assert_eq!(Into::::into(rgb3), (255.0, 204.0, 0.0, 1.0)); let rgb1 = Rgb::new(200.0, 200.0, 200.0, Some(0.3)); let rgb2 = Rgb::new(200.0, 200.0, 200.0, None); let rgb3: ColorTupleA = (rgb1 + rgb2).into(); assert_eq!(rgb3, (255.0, 255.0, 255.0, 0.3)); } #[test] fn rgb_eq() { let rgb1 = Rgb::default(); let mut rgb2 = Rgb::default(); let rgb3 = Rgb::from((12.12534, 123.21321, 12.002_310_123)); let hsl: Hsl = rgb3.as_ref().into(); let rgb4 = Rgb::from(&hsl); rgb2 += rgb3.clone(); rgb2 -= rgb3.clone(); assert_eq!(rgb1, rgb2); assert!(rgb3.approx_eq(&rgb4)); assert!(rgb3.approx_eq_clarify(&hsl, 0.000_000_000_001)); } } colorsys-0.6.5/src/rgb/ratio.rs000064400000000000000000000063160072674642500145740ustar 00000000000000use crate::units::{GetColorUnits, Units}; /// /// Rgb representation as ratio (from `0.0` to `1.0`). /// Cannot be modified, added, subtracted, etc. Can be converted to `Rgb` and vice versa. /// Used for compatibility in various libraries. /// /// # Example /// ```rust /// use colorsys::{ApproxEq, Rgb, RgbRatio}; /// /// let origin = Rgb::from([134.9, 11.1, 250.55, 1.0]); /// /// let ratio_f32: [f32; 4] = origin.as_ratio().into(); /// let ratio_f64: [f64; 3] = origin.as_ratio().into(); /// // ~[0.5290196, 0.04352941, 0.982549] /// /// let converted_f32: Rgb = RgbRatio::from(&ratio_f32).into(); /// let converted_f64: Rgb = RgbRatio::from(&ratio_f64).into(); /// /// assert!(origin.approx_eq_clarify(&converted_f32, 0.0001)); /// assert!(origin.approx_eq(&converted_f64)); /// /// ``` /// #[derive(Debug, PartialEq, Clone)] pub struct RgbRatio { pub(crate) units: Units, } impl RgbRatio { pub fn new(r: f64, g: f64, b: f64, a: f64) -> Self { let mut units = Units::new_ratios(&[r, g, b]); units.alpha.set(a); RgbRatio { units } } pub fn r(&self) -> f64 { self.units[0] } pub fn g(&self) -> f64 { self.units[1] } pub fn b(&self) -> f64 { self.units[2] } pub fn a(&self) -> f64 { self.units.alpha.get_f64() } pub(crate) fn from_units(u: Units) -> Self { RgbRatio { units: u } } } impl AsRef for RgbRatio { fn as_ref(&self) -> &RgbRatio { self } } impl GetColorUnits for RgbRatio { fn get_units(&self) -> &Units { &self.units } fn get_units_mut(&mut self) -> &mut Units { &mut self.units } } macro_rules! from_for_rgb_ratio { ($from_type: ty, $val: ident, $conv: block) => { impl From<&$from_type> for RgbRatio { fn from($val: &$from_type) -> RgbRatio { ($conv) } } impl From<$from_type> for RgbRatio { fn from($val: $from_type) -> RgbRatio { RgbRatio::from(&$val) } } }; } macro_rules! from_for_rgb_ratio_all { ($t: ty) => { from_for_rgb_ratio!(($t, $t, $t), v, { let (r, g, b) = *v; RgbRatio::new(r as f64, g as f64, b as f64, 1.0) }); from_for_rgb_ratio!(($t, $t, $t, $t), v, { let (r, g, b, a) = *v; RgbRatio::new(r as f64, g as f64, b as f64, a as f64) }); from_for_rgb_ratio!([$t; 3], v, { let [r, g, b] = *v; RgbRatio::new(r as f64, g as f64, b as f64, 1.0) }); from_for_rgb_ratio!([$t; 4], v, { let [r, g, b, a] = *v; RgbRatio::new(r as f64, g as f64, b as f64, a as f64) }); }; } from_for_rgb_ratio_all!(f32); from_for_rgb_ratio_all!(f64); macro_rules! into_for_rgb_ratio_all { ($t: ty) => { into_for_some!(($t, $t, $t), RgbRatio, self, { let u = &self.get_units(); (u[0] as $t, u[1] as $t, u[2] as $t) }); into_for_some!(($t, $t, $t, $t), RgbRatio, self, { let u = &self.get_units(); (u[0] as $t, u[1] as $t, u[2] as $t, u.alpha.get_f64() as $t) }); into_for_some!([$t; 3], RgbRatio, self, { let u = &self.get_units(); [u[0] as $t, u[1] as $t, u[2] as $t] }); into_for_some!([$t; 4], RgbRatio, self, { let u = &self.get_units(); [u[0] as $t, u[1] as $t, u[2] as $t, u.alpha.get_f64() as $t] }); }; } into_for_rgb_ratio_all!(f32); into_for_rgb_ratio_all!(f64); colorsys-0.6.5/src/rgb/tests.rs000064400000000000000000000052150072674642500146150ustar 00000000000000use crate::{ColorTransform, ColorTuple, ColorTupleA, ParseError, Rgb}; use crate::common::f64_round; fn round(n: f64) -> u32 { f64_round(n) as u32 } fn round_tuple(t: &ColorTuple) -> (u32, u32, u32) { let (x, y, z) = *t; (round(x), round(y), round(z)) } #[test] fn lighten() { let mut rgb = Rgb::from((80.0, 186.0, 90.0)); let mut rgb2 = rgb.clone(); let mut rgb3 = rgb.clone(); let mut rgb4 = rgb.clone(); rgb.lighten(15.0); #[cfg(feature = "std")] assert_eq!(round_tuple(&rgb.into()), (135, 208, 142)); #[cfg(not(feature = "std"))] assert_eq!(round_tuple(&rgb.into()), (134, 207, 141)); rgb2.lighten(45.0); #[cfg(feature = "std")] assert_eq!(round_tuple(&rgb2.into()), (245, 251, 245)); #[cfg(not(feature = "std"))] assert_eq!(round_tuple(&rgb2.into()), (244, 250, 245)); rgb3.lighten(-23.0); #[cfg(feature = "std")] assert_eq!(round_tuple(&rgb3.into()), (42, 107, 48)); rgb4.lighten(-203.0); assert_eq!(round_tuple(&rgb4.into()), (0, 0, 0)); } #[test] fn from_str_tst() { fn parse_rgb(s: &str) -> Result { s.parse::() } assert_eq!( Into::::into(parse_rgb("Rgb(134,11,251)").unwrap()), Rgb::from((134.0, 11.0, 251.0)).into() ); assert_eq!( Into::::into( parse_rgb("rgba(134.9,11.1,250.55, 0.9)").unwrap() ), Rgb::from((134.9, 11.1, 250.55, 0.9)).into() ); assert_eq!( Into::::into(parse_rgb("rgb (0, 0,0)").unwrap()), Rgb::default().into() ); assert!(parse_rgb("12,1,97)").is_err()); assert!(parse_rgb("").is_err()); assert!(parse_rgb("ffcc0g").is_err()); } #[test] fn rgb_iter() { let rgb1 = Rgb::from_hex_str("37ea4c").unwrap(); let rgb2 = Rgb::from_hex_str("ffcc00").unwrap(); let _t: ColorTuple = rgb1.as_ref().into(); let _rgb3 = &rgb1 + &rgb2; // println!(">>> {:?}", rgb3); // println!(">>> {:?}", t); } #[test] #[rustfmt::skip] fn rgb_from() { let rgb1 = Rgb::from_hex_str("37ea4c").unwrap(); let rgb2 = Rgb::from( Into::<[f32; 4]>::into(Rgb::from( Into::<[u8; 3]>::into( Rgb::from( Into::<(i32, i32, i32)>::into( Rgb::from( Into::<[i64; 3]>::into(&rgb1) ) ) ) ) )) ); assert_eq!(rgb1, rgb2); } // #[test] // fn tst() { // use std::time::Duration; // let count = 10000; // let mut tmp = Vec::new(); // let start = std::time::Instant::now(); // for _ in 0..count { // let mut rgb = Rgb::from(2.0, 55.0, 5.0); // rgb.lighten(13.0); // rgb.set_green(13.0); // tmp.push(rgb); // } // println!("Elapsed {:?} for {} times", start.elapsed(), tmp.len()); // // } // } colorsys-0.6.5/src/rgb/transform.rs000064400000000000000000000055060072674642500154710ustar 00000000000000use consts::RGB_UNIT_MAX; use crate::{ColorTransform, consts, SaturationInSpace}; use super::{grayscale, Hsl, Rgb}; impl ColorTransform for Rgb { /// Lighten or darken color. amt is a percent with negative values - `-100..100` /// # Example /// ``` /// use colorsys::{Rgb,ColorTransform, ColorTuple}; /// let tuple = (30.0, 108.0, 77.0); /// let mut rgb = Rgb::from(&tuple); /// /// rgb.lighten(20.0); /// assert_eq!(rgb.to_css_string(), "rgb(52,188,134)" ); /// /// rgb.lighten(-20.0); /// assert_eq!(rgb.to_css_string(), "rgb(30,108,77)" ); /// /// rgb.lighten(-20.0); /// assert_eq!(rgb.to_css_string(), "rgb(8,28,20)" ); /// /// rgb.lighten(301.123); /// assert_eq!(rgb.to_css_string(), "rgb(255,255,255)" ); /// ``` fn lighten(&mut self, amt: f64) { let mut hsl: Hsl = self.into(); hsl.lighten(amt); let lightened_rgb: Rgb = hsl.as_ref().into(); self._apply_tuple(&lightened_rgb.into()); } fn saturate(&mut self, sat: SaturationInSpace) { match sat { SaturationInSpace::Hsl(amt) => { let mut hsl: Hsl = self.into(); hsl.set_saturation(hsl.saturation() + amt); let new_rgb = Rgb::from(hsl); self._apply_tuple(&new_rgb.into()); } SaturationInSpace::Hsv(amt) => { unimplemented!("{}", amt); } } } fn adjust_hue(&mut self, hue: f64) { let mut hsl: Hsl = self.into(); hsl.adjust_hue(hue); self._apply_tuple(&Rgb::from(hsl).into()); } fn grayscale_simple(&mut self) { grayscale::rgb_grayscale( self, grayscale::GrayScaleMethod::AverageProminent, ); } fn invert(&mut self) { self.units.list[0].value = RGB_UNIT_MAX - self.units[0]; self.units.list[1].value = RGB_UNIT_MAX - self.units[1]; self.units.list[2].value = RGB_UNIT_MAX - self.units[2]; } } #[cfg(test)] mod test { use crate::{Rgb, ColorTransform}; #[test] fn lighten_darken_test() { use crate::ColorTuple; use crate::common::f64_round; pub fn as_rounded_rgb_tuple(t: &ColorTuple) -> (u16, u16, u16) { let (r, g, b) = *t; (f64_round(r) as u16, f64_round(g) as u16, f64_round(b) as u16) } let asserts = [ #[cfg(feature = "std")] ((30.0, 108.0, 77.0), 20.0, (52, 188, 134)), ((30.0, 108.0, 77.0), 90.0, (255, 255, 255)), #[cfg(feature = "std")] ((30.0, 108.0, 77.0), -20.0, (8, 28, 20)), #[cfg(feature = "std")] ((0.0, 0.0, 0.0), 50.0, (128, 128, 128)), #[cfg(not(feature = "std"))] ((0.0, 0.0, 0.0), 50.0, (127, 127, 127)), ((0.0, 0.0, 0.0), -50.0, (0, 0, 0)), ((0.0, 0.0, 0.0), 300.5, (255, 255, 255)), ]; for a in asserts.iter() { let (origin, amt, result) = *a; let mut rgb = Rgb::from(&origin); rgb.lighten(amt); assert_eq!(as_rounded_rgb_tuple(&rgb.into()), result); } } } colorsys-0.6.5/src/units/alpha.rs000064400000000000000000000030120072674642500151210ustar 00000000000000 use super::unit::Unit; use crate::consts::{RATIO_MAX}; use crate::{ApproxEq, DEFAULT_APPROX_EQ_PRECISION}; use crate::common::approx::approx; #[derive(Clone, PartialEq, Debug, Default)] pub(crate) struct Alpha { value: Option, } impl Alpha { // pub(crate) fn from_opt(opt: Option) -> Self { // if let Some(a) = opt { // if !a.eq(&RATIO_MAX) { // return Alpha { value: Some(Unit::new_ratio(a)) } // } // } // Alpha::default() // } pub(crate) fn set(&mut self, a: f64) { if let Some(ref mut u) = &mut self.value { u.set(a); } else { self.value = Some(Unit::new_ratio(a)); } if self.value.unwrap().value.eq(&RATIO_MAX) { self.value = None; } } pub(crate) fn set_opt(&mut self, av: Option) { if let Some(a) = av { self.set(a); } else { self.value = None; } } pub(crate) fn get(&self) -> Option { self.value.map(|u| u.value) } pub(crate) fn get_f64(&self) -> f64 { self.get().unwrap_or(RATIO_MAX) } pub(crate) fn opacify(&mut self, v: f64) { self.set(self.get_f64() + v); } } impl ApproxEq for Alpha { fn approx_eq(&self, other: &Alpha) -> bool { self.approx_eq_clarify(other,DEFAULT_APPROX_EQ_PRECISION) } fn approx_eq_clarify(&self, other: &Alpha, precision: f64) -> bool { if let Some(su) = &self.value { if let Some(ou) = &other.value { return approx(su.value, ou.value, precision) } } else { return other.value.is_none() } false } } colorsys-0.6.5/src/units/into.rs000064400000000000000000000015560072674642500150200ustar 00000000000000use crate::units::{Units}; impl<'a> Into<[f64;3]> for &'a Units { fn into(self) -> [f64; 3] { [self[0],self[1],self[2]] } } impl Into<[f64;3]> for Units { fn into(self) -> [f64; 3] { [self[0],self[1],self[2]] } } impl<'a> Into<[f64;4]> for &'a Units { fn into(self) -> [f64; 4] { [self[0],self[1],self[2],self[3]] } } impl Into<[f64;4]> for Units { fn into(self) -> [f64; 4] { [self[0],self[1],self[2],self[3]] } } // impl<'a> Into<[f32;3]> for &'a Units { // fn into(self) -> [f32; 3] { // [self[0] as f32,self[1] as f32,self[2] as f32] // } // } // // impl<'a> Into<(f64,f64,f64)> for &'a Units { // fn into(self) -> (f64,f64,f64) { // (self[0],self[1],self[2]) // } // } // impl<'a> Into<(f32,f32,f32)> for &'a Units { // fn into(self) -> (f32,f32,f32) { // (self[0] as f32,self[1] as f32,self[2] as f32) // } // } colorsys-0.6.5/src/units/iter.rs000064400000000000000000000023100072674642500147770ustar 00000000000000use super::Units; #[doc(hidden)] pub struct ColorUnitsIter { ind: usize, values: [f64;5], len: usize, } impl ColorUnitsIter { pub(crate) fn from_units(units: &Units) -> ColorUnitsIter { let mut values = [0.0;5]; for i in 0..units.len { values[i] = units.list[i].value; } let mut len = units.len; if let Some(a) = units.alpha.get() { values[len] = a; len += 1; } ColorUnitsIter { ind: 0, values, len } } } impl core::iter::Iterator for ColorUnitsIter { type Item = f64; fn next(&mut self) -> Option { if self.ind == self.len { return None } let v = self.values[self.ind]; self.ind += 1; Some(v) } } #[cfg(test)] mod test { use crate::Rgb; #[test] fn color_iter_collect_test() { let rgb = Rgb::new(1.0,2.0,3.0, None); let v: Vec = rgb.iter().collect(); assert_eq!(v, vec![1.0,2.0,3.0]); let rgba = Rgb::new(1.0,2.0,3.0, Some(0.5)); let v: Vec = rgba.iter().collect(); assert_eq!(v, vec![1.0,2.0,3.0, 0.5]); } #[test] fn color_iter_test() { let rgb = Rgb::new(1.0,2.0,3.0, None); let mut n = 1.0; for c in &rgb { assert!(n.eq(&c)); n += 1.0; } } } colorsys-0.6.5/src/units/mod.rs000064400000000000000000000053620072674642500146250ustar 00000000000000use core::ops::{Add, Index, Sub}; pub(crate) use alpha::Alpha; pub(crate) use unit::Unit; use crate::{ApproxEq, DEFAULT_APPROX_EQ_PRECISION}; use crate::common::approx::approx; mod unit; mod alpha; mod into; pub mod iter; pub trait GetColorUnits { fn get_units(&self) -> &Units; fn get_units_mut(&mut self) -> &mut Units; } #[derive(Clone, PartialEq, Debug)] pub struct Units { pub(crate) len: usize, pub(crate) list: [Unit; 4], pub(crate) alpha: Alpha, } impl Units { pub(crate) fn restrict(&mut self) { for i in 0..self.len { self.list[i].restrict(); } } fn add_sub(&self, other: &Units, is_add: bool) -> Self { let mut new = self.clone(); for i in 0..self.len { let a = new.list[i]; let b = other.list[i]; let x = if is_add { a + b } else { a - b }; new.list[i] = x; } new } pub(crate) fn as_ratio(&self) -> Self { let mut new = self.clone(); for i in 0..new.len { new.list[i].turn_into_ratio(); } new } pub(crate) fn max(&self) -> (f64, usize) { self.min_max(true) } pub(crate) fn min(&self) -> (f64, usize) { self.min_max(false) } fn min_max(&self, need_max: bool) -> (f64, usize) { let mut result = self.list[0].value; let mut ind = 0; if self.len != 1 { for i in 1..self.len { let v = self.list[i].value; let is_fit = if need_max { v > result } else { v < result }; if is_fit { result = v; ind = i; } } } (result, ind) } pub(crate) fn new_ratios(values: &[f64]) -> Units { if values.len() > 4 { panic!("length of units values is more than 4") } let mut ul: [Unit; 4] = Default::default(); for (ind, v) in values.iter().enumerate() { ul[ind].set(*v); } Units { len: values.len(), list: ul, alpha: Alpha::default() } } } impl Index for Units { type Output = f64; fn index(&self, ind: usize) -> &Self::Output { &self.list[ind].value } } impl<'a> Add for &'a Units { type Output = Units; fn add(self, rhs: &'a Units) -> Self::Output { self.add_sub(rhs, true) } } impl<'a> Sub for &'a Units { type Output = Units; fn sub(self, rhs: &'a Units) -> Self::Output { self.add_sub(rhs, false) } } impl ApproxEq for Units { fn approx_eq(&self, other: &Units) -> bool { self.approx_eq_clarify(other, DEFAULT_APPROX_EQ_PRECISION) } fn approx_eq_clarify(&self, other: &Units, precision: f64) -> bool { if !self.alpha.approx_eq_clarify(&other.alpha, precision) { return false } for i in 0..self.len { if !approx(self.list[i].value, other.list[i].value, precision) { return false } } true } } #[cfg(test)] mod test { #[test] fn test() {} } colorsys-0.6.5/src/units/unit.rs000064400000000000000000000042200072674642500150150ustar 00000000000000use core::fmt; use core::ops::{Add, Sub}; use crate::consts::{ALL_MIN, HUE_MAX, PERCENT_MAX, RATIO_MAX, RGB_UNIT_MAX}; #[derive(Clone, Copy)] pub struct Unit { pub(crate) value: f64, highest: &'static f64 } impl Default for Unit { fn default() -> Self { Unit::new_ratio(0.0 ) } } impl fmt::Debug for Unit { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ColorUnit") .field("value", &self.value) .finish() } } impl PartialEq for Unit { fn eq(&self, other: &Self) -> bool { self.value.eq(&other.value) } } impl Unit { fn new(value: f64, highest: &'static f64) -> Self { Unit { value, highest } } fn new_checked(value: f64, highest: &'static f64) -> Self { let mut u = Unit::new(value, highest); u.restrict(); u } pub(crate) fn set(&mut self, v: f64) { self.value = self.get_restricted(v); } pub(crate) fn new_rgb(v: f64) -> Self { Unit::new(v, &RGB_UNIT_MAX) } pub(crate) fn new_hue(v: f64) -> Self { Unit::new(v, &HUE_MAX) } pub(crate) fn new_percent(v: f64) -> Self { Unit::new(v, &PERCENT_MAX) } pub(crate) fn new_ratio(v: f64) -> Self { Unit::new(v, &RATIO_MAX) } fn get_restricted(&self, val: f64) -> f64 { if val < ALL_MIN { return ALL_MIN; } else if &val > self.highest { return *self.highest; } val } pub(crate) fn restrict(&mut self) { self.value = self.get_restricted(self.value); } pub(crate) fn turn_into_ratio(&mut self) { self.value /= self.highest; self.highest = &RATIO_MAX; } pub(crate) fn turn_into_whole(&mut self, highest: &'static f64) { self.highest = highest; self.value *= self.highest; } pub(crate) fn increase(&mut self, v: f64) { self.set(self.value + v) } } impl Add for Unit { type Output = Unit; fn add(self, rhs: Self) -> Self::Output { Unit::new_checked(self.value + rhs.value, self.highest) } } impl Sub for Unit { type Output = Unit; fn sub(self, rhs: Self) -> Self::Output { Unit::new_checked(self.value - rhs.value, self.highest) } } #[cfg(test)] mod test { #[test] fn test() {} } colorsys-0.6.5/tests/tst.rs000064400000000000000000000022520072674642500140640ustar 00000000000000#[allow(unused_imports)] use colorsys::{prelude::*, Hsl, Rgb}; #[test] fn for_docs() { use colorsys::{Rgb, Hsl}; let rbga_tuple = (57.3, 12.7, 53.0, 0.33); let rgba = Rgb::from(&rbga_tuple); let hsla: Hsl = rgba.as_ref().into(); // ~Hsl { h: 305.78, s: 63.71, l: 13.73, a: 0.33 } let rgb_arr: [u8; 3] = Rgb::from(&hsla).into(); // ~[57, 13, 53] let hsla_tuple: (f64,f64,f64,f64) = Hsl::from( Rgb::from(rgb_arr) ).into(); // ~Hsl { h: 305.78, s: 63.71, l: 13.73, a: 1 } let hex: String = rgba.to_hex_string(); // #390d35 // From/Into let rgb1 = Rgb::from_hex_str("37ea4c").unwrap(); let rgb2 = Rgb::from( Into::<[f32; 4]>::into(Rgb::from( Into::<[u16; 3]>::into( Rgb::from( Into::<(i32,i32,i32)>::into( Rgb::from( Into::<[i64; 3]>::into(&rgb1) ) ) ) ) )) ); assert_eq!(rgb1, rgb2); // // Ratio // use colorsys::{RgbRatio, ApproxEq}; let blue = Rgb::from([34, 111, 235]); let ratio: [f32; 4] = blue.as_ratio().into(); // ~[0.133, 0.435, 0.922, 1.0] let converted: Rgb = RgbRatio::from(&ratio).into(); assert!(blue.approx_eq_clarify(&converted, 0.0001)); }