From 50997dc1618c988ef3d66fbfb1cfba6c5cf1a87a Mon Sep 17 00:00:00 2001 From: sid Date: Sun, 7 Feb 2016 08:05:25 +0530 Subject: [PATCH] Add FromColor and IntoColor traits. updated comments --- src/convert.rs | 231 +++++++++++++++++++++++++++++++++++++++++++++++++ src/hsl.rs | 172 ++++++++++++++++-------------------- src/hsv.rs | 165 +++++++++++++++-------------------- src/lab.rs | 81 ++++++----------- src/lch.rs | 72 +++++---------- src/lib.rs | 69 ++++----------- src/luma.rs | 80 ++++++----------- src/rgb.rs | 185 ++++++++++++++++++--------------------- src/xyz.rs | 113 +++++++++--------------- src/yxy.rs | 85 +++++------------- 10 files changed, 612 insertions(+), 641 deletions(-) create mode 100644 src/convert.rs diff --git a/src/convert.rs b/src/convert.rs new file mode 100644 index 000000000..0b972efae --- /dev/null +++ b/src/convert.rs @@ -0,0 +1,231 @@ +use num::Float; + +use {Alpha, Rgb, Luma, Xyz, Yxy, Lab, Lch, Hsv, Hsl, Color}; + +///FromColor provides conversion between the colors. +/// +///It requires from_xyz and derives conversion to other colors as a default from this. +///These defaults must be overridden when direct conversion exists between colors. +///For example, Luma has direct conversion to Rgb. So from_rgb conversion for Luma and +///from_luma for Rgb is implemented directly. The from for the same color must override +///the default. For example, from_rgb for Rgb will convert via Xyz which needs to be overridden +///with self to avoid the unnecessary converison. +pub trait FromColor: Sized + where T: Float, +{ + ///Convert from XYZ color space + fn from_xyz(Xyz) -> Self; + + ///Convert from Yxy color space + fn from_yxy(inp: Yxy) -> Self { + Self::from_xyz(inp.into_xyz()) + } + + ///Convert from L*a*b* color space + fn from_lab(inp: Lab) -> Self { + Self::from_xyz(inp.into_xyz()) + } + + ///Convert from L*C*h° color space + fn from_lch(inp: Lch) -> Self { + Self::from_lab(inp.into_lab()) + } + + ///Convert from RGB color space + fn from_rgb(inp: Rgb) -> Self { + Self::from_xyz(inp.into_xyz()) + } + + ///Convert from HSL color space + fn from_hsl(inp: Hsl) -> Self { + Self::from_rgb(inp.into_rgb()) + } + + ///Convert from HSV color space + fn from_hsv(inp: Hsv) -> Self { + Self::from_rgb(inp.into_rgb()) + } + + ///Convert from Luma + fn from_luma(inp: Luma) -> Self { + Self::from_xyz(inp.into_xyz()) + } + +} + + +///IntoColor provides conversion between the colors. +/// +///It requires into into_xyz and derives conversion to other colors as a default from this. +///These defaults must be overridden when direct conversion exists between colors. +pub trait IntoColor: Sized + where T: Float, +{ + + ///Convert into XYZ space + fn into_xyz(self) -> Xyz; + + ///Convert into Yxy color space + fn into_yxy(self) -> Yxy { + Yxy::from_xyz(self.into_xyz()) + } + + ///Convert into L*a*b* color space + fn into_lab(self) -> Lab { + Lab::from_xyz(self.into_xyz()) + } + + ///Convert into L*C*h° color space + fn into_lch(self) -> Lch { + Lch::from_lab(self.into_lab()) + } + + ///Convert into RGB color space. + fn into_rgb(self) -> Rgb { + Rgb::from_xyz(self.into_xyz()) + } + + ///Convert into HSL color space + fn into_hsl(self) -> Hsl { + Hsl::from_rgb(self.into_rgb()) + } + + ///Convert into HSV color space + fn into_hsv(self) -> Hsv { + Hsv::from_rgb(self.into_rgb()) + } + + ///Convert into Luma + fn into_luma(self) -> Luma { + Luma::from_xyz(self.into_xyz()) + } + +} + +macro_rules! impl_into_color { + ($self_ty:ident, $from_fn: ident) => { + impl< T: Float > IntoColor for $self_ty { + + fn into_xyz(self) -> Xyz { + Xyz::$from_fn(self) + } + + fn into_yxy(self) -> Yxy { + Yxy::$from_fn(self) + } + + fn into_lab(self) -> Lab { + Lab::$from_fn(self) + } + + fn into_lch(self) -> Lch { + Lch::$from_fn(self) + } + + fn into_rgb(self) -> Rgb { + Rgb::$from_fn(self) + } + + fn into_hsl(self) -> Hsl { + Hsl::$from_fn(self) + } + + fn into_hsv(self) -> Hsv { + Hsv::$from_fn(self) + } + + fn into_luma(self) -> Luma { + Luma::$from_fn(self) + } + + } + + } +} + + +macro_rules! impl_from_trait { + (($self_ty:ident, $into_fn: ident) => {$($other:ident),+}) => ( + impl From, T>> for $self_ty { + fn from(color: Alpha<$self_ty, T>) -> $self_ty { + color.color + } + } + + impl From> for $self_ty { + fn from(color: Color) -> $self_ty { + match color { + Color::Luma(c) => c.$into_fn(), + Color::Rgb(c) => c.$into_fn(), + Color::Xyz(c) => c.$into_fn(), + Color::Yxy(c) => c.$into_fn(), + Color::Lab(c) => c.$into_fn(), + Color::Lch(c) => c.$into_fn(), + Color::Hsv(c) => c.$into_fn(), + Color::Hsl(c) => c.$into_fn(), + } + } + } + + impl From> for Alpha<$self_ty,T> { + fn from(color: Color) -> Alpha<$self_ty,T> { + Alpha { + color: color.into(), + alpha: T::one(), + } + } + } + + $( + impl From<$other> for $self_ty { + fn from(other: $other) -> $self_ty { + other.$into_fn() + } + } + + impl From, T>> for Alpha<$self_ty, T> { + fn from(other: Alpha<$other, T>) -> Alpha<$self_ty, T> { + Alpha { + color: other.color.$into_fn(), + alpha: other.alpha, + } + } + } + + impl From<$other> for Alpha<$self_ty, T> { + fn from(color: $other) -> Alpha<$self_ty, T> { + Alpha { + color: color.$into_fn(), + alpha: T::one(), + } + } + } + + impl From, T>> for $self_ty { + fn from(other: Alpha<$other, T>) -> $self_ty { + other.color.$into_fn() + } + } + + )+ + ) +} + +impl_into_color!(Xyz,from_xyz); +impl_into_color!(Yxy,from_yxy); +impl_into_color!(Lab,from_lab); +impl_into_color!(Lch,from_lch); +impl_into_color!(Rgb,from_rgb); +impl_into_color!(Hsl,from_hsl); +impl_into_color!(Hsv,from_hsv); +impl_into_color!(Luma,from_luma); + + +impl_from_trait!((Xyz,into_xyz) => {Yxy, Lab, Lch, Rgb, Hsl, Hsv, Luma}); +impl_from_trait!((Yxy,into_yxy) => {Xyz, Lab, Lch, Rgb, Hsl, Hsv, Luma}); +impl_from_trait!((Lab,into_lab) => {Xyz, Yxy, Lch, Rgb, Hsl, Hsv, Luma}); +impl_from_trait!((Lch,into_lch) => {Xyz, Yxy, Lab, Rgb, Hsl, Hsv, Luma}); +impl_from_trait!((Rgb,into_rgb) => {Xyz, Yxy, Lab, Lch, Hsl, Hsv, Luma}); +impl_from_trait!((Hsl,into_hsl) => {Xyz, Yxy, Lab, Lch, Rgb, Hsv, Luma}); +impl_from_trait!((Hsv,into_hsv) => {Xyz, Yxy, Lab, Lch, Rgb, Hsl, Luma}); +impl_from_trait!((Luma,into_luma) => {Xyz, Yxy, Lab, Lch, Rgb, Hsl, Hsv}); diff --git a/src/hsl.rs b/src/hsl.rs index fce04cc03..510420752 100644 --- a/src/hsl.rs +++ b/src/hsl.rs @@ -2,7 +2,7 @@ use num::Float; use std::ops::{Add, Sub}; -use {Color, Alpha, Rgb, Luma, Xyz, Yxy, Lab, Lch, Hsv, Limited, Mix, Shade, GetHue, Hue, Saturate, RgbHue, clamp, flt}; +use {Alpha, Rgb, Xyz, Hsv, Limited, Mix, Shade, GetHue, Hue, Saturate, RgbHue, FromColor, IntoColor, clamp, flt}; ///Linear HSL with an alpha component. See the [`Hsla` implementation in `Alpha`](struct.Alpha.html#Hsla). pub type Hsla = Alpha, T>; @@ -54,6 +54,78 @@ impl Alpha, T> { } } +impl FromColor for Hsl { + fn from_xyz(xyz: Xyz) -> Self { + let rgb: Rgb = xyz.into_rgb(); + Self::from_rgb(rgb) + } + + fn from_rgb(rgb: Rgb) -> Self { + enum Channel { Red, Green, Blue }; + + let val_min = rgb.red.min(rgb.green).min(rgb.blue); + let mut val_max = rgb.red; + let mut chan_max = Channel::Red; + + if rgb.green > val_max { + chan_max = Channel::Green; + val_max = rgb.green; + } + + if rgb.blue > val_max { + chan_max = Channel::Blue; + val_max = rgb.blue; + } + + let diff = val_max - val_min; + let lightness = (val_min + val_max) / flt(2.0); + + let hue = if diff == T::zero() { + T::zero() + } else { + flt::(60.0) * match chan_max { + Channel::Red => ((rgb.green - rgb.blue) / diff) % flt(6.0), + Channel::Green => ((rgb.blue - rgb.red) / diff + flt(2.0)), + Channel::Blue => ((rgb.red - rgb.green) / diff + flt(4.0)), + } + }; + + let saturation = if diff == T::zero() { + T::zero() + } else { + diff / (T::one() - ( lightness * flt(2.0) - T::one()).abs()) + }; + + Hsl { + hue: hue.into(), + saturation: saturation, + lightness: lightness, + } + } + + fn from_hsl(hsl: Hsl) -> Self { + hsl + } + + fn from_hsv(hsv: Hsv) -> Self { + let x = (flt::(2.0) - hsv.saturation) * hsv.value; + let saturation = if hsv.value == T::zero() { + T::zero() + } else if x < T::one() { + hsv.saturation * hsv.value / x + } else { + hsv.saturation * hsv.value / (flt::(2.0) - x) + }; + + Hsl { + hue: hsv.hue, + saturation: saturation, + lightness: x / flt(2.0), + } + } + +} + impl Limited for Hsl { fn is_valid(&self) -> bool { self.saturation >= T::zero() && self.saturation <= T::one() && @@ -195,104 +267,6 @@ impl Sub for Hsl { } } -from_color!(to Hsl from Rgb, Luma, Xyz, Yxy, Lab, Lch, Hsv); - -alpha_from!(Hsl {Rgb, Xyz, Yxy, Luma, Lab, Lch, Hsv, Color}); - -impl From> for Hsl { - fn from(rgb: Rgb) -> Hsl { - enum Channel { Red, Green, Blue }; - - let val_min = rgb.red.min(rgb.green).min(rgb.blue); - let mut val_max = rgb.red; - let mut chan_max = Channel::Red; - - if rgb.green > val_max { - chan_max = Channel::Green; - val_max = rgb.green; - } - - if rgb.blue > val_max { - chan_max = Channel::Blue; - val_max = rgb.blue; - } - - let diff = val_max - val_min; - let lightness = (val_min + val_max) / flt(2.0); - - let hue = if diff == T::zero() { - T::zero() - } else { - flt::(60.0) * match chan_max { - Channel::Red => ((rgb.green - rgb.blue) / diff) % flt(6.0), - Channel::Green => ((rgb.blue - rgb.red) / diff + flt(2.0)), - Channel::Blue => ((rgb.red - rgb.green) / diff + flt(4.0)), - } - }; - - let saturation = if diff == T::zero() { - T::zero() - } else { - diff / (T::one() - ( lightness * flt(2.0) - T::one()).abs()) - }; - - Hsl { - hue: hue.into(), - saturation: saturation, - lightness: lightness, - } - } -} - -impl From> for Hsl { - fn from(luma: Luma) -> Hsl { - Rgb::from(luma).into() - } -} - -impl From> for Hsl { - fn from(xyz: Xyz) -> Hsl { - Rgb::from(xyz).into() - } -} - -impl From> for Hsl { - fn from(yxy: Yxy) -> Hsl { - Rgb::from(yxy).into() - } -} - -impl From> for Hsl { - fn from(lab: Lab) -> Hsl { - Rgb::from(lab).into() - } -} - -impl From> for Hsl { - fn from(lch: Lch) -> Hsl { - Rgb::from(lch).into() - } -} - -impl From> for Hsl { - fn from(hsv: Hsv) -> Hsl { - let x = (flt::(2.0) - hsv.saturation) * hsv.value; - let saturation = if hsv.value == T::zero() { - T::zero() - } else if x < T::one() { - hsv.saturation * hsv.value / x - } else { - hsv.saturation * hsv.value / (flt::(2.0) - x) - }; - - Hsl { - hue: hsv.hue, - saturation: saturation, - lightness: x / flt(2.0), - } - } -} - #[cfg(test)] mod test { use super::Hsl; diff --git a/src/hsv.rs b/src/hsv.rs index e8e2d4c07..d21b21afb 100644 --- a/src/hsv.rs +++ b/src/hsv.rs @@ -2,7 +2,7 @@ use num::Float; use std::ops::{Add, Sub}; -use {Color, Alpha, Rgb, Luma, Xyz, Yxy, Lab, Lch, Hsl, Limited, Mix, Shade, GetHue, Hue, Saturate, RgbHue, clamp, flt}; +use {Alpha, Rgb, Xyz, Hsl, Limited, Mix, Shade, GetHue, Hue, Saturate, RgbHue, FromColor, clamp, flt}; ///Linear HSV with an alpha component. See the [`Hsva` implementation in `Alpha`](struct.Alpha.html#Hsva). pub type Hsva = Alpha, T>; @@ -53,6 +53,74 @@ impl Alpha, T> { } } +impl FromColor for Hsv { + fn from_xyz(xyz: Xyz) -> Self { + let rgb: Rgb = Rgb::from_xyz(xyz); + Self::from_rgb(rgb) + } + + fn from_rgb(rgb: Rgb) -> Self { + enum Channel { Red, Green, Blue }; + + let val_min = rgb.red.min(rgb.green).min(rgb.blue); + let mut val_max = rgb.red; + let mut chan_max = Channel::Red; + + if rgb.green > val_max { + chan_max = Channel::Green; + val_max = rgb.green; + } + + if rgb.blue > val_max { + chan_max = Channel::Blue; + val_max = rgb.blue; + } + + let diff = val_max - val_min; + + let hue = if diff == T::zero() { + T::zero() + } else { + flt::(60.0) * match chan_max { + Channel::Red => ((rgb.green - rgb.blue) / diff) % flt(6.0), + Channel::Green => ((rgb.blue - rgb.red) / diff + flt(2.0)), + Channel::Blue => ((rgb.red - rgb.green) / diff + flt(4.0)), + } + }; + + let saturation = if val_max == T::zero() { + T::zero() + } else { + diff / val_max + }; + + Hsv { + hue: hue.into(), + saturation: saturation, + value: val_max, + } + } + + fn from_hsl(hsl: Hsl) -> Self { + let x = hsl.saturation * if hsl.lightness < flt(0.5) { + hsl.lightness + } else { + T::one() - hsl.lightness + }; + + Hsv { + hue: hsl.hue, + saturation: x * flt(2.0) / (hsl.lightness + x), + value: hsl.lightness + x, + } + } + + fn from_hsv(hsv: Hsv) -> Self { + hsv + } + +} + impl Limited for Hsv { fn is_valid(&self) -> bool { self.saturation >= T::zero() && self.saturation <= T::one() && @@ -194,101 +262,6 @@ impl Sub for Hsv { } } -from_color!(to Hsv from Rgb, Luma, Xyz, Yxy, Lab, Lch, Hsl); - -alpha_from!(Hsv {Rgb, Xyz, Yxy, Luma, Lab, Lch, Hsl, Color}); - - -impl From> for Hsv { - fn from(rgb: Rgb) -> Hsv { - enum Channel { Red, Green, Blue }; - - let val_min = rgb.red.min(rgb.green).min(rgb.blue); - let mut val_max = rgb.red; - let mut chan_max = Channel::Red; - - if rgb.green > val_max { - chan_max = Channel::Green; - val_max = rgb.green; - } - - if rgb.blue > val_max { - chan_max = Channel::Blue; - val_max = rgb.blue; - } - - let diff = val_max - val_min; - - let hue = if diff == T::zero() { - T::zero() - } else { - flt::(60.0) * match chan_max { - Channel::Red => ((rgb.green - rgb.blue) / diff) % flt(6.0), - Channel::Green => ((rgb.blue - rgb.red) / diff + flt(2.0)), - Channel::Blue => ((rgb.red - rgb.green) / diff + flt(4.0)), - } - }; - - let saturation = if val_max == T::zero() { - T::zero() - } else { - diff / val_max - }; - - Hsv { - hue: hue.into(), - saturation: saturation, - value: val_max, - } - } -} - -impl From> for Hsv { - fn from(luma: Luma) -> Hsv { - Rgb::from(luma).into() - } -} - -impl From> for Hsv { - fn from(xyz: Xyz) -> Hsv { - Rgb::from(xyz).into() - } -} - -impl From> for Hsv { - fn from(yxy: Yxy) -> Hsv { - Rgb::from(yxy).into() - } -} - -impl From> for Hsv { - fn from(lab: Lab) -> Hsv { - Rgb::from(lab).into() - } -} - -impl From> for Hsv { - fn from(lch: Lch) -> Hsv { - Rgb::from(lch).into() - } -} - -impl From> for Hsv { - fn from(hsl: Hsl) -> Hsv { - let x = hsl.saturation * if hsl.lightness < flt(0.5) { - hsl.lightness - } else { - T::one() - hsl.lightness - }; - - Hsv { - hue: hsl.hue, - saturation: x * flt(2.0) / (hsl.lightness + x), - value: hsl.lightness + x, - } - } -} - #[cfg(test)] mod test { use super::Hsv; diff --git a/src/lab.rs b/src/lab.rs index e9324a057..3b515c1ba 100644 --- a/src/lab.rs +++ b/src/lab.rs @@ -2,8 +2,7 @@ use num::Float; use std::ops::{Add, Sub, Mul, Div}; -use {Color, Alpha, Rgb, Luma, Xyz, Yxy, Lch, Hsv, Hsl, Limited, Mix, Shade, GetHue, LabHue, clamp, flt}; - +use {Alpha, Xyz, Limited, Mix, Shade, GetHue, LabHue, FromColor, Lch, clamp, flt}; use tristimulus::{X_N, Y_N, Z_N}; ///CIE L*a*b* (CIELAB) with an alpha component. See the [`Laba` implementation in `Alpha`](struct.Alpha.html#Laba). @@ -55,6 +54,30 @@ impl Alpha, T> { } } +impl FromColor for Lab { + fn from_xyz(xyz: Xyz) -> Self { + Lab { + l: (f(xyz.y / flt(Y_N)) * flt(116.0) - flt(16.0)) / flt(100.0), + a: (f(xyz.x / flt(X_N)) - f(xyz.y / flt(Y_N))) * flt(500.0) / flt(128.0), + b: (f(xyz.y / flt(Y_N)) - f(xyz.z / flt(Z_N))) * flt(200.0) / flt(128.0), + } + } + + fn from_lab(lab: Lab) -> Self { + lab + } + + fn from_lch(lch: Lch) -> Self { + 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(), + } + } + + +} + impl Limited for Lab { fn is_valid(&self) -> bool { self.l >= T::zero() && self.l <= T::one() && @@ -215,60 +238,6 @@ impl Div for Lab { } } -from_color!(to Lab from Rgb, Luma, Xyz, Yxy, Lch, Hsv, Hsl); - -alpha_from!(Lab {Rgb, Xyz, Yxy, Luma, Lch, Hsv, Hsl, Color}); - -impl From> for Lab { - fn from(xyz: Xyz) -> Lab { - Lab { - l: (f(xyz.y / flt(Y_N)) * flt(116.0) - flt(16.0)) / flt(100.0), - a: (f(xyz.x / flt(X_N)) - f(xyz.y / flt(Y_N))) * flt(500.0) / flt(128.0), - b: (f(xyz.y / flt(Y_N)) - f(xyz.z / flt(Z_N))) * flt(200.0) / flt(128.0), - } - } -} - -impl From> for Lab { - fn from(yxy: Yxy) -> Lab { - Xyz::from(yxy).into() - } -} - -impl From> for Lab { - fn from(rgb: Rgb) -> Lab { - Xyz::from(rgb).into() - } -} - -impl From> for Lab { - fn from(luma: Luma) -> Lab { - Xyz::from(luma).into() - } -} - -impl From> for Lab { - fn from(lch: Lch) -> Lab { - 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(), - } - } -} - -impl From> for Lab { - fn from(hsv: Hsv) -> Lab { - Xyz::from(hsv).into() - } -} - -impl From> for Lab { - fn from(hsl: Hsl) -> Lab { - Xyz::from(hsl).into() - } -} - fn f(t: T) -> T { //(6/29)^3 let c_6_o_29_p_3: T = flt(0.00885645167); diff --git a/src/lch.rs b/src/lch.rs index 40adc4225..b032da26f 100644 --- a/src/lch.rs +++ b/src/lch.rs @@ -2,7 +2,7 @@ use num::Float; use std::ops::{Add, Sub}; -use {Color, Alpha, Limited, Mix, Shade, GetHue, Hue, Rgb, Luma, Xyz, Yxy, Lab, Hsv, Hsl, Saturate, LabHue, clamp}; +use {Alpha, Limited, Mix, Shade, GetHue, FromColor, IntoColor, Hue, Xyz, Lab, Saturate, LabHue, clamp}; ///CIE L*C*h° with an alpha component. See the [`Lcha` implementation in `Alpha`](struct.Alpha.html#Lcha). pub type Lcha = Alpha, T>; @@ -52,6 +52,26 @@ impl Alpha, T> { } } +impl FromColor for Lch { + fn from_xyz(xyz: Xyz) -> Self { + let lab: Lab = xyz.into_lab(); + Self::from_lab(lab) + } + + fn from_lab(lab: Lab) -> Self { + Lch { + l: lab.l, + chroma: (lab.a * lab.a + lab.b * lab.b).sqrt(), + hue: lab.get_hue().unwrap_or(LabHue::from(T::zero())), + } + } + + fn from_lch(lch: Lch) -> Self { + lch + } + +} + impl Limited for Lch { fn is_valid(&self) -> bool { self.l >= T::zero() && self.l <= T::one() && @@ -192,56 +212,6 @@ impl Sub for Lch { } } -from_color!(to Lch from Rgb, Luma, Xyz, Yxy, Lab, Hsv, Hsl); - -alpha_from!(Lch {Rgb, Xyz, Yxy, 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())), - } - } -} - -impl From> for Lch { - fn from(rgb: Rgb) -> Lch { - Lab::from(rgb).into() - } -} - -impl From> for Lch { - fn from(luma: Luma) -> Lch { - Lab::from(luma).into() - } -} - -impl From> for Lch { - fn from(xyz: Xyz) -> Lch { - Lab::from(xyz).into() - } -} - -impl From> for Lch { - fn from(yxy: Yxy) -> Lch { - Lab::from(yxy).into() - } -} - -impl From> for Lch { - fn from(hsv: Hsv) -> Lch { - Lab::from(hsv).into() - } -} - -impl From> for Lch { - fn from(hsl: Hsl) -> Lch { - Lab::from(hsl).into() - } -} - #[cfg(test)] mod test { use Lch; diff --git a/src/lib.rs b/src/lib.rs index 9340240c6..e774375e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,55 +68,7 @@ pub use hsl::{Hsl, Hsla}; pub use yxy::{Yxy, Yxya}; pub use hues::{LabHue, RgbHue}; - -macro_rules! from_color { - (to $to:ident from $($from:ident),+) => ( - impl From> for $to { - fn from(color: Color) -> $to { - match color { - Color::$to(c) => c, - $(Color::$from(c) => c.into(),)+ - } - } - } - ) -} - -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() - } - } - )+ - ) -} +pub use convert::{FromColor, IntoColor}; //Helper macro for approximate component wise comparison. Most color spaces //are in roughly the same ranges, so this epsilon should be alright. @@ -296,6 +248,7 @@ mod hsl; mod hues; mod tristimulus; +mod convert; macro_rules! make_color { ($( @@ -401,6 +354,21 @@ macro_rules! make_color { } } + impl From, T>> for Color { + fn from(color: Alpha<$variant,T>) -> Color { + Color::$variant(color.color) + } + } + + impl From, T>> for Alpha,T> { + fn from(color: Alpha<$variant,T>) -> Alpha,T> { + Alpha { + color: Color::$variant(color.color), + alpha: color.alpha, + } + } + } + $($( impl From<$representations> for Color { fn from(color: $representations) -> Color { @@ -409,9 +377,6 @@ macro_rules! make_color { } )+)* )+ - - alpha_from!(Color {$($variant),+}); - ) } diff --git a/src/luma.rs b/src/luma.rs index 67042a269..55bde3bdb 100644 --- a/src/luma.rs +++ b/src/luma.rs @@ -1,8 +1,9 @@ use num::Float; use std::ops::{Add, Sub, Mul, Div}; +use std::default::Default; -use {Color, Alpha, Rgb, Xyz, Yxy, Lab, Lch, Hsv, Hsl, Limited, Mix, Shade, clamp, flt}; +use {Alpha, Rgb, Xyz, Yxy, Limited, Mix, Shade, FromColor, clamp, flt}; ///Linear luminance with an alpha component. See the [`Lumaa` implementation in `Alpha`](struct.Alpha.html#Lumaa). pub type Lumaa = Alpha, T>; @@ -55,6 +56,31 @@ impl Alpha, T> { } } +impl FromColor for Luma { + fn from_xyz(xyz: Xyz) -> Self { + Luma { + luma: xyz.y, + } + } + + fn from_yxy(yxy: Yxy) -> Self { + Luma { + luma: yxy.luma, + } + } + + fn from_rgb(rgb: Rgb) -> Self { + Luma { + luma: rgb.red * flt(0.2126) + rgb.green * flt(0.7152) + rgb.blue * flt(0.0722), + } + } + + fn from_luma(luma: Luma) -> Self { + luma + } + +} + impl Limited for Luma { fn is_valid(&self) -> bool { self.luma >= T::zero() && self.luma <= T::one() @@ -179,58 +205,6 @@ impl Div for Luma { } } -from_color!(to Luma from Rgb, Xyz, Yxy, Lab, Lch, Hsv, Hsl); - -alpha_from!(Luma {Rgb, Xyz, Yxy, Lab, Lch, Hsv, Hsl, Color}); - -impl From> for Luma { - fn from(rgb: Rgb) -> Luma { - Luma { - luma: rgb.red * flt(0.2126) + rgb.green * flt(0.7152) + rgb.blue * flt(0.0722), - } - } -} - -impl From> for Luma { - fn from(xyz: Xyz) -> Luma { - Luma { - luma: xyz.y, - } - } -} - -impl From> for Luma { - fn from(yxy: Yxy) -> Luma { - Luma { - luma: yxy.luma, - } - } -} - -impl From> for Luma { - fn from(lab: Lab) -> Luma { - Xyz::from(lab).into() - } -} - -impl From> for Luma { - fn from(lch: Lch) -> Luma { - Xyz::from(lch).into() - } -} - -impl From> for Luma { - fn from(hsv: Hsv) -> Luma { - Rgb::from(hsv).into() - } -} - -impl From> for Luma { - fn from(hsl: Hsl) -> Luma { - Rgb::from(hsl).into() - } -} - #[cfg(test)] mod test { use Luma; diff --git a/src/rgb.rs b/src/rgb.rs index 83ebb97d7..7cdfeb8d8 100644 --- a/src/rgb.rs +++ b/src/rgb.rs @@ -2,7 +2,7 @@ use num::Float; use std::ops::{Add, Sub, Mul, Div}; -use {Color, Alpha, Luma, Xyz, Yxy, Lab, Lch, Hsv, Hsl, Limited, Mix, Shade, GetHue, RgbHue, clamp, flt}; +use {Alpha, Xyz, Luma, Hsl, Hsv, Limited, Mix, Shade, GetHue, RgbHue, FromColor, clamp, flt}; use pixel::{RgbPixel, Srgb, GammaRgb}; ///Linear RGB with an alpha component. See the [`Rgba` implementation in `Alpha`](struct.Alpha.html#Rgba). @@ -124,6 +124,87 @@ impl Alpha, T> { } } +impl FromColor for Rgb { + fn from_xyz(xyz: Xyz) -> Self { + Rgb { + red: xyz.x * flt(3.2406) + xyz.y * flt(-1.5372) + xyz.z * flt(-0.4986), + green: xyz.x * flt(-0.9689) + xyz.y * flt(1.8758) + xyz.z * flt(0.0415), + blue: xyz.x * flt(0.0557) + xyz.y * flt(-0.2040) + xyz.z * flt(1.0570), + } + } + + + fn from_rgb(rgb: Rgb) -> Self { + rgb + } + + fn from_hsl(hsl: Hsl) -> Self { + let c = (T::one() - ( hsl.lightness * flt(2.0) - T::one()).abs()) * hsl.saturation; + let h = hsl.hue.to_positive_degrees() / flt(60.0); + let x = c * (T::one() - (h % flt(2.0) - T::one()).abs()); + let m = hsl.lightness - c * flt(0.5); + + let (red, green, blue) = if h >= T::zero() && h < T::one() { + (c, x, T::zero()) + } else if h >= T::one() && h < flt(2.0) { + (x, c, T::zero()) + } else if h >= flt(2.0) && h < flt(3.0) { + (T::zero(), c, x) + } else if h >= flt(3.0) && h < flt(4.0) { + (T::zero(), x, c) + } else if h >= flt(4.0) && h < flt(5.0) { + (x, T::zero(), c) + } else { + (c, T::zero(), x) + }; + + + Rgb { + red: red + m, + green: green + m, + blue: blue + m, + } + } + + fn from_hsv(hsv: Hsv) -> Self { + let c = hsv.value * hsv.saturation; + let h = hsv.hue.to_positive_degrees() / flt(60.0); + let x = c * (T::one() - (h % flt(2.0) - T::one()).abs()); + let m = hsv.value - c; + + let (red, green, blue) = if h >= T::zero() && h < T::one() { + (c, x, T::zero()) + } else if h >= T::one() && h < flt(2.0) { + (x, c, T::zero()) + } else if h >= flt(2.0) && h < flt(3.0) { + (T::zero(), c, x) + } else if h >= flt(3.0) && h < flt(4.0) { + (T::zero(), x, c) + } else if h >= flt(4.0) && h < flt(5.0) { + (x, T::zero(), c) + } else { + (c, T::zero(), x) + }; + + + Rgb { + red: red + m, + green: green + m, + blue: blue + m, + } + + } + + fn from_luma(luma: Luma) -> Self { + Rgb { + red: luma.luma, + green: luma.luma, + blue: luma.luma, + } + } + +} + impl Limited for Rgb { fn is_valid(&self) -> bool { self.red >= T::zero() && self.red <= T::one() && @@ -286,108 +367,6 @@ impl Div for Rgb { } } -from_color!(to Rgb from Xyz, Yxy, Luma, Lab, Lch, Hsv, Hsl); - -alpha_from!(Rgb {Xyz, Yxy, 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, - } - } -} - -impl From> for Rgb { - fn from(xyz: Xyz) -> Rgb { - Rgb { - red: xyz.x * flt(3.2406) + xyz.y * flt(-1.5372) + xyz.z * flt(-0.4986), - green: xyz.x * flt(-0.9689) + xyz.y * flt(1.8758) + xyz.z * flt(0.0415), - blue: xyz.x * flt(0.0557) + xyz.y * flt(-0.2040) + xyz.z * flt(1.0570), - } - } -} - -impl From> for Rgb { - fn from(yxy: Yxy) -> Rgb { - Xyz::from(yxy).into() - } -} - -impl From> for Rgb { - fn from(lab: Lab) -> Rgb { - Xyz::from(lab).into() - } -} - -impl From> for Rgb { - fn from(lch: Lch) -> Rgb { - Lab::from(lch).into() - } -} - -impl From> for Rgb { - fn from(hsv: Hsv) -> Rgb { - let c = hsv.value * hsv.saturation; - let h = hsv.hue.to_positive_degrees() / flt(60.0); - let x = c * (T::one() - (h % flt(2.0) - T::one()).abs()); - let m = hsv.value - c; - - let (red, green, blue) = if h >= T::zero() && h < T::one() { - (c, x, T::zero()) - } else if h >= T::one() && h < flt(2.0) { - (x, c, T::zero()) - } else if h >= flt(2.0) && h < flt(3.0) { - (T::zero(), c, x) - } else if h >= flt(3.0) && h < flt(4.0) { - (T::zero(), x, c) - } else if h >= flt(4.0) && h < flt(5.0) { - (x, T::zero(), c) - } else { - (c, T::zero(), x) - }; - - - Rgb { - red: red + m, - green: green + m, - blue: blue + m, - } - } -} - -impl From> for Rgb { - fn from(hsl: Hsl) -> Rgb { - let c = (T::one() - ( hsl.lightness * flt(2.0) - T::one()).abs()) * hsl.saturation; - let h = hsl.hue.to_positive_degrees() / flt(60.0); - let x = c * (T::one() - (h % flt(2.0) - T::one()).abs()); - let m = hsl.lightness - c * flt(0.5); - - let (red, green, blue) = if h >= T::zero() && h < T::one() { - (c, x, T::zero()) - } else if h >= T::one() && h < flt(2.0) { - (x, c, T::zero()) - } else if h >= flt(2.0) && h < flt(3.0) { - (T::zero(), c, x) - } else if h >= flt(3.0) && h < flt(4.0) { - (T::zero(), x, c) - } else if h >= flt(4.0) && h < flt(5.0) { - (x, T::zero(), c) - } else { - (c, T::zero(), x) - }; - - - Rgb { - red: red + m, - green: green + m, - blue: blue + m, - } - } -} - impl From> for Rgb { fn from(srgb: Srgb) -> Rgb { srgb.to_linear().into() diff --git a/src/xyz.rs b/src/xyz.rs index 245026879..2d937fe50 100644 --- a/src/xyz.rs +++ b/src/xyz.rs @@ -2,8 +2,7 @@ use num::Float; use std::ops::{Add, Sub, Mul, Div}; -use {Color, Alpha, Yxy, Rgb, Luma, Lab, Lch, Hsv, Hsl, Limited, Mix, Shade, clamp, flt}; - +use {Alpha, Limited, Mix, Shade, FromColor, Rgb, Lab, Yxy, Luma, clamp, flt}; use tristimulus::{X_N, Y_N, Z_N}; ///CIE 1931 XYZ with an alpha component. See the [`Xyza` implementation in `Alpha`](struct.Alpha.html#Xyza). @@ -55,6 +54,48 @@ impl Alpha, T> { } } +impl FromColor for Xyz { + fn from_xyz(xyz: Xyz) -> Self { + xyz + } + + fn from_rgb(rgb: Rgb) -> Self { + Xyz { + x: rgb.red * flt(0.4124) + rgb.green * flt(0.3576) + rgb.blue * flt(0.1805), + y: rgb.red * flt(0.2126) + rgb.green * flt(0.7152) + rgb.blue * flt(0.0722), + z: rgb.red * flt(0.0193) + rgb.green * flt(0.1192) + rgb.blue * flt(0.9505), + } + } + + fn from_yxy(yxy: Yxy) -> Self { + let mut xyz = Xyz { y: yxy.luma, ..Default::default() }; + // If denominator is zero, NAN or INFINITE leave x and z at the default 0 + if yxy.y.is_normal() { + xyz.x = yxy.luma * yxy.x / yxy.y; + xyz.z = yxy.luma * ( T::one() - yxy.x - yxy.y ) / yxy.y; + } + xyz + } + + fn from_lab(lab: Lab) -> Self { + Xyz { + x: flt::(X_N) * f_inv((T::one() / flt(116.0)) * (lab.l * flt(100.0) + flt(16.0)) + + (T::one() / flt(500.0)) * lab.a * flt(128.0)), + y: flt::(Y_N) * f_inv((T::one() / flt(116.0)) * (lab.l * flt(100.0) + flt(16.0))), + z: flt::(Z_N) * f_inv((T::one() / flt(116.0)) * (lab.l * flt(100.0) + flt(16.0)) - + (T::one() / flt(200.0)) * lab.b * flt(128.0)), + } + } + fn from_luma(luma: Luma) -> Self { + Xyz { + x: luma.luma * flt(X_N), + y: luma.luma, + z: luma.luma * flt(Z_N), + } + } + +} + impl Limited for Xyz { fn is_valid(&self) -> bool { self.x >= T::zero() && self.x <= flt(X_N) && @@ -203,74 +244,6 @@ impl Div for Xyz { } } -from_color!(to Xyz from Yxy, Rgb, Luma, Lab, Lch, Hsv, Hsl); - -alpha_from!(Xyz {Yxy, Rgb, Luma, Lab, Lch, Hsv, Hsl, Color}); - -impl From> for Xyz { - fn from(yxy: Yxy) -> Xyz { - let mut xyz = Xyz::new(T::zero(), yxy.luma, T::zero()); - // If denominator is zero, NAN or INFINITE leave x and z at the default 0 - if yxy.y.is_normal() { - xyz.x = yxy.luma * yxy.x / yxy.y; - xyz.z = yxy.luma * ( T::one() - yxy.x - yxy.y ) / yxy.y; - } - xyz - } -} - -impl From> for Xyz { - fn from(rgb: Rgb) -> Xyz { - Xyz { - x: rgb.red * flt(0.4124) + rgb.green * flt(0.3576) + rgb.blue * flt(0.1805), - y: rgb.red * flt(0.2126) + rgb.green * flt(0.7152) + rgb.blue * flt(0.0722), - z: rgb.red * flt(0.0193) + rgb.green * flt(0.1192) + rgb.blue * flt(0.9505), - } - } -} - -impl From> for Xyz { - fn from(luma: Luma) -> Xyz { - // Use the D65 white point Xyz values for x and z as D65 is used as the default and scale by luma - Xyz { - x: luma.luma * flt(X_N), - y: luma.luma, - z: luma.luma * flt(Z_N), - } - } -} - -impl From> for Xyz { - fn from(lab: Lab) -> Xyz { - Xyz { - x: flt::(X_N) * f_inv((T::one() / flt(116.0)) * (lab.l * flt(100.0) + flt(16.0)) + - (T::one() / flt(500.0)) * lab.a * flt(128.0)), - y: flt::(Y_N) * f_inv((T::one() / flt(116.0)) * (lab.l * flt(100.0) + flt(16.0))), - z: flt::(Z_N) * f_inv((T::one() / flt(116.0)) * (lab.l * flt(100.0) + flt(16.0)) - - (T::one() / flt(200.0)) * lab.b * flt(128.0)), - } - } -} - -impl From> for Xyz { - fn from(lch: Lch) -> Xyz { - Lab::from(lch).into() - } -} - -impl From> for Xyz { - fn from(hsv: Hsv) -> Xyz { - Rgb::from(hsv).into() - } -} - -impl From> for Xyz { - fn from(hsl: Hsl) -> Xyz { - Rgb::from(hsl).into() - } -} - - fn f_inv(t: T) -> T { //(6/29)^2 let c_6_o_29_p_2: T = flt(0.04280618311); diff --git a/src/yxy.rs b/src/yxy.rs index 69f82998e..d9c69e4e5 100644 --- a/src/yxy.rs +++ b/src/yxy.rs @@ -2,7 +2,7 @@ use num::Float; use std::ops::{Add, Sub, Mul, Div}; -use {Color, Alpha, Rgb, Luma, Lab, Lch, Hsv, Hsl, Limited, Mix, Shade, clamp, flt, Xyz}; +use {Alpha, Xyz, Luma, Limited, Mix, Shade, FromColor, clamp, flt}; const D65_X: f64 = 0.312727; const D65_Y: f64 = 0.329023; @@ -58,6 +58,29 @@ impl Alpha, T> { } } +impl FromColor for Yxy { + fn from_xyz(xyz: Xyz) -> Self { + let mut yxy = Yxy{ luma: xyz.y, ..Default::default() }; + let sum = xyz.x + xyz.y + xyz.z; + // If denominator is zero, NAN or INFINITE leave x and y at the default 0 + if sum.is_normal() { + yxy.x = xyz.x / sum; + yxy.y = xyz.y / sum; + } + yxy + } + + fn from_yxy(yxy: Yxy) -> Self { + yxy + } + + // direct conversion implemented in Luma + fn from_luma(luma: Luma) -> Self { + Yxy { luma: luma.luma, ..Default::default() } + } + +} + impl Limited for Yxy { fn is_valid(&self) -> bool { self.x >= T::zero() && self.x <= T::one() && @@ -210,66 +233,6 @@ impl Div for Yxy { } } -from_color!(to Yxy from Xyz, Rgb, Luma, Lab, Lch, Hsv, Hsl); - -alpha_from!(Yxy {Xyz, Rgb, Luma, Lab, Lch, Hsv, Hsl, Color}); - -impl From> for Yxy { - fn from(xyz: Xyz) -> Yxy { - let mut yxy = Yxy::new(T::zero(), T::zero(), xyz.y); - let sum = xyz.x + xyz.y + xyz.z; - // If denominator is zero, NAN or INFINITE leave x and y at the default 0 - if sum.is_normal() { - yxy.x = xyz.x / sum; - yxy.y = xyz.y / sum; - } - yxy - } -} - -impl From> for Yxy { - fn from(luma: Luma) -> Yxy { - // Use the D65 white point Yxy values for x and y as D65 is used as the default - Yxy { - x: flt(D65_X), - y: flt(D65_Y), - luma: luma.luma, - } - } -} - -impl From> for Yxy { - fn from(lab: Lab) -> Yxy { - Xyz::from(lab).into() - } -} - -impl From> for Yxy { - fn from(lch: Lch) -> Yxy { - Lab::from(lch).into() - } -} - -impl From> for Yxy { - fn from(rgb: Rgb) -> Yxy { - Xyz::from(rgb).into() - } -} - - -impl From> for Yxy { - fn from(hsv: Hsv) -> Yxy { - Rgb::from(hsv).into() - } -} - -impl From> for Yxy { - fn from(hsl: Hsl) -> Yxy { - Rgb::from(hsl).into() - } -} - - #[cfg(test)] mod test { use super::Yxy;