colorsys-0.5.7/.cargo_vcs_info.json0000644000000001121373416717100130240ustar00{ "git": { "sha1": "201eab30c8247c275080a8702c0b1da99b258fd7" } } colorsys-0.5.7/.gitignore010064400017500001750000000000541372154133700136160ustar0000000000000000/target **/*.rs.bk Cargo.lock .vscode .idea colorsys-0.5.7/Cargo.toml0000644000000016331373416717100110330ustar00# 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 believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "colorsys" version = "0.5.7" authors = ["mz "] description = "Module for convert and transform colors" homepage = "https://github.com/emgyrz/colorsys.rs" readme = "README.md" keywords = ["colors", "converter", "css", "rgb", "hsl"] categories = ["graphics"] license = "MIT" repository = "https://github.com/emgyrz/colorsys.rs.git" [dependencies] colorsys-0.5.7/Cargo.toml.orig010064400017500001750000000006171373416434000145210ustar0000000000000000[package] name = "colorsys" version = "0.5.7" description = "Module for convert and transform colors" authors = ["mz "] license = "MIT" homepage = "https://github.com/emgyrz/colorsys.rs" repository = "https://github.com/emgyrz/colorsys.rs.git" keywords = ["colors", "converter", "css", "rgb", "hsl"] categories = [ "graphics"] edition = "2018" readme = "README.md" [dependencies] colorsys-0.5.7/LICENSE010064400017500001750000000020721345543734200126410ustar0000000000000000The 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.5.7/README.md010064400017500001750000000066331373416434000131150ustar0000000000000000# 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 RGB(a)( as hexadecimal too), HSL(a) color models [Online documentation](https://docs.rs/colorsys/0.5.7/colorsys/) ## What It Can Do #### getters & setters ```Rust use colorsys::{Rgb, Hsl}; let rgb = Rgb::from((57.3, 12.7, 53.0)); let r = rgb.get_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, u8, u8] = 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; 4]>::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%) ``` ## 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. ##### Enjoy using! ### License This module is [MIT licensed](./LICENSE). colorsys-0.5.7/rustfmt.toml010064400017500001750000000001141372154247400142270ustar0000000000000000use_small_heuristics = "Max" edition= "2018" tab_spaces = 2 max_width = 80 colorsys-0.5.7/src/common/approx.rs010064400017500001750000000014621373416477500156030ustar0000000000000000use crate::ColorTuple; /// 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 { (x - y).abs() < precision } pub fn approx_def(x: f64, y: f64) -> bool { approx(x, y, DEFAULT_APPROX_EQ_PRECISION) } pub fn approx_tuple(x: &ColorTuple, y: &ColorTuple, precision: f64) -> bool { approx(x.0, y.0, precision) && approx(x.1, y.1, precision) && approx(x.2, y.2, precision) } pub fn approx_tuple_def(x: &ColorTuple, y: &ColorTuple) -> bool { approx_def(x.0, y.0) && approx_def(x.1, y.1) && approx_def(x.2, y.2) } colorsys-0.5.7/src/common/hsv_hsl_from_str.rs010064400017500001750000000033231373416503500176370ustar0000000000000000use 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.5.7/src/common/iter.rs010064400017500001750000000007771372154250000152230ustar0000000000000000use crate::ColorTuple; pub struct ColorIter { ind: usize, vals: [Option; 4], } impl ColorIter { pub fn from_tuple_w_alpha(t: ColorTuple, a: Option) -> ColorIter { ColorIter { ind: 0, vals: [Some(t.0), Some(t.1), Some(t.2), a] } } } impl std::iter::Iterator for ColorIter { type Item = f64; fn next(&mut self) -> Option { match self.ind { 0...3 => { let val = self.vals[self.ind]; self.ind += 1; val } _ => None, } } } colorsys-0.5.7/src/common/mod.rs010064400017500001750000000010661372154250000150270ustar0000000000000000// use std::time::{SystemTime, UNIX_EPOCH}; mod hsv_hsl_from_str; mod iter; mod tuple_to_string; pub mod approx; pub mod ops; pub use hsv_hsl_from_str::hsl_hsv_from_str; pub use iter::ColorIter; pub use tuple_to_string::tuple_to_string; pub enum Hs { Hsv, Hsl, } // pub fn simple_rand(max: f64) -> f64 { // let num = vec![1, 2, 3]; // let address = &num as *const Vec; // let num = f64::from((address as i32).abs()); // let nanos = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().subsec_nanos(); // (f64::from(nanos) * num) % max // } colorsys-0.5.7/src/common/ops.rs010064400017500001750000000016171372154250000150530ustar0000000000000000use crate::ColorTuple; type Op = Option; type TupleA = (ColorTuple, Op); pub fn add_sub_alpha(a1: &Op, a2: &Op, is_add: bool) -> Op { let has1 = a1.is_some(); let has2 = a2.is_some(); if !has1 && !has2 { return None; } else if has1 && has2 { let val1 = a1.unwrap(); let val2 = a2.unwrap(); let val = if is_add { val1 + val2 } else { val1 - val2 }; return Some(val); } if has1 { *a1 } else { *a2 } } pub fn add_sub_tuples( t1: &ColorTuple, t2: &ColorTuple, is_add: bool, ) -> ColorTuple { let (x1, y1, z1) = t1; let (x2, y2, z2) = t2; if is_add { (x1 + x2, y1 + y2, z1 + z2) } else { (x1 - x2, y1 - y2, z1 - z2) } } pub fn add_sub_tuples_a(ta1: &TupleA, ta2: &TupleA, is_add: bool) -> TupleA { let (t1, a1) = ta1; let (t2, a2) = ta2; let t = add_sub_tuples(t1, t2, is_add); let a = add_sub_alpha(a1, a2, is_add); (t, a) } colorsys-0.5.7/src/common/tuple_to_string.rs010064400017500001750000000013151372154250000174660ustar0000000000000000use crate::{normalize::round_ratio, ColorTupleA}; 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 (a - 1.0).abs() < std::f64::EPSILON { String::from("1") } else { round_ratio(*a).to_string() }; let is_hsl = prefix == "hsl"; let mut vals = [x, y, z] .iter() .enumerate() .map(|(ind, u)| { let mut s = u.round().to_string(); if is_hsl && (ind == 1 || ind == 2) { s.push('%'); } s }) .collect::>() .join(","); if a != "1" { start.push('a'); vals.push_str(&(",".to_owned() + &a)); } format!("{}({})", start, vals) } colorsys-0.5.7/src/consts.rs010064400017500001750000000002541351666576200143110ustar0000000000000000pub 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.5.7/src/converters/hex_to_rgb.rs010064400017500001750000000014221372154250000172660ustar0000000000000000use crate::{err, ColorTuple}; use err::{make_parse_err, ParseError}; pub fn hex_to_rgb(s: &str) -> Result { let mut hex = s.replace("#", "").to_lowercase(); let hex_chars = hex.chars().collect::>(); let count = hex_chars.len(); if count == 3 { hex = hex_chars .iter() .map(|c| c.to_string().repeat(2)) .collect::>() .join(""); } else if count != 6 { return Err(make_parse_err(s, "hex")); } match usize::from_str_radix(&hex, 16) { Ok(num) => Ok(hex_num_to_rgb(num)), Err(_) => Err(make_parse_err(s, "hex")), } } fn hex_num_to_rgb(num: usize) -> ColorTuple { let r = (num >> 16) as f64; let g = ((num >> 8) & 0x00FF) as f64; let b = (num & 0x0000_00FF) as f64; (r, g, b) } colorsys-0.5.7/src/converters/hsl_to_rgb.rs010064400017500001750000000031071372154250000172720ustar0000000000000000use crate::consts::RGB_UNIT_MAX; use crate::normalize::bound_ratio; use crate::ratio_converters::hsl_to_ratio; use crate::ColorTuple; 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 fn hsl_to_rgb(hsl: &ColorTuple) -> ColorTuple { let 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 }; let (h, s, l) = hsl_to_ratio(hsl); if s == 0.0 { let unit = RGB_UNIT_MAX * l; return (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); (r, g, b) } #[test] fn hsl_to_rgb_tst() { use crate::common::approx::approx_tuple; fn a(x: ColorTuple, y: ColorTuple) -> bool { approx_tuple(&hsl_to_rgb(&x), &y, 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.5.7/src/converters/mod.rs010064400017500001750000000003011351666576200157420ustar0000000000000000mod hex_to_rgb; mod hsl_to_rgb; mod rgb_to_hex; mod rgb_to_hsl; pub use hex_to_rgb::hex_to_rgb; pub use hsl_to_rgb::hsl_to_rgb; pub use rgb_to_hex::rgb_to_hex; pub use rgb_to_hsl::rgb_to_hsl; colorsys-0.5.7/src/converters/rgb_to_hex.rs010064400017500001750000000004451372154250000172720ustar0000000000000000use crate::ColorTuple; fn to_hex(n: f64) -> String { let s = format!("{:x}", n.round() as u32); if s.len() == 1 { "0".to_string() + &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.5.7/src/converters/rgb_to_hsl.rs010064400017500001750000000036321373416521500173050ustar0000000000000000use crate::consts::{ALL_MIN, PERCENT_MAX}; use crate::ratio_converters::rgb_to_ratio; use crate::ColorTuple; enum RgbUnit { Red, Green, Blue, } fn get_max(red: f64, green: f64, blue: f64) -> (f64, RgbUnit) { if (red > green) && (red > blue) { return (red, RgbUnit::Red); } if green > blue { (green, RgbUnit::Green) } else { (blue, RgbUnit::Blue) } } fn get_min(red: f64, green: f64, blue: f64) -> f64 { if (red < green) && (red < blue) { red } else if green < blue { green } else { blue } } pub fn rgb_to_hsl(rgb: &ColorTuple) -> ColorTuple { let (red, green, blue) = rgb_to_ratio(&rgb); let (max, max_unit) = get_max(red, green, blue); let min = get_min(red, green, blue); let max_plus_min = max + min; let luminance = (max_plus_min) / 2.0; if max.eq(&min) { return (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 hue = match max_unit { RgbUnit::Red => { let x = if green < blue { 6.0 } else { ALL_MIN }; (green - blue) / max_min_delta + x } RgbUnit::Green => (blue - red) / max_min_delta + 2.0, RgbUnit::Blue => (red - green) / max_min_delta + 4.0, }; (hue * 60.0, saturation * PERCENT_MAX, luminance * PERCENT_MAX) } #[test] fn rgb_to_hsl_tst() { use crate::common::approx::approx_tuple; fn a(x: ColorTuple, y: ColorTuple) -> bool { approx_tuple(&rgb_to_hsl(&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.5.7/src/err.rs010064400017500001750000000011741372154250000135500ustar0000000000000000use std::fmt; #[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) } } impl std::error::Error for ParseError {} colorsys-0.5.7/src/hsl/from.rs010064400017500001750000000067631373416255700145400ustar0000000000000000use super::{Hsl, HslRatio, Rgb}; use crate::{ converters::*, ratio_converters::ratio_to_hsla, ColorAlpha, ColorTuple, }; 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, $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; 3], v, { let [h, s, l] = *v; Hsl::new(h as f64, s as f64, l as f64, None) }); 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!(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.get_alpha(); let tuple: ColorTuple = rgb.into(); let mut hsl = Hsl::from(rgb_to_hsl(&tuple)); 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 t = ratio_to_hsla(&(ratio.h, ratio.s, ratio.l, ratio.a)); Hsl { h: t.0, s: t.1, l: t.2, a: 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 Hsl { h, s, l, .. } = *self; (h as $t, s as $t, l as $t) }); into_for_some!(($t, $t, $t, $t), Hsl, self, { let Hsl { h, s, l, .. } = *self; (h as $t, s as $t, l as $t, self.get_alpha() as $t) }); into_for_some!([$t; 3], Hsl, self, { let Hsl { h, s, l, .. } = *self; [h as $t, s as $t, l as $t] }); into_for_some!([$t; 4], Hsl, self, { let Hsl { h, s, l, .. } = *self; [h as $t, s as $t, l as $t, self.get_alpha() as $t] }); }; } into_for_hsl_all!(f32); into_for_hsl_all!(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.5.7/src/hsl/mod.rs010064400017500001750000000056151373415371500143430ustar0000000000000000pub use ratio::HslRatio; use crate::common::{ approx::approx_def, hsl_hsv_from_str, tuple_to_string, ColorIter, Hs, }; use crate::consts::RATIO_MAX; use crate::normalize::{normalize_hue, normalize_percent, normalize_ratio}; use crate::ratio_converters::hsla_to_ratio; use crate::{ColorAlpha, ColorTuple, ColorTupleA, ParseError, Rgb}; #[cfg(test)] mod tests; mod from; mod opts; mod ratio; mod transform; /// The HSL or HSI (hue, saturation, lightness (intensity)) color model /// /// Ranges: /// * h (hue): 0.0 - 360.0 /// * s (saturation): 0.0 - 100.0 /// * l (saturation): 0.0 - 100.0 /// * a (alpha): 0.0 - 1.0 #[derive(Debug, PartialEq, Clone)] pub struct Hsl { h: f64, s: f64, l: f64, a: Option, } impl Hsl { fn _apply_tuple(&mut self, t: &ColorTuple) { self.h = t.0; self.s = t.1; self.l = t.2; } pub fn new(h: f64, s: f64, l: f64, a: Option) -> Hsl { let a = a.map(normalize_ratio).filter(|al| !approx_def(*al, RATIO_MAX)); let np = normalize_percent; Hsl { h: normalize_hue(h), s: np(s), l: np(l), a } } pub fn to_css_string(&self) -> String { let t: ColorTupleA = self.into(); tuple_to_string(&t, "hsl") } pub fn get_hue(&self) -> f64 { self.h } pub fn get_saturation(&self) -> f64 { self.s } pub fn get_lightness(&self) -> f64 { self.l } pub fn set_hue(&mut self, val: f64) { self.h = normalize_hue(val); } pub fn set_saturation(&mut self, val: f64) { self.s = normalize_percent(val); } pub fn set_lightness(&mut self, val: f64) { self.l = normalize_percent(val); } pub fn iter(&self) -> ColorIter { ColorIter::from_tuple_w_alpha(self.into(), self.a) } pub fn as_ratio(&self) -> HslRatio { let t = hsla_to_ratio(&self.into()); HslRatio { h: t.0, s: t.1, l: t.2, a: t.3 } } } // // // // Default // impl Default for Hsl { fn default() -> Hsl { Hsl { h: 0.0, s: 0.0, l: 0.0, a: None } } } // // // // AsRef // impl AsRef for Hsl { fn as_ref(&self) -> &Hsl { &self } } // // // // FromStr // impl std::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) } } // // // // ColorAlpha // impl ColorAlpha for Hsl { fn get_alpha(&self) -> f64 { self.a.unwrap_or(1.0) } fn set_alpha(&mut self, val: f64) { self.a = Some(normalize_ratio(val)); } fn opacify(&mut self, val: f64) { self.set_alpha(self.get_alpha() + val); } } // // // // Iter // impl<'a> std::iter::IntoIterator for &'a Hsl { type Item = f64; type IntoIter = ColorIter; fn into_iter(self) -> ColorIter { self.iter() } } impl std::iter::IntoIterator for Hsl { type Item = f64; type IntoIter = ColorIter; fn into_iter(self) -> ColorIter { self.iter() } } colorsys-0.5.7/src/hsl/opts.rs010064400017500001750000000047211372154250000145340ustar0000000000000000use super::Hsl; // use crate::common::simple_rand; #[allow(unused_imports)] use crate::{ common, normalize::normalize_opt_ratio, ColorAlpha, ColorTuple, ColorTupleA, Rgb, }; use common::{approx::*, ops}; use std::ops::{Add, AddAssign, Sub, SubAssign}; fn add_sub(hsl1: &Hsl, hsl2: &Hsl, is_add: bool) -> Hsl { type TA = (ColorTuple, Option); let ta1: TA = (hsl1.into(), hsl1.a); let ta2: TA = (hsl2.into(), hsl2.a); let (t, a) = ops::add_sub_tuples_a(&ta1, &ta2, is_add); let mut hsl = Hsl::from(t); hsl.a = normalize_opt_ratio(a); hsl } fn add(hsl1: &Hsl, hsl2: &Hsl) -> Hsl { add_sub(hsl1, hsl2, true) } fn sub(hsl1: &Hsl, hsl2: &Hsl) -> Hsl { add_sub(hsl1, hsl2, false) } impl<'a> Add for &'a Hsl { type Output = Hsl; fn add(self, rhs: &Hsl) -> Hsl { add(self, rhs) } } impl<'a> Add for &'a mut Hsl { type Output = Hsl; fn add(self, rhs: &'a mut Hsl) -> Hsl { add(self, rhs) } } impl Add for Hsl { type Output = Hsl; fn add(self, rhs: Self) -> Self { add(&self, &rhs) } } impl AddAssign for Hsl { fn add_assign(&mut self, rhs: Self) { *self = add(self, &rhs); } } impl Sub for Hsl { type Output = Hsl; fn sub(self, rhs: Self) -> Self { sub(&self, &rhs) } } impl<'a> Sub for &'a Hsl { type Output = Hsl; fn sub(self, rhs: Self) -> Hsl { sub(self, rhs) } } impl<'a> Sub for &'a mut Hsl { type Output = Hsl; fn sub(self, rhs: Self) -> Hsl { sub(self, rhs) } } impl SubAssign for Hsl { fn sub_assign(&mut self, rhs: Self) { *self = sub(self, &rhs); } } impl ApproxEq for Hsl { fn approx_eq(&self, other: &Hsl) -> bool { let t1: ColorTuple = self.into(); let t2: ColorTuple = other.into(); approx_tuple_def(&t1, &t2) && approx_def(self.get_alpha(), other.get_alpha()) } fn approx_eq_clarify(&self, other: &Hsl, precision: f64) -> bool { let t1: ColorTuple = self.into(); let t2: ColorTuple = other.into(); approx_tuple(&t1, &t2, precision) && approx(self.get_alpha(), other.get_alpha(), precision) } } 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 t1: ColorTuple = self.into(); let t2: ColorTuple = Hsl::from(rgb).into(); approx_tuple(&t1, &t2, precision) && approx(self.get_alpha(), rgb.get_alpha(), precision) } } #[test] fn hsl_add() {} #[test] fn hsl_eq() {} colorsys-0.5.7/src/hsl/ratio.rs010064400017500001750000000057551373416304500147040ustar0000000000000000use crate::normalize::normalize_ratio; /// /// 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) h: f64, pub(super) s: f64, pub(super) l: f64, pub(super) a: f64, } impl HslRatio { pub fn new(h: f64, s: f64, l: f64, a: f64) -> Self { HslRatio { h: normalize_ratio(h), s: normalize_ratio(s), l: normalize_ratio(l), a: normalize_ratio(a), } } pub fn h(&self) -> f64 { self.h } pub fn s(&self) -> f64 { self.s } pub fn l(&self) -> f64 { self.l } pub fn a(&self) -> f64 { self.a } } impl AsRef for HslRatio { fn as_ref(&self) -> &HslRatio { &self } } 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 HslRatio { h, s, l, .. } = *self; (h as $t, s as $t, l as $t) }); into_for_some!(($t, $t, $t, $t), HslRatio, self, { let HslRatio { h, s, l, a } = *self; (h as $t, s as $t, l as $t, a as $t) }); into_for_some!([$t; 3], HslRatio, self, { let HslRatio { h, s, l, .. } = *self; [h as $t, s as $t, l as $t] }); into_for_some!([$t; 4], HslRatio, self, { let HslRatio { h, s, l, a } = *self; [h as $t, s as $t, l as $t, a as $t] }); }; } into_for_hsl_ratio_all!(f32); into_for_hsl_ratio_all!(f64); colorsys-0.5.7/src/hsl/tests.rs010064400017500001750000000014211373416262700147170ustar0000000000000000use crate::{ApproxEq, ColorTuple, Hsl, Rgb}; fn round(n: f64) -> u32 { n.round() 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); assert_eq!(round_tuple(&rgb.as_ref().into()), (80, 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.5.7/src/hsl/transform.rs010064400017500001750000000013361372154250000155610ustar0000000000000000use crate::consts::{ALL_MIN, HUE_MAX}; use crate::normalize::bound_hue; use crate::{ColorTransform, SaturationInSpace}; use super::Hsl; impl ColorTransform for Hsl { fn lighten(&mut self, amt: f64) { self.set_lightness(self.l + amt) } fn saturate(&mut self, sat: SaturationInSpace) { match sat { SaturationInSpace::Hsl(s) => self.set_saturation(self.s + s), SaturationInSpace::Hsv(s) => { println!("{}", s); unimplemented!(); } } } fn adjust_hue(&mut self, hue: f64) { self.h = bound_hue(self.h + hue); } fn grayscale_simple(&mut self) { self.h = ALL_MIN; self.s = ALL_MIN; } fn invert(&mut self) { self.h = (self.h + HUE_MAX * 0.5) % HUE_MAX } } colorsys-0.5.7/src/lib.rs010064400017500001750000000047701373416623500135460ustar0000000000000000//! A module for color conversion, mutation, modification. //! For now works with RGB(a)( as hexadecimal too), HSL(a) color models #![allow(clippy::many_single_char_names)] mod macros; mod common; mod consts; mod converters; mod err; mod hsl; mod normalize; mod rgb; pub mod prelude; pub mod ratio_converters; pub use common::approx::{ApproxEq, DEFAULT_APPROX_EQ_PRECISION}; pub use err::ParseError; pub use hsl::{Hsl, HslRatio}; pub use rgb::{GrayScaleMethod, Rgb, RgbRatio}; /// Use to transfer nad 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), } /// Methods to work with alpha channel in color. pub trait ColorAlpha { /// Returns alpha channel. If it not set will returns 1.0 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); } /// 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.5.7/src/macros.rs010064400017500001750000000006511373322161500142470ustar0000000000000000#![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() } } }; } colorsys-0.5.7/src/normalize.rs010064400017500001750000000022601372154250000147550ustar0000000000000000use crate::common::approx::approx_def; use super::consts::{ALL_MIN, HUE_MAX, PERCENT_MAX, RATIO_MAX, RGB_UNIT_MAX}; 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_hue(h: f64) -> f64 { let h = normalize(h, HUE_MAX); if (h - HUE_MAX).abs() < std::f64::EPSILON { 0.0 } else { h } } 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 normalize_opt_ratio(val: Option) -> Option { val.map(normalize_ratio).filter(|al| !approx_def(*al, 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 { (r * 100.0).round() / 100.0 } colorsys-0.5.7/src/prelude.rs010064400017500001750000000001661372154250000144200ustar0000000000000000pub use super::{ ApproxEq, ColorAlpha, ColorTransform, ColorTuple, ColorTupleA, ParseError, SaturationInSpace, }; colorsys-0.5.7/src/ratio_converters.rs010064400017500001750000000031071372154250000163460ustar0000000000000000use 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.5.7/src/rgb/from.rs010064400017500001750000000071621373415650300145100ustar0000000000000000use crate::converters::*; use crate::rgb::RgbRatio; use crate::{ ratio_converters::ratio_to_rgba, ColorAlpha, ColorTuple, Hsl, Rgb, }; 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, $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; 3], v, { let [r, g, b] = *v; Rgb::new(r as f64, g as f64, b as f64, None) }); 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!(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.get_alpha(); let tuple: ColorTuple = hsl.into(); let mut rgb = Rgb::from(hsl_to_rgb(&tuple)); 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 t = ratio_to_rgba(&(ratio.r, ratio.g, ratio.b, ratio.a)); Rgb { r: t.0, g: t.1, b: t.2, a: 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 Rgb { r, g, b, .. } = *self; (r as $t, g as $t, b as $t) }); into_for_some!(($t, $t, $t, $t), Rgb, self, { let Rgb { r, g, b, .. } = *self; (r as $t, g as $t, b as $t, self.get_alpha() as $t) }); into_for_some!([$t; 3], Rgb, self, { let Rgb { r, g, b, .. } = *self; [r as $t, g as $t, b as $t] }); into_for_some!([$t; 4], Rgb, self, { let Rgb { r, g, b, .. } = *self; [r as $t, g as $t, b as $t, self.get_alpha() as $t] }); }; } into_for_rgb_all!(f32); into_for_rgb_all!(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.5.7/src/rgb/from_str.rs010064400017500001750000000023151373416501600153720ustar0000000000000000use crate::err::{make_parse_err, ParseError}; use crate::{consts, ColorTuple}; use consts::{ALL_MIN, RATIO_MAX, RGB_UNIT_MAX}; 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.5.7/src/rgb/grayscale.rs010064400017500001750000000035371372154250000155110ustar0000000000000000use 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) { rgb.r *= factors.0; rgb.g *= factors.1; rgb.b *= 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 y = (rgb.r + rgb.g + rgb.b) / 3.0; rgb.r = y; rgb.g = y; rgb.b = y; } fn rgb_to_grayscale_avg_prom(rgb: &mut Rgb) { let rgb_vec = vec![rgb.r, rgb.g, rgb.b]; let max = rgb_vec.iter().fold(std::f64::MIN, |a, &b| a.max(b)); let min = rgb_vec.iter().fold(std::f64::MAX, |a, &b| a.min(b)); let y = (max + min) / 2.0; rgb.r = y; rgb.g = y; rgb.b = 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.5.7/src/rgb/mod.rs010064400017500001750000000107741373322013500143170ustar0000000000000000#[cfg(test)] mod tests; use crate::common::{approx::approx_def, tuple_to_string, ColorIter}; use crate::consts::RATIO_MAX; use crate::err::ParseError; use crate::normalize::{normalize_ratio, normalize_rgb_unit}; use crate::{converters, ColorAlpha, ColorTuple, ColorTupleA, Hsl}; mod from; mod from_str; mod grayscale; mod ops; mod ratio; mod transform; use crate::ratio_converters::rgba_to_ratio; use grayscale::rgb_grayscale; pub use grayscale::GrayScaleMethod; pub use ratio::RgbRatio; /// The RGB color model. /// /// Has r (red), g (green), b(blue) and optional a(alpha channel) fields. /// Red, green, blue values are stored between 0.0 and 255.0, alpha is between 0.0 and 1.0. /// If inputed or recieved values are exceeds the allowed value, or is less than zero /// it will be equalize to limit. /// /// Can be converted into (and from) other color models. /// /// /// # 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.get_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.get_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 { r: f64, g: f64, b: f64, a: Option, } impl Rgb { fn _apply_tuple(&mut self, t: &ColorTuple) { self.r = t.0; self.g = t.1; self.b = t.2; } pub fn new(r: f64, g: f64, b: f64, a: Option) -> Rgb { let n = normalize_rgb_unit; let a = a.map(normalize_ratio).filter(|al| !approx_def(*al, RATIO_MAX)); Rgb { r: n(r), g: n(g), b: n(b), a } } 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 get_red(&self) -> f64 { self.r } pub fn get_green(&self) -> f64 { self.g } pub fn get_blue(&self) -> f64 { self.b } pub fn set_red(&mut self, val: f64) { self.r = normalize_rgb_unit(val); } pub fn set_green(&mut self, val: f64) { self.g = normalize_rgb_unit(val); } pub fn set_blue(&mut self, val: f64) { self.b = normalize_rgb_unit(val); } 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); } pub fn iter(&self) -> ColorIter { ColorIter::from_tuple_w_alpha(self.into(), self.a) } pub fn as_ratio(&self) -> RgbRatio { let t = rgba_to_ratio(&self.into()); RgbRatio { r: t.0, g: t.1, b: t.2, a: t.3 } } } // // // // Default // impl Default for Rgb { fn default() -> Rgb { Rgb { r: 0.0, g: 0.0, b: 0.0, a: None } } } // // // // AsRef // impl AsRef for Rgb { fn as_ref(&self) -> &Rgb { &self } } // // // // FromStr // impl std::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) } } // // // // ColorAlpha // impl ColorAlpha for Rgb { fn get_alpha(&self) -> f64 { self.a.unwrap_or(1.0) } fn set_alpha(&mut self, val: f64) { self.a = Some(normalize_ratio(val)); } fn opacify(&mut self, val: f64) { self.set_alpha(self.get_alpha() + val); } } // // // // Iter // impl<'a> std::iter::IntoIterator for &'a Rgb { type Item = f64; type IntoIter = ColorIter; fn into_iter(self) -> ColorIter { self.iter() } } impl std::iter::IntoIterator for Rgb { type Item = f64; type IntoIter = ColorIter; fn into_iter(self) -> ColorIter { self.iter() } } colorsys-0.5.7/src/rgb/ops.rs010064400017500001750000000065421372154250000143370ustar0000000000000000use super::Rgb; // use crate::common::simple_rand; #[allow(unused_imports)] use crate::{ common, normalize::normalize_opt_ratio, ColorAlpha, ColorTuple, ColorTupleA, Hsl, }; use common::{approx::*, ops}; use std::ops::{Add, AddAssign, Sub, SubAssign}; fn add_sub(rgb1: &Rgb, rgb2: &Rgb, is_add: bool) -> Rgb { type TA = (ColorTuple, Option); let ta1: TA = (rgb1.into(), rgb1.a); let ta2: TA = (rgb2.into(), rgb2.a); let (t, a) = ops::add_sub_tuples_a(&ta1, &ta2, is_add); let mut rgb = Rgb::from(t); rgb.a = normalize_opt_ratio(a); rgb } fn add(rgb1: &Rgb, rgb2: &Rgb) -> Rgb { add_sub(rgb1, rgb2, true) } fn sub(rgb1: &Rgb, rgb2: &Rgb) -> Rgb { add_sub(rgb1, rgb2, false) } impl<'a> Add for &'a Rgb { type Output = Rgb; fn add(self, rhs: &Rgb) -> Rgb { add(self, rhs) } } impl<'a> Add for &'a mut Rgb { type Output = Rgb; fn add(self, rhs: &'a mut Rgb) -> Rgb { add(self, rhs) } } impl Add for Rgb { type Output = Rgb; fn add(self, rhs: Self) -> Self { add(&self, &rhs) } } impl AddAssign for Rgb { fn add_assign(&mut self, rhs: Self) { *self = add(self, &rhs); } } impl Sub for Rgb { type Output = Rgb; fn sub(self, rhs: Self) -> Self { sub(&self, &rhs) } } impl<'a> Sub for &'a Rgb { type Output = Rgb; fn sub(self, rhs: Self) -> Rgb { sub(self, rhs) } } impl<'a> Sub for &'a mut Rgb { type Output = Rgb; fn sub(self, rhs: Self) -> Rgb { sub(self, rhs) } } impl SubAssign for Rgb { fn sub_assign(&mut self, rhs: Self) { *self = sub(self, &rhs); } } impl ApproxEq for Rgb { fn approx_eq(&self, other: &Rgb) -> bool { let t1: ColorTuple = self.into(); let t2: ColorTuple = other.into(); approx_tuple_def(&t1, &t2) && approx_def(self.get_alpha(), other.get_alpha()) } fn approx_eq_clarify(&self, other: &Rgb, precision: f64) -> bool { let t1: ColorTuple = self.into(); let t2: ColorTuple = other.into(); approx_tuple(&t1, &t2, precision) && approx(self.get_alpha(), other.get_alpha(), precision) } } 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 t1: ColorTuple = self.into(); let t2: ColorTuple = Rgb::from(hsl).into(); approx_tuple(&t1, &t2, precision) && approx(self.get_alpha(), hsl.get_alpha(), precision) } } #[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)); // println!("time {:?}", simple_rand(255.0)); // println!("time {:?}", simple_rand(255.0)); // println!("time {:?}", simple_rand(255.0)); } colorsys-0.5.7/src/rgb/ratio.rs010064400017500001750000000060131373416327100146550ustar0000000000000000use crate::normalize::normalize_ratio; /// /// 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(Clone)] pub struct RgbRatio { pub(super) r: f64, pub(super) g: f64, pub(super) b: f64, pub(super) a: f64, } impl RgbRatio { pub fn new(r: f64, g: f64, b: f64, a: f64) -> Self { RgbRatio { r: normalize_ratio(r), g: normalize_ratio(g), b: normalize_ratio(b), a: normalize_ratio(a), } } pub fn r(&self) -> f64 { self.r } pub fn g(&self) -> f64 { self.g } pub fn b(&self) -> f64 { self.b } pub fn a(&self) -> f64 { self.a } } impl AsRef for RgbRatio { fn as_ref(&self) -> &RgbRatio { &self } } 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 RgbRatio { r, g, b, .. } = *self; (r as $t, g as $t, b as $t) }); into_for_some!(($t, $t, $t, $t), RgbRatio, self, { let RgbRatio { r, g, b, a } = *self; (r as $t, g as $t, b as $t, a as $t) }); into_for_some!([$t; 3], RgbRatio, self, { let RgbRatio { r, g, b, .. } = *self; [r as $t, g as $t, b as $t] }); into_for_some!([$t; 4], RgbRatio, self, { let RgbRatio { r, g, b, a } = *self; [r as $t, g as $t, b as $t, a as $t] }); }; } into_for_rgb_ratio_all!(f32); into_for_rgb_ratio_all!(f64); colorsys-0.5.7/src/rgb/tests.rs010064400017500001750000000045741373416542200147130ustar0000000000000000use crate::{ ApproxEq, ColorTransform, ColorTuple, ColorTupleA, ParseError, Rgb, RgbRatio, }; fn round(n: f64) -> u32 { n.round() 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); assert_eq!(round_tuple(&rgb.into()), (135, 208, 142)); rgb2.lighten(45.0); assert_eq!(round_tuple(&rgb2.into()), (245, 251, 245)); rgb3.lighten(-23.0); 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; 4]>::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.5.7/src/rgb/transform.rs010064400017500001750000000047051372154250000155500ustar0000000000000000use super::{grayscale, Hsl, Rgb}; use crate::{consts, ColorTransform, SaturationInSpace}; use consts::RGB_UNIT_MAX; 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.get_saturation() + amt); let new_rgb = Rgb::from(hsl); self._apply_tuple(&new_rgb.into()); } SaturationInSpace::Hsv(amt) => { println!("{}", amt); unimplemented!(); } } } 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.r = RGB_UNIT_MAX - self.r; self.g = RGB_UNIT_MAX - self.g; self.b = RGB_UNIT_MAX - self.b; } } #[test] fn lighten_darken_test() { use crate::ColorTuple; pub fn as_rounded_rgb_tuple(t: &ColorTuple) -> (u16, u16, u16) { let (r, g, b) = *t; (r.round() as u16, g.round() as u16, b.round() as u16) } let asserts = [ ((30.0, 108.0, 77.0), 20.0, (52, 188, 134)), ((30.0, 108.0, 77.0), 90.0, (255, 255, 255)), ((30.0, 108.0, 77.0), -20.0, (8, 28, 20)), ((0.0, 0.0, 0.0), 50.0, (128, 128, 128)), ((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.5.7/tests/integration_test.rs010064400017500001750000000021321372154250000167100ustar0000000000000000#[allow(unused_imports)] use colorsys::{prelude::*, Hsl, Rgb}; #[test] fn for_docs() { // let mut rgb1 = Rgb::from((100.0, 255.0, 17.0)); // Rgb { r: 100.0, g: 255.0, b: 17.0, a: None } // println!(">>> {:?}", rgb1); // let green = rgb1.get_green(); // 255.0 // println!(">>> {:?}", green); // rgb1.set_red(108.3); // Rgb { r: 108.0, g: 255.0, b: 17.0, a: None } // println!(">>> {:?}", rgb1); // let mut hsl: Hsl = rgb1.into(); // println!(">>> {:?}", hsl); // hsl.saturate( SaturationInSpace::Hsl(-57.901) ); // println!(">>> {:?}", hsl); // let mut rgb2 = Rgb::from(&hsl); // println!(">>> {:?}", rgb2); // let rgb2tuple: (f64,f64,f64) = rgb2.as_ref().into(); // println!("<><><>{:?}", rgb2tuple); // rgb2 += Rgb::from_hex_str("#35f15b").unwrap(); // println!(">>> {:?}", rgb2); // rgb2.set_green(-150.0); // rgb2.lighten(-13.123); // println!(">>> {:?}", rgb2); // rgb2.grayscale_simple(); // println!(">>> GR {:?}", rgb2); // let css_string = rgb2.to_css_string(); // println!(">>> CSS {:?}", css_string); // assert_eq!(rgb2.to_css_string(), ""); }