diff --git a/README.md b/README.md index 4c69a9447..df994f644 100644 --- a/README.md +++ b/README.md @@ -110,13 +110,13 @@ extern crate palette; use palette::{Rgb, Hsv, Gradient}; let grad1 = Gradient::new(vec![ - Rgb::rgb(1.0, 0.1, 0.1), - Rgb::rgb(0.1, 1.0, 1.0) + Rgb::new(1.0, 0.1, 0.1), + Rgb::new(0.1, 1.0, 1.0) ]); let grad2 = Gradient::new(vec![ - Hsv::from(Rgb::rgb(1.0, 0.1, 0.1)), - Hsv::from(Rgb::rgb(0.1, 1.0, 1.0)) + Hsv::from(Rgb::new(1.0, 0.1, 0.1)), + Hsv::from(Rgb::new(0.1, 1.0, 1.0)) ]); ``` diff --git a/examples/gradient.rs b/examples/gradient.rs index d54f44161..fc3776bc6 100644 --- a/examples/gradient.rs +++ b/examples/gradient.rs @@ -9,30 +9,30 @@ use image::{RgbImage, GenericImage}; fn main() { //A gradient of evenly spaced colors let grad1 = Gradient::new(vec![ - Rgb::rgb(1.0, 0.1, 0.1), - Rgb::rgb(0.1, 0.1, 1.0), - Rgb::rgb(0.1, 1.0, 0.1) + Rgb::new(1.0, 0.1, 0.1), + Rgb::new(0.1, 0.1, 1.0), + Rgb::new(0.1, 1.0, 0.1) ]); //The same colors as in grad1, but with the blue point shifted down let grad2 = Gradient::with_domain(vec![ - (0.0, Rgb::rgb(1.0, 0.1, 0.1)), - (0.25, Rgb::rgb(0.1, 0.1, 1.0)), - (1.0, Rgb::rgb(0.1, 1.0, 0.1)) + (0.0, Rgb::new(1.0, 0.1, 0.1)), + (0.25, Rgb::new(0.1, 0.1, 1.0)), + (1.0, Rgb::new(0.1, 1.0, 0.1)) ]); //The same colors and offsets as in grad1, but in a color space where the hue is a component let grad3 = Gradient::new(vec![ - Lch::from(Rgb::rgb(1.0, 0.1, 0.1)), - Lch::from(Rgb::rgb(0.1, 0.1, 1.0)), - Lch::from(Rgb::rgb(0.1, 1.0, 0.1)) + Lch::from(Rgb::new(1.0, 0.1, 0.1)), + Lch::from(Rgb::new(0.1, 0.1, 1.0)), + Lch::from(Rgb::new(0.1, 1.0, 0.1)) ]); //The same colors and and color space as in grad3, but with the blue point shifted down let grad4 = Gradient::with_domain(vec![ - (0.0, Lch::from(Rgb::rgb(1.0, 0.1, 0.1))), - (0.25, Lch::from(Rgb::rgb(0.1, 0.1, 1.0))), - (1.0, Lch::from(Rgb::rgb(0.1, 1.0, 0.1))) + (0.0, Lch::from(Rgb::new(1.0, 0.1, 0.1))), + (0.25, Lch::from(Rgb::new(0.1, 0.1, 1.0))), + (1.0, Lch::from(Rgb::new(0.1, 1.0, 0.1))) ]); let mut image = RgbImage::new(256, 128); diff --git a/examples/readme_examples.rs b/examples/readme_examples.rs index d5a0a82c4..7d4208e35 100644 --- a/examples/readme_examples.rs +++ b/examples/readme_examples.rs @@ -5,7 +5,7 @@ extern crate num; use image::{RgbImage, GenericImage}; use num::traits::Float; -use palette::{Rgb, Gradient, Mix}; +use palette::{Rgba, Gradient, Mix}; use palette::pixel::Srgb; mod color_spaces { @@ -48,13 +48,13 @@ mod gradients { pub fn run() { let grad1 = Gradient::new(vec![ - Rgb::rgb(1.0, 0.1, 0.1), - Rgb::rgb(0.1, 1.0, 1.0) + Rgb::new(1.0, 0.1, 0.1), + Rgb::new(0.1, 1.0, 1.0) ]); let grad2 = Gradient::new(vec![ - Hsv::from(Rgb::rgb(1.0, 0.1, 0.1)), - Hsv::from(Rgb::rgb(0.1, 1.0, 1.0)) + Hsv::from(Rgb::new(1.0, 0.1, 0.1)), + Hsv::from(Rgb::new(0.1, 1.0, 1.0)) ]); display_gradients("examples/readme_gradients.png", grad1, grad2); @@ -76,8 +76,8 @@ fn display_colors(filename: &str, colors: &[[u8; 3]]) { } fn display_gradients + Clone, B: Mix + Clone>(filename: &str, grad1: Gradient, grad2: Gradient) where - Rgb: From, - Rgb: From, + Rgba: From, + Rgba: From, { let mut image = RgbImage::new(256, 64); diff --git a/examples/shade.rs b/examples/shade.rs index fa71e24de..b81672b2d 100644 --- a/examples/shade.rs +++ b/examples/shade.rs @@ -8,7 +8,7 @@ use image::{RgbImage, GenericImage}; fn main() { //The same color in linear RGB, CIE L*a*b*, and HSV - let rgb = Rgb::rgb(0.5, 0.0, 0.0); + let rgb = Rgb::new(0.5, 0.0, 0.0); let lab = Lab::from(rgb); let hsv = Hsv::from(rgb); diff --git a/src/alpha.rs b/src/alpha.rs new file mode 100644 index 000000000..640af9d8a --- /dev/null +++ b/src/alpha.rs @@ -0,0 +1,193 @@ +use std::ops::{Deref, DerefMut, Add, Sub, Mul, Div}; + +use num::Float; + +use {Mix, Shade, GetHue, Hue, Saturate}; + +///An alpha component wrapper for colors. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Alpha { + ///The color. + pub color: C, + + ///The transparency component. 0.0 is fully transparent and 1.0 is fully + ///opaque. + pub alpha: T, +} + +impl Deref for Alpha { + type Target = C; + + fn deref(&self) -> &C { + &self.color + } +} + +impl DerefMut for Alpha { + fn deref_mut(&mut self) -> &mut C { + &mut self.color + } +} + +impl Mix for Alpha { + type Scalar = C::Scalar; + + fn mix(&self, other: &Alpha, factor: C::Scalar) -> Alpha { + Alpha { + color: self.color.mix(&other.color, factor), + alpha: self.alpha + factor * (other.alpha - self.alpha), + } + } +} + +impl Shade for Alpha { + type Scalar = C::Scalar; + + fn lighten(&self, amount: C::Scalar) -> Alpha { + Alpha { + color: self.color.lighten(amount), + alpha: self.alpha, + } + } +} + +impl GetHue for Alpha { + type Hue = C::Hue; + + fn get_hue(&self) -> Option { + self.color.get_hue() + } +} + +impl Hue for Alpha { + fn with_hue(&self, hue: C::Hue) -> Alpha { + Alpha { + color: self.color.with_hue(hue), + alpha: self.alpha, + } + } + + fn shift_hue(&self, amount: C::Hue) -> Alpha { + Alpha { + color: self.color.shift_hue(amount), + alpha: self.alpha, + } + } +} + +impl Saturate for Alpha { + type Scalar = C::Scalar; + + fn saturate(&self, factor: C::Scalar) -> Alpha { + Alpha { + color: self.color.saturate(factor), + alpha: self.alpha, + } + } +} + +impl Default for Alpha { + fn default() -> Alpha { + Alpha { + color: C::default(), + alpha: T::one(), + } + } +} + +impl Add for Alpha { + type Output = Alpha; + + fn add(self, other: Alpha) -> Alpha { + Alpha { + color: self.color + other.color, + alpha: self.alpha + other.alpha, + } + } +} + +impl> Add for Alpha { + type Output = Alpha; + + fn add(self, c: T) -> Alpha { + Alpha { + color: self.color + c.clone(), + alpha: self.alpha + c, + } + } +} + +impl Sub for Alpha { + type Output = Alpha; + + fn sub(self, other: Alpha) -> Alpha { + Alpha { + color: self.color - other.color, + alpha: self.alpha - other.alpha, + } + } +} + +impl> Sub for Alpha { + type Output = Alpha; + + fn sub(self, c: T) -> Alpha { + Alpha { + color: self.color - c.clone(), + alpha: self.alpha - c, + } + } +} + +impl Mul for Alpha { + type Output = Alpha; + + fn mul(self, other: Alpha) -> Alpha { + Alpha { + color: self.color * other.color, + alpha: self.alpha * other.alpha, + } + } +} + +impl> Mul for Alpha { + type Output = Alpha; + + fn mul(self, c: T) -> Alpha { + Alpha { + color: self.color * c.clone(), + alpha: self.alpha * c, + } + } +} + +impl Div for Alpha { + type Output = Alpha; + + fn div(self, other: Alpha) -> Alpha { + Alpha { + color: self.color / other.color, + alpha: self.alpha / other.alpha, + } + } +} + +impl> Div for Alpha { + type Output = Alpha; + + fn div(self, c: T) -> Alpha { + Alpha { + color: self.color / c.clone(), + alpha: self.alpha / c, + } + } +} + +impl From for Alpha { + fn from(color: C) -> Alpha { + Alpha { + color: color, + alpha: T::one(), + } + } +} diff --git a/src/gradient.rs b/src/gradient.rs index 33a3757c4..1ad837ce7 100644 --- a/src/gradient.rs +++ b/src/gradient.rs @@ -314,7 +314,7 @@ mod test { #[test] fn simple_slice() { - let g1 = Gradient::new(vec![Rgb::rgb(1.0, 0.0, 0.0), Rgb::rgb(0.0, 0.0, 1.0)]); + let g1 = Gradient::new(vec![Rgb::new(1.0, 0.0, 0.0), Rgb::new(0.0, 0.0, 1.0)]); let g2 = g1.slice(..0.5); let v1: Vec<_> = g1.take(10).take(5).collect(); diff --git a/src/hsl.rs b/src/hsl.rs index ae38a3b99..2179bdce2 100644 --- a/src/hsl.rs +++ b/src/hsl.rs @@ -2,9 +2,12 @@ use num::traits::Float; use std::ops::{Add, Sub}; -use {Color, Rgb, Luma, Xyz, Lab, Lch, Hsv, ColorSpace, Mix, Shade, GetHue, Hue, Saturate, RgbHue, clamp}; +use {Color, Alpha, Rgb, Luma, Xyz, Lab, Lch, Hsv, ColorSpace, Mix, Shade, GetHue, Hue, Saturate, RgbHue, clamp}; -///Linear HSL color space with an alpha component. +///Linear HSL with an alpha component. See the [`Hsla` implementation in `Alpha`](struct.Alpha.html#Hsla). +pub type Hsla = Alpha, T>; + +///Linear HSL color space. /// ///The HSL color space can be seen as a cylindrical version of ///[RGB](struct.Rgb.html), where the `hue` is the angle around the color @@ -27,29 +30,25 @@ pub struct Hsl { ///Decides how light the color will look. 0.0 will be black, 0.5 will give ///a clear color, and 1.0 will give white. pub lightness: T, - - ///The transparency of the color. 0.0 is completely transparent and 1.0 is - ///completely opaque. - pub alpha: T, } impl Hsl { ///Linear HSL. - pub fn hsl(hue: RgbHue, saturation: T, lightness: T) -> Hsl { + pub fn new(hue: RgbHue, saturation: T, lightness: T) -> Hsl { Hsl { hue: hue, saturation: saturation, lightness: lightness, - alpha: T::one(), } } +} +///[`Hsla`](type.Hsla.html) implementations. +impl Alpha, T> { ///Linear HSL and transparency. - pub fn hsla(hue: RgbHue, saturation: T, lightness: T, alpha: T) -> Hsl { - Hsl { - hue: hue, - saturation: saturation, - lightness: lightness, + pub fn new(hue: RgbHue, saturation: T, lightness: T, alpha: T) -> Hsla { + Alpha { + color: Hsl::new(hue, saturation, lightness), alpha: alpha, } } @@ -58,8 +57,7 @@ impl Hsl { impl ColorSpace for Hsl { fn is_valid(&self) -> bool { self.saturation >= T::zero() && self.saturation <= T::one() && - self.lightness >= T::zero() && self.lightness <= T::one() && - self.alpha >= T::zero() && self.alpha <= T::one() + self.lightness >= T::zero() && self.lightness <= T::one() } fn clamp(&self) -> Hsl { @@ -71,7 +69,6 @@ impl ColorSpace for Hsl { fn clamp_self(&mut self) { self.saturation = clamp(self.saturation, T::zero(), T::one()); self.lightness = clamp(self.lightness, T::zero(), T::one()); - self.alpha = clamp(self.alpha, T::zero(), T::one()); } } @@ -86,7 +83,6 @@ impl Mix for Hsl { hue: self.hue + factor * hue_diff, saturation: self.saturation + factor * (other.saturation - self.saturation), lightness: self.lightness + factor * (other.lightness - self.lightness), - alpha: self.alpha + factor * (other.alpha - self.alpha), } } } @@ -99,7 +95,6 @@ impl Shade for Hsl { hue: self.hue, saturation: self.saturation, lightness: self.lightness + amount, - alpha: self.alpha, } } } @@ -122,7 +117,6 @@ impl Hue for Hsl { hue: hue, saturation: self.saturation, lightness: self.lightness, - alpha: self.alpha, } } @@ -131,7 +125,6 @@ impl Hue for Hsl { hue: self.hue + amount, saturation: self.saturation, lightness: self.lightness, - alpha: self.alpha, } } } @@ -144,14 +137,13 @@ impl Saturate for Hsl { hue: self.hue, saturation: self.saturation * (T::one() + factor), lightness: self.lightness, - alpha: self.alpha, } } } impl Default for Hsl { fn default() -> Hsl { - Hsl::hsl(RgbHue::from(T::zero()), T::zero(), T::zero()) + Hsl::new(RgbHue::from(T::zero()), T::zero(), T::zero()) } } @@ -163,7 +155,6 @@ impl Add> for Hsl { hue: self.hue + other.hue, saturation: self.saturation + other.saturation, lightness: self.lightness + other.lightness, - alpha: self.alpha + other.alpha, } } } @@ -176,7 +167,6 @@ impl Add for Hsl { hue: self.hue + c, saturation: self.saturation + c, lightness: self.lightness + c, - alpha: self.alpha + c, } } } @@ -189,7 +179,6 @@ impl Sub> for Hsl { hue: self.hue - other.hue, saturation: self.saturation - other.saturation, lightness: self.lightness - other.lightness, - alpha: self.alpha - other.alpha, } } } @@ -202,13 +191,14 @@ impl Sub for Hsl { hue: self.hue - c, saturation: self.saturation - c, lightness: self.lightness - c, - alpha: self.alpha - c, } } } from_color!(to Hsl from Rgb, Luma, Xyz, Lab, Lch, Hsv); +alpha_from!(Hsl {Rgb, Xyz, Luma, Lab, Lch, Hsv, Color}); + impl From> for Hsl { fn from(rgb: Rgb) -> Hsl { enum Channel { Red, Green, Blue }; @@ -250,7 +240,6 @@ impl From> for Hsl { hue: hue.into(), saturation: saturation, lightness: lightness, - alpha: rgb.alpha, } } } @@ -294,7 +283,6 @@ impl From> for Hsl { hue: hsv.hue, saturation: saturation, lightness: x / T::from(2.0).unwrap(), - alpha: hsv.alpha, } } } @@ -306,9 +294,9 @@ mod test { #[test] fn red() { - let a = Hsl::from(Rgb::rgb(1.0, 0.0, 0.0)); - let b = Hsl::hsl(0.0.into(), 1.0, 0.5); - let c = Hsl::from(Hsv::hsv(0.0.into(), 1.0, 1.0)); + let a = Hsl::from(Rgb::new(1.0, 0.0, 0.0)); + let b = Hsl::new(0.0.into(), 1.0, 0.5); + let c = Hsl::from(Hsv::new(0.0.into(), 1.0, 1.0)); assert_approx_eq!(a, b, [hue, saturation, lightness]); assert_approx_eq!(a, c, [hue, saturation, lightness]); @@ -316,9 +304,9 @@ mod test { #[test] fn orange() { - let a = Hsl::from(Rgb::rgb(1.0, 0.5, 0.0)); - let b = Hsl::hsl(30.0.into(), 1.0, 0.5); - let c = Hsl::from(Hsv::hsv(30.0.into(), 1.0, 1.0)); + let a = Hsl::from(Rgb::new(1.0, 0.5, 0.0)); + let b = Hsl::new(30.0.into(), 1.0, 0.5); + let c = Hsl::from(Hsv::new(30.0.into(), 1.0, 1.0)); assert_approx_eq!(a, b, [hue, saturation, lightness]); assert_approx_eq!(a, c, [hue, saturation, lightness]); @@ -326,9 +314,9 @@ mod test { #[test] fn green() { - let a = Hsl::from(Rgb::rgb(0.0, 1.0, 0.0)); - let b = Hsl::hsl(120.0.into(), 1.0, 0.5); - let c = Hsl::from(Hsv::hsv(120.0.into(), 1.0, 1.0)); + let a = Hsl::from(Rgb::new(0.0, 1.0, 0.0)); + let b = Hsl::new(120.0.into(), 1.0, 0.5); + let c = Hsl::from(Hsv::new(120.0.into(), 1.0, 1.0)); assert_approx_eq!(a, b, [hue, saturation, lightness]); assert_approx_eq!(a, c, [hue, saturation, lightness]); @@ -336,9 +324,9 @@ mod test { #[test] fn blue() { - let a = Hsl::from(Rgb::rgb(0.0, 0.0, 1.0)); - let b = Hsl::hsl(240.0.into(), 1.0, 0.5); - let c = Hsl::from(Hsv::hsv(240.0.into(), 1.0, 1.0)); + let a = Hsl::from(Rgb::new(0.0, 0.0, 1.0)); + let b = Hsl::new(240.0.into(), 1.0, 0.5); + let c = Hsl::from(Hsv::new(240.0.into(), 1.0, 1.0)); assert_approx_eq!(a, b, [hue, saturation, lightness]); assert_approx_eq!(a, c, [hue, saturation, lightness]); @@ -346,9 +334,9 @@ mod test { #[test] fn purple() { - let a = Hsl::from(Rgb::rgb(0.5, 0.0, 1.0)); - let b = Hsl::hsl(270.0.into(), 1.0, 0.5); - let c = Hsl::from(Hsv::hsv(270.0.into(), 1.0, 1.0)); + let a = Hsl::from(Rgb::new(0.5, 0.0, 1.0)); + let b = Hsl::new(270.0.into(), 1.0, 0.5); + let c = Hsl::from(Hsv::new(270.0.into(), 1.0, 1.0)); assert_approx_eq!(a, b, [hue, saturation, lightness]); assert_approx_eq!(a, c, [hue, saturation, lightness]); diff --git a/src/hsv.rs b/src/hsv.rs index e42f6baef..788a55bc3 100644 --- a/src/hsv.rs +++ b/src/hsv.rs @@ -2,9 +2,12 @@ use num::traits::Float; use std::ops::{Add, Sub}; -use {Color, Rgb, Luma, Xyz, Lab, Lch, Hsl, ColorSpace, Mix, Shade, GetHue, Hue, Saturate, RgbHue, clamp}; +use {Color, Alpha, Rgb, Luma, Xyz, Lab, Lch, Hsl, ColorSpace, Mix, Shade, GetHue, Hue, Saturate, RgbHue, clamp}; -///Linear HSV color space with an alpha component. +///Linear HSV with an alpha component. See the [`Hsva` implementation in `Alpha`](struct.Alpha.html#Hsva). +pub type Hsva = Alpha, T>; + +///Linear HSV color space. /// ///HSV is a cylindrical version of [RGB](struct.Rgb.html) and it's very ///similar to [HSL](struct.Hsl.html). The difference is that the `value` @@ -26,29 +29,25 @@ pub struct Hsv { ///give a bright an clear color that goes towards white when `saturation` ///goes towards 0.0. pub value: T, - - ///The transparency of the color. 0.0 is completely transparent and 1.0 is - ///completely opaque. - pub alpha: T, } impl Hsv { ///Linear HSV. - pub fn hsv(hue: RgbHue, saturation: T, value: T) -> Hsv { + pub fn new(hue: RgbHue, saturation: T, value: T) -> Hsv { Hsv { hue: hue, saturation: saturation, value: value, - alpha: T::one(), } } +} +///[`Hsva`](type.Hsva.html) implementations. +impl Alpha, T> { ///Linear HSV and transparency. - pub fn hsva(hue: RgbHue, saturation: T, value: T, alpha: T) -> Hsv { - Hsv { - hue: hue, - saturation: saturation, - value: value, + pub fn new(hue: RgbHue, saturation: T, value: T, alpha: T) -> Hsva { + Alpha { + color: Hsv::new(hue, saturation, value), alpha: alpha, } } @@ -57,8 +56,7 @@ impl Hsv { impl ColorSpace for Hsv { fn is_valid(&self) -> bool { self.saturation >= T::zero() && self.saturation <= T::one() && - self.value >= T::zero() && self.value <= T::one() && self.alpha >= T::zero() && - self.alpha <= T::one() + self.value >= T::zero() && self.value <= T::one() } fn clamp(&self) -> Hsv { @@ -70,7 +68,6 @@ impl ColorSpace for Hsv { fn clamp_self(&mut self) { self.saturation = clamp(self.saturation, T::zero(), T::one()); self.value = clamp(self.value, T::zero(), T::one()); - self.alpha = clamp(self.alpha, T::zero(), T::one()); } } @@ -85,7 +82,6 @@ impl Mix for Hsv { hue: self.hue + factor * hue_diff, saturation: self.saturation + factor * (other.saturation - self.saturation), value: self.value + factor * (other.value - self.value), - alpha: self.alpha + factor * (other.alpha - self.alpha), } } } @@ -98,7 +94,6 @@ impl Shade for Hsv { hue: self.hue, saturation: self.saturation, value: self.value + amount, - alpha: self.alpha, } } } @@ -121,7 +116,6 @@ impl Hue for Hsv { hue: hue, saturation: self.saturation, value: self.value, - alpha: self.alpha, } } @@ -130,7 +124,6 @@ impl Hue for Hsv { hue: self.hue + amount, saturation: self.saturation, value: self.value, - alpha: self.alpha, } } } @@ -143,14 +136,13 @@ impl Saturate for Hsv { hue: self.hue, saturation: self.saturation * (T::one() + factor), value: self.value, - alpha: self.alpha, } } } impl Default for Hsv { fn default() -> Hsv { - Hsv::hsv(RgbHue::from(T::zero()), T::zero(), T::zero()) + Hsv::new(RgbHue::from(T::zero()), T::zero(), T::zero()) } } @@ -162,7 +154,6 @@ impl Add> for Hsv { hue: self.hue + other.hue, saturation: self.saturation + other.saturation, value: self.value + other.value, - alpha: self.alpha + other.alpha, } } } @@ -175,7 +166,6 @@ impl Add for Hsv { hue: self.hue + c, saturation: self.saturation + c, value: self.value + c, - alpha: self.alpha + c, } } } @@ -188,7 +178,6 @@ impl Sub> for Hsv { hue: self.hue - other.hue, saturation: self.saturation - other.saturation, value: self.value - other.value, - alpha: self.alpha - other.alpha, } } } @@ -201,13 +190,15 @@ impl Sub for Hsv { hue: self.hue - c, saturation: self.saturation - c, value: self.value - c, - alpha: self.alpha - c, } } } from_color!(to Hsv from Rgb, Luma, Xyz, Lab, Lch, Hsl); +alpha_from!(Hsv {Rgb, Xyz, Luma, Lab, Lch, Hsl, Color}); + + impl From> for Hsv { fn from(rgb: Rgb) -> Hsv { enum Channel { Red, Green, Blue }; @@ -248,7 +239,6 @@ impl From> for Hsv { hue: hue.into(), saturation: saturation, value: val_max, - alpha: rgb.alpha, } } } @@ -289,7 +279,6 @@ impl From> for Hsv { hue: hsl.hue, saturation: T::from(2.0).unwrap() * x / (hsl.lightness + x), value: hsl.lightness + x, - alpha: hsl.alpha, } } } @@ -301,9 +290,9 @@ mod test { #[test] fn red() { - let a = Hsv::from(Rgb::rgb(1.0, 0.0, 0.0)); - let b = Hsv::hsv(0.0.into(), 1.0, 1.0); - let c = Hsv::from(Hsl::hsl(0.0.into(), 1.0, 0.5)); + let a = Hsv::from(Rgb::new(1.0, 0.0, 0.0)); + let b = Hsv::new(0.0.into(), 1.0, 1.0); + let c = Hsv::from(Hsl::new(0.0.into(), 1.0, 0.5)); assert_approx_eq!(a, b, [hue, saturation, value]); assert_approx_eq!(a, c, [hue, saturation, value]); @@ -311,9 +300,9 @@ mod test { #[test] fn orange() { - let a = Hsv::from(Rgb::rgb(1.0, 0.5, 0.0)); - let b = Hsv::hsv(30.0.into(), 1.0, 1.0); - let c = Hsv::from(Hsl::hsl(30.0.into(), 1.0, 0.5)); + let a = Hsv::from(Rgb::new(1.0, 0.5, 0.0)); + let b = Hsv::new(30.0.into(), 1.0, 1.0); + let c = Hsv::from(Hsl::new(30.0.into(), 1.0, 0.5)); assert_approx_eq!(a, b, [hue, saturation, value]); assert_approx_eq!(a, c, [hue, saturation, value]); @@ -321,9 +310,9 @@ mod test { #[test] fn green() { - let a = Hsv::from(Rgb::rgb(0.0, 1.0, 0.0)); - let b = Hsv::hsv(120.0.into(), 1.0, 1.0); - let c = Hsv::from(Hsl::hsl(120.0.into(), 1.0, 0.5)); + let a = Hsv::from(Rgb::new(0.0, 1.0, 0.0)); + let b = Hsv::new(120.0.into(), 1.0, 1.0); + let c = Hsv::from(Hsl::new(120.0.into(), 1.0, 0.5)); assert_approx_eq!(a, b, [hue, saturation, value]); assert_approx_eq!(a, c, [hue, saturation, value]); @@ -331,9 +320,9 @@ mod test { #[test] fn blue() { - let a = Hsv::from(Rgb::rgb(0.0, 0.0, 1.0)); - let b = Hsv::hsv(240.0.into(), 1.0, 1.0); - let c = Hsv::from(Hsl::hsl(240.0.into(), 1.0, 0.5)); + let a = Hsv::from(Rgb::new(0.0, 0.0, 1.0)); + let b = Hsv::new(240.0.into(), 1.0, 1.0); + let c = Hsv::from(Hsl::new(240.0.into(), 1.0, 0.5)); assert_approx_eq!(a, b, [hue, saturation, value]); assert_approx_eq!(a, c, [hue, saturation, value]); @@ -341,9 +330,9 @@ mod test { #[test] fn purple() { - let a = Hsv::from(Rgb::rgb(0.5, 0.0, 1.0)); - let b = Hsv::hsv(270.0.into(), 1.0, 1.0); - let c = Hsv::from(Hsl::hsl(270.0.into(), 1.0, 0.5)); + let a = Hsv::from(Rgb::new(0.5, 0.0, 1.0)); + let b = Hsv::new(270.0.into(), 1.0, 1.0); + let c = Hsv::from(Hsl::new(270.0.into(), 1.0, 0.5)); assert_approx_eq!(a, b, [hue, saturation, value]); assert_approx_eq!(a, c, [hue, saturation, value]); diff --git a/src/lab.rs b/src/lab.rs index 5566f76c1..fc63f8323 100644 --- a/src/lab.rs +++ b/src/lab.rs @@ -2,11 +2,14 @@ use num::traits::Float; use std::ops::{Add, Sub, Mul, Div}; -use {Color, Rgb, Luma, Xyz, Lch, Hsv, Hsl, ColorSpace, Mix, Shade, GetHue, LabHue, clamp}; +use {Color, Alpha, Rgb, Luma, Xyz, Lch, Hsv, Hsl, ColorSpace, Mix, Shade, GetHue, LabHue, clamp}; use tristimulus::{X_N, Y_N, Z_N}; -///The CIE L*a*b* (CIELAB) color space with an alpha component. +///CIE L*a*b* (CIELAB) with an alpha component. See the [`Laba` implementation in `Alpha`](struct.Alpha.html#Laba). +pub type Laba = Alpha, T>; + +///The CIE L*a*b* (CIELAB) color space. /// ///CIE L*a*b* is a device independent color space which includes all ///perceivable colors. It's sometimes used to convert between other color @@ -28,29 +31,25 @@ pub struct Lab { ///b* goes from yellow at -1.0 to blue at 1.0. pub b: T, - - ///The transparency of the color. 0.0 is completely transparent and 1.0 is - ///completely opaque. - pub alpha: T, } impl Lab { ///CIE L*a*b*. - pub fn lab(l: T, a: T, b: T) -> Lab { + pub fn new(l: T, a: T, b: T) -> Lab { Lab { l: l, a: a, b: b, - alpha: T::one(), } } +} +///[`Laba`](type.Laba.html) implementations. +impl Alpha, T> { ///CIE L*a*b* and transparency. - pub fn laba(l: T, a: T, b: T, alpha: T) -> Lab { - Lab { - l: l, - a: a, - b: b, + pub fn new(l: T, a: T, b: T, alpha: T) -> Laba { + Alpha { + color: Lab::new(l, a, b), alpha: alpha, } } @@ -60,8 +59,7 @@ impl ColorSpace for Lab { fn is_valid(&self) -> bool { self.l >= T::zero() && self.l <= T::one() && self.a >= -T::one() && self.a <= T::one() && - self.b >= -T::one() && self.b <= T::one() && - self.alpha >= T::zero() && self.alpha <= T::one() + self.b >= -T::one() && self.b <= T::one() } fn clamp(&self) -> Lab { @@ -74,7 +72,6 @@ impl ColorSpace for Lab { self.l = clamp(self.l, T::zero(), T::one()); self.a = clamp(self.a, -T::one(), T::one()); self.b = clamp(self.b, -T::one(), T::one()); - self.alpha = clamp(self.alpha, T::zero(), T::one()); } } @@ -88,7 +85,6 @@ impl Mix for Lab { l: self.l + factor * (other.l - self.l), a: self.a + factor * (other.a - self.a), b: self.b + factor * (other.b - self.b), - alpha: self.alpha + factor * (other.alpha - self.alpha), } } } @@ -101,7 +97,6 @@ impl Shade for Lab { l: self.l + amount, a: self.a, b: self.b, - alpha: self.alpha, } } } @@ -120,7 +115,7 @@ impl GetHue for Lab { impl Default for Lab { fn default() -> Lab { - Lab::lab(T::zero(), T::zero(), T::zero()) + Lab::new(T::zero(), T::zero(), T::zero()) } } @@ -132,7 +127,6 @@ impl Add> for Lab { l: self.l + other.l, a: self.a + other.a, b: self.b + other.b, - alpha: self.alpha + other.alpha, } } } @@ -145,7 +139,6 @@ impl Add for Lab { l: self.l + c, a: self.a + c, b: self.b + c, - alpha: self.alpha + c, } } } @@ -158,7 +151,6 @@ impl Sub> for Lab { l: self.l - other.l, a: self.a - other.a, b: self.b - other.b, - alpha: self.alpha - other.alpha, } } } @@ -171,7 +163,6 @@ impl Sub for Lab { l: self.l - c, a: self.a - c, b: self.b - c, - alpha: self.alpha - c, } } } @@ -184,7 +175,6 @@ impl Mul> for Lab { l: self.l * other.l, a: self.a * other.a, b: self.b * other.b, - alpha: self.alpha * other.alpha, } } } @@ -197,7 +187,6 @@ impl Mul for Lab { l: self.l * c, a: self.a * c, b: self.b * c, - alpha: self.alpha * c, } } } @@ -210,7 +199,6 @@ impl Div> for Lab { l: self.l / other.l, a: self.a / other.a, b: self.b / other.b, - alpha: self.alpha / other.alpha, } } } @@ -223,13 +211,14 @@ impl Div for Lab { l: self.l / c, a: self.a / c, b: self.b / c, - alpha: self.alpha / c, } } } from_color!(to Lab from Rgb, Luma, Xyz, Lch, Hsv, Hsl); +alpha_from!(Lab {Rgb, Xyz, Luma, Lch, Hsv, Hsl, Color}); + impl From> for Lab { fn from(xyz: Xyz) -> Lab { Lab { @@ -241,7 +230,6 @@ impl From> for Lab { b: (T::from(200.0).unwrap() * (f(xyz.y / T::from(Y_N).unwrap()) - f(xyz.z / T::from(Z_N).unwrap()))) / T::from(128.0).unwrap(), - alpha: xyz.alpha, } } } @@ -264,7 +252,6 @@ impl From> for Lab { l: lch.l, a: lch.chroma.max(T::zero()) * lch.hue.to_radians().cos(), b: lch.chroma.max(T::zero()) * lch.hue.to_radians().sin(), - alpha: lch.alpha, } } } @@ -302,22 +289,22 @@ mod test { #[test] fn red() { - let a = Lab::from(Rgb::rgb(1.0, 0.0, 0.0)); - let b = Lab::lab(53.23288 / 100.0, 80.10933 / 128.0, 67.22006 / 128.0); + let a = Lab::from(Rgb::new(1.0, 0.0, 0.0)); + let b = Lab::new(53.23288 / 100.0, 80.10933 / 128.0, 67.22006 / 128.0); assert_approx_eq!(a, b, [l, a, b]); } #[test] fn green() { - let a = Lab::from(Rgb::rgb(0.0, 1.0, 0.0)); - let b = Lab::lab(87.73704 / 100.0, -86.184654 / 128.0, 83.18117 / 128.0); + let a = Lab::from(Rgb::new(0.0, 1.0, 0.0)); + let b = Lab::new(87.73704 / 100.0, -86.184654 / 128.0, 83.18117 / 128.0); assert_approx_eq!(a, b, [l, a, b]); } #[test] fn blue() { - let a = Lab::from(Rgb::rgb(0.0, 0.0, 1.0)); - let b = Lab::lab(32.302586 / 100.0, 79.19668 / 128.0, -107.863686 / 128.0); + let a = Lab::from(Rgb::new(0.0, 0.0, 1.0)); + let b = Lab::new(32.302586 / 100.0, 79.19668 / 128.0, -107.863686 / 128.0); assert_approx_eq!(a, b, [l, a, b]); } } diff --git a/src/lch.rs b/src/lch.rs index 959a39625..d7204304d 100644 --- a/src/lch.rs +++ b/src/lch.rs @@ -2,10 +2,12 @@ use num::traits::Float; use std::ops::{Add, Sub}; -use {Color, ColorSpace, Mix, Shade, GetHue, Hue, Rgb, Luma, Xyz, Lab, Hsv, Hsl, Saturate, LabHue, clamp}; +use {Color, Alpha, ColorSpace, Mix, Shade, GetHue, Hue, Rgb, Luma, Xyz, Lab, Hsv, Hsl, Saturate, LabHue, clamp}; -///CIE L*C*h°, a polar version of [CIE L*a*b*](struct.Lab.html), with an alpha -///component. +///CIE L*C*h° with an alpha component. See the [`Lcha` implementation in `Alpha`](struct.Alpha.html#Lcha). +pub type Lcha = Alpha, T>; + +///CIE L*C*h°, a polar version of [CIE L*a*b*](struct.Lab.html). /// ///L*C*h° shares its range and perceptual uniformity with L*a*b*, but it's a ///cylindrical color space, like [HSL](struct.Hsl.html) and @@ -26,29 +28,25 @@ pub struct Lch { ///The hue of the color, in degrees. Decides if it's red, blue, purple, ///etc. pub hue: LabHue, - - ///The transparency of the color. 0.0 is completely transparent and 1.0 is - ///completely opaque. - pub alpha: T, } impl Lch { ///CIE L*C*h°. - pub fn lch(l: T, chroma: T, hue: LabHue) -> Lch { + pub fn new(l: T, chroma: T, hue: LabHue) -> Lch { Lch { l: l, chroma: chroma, hue: hue, - alpha: T::one(), } } +} +///[`Lcha`](type.Lcha.html) implementations. +impl Alpha, T> { ///CIE L*C*h° and transparency. - pub fn lcha(l: T, chroma: T, hue: LabHue, alpha: T) -> Lch { - Lch { - l: l, - chroma: chroma, - hue: hue, + pub fn new(l: T, chroma: T, hue: LabHue, alpha: T) -> Lcha { + Alpha { + color: Lch::new(l, chroma, hue), alpha: alpha, } } @@ -57,8 +55,7 @@ impl Lch { impl ColorSpace for Lch { fn is_valid(&self) -> bool { self.l >= T::zero() && self.l <= T::one() && - self.chroma >= T::zero() && self.chroma <= T::from(1.41421356).unwrap() && //should include all of L*a*b*, but will also overshoot... - self.alpha >= T::zero() && self.alpha <= T::one() + self.chroma >= T::zero() && self.chroma <= T::from(1.41421356).unwrap() //should include all of L*a*b*, but will also overshoot... } fn clamp(&self) -> Lch { @@ -70,7 +67,6 @@ impl ColorSpace for Lch { fn clamp_self(&mut self) { self.l = clamp(self.l, T::zero(), T::one()); self.chroma = clamp(self.chroma, T::zero(), T::from(1.41421356).unwrap()); //should include all of L*a*b*, but will also overshoot... - self.alpha = clamp(self.alpha, T::zero(), T::one()); } } @@ -84,7 +80,6 @@ impl Mix for Lch { l: self.l + factor * (other.l - self.l), chroma: self.chroma + factor * (other.chroma - self.chroma), hue: self.hue + factor * hue_diff, - alpha: self.alpha + factor * (other.alpha - self.alpha), } } } @@ -97,7 +92,6 @@ impl Shade for Lch { l: self.l + amount, chroma: self.chroma, hue: self.hue, - alpha: self.alpha, } } } @@ -120,7 +114,6 @@ impl Hue for Lch { l: self.l, chroma: self.chroma, hue: hue, - alpha: self.alpha, } } @@ -129,7 +122,6 @@ impl Hue for Lch { l: self.l, chroma: self.chroma, hue: self.hue + amount, - alpha: self.alpha, } } } @@ -142,14 +134,13 @@ impl Saturate for Lch { l: self.l, chroma: self.chroma * (T::one() + factor), hue: self.hue, - alpha: self.alpha, } } } impl Default for Lch { fn default() -> Lch { - Lch::lch(T::zero(), T::zero(), LabHue::from(T::zero())) + Lch::new(T::zero(), T::zero(), LabHue::from(T::zero())) } } @@ -161,7 +152,6 @@ impl Add> for Lch { l: self.l + other.l, chroma: self.chroma + other.chroma, hue: self.hue + other.hue, - alpha: self.alpha + other.alpha, } } } @@ -174,7 +164,6 @@ impl Add for Lch { l: self.l + c, chroma: self.chroma + c, hue: self.hue + c, - alpha: self.alpha + c, } } } @@ -187,7 +176,6 @@ impl Sub> for Lch { l: self.l - other.l, chroma: self.chroma - other.chroma, hue: self.hue - other.hue, - alpha: self.alpha - other.alpha, } } } @@ -200,20 +188,20 @@ impl Sub for Lch { l: self.l - c, chroma: self.chroma - c, hue: self.hue - c, - alpha: self.alpha - c, } } } from_color!(to Lch from Rgb, Luma, Xyz, Lab, Hsv, Hsl); +alpha_from!(Lch {Rgb, Xyz, Luma, Lab, Hsv, Hsl, Color}); + impl From> for Lch { fn from(lab: Lab) -> Lch { Lch { l: lab.l, chroma: (lab.a * lab.a + lab.b * lab.b).sqrt(), hue: lab.get_hue().unwrap_or(LabHue::from(T::zero())), - alpha: lab.alpha, } } } diff --git a/src/lib.rs b/src/lib.rs index 2891e252e..102fb0f07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,29 @@ //!make sure that any operations on the colors are accurate. This library uses //!a completely linear work flow, and comes with the tools for transitioning //!between linear and non-linear RGB. +//! +//!# Transparency +//! +//!There are many cases where pixel transparency is important, but there are +//!also many cases where it becomes a dead weight, if it's always stored +//!together with the color, but not used. Palette has therefore adopted a +//!structure where the transparency component (alpha) is attachable using the +//![`Alpha`](struct.Alpha.html) type, instead of having copies of each color +//!space. +//! +//!This approach comes with the extra benefit of allowing operations to +//!selectively affect the alpha component: +//! +//!``` +//!use palette::{Rgb, Rgba}; +//! +//!let mut c1 = Rgba::new(1.0, 0.5, 0.5, 0.8); +//!let c2 = Rgb::new(0.5, 1.0, 1.0); +//! +//!c1.color = c1.color * c2; //Leave the alpha as it is +//!c1.blue += 0.2; //The color components can easily be accessed +//!c1 = c1 * 0.5; //Scale both the color and the alpha +//!``` #![doc(html_root_url = "http://ogeon.github.io/docs/palette/master/")] @@ -27,16 +50,17 @@ extern crate num; use num::traits::Float; -use pixel::{RgbPixel, Srgb, GammaRgb}; +use pixel::{Srgb, GammaRgb}; pub use gradient::Gradient; -pub use rgb::Rgb; -pub use luma::Luma; -pub use xyz::Xyz; -pub use lab::Lab; -pub use lch::Lch; -pub use hsv::Hsv; -pub use hsl::Hsl; +pub use alpha::Alpha; +pub use rgb::{Rgb, Rgba}; +pub use luma::{Luma, Lumaa}; +pub use xyz::{Xyz, Xyza}; +pub use lab::{Lab, Laba}; +pub use lch::{Lch, Lcha}; +pub use hsv::{Hsv, Hsva}; +pub use hsl::{Hsl, Hsla}; pub use hues::{LabHue, RgbHue}; @@ -53,6 +77,42 @@ macro_rules! from_color { ) } +macro_rules! alpha_from { + ($self_ty:ident {$($other:ident),+}) => ( + impl From, T>> for $self_ty { + fn from(color: Alpha<$self_ty, T>) -> $self_ty { + color.color + } + } + + $( + impl From, T>> for Alpha<$self_ty, T> { + fn from(other: Alpha<$other, T>) -> Alpha<$self_ty, T> { + Alpha { + color: other.color.into(), + alpha: other.alpha, + } + } + } + + impl From<$other> for Alpha<$self_ty, T> { + fn from(color: $other) -> Alpha<$self_ty, T> { + Alpha { + color: color.into(), + alpha: T::one(), + } + } + } + + impl From, T>> for $self_ty { + fn from(other: Alpha<$other, T>) -> $self_ty { + other.color.into() + } + } + )+ + ) +} + //Helper macro for approximate component wise comparison. Most color spaces //are in roughly the same ranges, so this epsilon should be alright. #[cfg(test)] @@ -63,13 +123,13 @@ macro_rules! assert_approx_eq { let b: f32 = $b.$components.into(); assert_relative_eq!(a, b, epsilon = 0.0001); )+ - assert_relative_eq!($a.alpha, $b.alpha, epsilon = 0.0001); }) } pub mod gradient; pub mod pixel; +mod alpha; mod rgb; mod luma; mod xyz; @@ -87,10 +147,13 @@ macro_rules! make_color { #[$variant_comment:meta] $variant:ident $(and $($representations:ident),+ )* {$( #[$ctor_comment:meta] - $ctor_name:ident $( <$( $ty_params:ident: $ty_param_traits:ident $( <$( $ty_inner_traits:ident ),*> )*),*> )* ($($ctor_field:ident : $ctor_ty:ty),*); + $ctor_name:ident $( <$( $ty_params:ident: $ty_param_traits:ident $( <$( $ty_inner_traits:ident ),*> )*),*> )* ($($ctor_field:ident : $ctor_ty:ty),*) [alpha: $alpha_ty:ty] => $ctor_original:ident; )+} )+) => ( + ///Generic color with an alpha component. See the [`Colora` implementation in `Alpha`](struct.Alpha.html#Colora). + pub type Colora = Alpha, T>; + ///A generic color type. /// ///The `Color` may belong to any color space and it may change @@ -111,16 +174,28 @@ macro_rules! make_color { $(#[$variant_comment] $variant($variant)),+ } - $( - impl Color { + impl Color { + $( $( #[$ctor_comment] pub fn $ctor_name$(<$($ty_params : $ty_param_traits$( <$( $ty_inner_traits ),*> )*),*>)*($($ctor_field: $ctor_ty),*) -> Color { - Color::$variant($variant::$ctor_name($($ctor_field),*)) + Color::$variant($variant::$ctor_original($($ctor_field),*)) } )+ - } - )+ + )+ + } + + ///[`Colora`](type.Colora.html) implementations. + impl Alpha, T> { + $( + $( + #[$ctor_comment] + pub fn $ctor_name$(<$($ty_params : $ty_param_traits$( <$( $ty_inner_traits ),*> )*),*>)*($($ctor_field: $ctor_ty,)* alpha: $alpha_ty) -> Colora { + Alpha::<$variant, T>::$ctor_original($($ctor_field,)* alpha).into() + } + )+ + )+ + } impl Mix for Color { type Scalar = T; @@ -179,6 +254,9 @@ macro_rules! make_color { } )+)* )+ + + alpha_from!(Color {$($variant),+}); + ) } @@ -196,79 +274,49 @@ make_color! { ///Linear luminance. Luma { ///Linear luminance. - y(luma: T); - - ///Linear luminance with transparency. - ya(luma: T, alpha: T); + y(luma: T)[alpha: T] => new; ///Linear luminance from an 8 bit value. - y8(luma: u8); - - ///Linear luminance and transparency from 8 bit values. - ya8(luma: u8, alpha: u8); + y_u8(luma: u8)[alpha: u8] => new_u8; } ///Linear RGB. Rgb and Srgb, GammaRgb { ///Linear RGB. - rgb(red: T, green: T, blue: T); - - ///Linear RGB and transparency. - rgba(red: T, green: T, blue: T, alpha: T); + rgb(red: T, green: T, blue: T)[alpha: T] => new; ///Linear RGB from 8 bit values. - rgb8(red: u8, green: u8, blue: u8); - - ///Linear RGB and transparency from 8 bit values. - rgba8(red: u8, green: u8, blue: u8, alpha: u8); - - ///Linear RGB from a linear pixel value. - from_pixel >(pixel: &P); + rgb_u8(red: u8, green: u8, blue: u8)[alpha: u8] => new_u8; } ///CIE 1931 XYZ. Xyz { ///CIE XYZ. - xyz(x: T, y: T, z: T); - - ///CIE XYZ and transparency. - xyza(x: T, y: T, z: T, alpha: T); + xyz(x: T, y: T, z: T)[alpha: T] => new; } ///CIE L*a*b* (CIELAB). Lab { ///CIE L*a*b*. - lab(l: T, a: T, b: T); - - ///CIE L*a*b* and transparency. - laba(l: T, a: T, b: T, alpha: T); + lab(l: T, a: T, b: T)[alpha: T] => new; } ///CIE L*C*h°, a polar version of CIE L*a*b*. Lch { ///CIE L*C*h°. - lch(l: T, chroma: T, hue: LabHue); - - ///CIE L*C*h° and transparency. - lcha(l: T, chroma: T, hue: LabHue, alpha: T); + lch(l: T, chroma: T, hue: LabHue)[alpha: T] => new; } ///Linear HSV, a cylindrical version of RGB. Hsv { ///Linear HSV. - hsv(hue: RgbHue, saturation: T, value: T); - - ///Linear HSV and transparency. - hsva(hue: RgbHue, saturation: T, value: T, alpha: T); + hsv(hue: RgbHue, saturation: T, value: T)[alpha: T] => new; } ///Linear HSL, a cylindrical version of RGB. Hsl { ///Linear HSL. - hsl(hue: RgbHue, saturation: T, lightness: T); - - ///Linear HSL and transparency. - hsla(hue: RgbHue, saturation: T, lightness: T, alpha: T); + hsl(hue: RgbHue, saturation: T, lightness: T)[alpha: T] => new; } } @@ -290,11 +338,11 @@ pub trait ColorSpace { ///``` ///use palette::{Rgb, Mix}; /// -///let a = Rgb::rgb(0.0, 0.5, 1.0); -///let b = Rgb::rgb(1.0, 0.5, 0.0); +///let a = Rgb::new(0.0, 0.5, 1.0); +///let b = Rgb::new(1.0, 0.5, 0.0); /// ///assert_eq!(a.mix(&b, 0.0), a); -///assert_eq!(a.mix(&b, 0.5), Rgb::rgb(0.5, 0.5, 0.5)); +///assert_eq!(a.mix(&b, 0.5), Rgb::new(0.5, 0.5, 0.5)); ///assert_eq!(a.mix(&b, 1.0), b); ///``` pub trait Mix { @@ -314,8 +362,8 @@ pub trait Mix { ///``` ///use palette::{Rgb, Shade}; /// -///let a = Rgb::rgb(0.4, 0.4, 0.4); -///let b = Rgb::rgb(0.6, 0.6, 0.6); +///let a = Rgb::new(0.4, 0.4, 0.4); +///let b = Rgb::new(0.6, 0.6, 0.6); /// ///assert_eq!(a.lighten(0.1), b.darken(0.1)); ///``` @@ -337,10 +385,10 @@ pub trait Shade: Sized { ///``` ///use palette::{Rgb, GetHue}; /// -///let red = Rgb::rgb(1.0f32, 0.0, 0.0); -///let green = Rgb::rgb(0.0f32, 1.0, 0.0); -///let blue = Rgb::rgb(0.0f32, 0.0, 1.0); -///let gray = Rgb::rgb(0.5f32, 0.5, 0.5); +///let red = Rgb::new(1.0f32, 0.0, 0.0); +///let green = Rgb::new(0.0f32, 1.0, 0.0); +///let blue = Rgb::new(0.0f32, 0.0, 1.0); +///let gray = Rgb::new(0.5f32, 0.5, 0.5); /// ///assert_eq!(red.get_hue(), Some(0.0.into())); ///assert_eq!(green.get_hue(), Some(120.0.into())); @@ -378,8 +426,8 @@ pub trait Hue: GetHue { ///``` ///use palette::{Hsv, Saturate}; /// -///let a = Hsv::hsv(0.0.into(), 0.25, 1.0); -///let b = Hsv::hsv(0.0.into(), 1.0, 1.0); +///let a = Hsv::new(0.0.into(), 0.25, 1.0); +///let b = Hsv::new(0.0.into(), 1.0, 1.0); /// ///assert_eq!(a.saturate(1.0), b.desaturate(0.5)); ///``` diff --git a/src/luma.rs b/src/luma.rs index 8e741b58d..bd9bb5f61 100644 --- a/src/luma.rs +++ b/src/luma.rs @@ -2,9 +2,12 @@ use num::traits::Float; use std::ops::{Add, Sub, Mul, Div}; -use {Color, Rgb, Xyz, Lab, Lch, Hsv, Hsl, ColorSpace, Mix, Shade, clamp}; +use {Color, Alpha, Rgb, Xyz, Lab, Lch, Hsv, Hsl, ColorSpace, Mix, Shade, clamp}; -///Linear luminance with an alpha component. +///Linear luminance with an alpha component. See the [`Lumaa` implementation in `Alpha`](struct.Alpha.html#Lumaa). +pub type Lumaa = Alpha, T>; + +///Linear luminance. /// ///Luma is a purely gray scale color space, which is included more for ///completeness than anything else, and represents how bright a color is @@ -15,41 +18,38 @@ use {Color, Rgb, Xyz, Lab, Lch, Hsv, Hsl, ColorSpace, Mix, Shade, clamp}; pub struct Luma { ///The lightness of the color. 0.0 is black and 1.0 is white. pub luma: T, - - ///The transparency of the color. 0.0 is completely transparent and 1.0 is - ///completely opaque. - pub alpha: T, } impl Luma { ///Linear luminance. - pub fn y(luma: T) -> Luma { + pub fn new(luma: T) -> Luma { Luma { luma: luma, - alpha: T::zero(), } } - ///Linear luminance with transparency. - pub fn ya(luma: T, alpha: T) -> Luma { + ///Linear luminance from an 8 bit value. + pub fn new_u8(luma: u8) -> Luma { Luma { - luma: luma, - alpha: alpha, + luma: T::from(luma).unwrap() / T::from(255.0).unwrap(), } } +} - ///Linear luminance from an 8 bit value. - pub fn y8(luma: u8) -> Luma { - Luma { - luma: T::from(luma).unwrap() / T::from(255.0).unwrap(), - alpha: T::zero(), +///[`Lumaa`](type.Lumaa.html) implementations. +impl Alpha, T> { + ///Linear luminance with transparency. + pub fn new(luma: T, alpha: T) -> Lumaa { + Alpha { + color: Luma::new(luma), + alpha: alpha, } } ///Linear luminance and transparency from 8 bit values. - pub fn ya8(luma: u8, alpha: u8) -> Luma { - Luma { - luma: T::from(luma).unwrap() / T::from(255.0).unwrap(), + pub fn new_u8(luma: u8, alpha: u8) -> Lumaa { + Alpha { + color: Luma::new_u8(luma), alpha: T::from(alpha).unwrap() / T::from(255.0).unwrap(), } } @@ -57,8 +57,7 @@ impl Luma { impl ColorSpace for Luma { fn is_valid(&self) -> bool { - self.luma >= T::zero() && self.luma <= T::one() && self.alpha >= T::zero() && - self.alpha <= T::one() + self.luma >= T::zero() && self.luma <= T::one() } fn clamp(&self) -> Luma { @@ -69,7 +68,6 @@ impl ColorSpace for Luma { fn clamp_self(&mut self) { self.luma = clamp(self.luma, T::zero(), T::one()); - self.alpha = clamp(self.alpha, T::zero(), T::one()); } } @@ -81,7 +79,6 @@ impl Mix for Luma { Luma { luma: self.luma + factor * (other.luma - self.luma), - alpha: self.alpha + factor * (other.alpha - self.alpha), } } } @@ -92,14 +89,13 @@ impl Shade for Luma { fn lighten(&self, amount: T) -> Luma { Luma { luma: (self.luma + amount).max(T::zero()), - alpha: self.alpha, } } } impl Default for Luma { fn default() -> Luma { - Luma::y(T::zero()) + Luma::new(T::zero()) } } @@ -109,7 +105,6 @@ impl Add> for Luma { fn add(self, other: Luma) -> Luma { Luma { luma: self.luma + other.luma, - alpha: self.alpha + other.alpha, } } } @@ -120,7 +115,6 @@ impl Add for Luma { fn add(self, c: T) -> Luma { Luma { luma: self.luma + c, - alpha: self.alpha + c, } } } @@ -131,7 +125,6 @@ impl Sub> for Luma { fn sub(self, other: Luma) -> Luma { Luma { luma: self.luma - other.luma, - alpha: self.alpha - other.alpha, } } } @@ -142,7 +135,6 @@ impl Sub for Luma { fn sub(self, c: T) -> Luma { Luma { luma: self.luma - c, - alpha: self.alpha - c, } } } @@ -153,7 +145,6 @@ impl Mul> for Luma { fn mul(self, other: Luma) -> Luma { Luma { luma: self.luma * other.luma, - alpha: self.alpha * other.alpha, } } } @@ -164,7 +155,6 @@ impl Mul for Luma { fn mul(self, c: T) -> Luma { Luma { luma: self.luma * c, - alpha: self.alpha * c, } } } @@ -175,7 +165,6 @@ impl Div> for Luma { fn div(self, other: Luma) -> Luma { Luma { luma: self.luma / other.luma, - alpha: self.alpha / other.alpha, } } } @@ -186,18 +175,18 @@ impl Div for Luma { fn div(self, c: T) -> Luma { Luma { luma: self.luma / c, - alpha: self.alpha / c, } } } from_color!(to Luma from Rgb, Xyz, Lab, Lch, Hsv, Hsl); +alpha_from!(Luma {Rgb, Xyz, Lab, Lch, Hsv, Hsl, Color}); + impl From> for Luma { fn from(rgb: Rgb) -> Luma { Luma { luma: rgb.red * T::from(0.2126).unwrap() + rgb.green * T::from(0.7152).unwrap() + rgb.blue * T::from(0.0722).unwrap(), - alpha: rgb.alpha } } } @@ -206,7 +195,6 @@ impl From> for Luma { fn from(xyz: Xyz) -> Luma { Luma { luma: xyz.y, - alpha: xyz.alpha, } } } diff --git a/src/pixel/gamma_rgb.rs b/src/pixel/gamma_rgb.rs index 2a18fb2f8..dd42bc962 100644 --- a/src/pixel/gamma_rgb.rs +++ b/src/pixel/gamma_rgb.rs @@ -1,6 +1,6 @@ use num::Float; -use {Rgb, clamp}; +use {Alpha, Rgb, Rgba, clamp}; use pixel::RgbPixel; @@ -96,7 +96,7 @@ impl GammaRgb { } ///Convert linear color components to gamma encoding. - pub fn from_linear>>(color: C, gamma: T) -> GammaRgb { + pub fn from_linear>>(color: C, gamma: T) -> GammaRgb { let rgb = color.into(); GammaRgb { red: to_gamma(rgb.red, gamma), @@ -108,17 +108,19 @@ impl GammaRgb { } ///Decode this color to a linear representation. - pub fn to_linear(&self) -> Rgb { - Rgb { - red: from_gamma(self.red, self.gamma), - green: from_gamma(self.green, self.gamma), - blue: from_gamma(self.blue, self.gamma), + pub fn to_linear(&self) -> Rgba { + Alpha { + color: Rgb { + red: from_gamma(self.red, self.gamma), + green: from_gamma(self.green, self.gamma), + blue: from_gamma(self.blue, self.gamma), + }, alpha: self.alpha, } } ///Shortcut to convert a linear color to a gamma encoded pixel. - pub fn linear_to_pixel>, P: RgbPixel>(color: C, gamma: T) -> P { + pub fn linear_to_pixel>, P: RgbPixel>(color: C, gamma: T) -> P { GammaRgb::from_linear(color, gamma).to_pixel() } } diff --git a/src/pixel/srgb.rs b/src/pixel/srgb.rs index b56f57afe..c541bd1a3 100644 --- a/src/pixel/srgb.rs +++ b/src/pixel/srgb.rs @@ -1,6 +1,6 @@ use num::Float; -use {Color, Rgb, clamp}; +use {Color, Alpha, Rgb, Rgba, clamp}; use pixel::RgbPixel; @@ -85,7 +85,7 @@ impl Srgb { } ///Convert linear color components to sRGB encoding. - pub fn from_linear>>(color: C) -> Srgb { + pub fn from_linear>>(color: C) -> Srgb { let rgb = color.into(); Srgb { red: to_srgb(rgb.red), @@ -96,24 +96,32 @@ impl Srgb { } ///Decode this color to a linear representation. - pub fn to_linear(&self) -> Rgb { - Rgb { - red: from_srgb(self.red), - green: from_srgb(self.green), - blue: from_srgb(self.blue), + pub fn to_linear(&self) -> Rgba { + Alpha { + color: Rgb { + red: from_srgb(self.red), + green: from_srgb(self.green), + blue: from_srgb(self.blue), + }, alpha: self.alpha, } } ///Shortcut to convert a linear color to an sRGB encoded pixel. - pub fn linear_to_pixel>, P: RgbPixel>(color: C) -> P { + pub fn linear_to_pixel>, P: RgbPixel>(color: C) -> P { Srgb::from_linear(color).to_pixel() } } impl From> for Srgb { fn from(rgb: Rgb) -> Srgb { - Srgb::from_linear(rgb) + Rgba::from(rgb).into() + } +} + +impl From> for Srgb { + fn from(rgba: Rgba) -> Srgb { + Srgb::from_linear(rgba) } } diff --git a/src/rgb.rs b/src/rgb.rs index 033ac6511..856c03faa 100644 --- a/src/rgb.rs +++ b/src/rgb.rs @@ -2,10 +2,13 @@ use num::traits::Float; use std::ops::{Add, Sub, Mul, Div}; -use {Color, Luma, Xyz, Lab, Lch, Hsv, Hsl, ColorSpace, Mix, Shade, GetHue, RgbHue, clamp}; +use {Color, Alpha, Luma, Xyz, Lab, Lch, Hsv, Hsl, ColorSpace, Mix, Shade, GetHue, RgbHue, clamp}; use pixel::{RgbPixel, Srgb, GammaRgb}; -///Linear RGB with an alpha component. +///Linear RGB with an alpha component. See the [`Rgba` implementation in `Alpha`](struct.Alpha.html#Rgba). +pub type Rgba = Alpha, T>; + +///Linear RGB. /// ///RGB is probably the most common color space, when it comes to computer ///graphics, and it's defined as an additive mixture of red, green and blue @@ -30,69 +33,86 @@ pub struct Rgb { ///The amount of blue light, where 0.0 is no blue light and 1.0 is the ///highest displayable amount. pub blue: T, - - ///The transparency of the color. 0.0 is completely transparent and 1.0 is - ///completely opaque. - pub alpha: T, } -///Creation from linear RGB. impl Rgb { ///Linear RGB. - pub fn rgb(red: T, green: T, blue: T) -> Rgb { - Rgb { - red: red, - green: green, - blue: blue, - alpha: T::one(), - } - } - - ///Linear RGB with transparency. - pub fn rgba(red: T, green: T, blue: T, alpha: T) -> Rgb { + pub fn new(red: T, green: T, blue: T) -> Rgb { Rgb { red: red, green: green, blue: blue, - alpha: alpha, } } ///Linear RGB from 8 bit values. - pub fn rgb8(red: u8, green: u8, blue: u8) -> Rgb { + pub fn new_u8(red: u8, green: u8, blue: u8) -> Rgb { Rgb { red: T::from(red).unwrap() / T::from(255.0).unwrap(), green: T::from(green).unwrap() / T::from(255.0).unwrap(), blue: T::from(blue).unwrap() / T::from(255.0).unwrap(), - alpha: T::one(), + } + } + + ///Linear RGB from a linear pixel value. + pub fn from_pixel>(pixel: &P) -> Rgb { + let (r, g, b, _) = pixel.to_rgba(); + Rgb::new(r, g, b) + } + + ///Convert to a linear RGB pixel. `Rgb` is already assumed to be linear, + ///so the components will just be clamped to [0.0, 1.0] before conversion. + /// + ///``` + ///use palette::Rgb; + /// + ///let c = Rgb::new(0.5, 0.3, 0.1); + ///assert_eq!((c.red, c.green, c.blue), c.to_pixel()); + ///assert_eq!((0.5, 0.3, 0.1), c.to_pixel()); + ///``` + pub fn to_pixel>(&self) -> P { + P::from_rgba( + clamp(self.red, T::zero(), T::one()), + clamp(self.green, T::zero(), T::one()), + clamp(self.blue, T::zero(), T::one()), + T::one(), + ) + } +} + +///[`Rgba`](type.Rgba.html) implementations. +impl Alpha, T> { + ///Linear RGB with transparency. + pub fn new(red: T, green: T, blue: T, alpha: T) -> Rgba { + Alpha { + color: Rgb::new(red, green, blue), + alpha: alpha, } } ///Linear RGB with transparency from 8 bit values. - pub fn rgba8(red: u8, green: u8, blue: u8, alpha: u8) -> Rgb { - Rgb { - red: T::from(red).unwrap() / T::from(255.0).unwrap(), - green: T::from(green).unwrap() / T::from(255.0).unwrap(), - blue: T::from(blue).unwrap() / T::from(255.0).unwrap(), + pub fn new_u8(red: u8, green: u8, blue: u8, alpha: u8) -> Rgba { + Alpha { + color: Rgb::new_u8(red, green, blue), alpha: T::from(alpha).unwrap() / T::from(255.0).unwrap(), } } ///Linear RGB from a linear pixel value. - pub fn from_pixel>(pixel: &P) -> Rgb { + pub fn from_pixel>(pixel: &P) -> Rgba { let (r, g, b, a) = pixel.to_rgba(); - Rgb::rgba(r, g, b, a) + Rgba::new(r, g, b, a) } - ///Convert to a linear RGB pixel. `Rgb` is already assumed to be linear, + ///Convert to a linear RGB pixel. `Rgb` is already assumed to be linear, ///so the components will just be clamped to [0.0, 1.0] before conversion. /// ///``` - ///use palette::Rgb; + ///use palette::Rgba; /// - ///let c = Rgb::rgb(0.5, 0.3, 0.1); - ///assert_eq!((c.red, c.green, c.blue), c.to_pixel()); - ///assert_eq!((0.5, 0.3, 0.1), c.to_pixel()); + ///let c = Rgba::new(0.5, 0.3, 0.1, 0.5); + ///assert_eq!((c.red, c.green, c.blue, c.alpha), c.to_pixel()); + ///assert_eq!((0.5, 0.3, 0.1, 0.5), c.to_pixel()); ///``` pub fn to_pixel>(&self) -> P { P::from_rgba( @@ -108,8 +128,7 @@ impl ColorSpace for Rgb { fn is_valid(&self) -> bool { self.red >= T::zero() && self.red <= T::one() && self.green >= T::zero() && self.green <= T::one() && - self.blue >= T::zero() && self.blue <= T::one() && - self.alpha >= T::zero() && self.alpha <= T::one() + self.blue >= T::zero() && self.blue <= T::one() } fn clamp(&self) -> Rgb { @@ -122,7 +141,6 @@ impl ColorSpace for Rgb { self.red = clamp(self.red, T::zero(), T::one()); self.green = clamp(self.green, T::zero(), T::one()); self.blue = clamp(self.blue, T::zero(), T::one()); - self.alpha = clamp(self.alpha, T::zero(), T::one()); } } @@ -136,7 +154,6 @@ impl Mix for Rgb { red: self.red + factor * (other.red - self.red), green: self.green + factor * (other.green - self.green), blue: self.blue + factor * (other.blue - self.blue), - alpha: self.alpha + factor * (other.alpha - self.alpha), } } } @@ -149,7 +166,6 @@ impl Shade for Rgb { red: self.red + amount, green: self.green + amount, blue: self.blue + amount, - alpha: self.alpha, } } } @@ -170,7 +186,7 @@ impl GetHue for Rgb { impl Default for Rgb { fn default() -> Rgb { - Rgb::rgb(T::zero(), T::zero(), T::zero()) + Rgb::new(T::zero(), T::zero(), T::zero()) } } @@ -182,7 +198,6 @@ impl Add> for Rgb { red: self.red + other.red, green: self.green + other.green, blue: self.blue + other.blue, - alpha: self.alpha + other.alpha, } } } @@ -195,7 +210,6 @@ impl Add for Rgb { red: self.red + c, green: self.green + c, blue: self.blue + c, - alpha: self.alpha + c, } } } @@ -208,7 +222,6 @@ impl Sub> for Rgb { red: self.red - other.red, green: self.green - other.green, blue: self.blue - other.blue, - alpha: self.alpha - other.alpha, } } } @@ -221,7 +234,6 @@ impl Sub for Rgb { red: self.red - c, green: self.green - c, blue: self.blue - c, - alpha: self.alpha - c, } } } @@ -234,7 +246,6 @@ impl Mul> for Rgb { red: self.red * other.red, green: self.green * other.green, blue: self.blue * other.blue, - alpha: self.alpha * other.alpha, } } } @@ -247,7 +258,6 @@ impl Mul for Rgb { red: self.red * c, green: self.green * c, blue: self.blue * c, - alpha: self.alpha * c, } } } @@ -260,7 +270,6 @@ impl Div> for Rgb { red: self.red / other.red, green: self.green / other.green, blue: self.blue / other.blue, - alpha: self.alpha / other.alpha, } } } @@ -273,20 +282,20 @@ impl Div for Rgb { red: self.red / c, green: self.green / c, blue: self.blue / c, - alpha: self.alpha / c, } } } from_color!(to Rgb from Xyz, Luma, Lab, Lch, Hsv, Hsl); +alpha_from!(Rgb {Xyz, Luma, Lab, Lch, Hsv, Hsl, Color}); + impl From> for Rgb { fn from(luma: Luma) -> Rgb { Rgb { red: luma.luma, green: luma.luma, blue: luma.luma, - alpha: luma.alpha, } } } @@ -297,7 +306,6 @@ impl From> for Rgb { red: xyz.x * T::from(3.2406).unwrap() + xyz.y * T::from(-1.5372).unwrap() + xyz.z * T::from(-0.4986).unwrap(), green: xyz.x * T::from(-0.9689).unwrap() + xyz.y * T::from(1.8758).unwrap() + xyz.z * T::from(0.0415).unwrap(), blue: xyz.x * T::from(0.0557).unwrap() + xyz.y * T::from(-0.2040).unwrap() + xyz.z * T::from(1.0570).unwrap(), - alpha: xyz.alpha, } } } @@ -340,7 +348,6 @@ impl From> for Rgb { red: red + m, green: green + m, blue: blue + m, - alpha: hsv.alpha, } } } @@ -371,19 +378,30 @@ impl From> for Rgb { red: red + m, green: green + m, blue: blue + m, - alpha: hsl.alpha, } } } impl From> for Rgb { fn from(srgb: Srgb) -> Rgb { - srgb.to_linear() + srgb.to_linear().into() } } impl From> for Rgb { fn from(gamma_rgb: GammaRgb) -> Rgb { + gamma_rgb.to_linear().into() + } +} + +impl From> for Alpha, T> { + fn from(srgb: Srgb) -> Alpha, T> { + srgb.to_linear() + } +} + +impl From> for Alpha, T> { + fn from(gamma_rgb: GammaRgb) -> Alpha, T> { gamma_rgb.to_linear() } } diff --git a/src/xyz.rs b/src/xyz.rs index f87ad0178..2a518e5c2 100644 --- a/src/xyz.rs +++ b/src/xyz.rs @@ -2,11 +2,14 @@ use num::traits::Float; use std::ops::{Add, Sub, Mul, Div}; -use {Color, Rgb, Luma, Lab, Lch, Hsv, Hsl, ColorSpace, Mix, Shade, clamp}; +use {Color, Alpha, Rgb, Luma, Lab, Lch, Hsv, Hsl, ColorSpace, Mix, Shade, clamp}; use tristimulus::{X_N, Y_N, Z_N}; -///The CIE 1931 XYZ color space with an alpha component. +///CIE 1931 XYZ with an alpha component. See the [`Xyza` implementation in `Alpha`](struct.Alpha.html#Xyza). +pub type Xyza = Alpha, T>; + +///The CIE 1931 XYZ color space. /// ///XYZ links the perceived colors to their wavelengths and simply makes it ///possible to describe the way we see colors as numbers. It's often used when @@ -28,29 +31,25 @@ pub struct Xyz { ///Z is the scale of what can be seen as the blue stimulation. It goes ///from 0.0 to 1.0. pub z: T, - - ///The transparency of the color. 0.0 is completely transparent and 1.0 is - ///completely opaque. - pub alpha: T, } impl Xyz { ///CIE XYZ. - pub fn xyz(x: T, y: T, z: T) -> Xyz { + pub fn new(x: T, y: T, z: T) -> Xyz { Xyz { x: x, y: y, z: z, - alpha: T::one(), } } +} +///[`Xyza`](type.Xyza.html) implementations. +impl Alpha, T> { ///CIE XYZ and transparency. - pub fn xyza(x: T, y: T, z: T, alpha: T) -> Xyz { - Xyz { - x: x, - y: y, - z: z, + pub fn new(x: T, y: T, z: T, alpha: T) -> Xyza { + Alpha { + color: Xyz::new(x, y, z), alpha: alpha, } } @@ -58,9 +57,9 @@ impl Xyz { impl ColorSpace for Xyz { fn is_valid(&self) -> bool { - self.x >= T::zero() && self.x <= T::one() && self.y >= T::zero() && - self.y <= T::one() && self.z >= T::zero() && self.z <= T::one() && - self.alpha >= T::zero() && self.alpha <= T::one() + self.x >= T::zero() && self.x <= T::one() && + self.y >= T::zero() && self.y <= T::one() && + self.z >= T::zero() && self.z <= T::one() } fn clamp(&self) -> Xyz { @@ -73,7 +72,6 @@ impl ColorSpace for Xyz { self.x = clamp(self.x, T::zero(), T::one()); self.y = clamp(self.y, T::zero(), T::one()); self.z = clamp(self.z, T::zero(), T::one()); - self.alpha = clamp(self.alpha, T::zero(), T::one()); } } @@ -87,7 +85,6 @@ impl Mix for Xyz { x: self.x + factor * (other.x - self.x), y: self.y + factor * (other.y - self.y), z: self.z + factor * (other.z - self.z), - alpha: self.alpha + factor * (other.alpha - self.alpha), } } } @@ -100,14 +97,13 @@ impl Shade for Xyz { x: self.x, y: self.y + amount, z: self.z, - alpha: self.alpha, } } } impl Default for Xyz { fn default() -> Xyz { - Xyz::xyz(T::zero(), T::zero(), T::zero()) + Xyz::new(T::zero(), T::zero(), T::zero()) } } @@ -119,7 +115,6 @@ impl Add> for Xyz { x: self.x + other.x, y: self.y + other.y, z: self.z + other.z, - alpha: self.alpha + other.alpha, } } } @@ -132,7 +127,6 @@ impl Add for Xyz { x: self.x + c, y: self.y + c, z: self.z + c, - alpha: self.alpha + c, } } } @@ -145,7 +139,6 @@ impl Sub> for Xyz { x: self.x - other.x, y: self.y - other.y, z: self.z - other.z, - alpha: self.alpha - other.alpha, } } } @@ -158,7 +151,6 @@ impl Sub for Xyz { x: self.x - c, y: self.y - c, z: self.z - c, - alpha: self.alpha - c, } } } @@ -171,7 +163,6 @@ impl Mul> for Xyz { x: self.x * other.x, y: self.y * other.y, z: self.z * other.z, - alpha: self.alpha * other.alpha, } } } @@ -184,7 +175,6 @@ impl Mul for Xyz { x: self.x * c, y: self.y * c, z: self.z * c, - alpha: self.alpha * c, } } } @@ -197,7 +187,6 @@ impl Div> for Xyz { x: self.x / other.x, y: self.y / other.y, z: self.z / other.z, - alpha: self.alpha / other.alpha, } } } @@ -210,20 +199,20 @@ impl Div for Xyz { x: self.x / c, y: self.y / c, z: self.z / c, - alpha: self.alpha / c, } } } from_color!(to Xyz from Rgb, Luma, Lab, Lch, Hsv, Hsl); +alpha_from!(Xyz {Rgb, Luma, Lab, Lch, Hsv, Hsl, Color}); + impl From> for Xyz { fn from(rgb: Rgb) -> Xyz { Xyz { x: rgb.red * T::from(0.4124).unwrap() + rgb.green * T::from(0.3576).unwrap() + rgb.blue * T::from(0.1805).unwrap(), y: rgb.red * T::from(0.2126).unwrap() + rgb.green * T::from(0.7152).unwrap() + rgb.blue * T::from(0.0722).unwrap(), z: rgb.red * T::from(0.0193).unwrap() + rgb.green * T::from(0.1192).unwrap() + rgb.blue * T::from(0.9505).unwrap(), - alpha: rgb.alpha, } } } @@ -234,7 +223,6 @@ impl From> for Xyz { x: T::zero(), y: luma.luma, z: T::zero(), - alpha: luma.alpha, } } } @@ -250,7 +238,6 @@ impl From> for Xyz { z: T::from(Z_N).unwrap() * f_inv((T::one() / T::from(116.0).unwrap()) * (lab.l * T::from(100.0).unwrap() + T::from(16.0).unwrap()) - (T::one() / T::from(200.0).unwrap()) * lab.b * T::from(128.0).unwrap()), - alpha: lab.alpha, } } } @@ -292,22 +279,22 @@ mod test { #[test] fn red() { - let a = Xyz::from(Rgb::rgb(1.0, 0.0, 0.0)); - let b = Xyz::xyz(0.41240, 0.21260, 0.01930); + let a = Xyz::from(Rgb::new(1.0, 0.0, 0.0)); + let b = Xyz::new(0.41240, 0.21260, 0.01930); assert_approx_eq!(a, b, [x, y, z]); } #[test] fn green() { - let a = Xyz::from(Rgb::rgb(0.0, 1.0, 0.0)); - let b = Xyz::xyz(0.35760, 0.71520, 0.11920); + let a = Xyz::from(Rgb::new(0.0, 1.0, 0.0)); + let b = Xyz::new(0.35760, 0.71520, 0.11920); assert_approx_eq!(a, b, [x, y, z]); } #[test] fn blue() { - let a = Xyz::from(Rgb::rgb(0.0, 0.0, 1.0)); - let b = Xyz::xyz(0.18050, 0.07220, 0.95050); + let a = Xyz::from(Rgb::new(0.0, 0.0, 1.0)); + let b = Xyz::new(0.18050, 0.07220, 0.95050); assert_approx_eq!(a, b, [x, y, z]); } }