From 38a06a4c48bb7f7214ba7290c65aee640e7808b0 Mon Sep 17 00:00:00 2001 From: Erik Hedvall Date: Thu, 21 Jan 2016 21:46:16 +0100 Subject: [PATCH] WIP --- README.md | 8 +- examples/color_scheme.rs | 2 +- examples/gradient.rs | 24 ++-- examples/readme_examples.rs | 8 +- examples/shade.rs | 2 +- src/alpha.rs | 185 ++++++++++++++++++++++++++ src/gradient.rs | 2 +- src/hsl.rs | 44 ++---- src/hsv.rs | 44 ++---- src/lab.rs | 44 ++---- src/lch.rs | 33 ++--- src/lib.rs | 106 +++++++++------ src/luma.rs | 40 ++---- src/rgb.rs | 257 +++++++++++++++++++----------------- src/xyz.rs | 45 ++----- 15 files changed, 487 insertions(+), 357 deletions(-) create mode 100644 src/alpha.rs diff --git a/README.md b/README.md index 6fa0abbc5..babf462b2 100644 --- a/README.md +++ b/README.md @@ -108,13 +108,13 @@ extern crate palette; use palette::{Rgb, Hsv, Gradient}; let grad1 = Gradient::new(vec![ - Rgb::linear_rgb(1.0, 0.1, 0.1), - Rgb::linear_rgb(0.1, 1.0, 1.0) + Rgb::linear(1.0, 0.1, 0.1), + Rgb::linear(0.1, 1.0, 1.0) ]); let grad2 = Gradient::new(vec![ - Hsv::from(Rgb::linear_rgb(1.0, 0.1, 0.1)), - Hsv::from(Rgb::linear_rgb(0.1, 1.0, 1.0)) + Hsv::from(Rgb::linear(1.0, 0.1, 0.1)), + Hsv::from(Rgb::linear(0.1, 1.0, 1.0)) ]); ``` diff --git a/examples/color_scheme.rs b/examples/color_scheme.rs index baddbb993..64b2f042c 100644 --- a/examples/color_scheme.rs +++ b/examples/color_scheme.rs @@ -84,7 +84,7 @@ fn main() { .and_then(|r| r.parse().ok()) .expect("the blue channel must be a number in the range [0-255]"); - let primary = Color::srgb8(red, green, blue); + let primary = Color::srgb_u8(red, green, blue); //Generate the secondary colors, depending on the input arguments let secondary = match matches.subcommand() { diff --git a/examples/gradient.rs b/examples/gradient.rs index 47be97c9c..56d24f249 100644 --- a/examples/gradient.rs +++ b/examples/gradient.rs @@ -8,30 +8,30 @@ use image::{RgbImage, GenericImage}; fn main() { //A gradient of evenly spaced colors let grad1 = Gradient::new(vec![ - Rgb::linear_rgb(1.0, 0.1, 0.1), - Rgb::linear_rgb(0.1, 0.1, 1.0), - Rgb::linear_rgb(0.1, 1.0, 0.1) + Rgb::linear(1.0, 0.1, 0.1), + Rgb::linear(0.1, 0.1, 1.0), + Rgb::linear(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::linear_rgb(1.0, 0.1, 0.1)), - (0.25, Rgb::linear_rgb(0.1, 0.1, 1.0)), - (1.0, Rgb::linear_rgb(0.1, 1.0, 0.1)) + (0.0, Rgb::linear(1.0, 0.1, 0.1)), + (0.25, Rgb::linear(0.1, 0.1, 1.0)), + (1.0, Rgb::linear(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::linear_rgb(1.0, 0.1, 0.1)), - Lch::from(Rgb::linear_rgb(0.1, 0.1, 1.0)), - Lch::from(Rgb::linear_rgb(0.1, 1.0, 0.1)) + Lch::from(Rgb::linear(1.0, 0.1, 0.1)), + Lch::from(Rgb::linear(0.1, 0.1, 1.0)), + Lch::from(Rgb::linear(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::linear_rgb(1.0, 0.1, 0.1))), - (0.25, Lch::from(Rgb::linear_rgb(0.1, 0.1, 1.0))), - (1.0, Lch::from(Rgb::linear_rgb(0.1, 1.0, 0.1))) + (0.0, Lch::from(Rgb::linear(1.0, 0.1, 0.1))), + (0.25, Lch::from(Rgb::linear(0.1, 0.1, 1.0))), + (1.0, Lch::from(Rgb::linear(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 d44148eed..e3a9cd310 100644 --- a/examples/readme_examples.rs +++ b/examples/readme_examples.rs @@ -42,13 +42,13 @@ mod gradients { pub fn run() { let grad1 = Gradient::new(vec![ - Rgb::linear_rgb(1.0, 0.1, 0.1), - Rgb::linear_rgb(0.1, 1.0, 1.0) + Rgb::linear(1.0, 0.1, 0.1), + Rgb::linear(0.1, 1.0, 1.0) ]); let grad2 = Gradient::new(vec![ - Hsv::from(Rgb::linear_rgb(1.0, 0.1, 0.1)), - Hsv::from(Rgb::linear_rgb(0.1, 1.0, 1.0)) + Hsv::from(Rgb::linear(1.0, 0.1, 0.1)), + Hsv::from(Rgb::linear(0.1, 1.0, 1.0)) ]); display_gradients("examples/readme_gradients.png", grad1, grad2); diff --git a/examples/shade.rs b/examples/shade.rs index fc7d3332b..57613886e 100644 --- a/examples/shade.rs +++ b/examples/shade.rs @@ -7,7 +7,7 @@ use image::{RgbImage, GenericImage}; fn main() { //The same color in linear RGB, CIE L*a*b*, and HSV - let rgb = Rgb::linear_rgb(0.5, 0.0, 0.0); + let rgb = Rgb::linear(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..9c3af13a7 --- /dev/null +++ b/src/alpha.rs @@ -0,0 +1,185 @@ +use std::ops::{Deref, DerefMut, Add, Sub, Mul, Div}; + +use num::Float; + +use {Mix, Shade, GetHue, Hue, Saturate}; + +///An alpha channel wrapper for colors. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Alpha { + ///Any color. + pub color: C, + ///The bolted on alpha channel. + 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, T: Float> Mix for Alpha { + fn mix(&self, other: &Alpha, factor: T) -> Alpha { + Alpha { + color: self.color.mix(&other.color, factor), + alpha: self.alpha + factor * (other.alpha - self.alpha), + } + } +} + +impl, T: Float> Shade for Alpha { + fn lighten(&self, amount: T) -> 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, T: Float> Saturate for Alpha { + fn saturate(&self, factor: T) -> 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 b2bbf4208..9bb215ee4 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::linear_rgb(1.0, 0.0, 0.0), Rgb::linear_rgb(0.0, 0.0, 1.0)]); + let g1 = Gradient::new(vec![Rgb::linear(1.0, 0.0, 0.0), Rgb::linear(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 72eca2f76..2a04c0a8b 100644 --- a/src/hsl.rs +++ b/src/hsl.rs @@ -2,7 +2,7 @@ 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. /// @@ -27,10 +27,6 @@ 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 { @@ -40,16 +36,17 @@ impl Hsl { hue: hue, saturation: saturation, lightness: lightness, - alpha: T::one(), } } ///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 hsla(hue: RgbHue, saturation: T, lightness: T, alpha: T) -> Alpha, T> { + Alpha { + color: Hsl { + hue: hue, + saturation: saturation, + lightness: lightness, + }, alpha: alpha, } } @@ -58,8 +55,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 +67,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()); } } @@ -84,7 +79,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), } } } @@ -95,7 +89,6 @@ impl Shade for Hsl { hue: self.hue, saturation: self.saturation, lightness: self.lightness + amount, - alpha: self.alpha, } } } @@ -118,7 +111,6 @@ impl Hue for Hsl { hue: hue, saturation: self.saturation, lightness: self.lightness, - alpha: self.alpha, } } @@ -127,7 +119,6 @@ impl Hue for Hsl { hue: self.hue + amount, saturation: self.saturation, lightness: self.lightness, - alpha: self.alpha, } } } @@ -138,7 +129,6 @@ impl Saturate for Hsl { hue: self.hue, saturation: self.saturation * (T::one() + factor), lightness: self.lightness, - alpha: self.alpha, } } } @@ -157,7 +147,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, } } } @@ -170,7 +159,6 @@ impl Add for Hsl { hue: self.hue + c, saturation: self.saturation + c, lightness: self.lightness + c, - alpha: self.alpha + c, } } } @@ -183,7 +171,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, } } } @@ -196,7 +183,6 @@ impl Sub for Hsl { hue: self.hue - c, saturation: self.saturation - c, lightness: self.lightness - c, - alpha: self.alpha - c, } } } @@ -244,7 +230,6 @@ impl From> for Hsl { hue: hue.into(), saturation: saturation, lightness: lightness, - alpha: rgb.alpha, } } } @@ -288,7 +273,6 @@ impl From> for Hsl { hue: hsv.hue, saturation: saturation, lightness: x / T::from(2.0).unwrap(), - alpha: hsv.alpha, } } } @@ -300,7 +284,7 @@ mod test { #[test] fn red() { - let a = Hsl::from(Rgb::linear_rgb(1.0, 0.0, 0.0)); + let a = Hsl::from(Rgb::linear(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)); @@ -310,7 +294,7 @@ mod test { #[test] fn orange() { - let a = Hsl::from(Rgb::linear_rgb(1.0, 0.5, 0.0)); + let a = Hsl::from(Rgb::linear(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)); @@ -320,7 +304,7 @@ mod test { #[test] fn green() { - let a = Hsl::from(Rgb::linear_rgb(0.0, 1.0, 0.0)); + let a = Hsl::from(Rgb::linear(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)); @@ -330,7 +314,7 @@ mod test { #[test] fn blue() { - let a = Hsl::from(Rgb::linear_rgb(0.0, 0.0, 1.0)); + let a = Hsl::from(Rgb::linear(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)); @@ -340,7 +324,7 @@ mod test { #[test] fn purple() { - let a = Hsl::from(Rgb::linear_rgb(0.5, 0.0, 1.0)); + let a = Hsl::from(Rgb::linear(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)); diff --git a/src/hsv.rs b/src/hsv.rs index 0d034cf04..f2dfd1f47 100644 --- a/src/hsv.rs +++ b/src/hsv.rs @@ -2,7 +2,7 @@ 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. /// @@ -26,10 +26,6 @@ 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 { @@ -39,16 +35,17 @@ impl Hsv { hue: hue, saturation: saturation, value: value, - alpha: T::one(), } } ///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 hsva(hue: RgbHue, saturation: T, value: T, alpha: T) -> Alpha, T> { + Alpha { + color: Hsv { + hue: hue, + saturation: saturation, + value: value, + }, alpha: alpha, } } @@ -57,8 +54,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 +66,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()); } } @@ -83,7 +78,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), } } } @@ -94,7 +88,6 @@ impl Shade for Hsv { hue: self.hue, saturation: self.saturation, value: self.value + amount, - alpha: self.alpha, } } } @@ -117,7 +110,6 @@ impl Hue for Hsv { hue: hue, saturation: self.saturation, value: self.value, - alpha: self.alpha, } } @@ -126,7 +118,6 @@ impl Hue for Hsv { hue: self.hue + amount, saturation: self.saturation, value: self.value, - alpha: self.alpha, } } } @@ -137,7 +128,6 @@ impl Saturate for Hsv { hue: self.hue, saturation: self.saturation * (T::one() + factor), value: self.value, - alpha: self.alpha, } } } @@ -156,7 +146,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, } } } @@ -169,7 +158,6 @@ impl Add for Hsv { hue: self.hue + c, saturation: self.saturation + c, value: self.value + c, - alpha: self.alpha + c, } } } @@ -182,7 +170,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, } } } @@ -195,7 +182,6 @@ impl Sub for Hsv { hue: self.hue - c, saturation: self.saturation - c, value: self.value - c, - alpha: self.alpha - c, } } } @@ -242,7 +228,6 @@ impl From> for Hsv { hue: hue.into(), saturation: saturation, value: val_max, - alpha: rgb.alpha, } } } @@ -283,7 +268,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, } } } @@ -295,7 +279,7 @@ mod test { #[test] fn red() { - let a = Hsv::from(Rgb::linear_rgb(1.0, 0.0, 0.0)); + let a = Hsv::from(Rgb::linear(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)); @@ -305,7 +289,7 @@ mod test { #[test] fn orange() { - let a = Hsv::from(Rgb::linear_rgb(1.0, 0.5, 0.0)); + let a = Hsv::from(Rgb::linear(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)); @@ -315,7 +299,7 @@ mod test { #[test] fn green() { - let a = Hsv::from(Rgb::linear_rgb(0.0, 1.0, 0.0)); + let a = Hsv::from(Rgb::linear(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)); @@ -325,7 +309,7 @@ mod test { #[test] fn blue() { - let a = Hsv::from(Rgb::linear_rgb(0.0, 0.0, 1.0)); + let a = Hsv::from(Rgb::linear(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)); @@ -335,7 +319,7 @@ mod test { #[test] fn purple() { - let a = Hsv::from(Rgb::linear_rgb(0.5, 0.0, 1.0)); + let a = Hsv::from(Rgb::linear(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)); diff --git a/src/lab.rs b/src/lab.rs index 065a4a571..e02fd3a2c 100644 --- a/src/lab.rs +++ b/src/lab.rs @@ -2,7 +2,7 @@ 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}; @@ -28,10 +28,6 @@ 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 { @@ -41,16 +37,17 @@ impl Lab { l: l, a: a, b: b, - alpha: T::one(), } } ///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 laba(l: T, a: T, b: T, alpha: T) -> Alpha, T> { + Alpha { + color: Lab { + l: l, + a: a, + b: b, + }, alpha: alpha, } } @@ -58,9 +55,9 @@ impl Lab { 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.l >= T::zero() && self.l <= T::one() && + self.a >= -T::one() && self.a <= T::one() && + self.b >= -T::one() && self.b <= T::one() } fn clamp(&self) -> Lab { @@ -73,7 +70,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()); } } @@ -85,7 +81,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), } } } @@ -96,7 +91,6 @@ impl Shade for Lab { l: self.l + amount, a: self.a, b: self.b, - alpha: self.alpha, } } } @@ -127,7 +121,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, } } } @@ -140,7 +133,6 @@ impl Add for Lab { l: self.l + c, a: self.a + c, b: self.b + c, - alpha: self.alpha + c, } } } @@ -153,7 +145,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, } } } @@ -166,7 +157,6 @@ impl Sub for Lab { l: self.l - c, a: self.a - c, b: self.b - c, - alpha: self.alpha - c, } } } @@ -179,7 +169,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, } } } @@ -192,7 +181,6 @@ impl Mul for Lab { l: self.l * c, a: self.a * c, b: self.b * c, - alpha: self.alpha * c, } } } @@ -205,7 +193,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, } } } @@ -218,7 +205,6 @@ impl Div for Lab { l: self.l / c, a: self.a / c, b: self.b / c, - alpha: self.alpha / c, } } } @@ -236,7 +222,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, } } } @@ -259,7 +244,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, } } } @@ -297,21 +281,21 @@ mod test { #[test] fn red() { - let a = Lab::from(Rgb::linear_rgb(1.0, 0.0, 0.0)); + let a = Lab::from(Rgb::linear(1.0, 0.0, 0.0)); let b = Lab::lab(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::linear_rgb(0.0, 1.0, 0.0)); + let a = Lab::from(Rgb::linear(0.0, 1.0, 0.0)); let b = Lab::lab(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::linear_rgb(0.0, 0.0, 1.0)); + let a = Lab::from(Rgb::linear(0.0, 0.0, 1.0)); let b = Lab::lab(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 8265fa58e..63132c65d 100644 --- a/src/lch.rs +++ b/src/lch.rs @@ -2,7 +2,7 @@ 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. @@ -26,10 +26,6 @@ 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 { @@ -39,16 +35,17 @@ impl Lch { l: l, chroma: chroma, hue: hue, - alpha: T::one(), } } ///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 lcha(l: T, chroma: T, hue: LabHue, alpha: T) -> Alpha, T> { + Alpha { + color: Lch { + l: l, + chroma: chroma, + hue: hue, + }, alpha: alpha, } } @@ -57,8 +54,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 +66,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()); } } @@ -82,7 +77,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), } } } @@ -93,7 +87,6 @@ impl Shade for Lch { l: self.l + amount, chroma: self.chroma, hue: self.hue, - alpha: self.alpha, } } } @@ -116,7 +109,6 @@ impl Hue for Lch { l: self.l, chroma: self.chroma, hue: hue, - alpha: self.alpha, } } @@ -125,7 +117,6 @@ impl Hue for Lch { l: self.l, chroma: self.chroma, hue: self.hue + amount, - alpha: self.alpha, } } } @@ -136,7 +127,6 @@ impl Saturate for Lch { l: self.l, chroma: self.chroma * (T::one() + factor), hue: self.hue, - alpha: self.alpha, } } } @@ -155,7 +145,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, } } } @@ -168,7 +157,6 @@ impl Add for Lch { l: self.l + c, chroma: self.chroma + c, hue: self.hue + c, - alpha: self.alpha + c, } } } @@ -181,7 +169,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, } } } @@ -194,7 +181,6 @@ impl Sub for Lch { l: self.l - c, chroma: self.chroma - c, hue: self.hue - c, - alpha: self.alpha - c, } } } @@ -207,7 +193,6 @@ impl From> for 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 c6fd55f89..ba234e311 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,8 @@ extern crate num; use num::traits::Float; pub use gradient::Gradient; -pub use rgb::{Rgb, RgbPixel}; +pub use alpha::Alpha; +pub use rgb::{Rgb, Rgba, RgbPixel}; pub use luma::Luma; pub use xyz::Xyz; pub use lab::Lab; @@ -51,6 +52,27 @@ 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, + } + } + } + )+ + ) +} + //Helper macro for approximate component wise comparison. Most color spaces //are in roughly the same ranges, so this epsilon should be alright. #[cfg(test)] @@ -61,12 +83,12 @@ 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; +mod alpha; mod rgb; mod luma; mod xyz; @@ -181,29 +203,29 @@ make_color! { ///Linear luminance. y(luma: T); - ///Linear luminance with transparency. - ya(luma: T, alpha: T); + /*///Linear luminance with transparency. + ya(luma: T, alpha: T);*/ ///Linear luminance from an 8 bit value. y8(luma: u8); - ///Linear luminance and transparency from 8 bit values. - ya8(luma: u8, alpha: u8); + /*///Linear luminance and transparency from 8 bit values. + ya8(luma: u8, alpha: u8);*/ } ///Linear RGB. Rgb { ///Linear RGB. - linear_rgb(red: T, green: T, blue: T); + linear(red: T, green: T, blue: T); - ///Linear RGB and transparency. - linear_rgba(red: T, green: T, blue: T, alpha: T); + /*///Linear RGB and transparency. + lineara(red: T, green: T, blue: T, alpha: T);*/ ///Linear RGB from 8 bit values. - linear_rgb8(red: u8, green: u8, blue: u8); + linear_u8(red: u8, green: u8, blue: u8); - ///Linear RGB and transparency from 8 bit values. - linear_rgba8(red: u8, green: u8, blue: u8, alpha: u8); + /*///Linear RGB and transparency from 8 bit values. + lineara8(red: u8, green: u8, blue: u8, alpha: u8);*/ ///Linear RGB from a linear pixel value. linear_pixel >(pixel: &P); @@ -211,29 +233,29 @@ make_color! { ///Linear RGB from sRGB. srgb(red: T, green: T, blue: T); - ///Linear RGB from sRGB with transparency. - srgba(red: T, green: T, blue: T, alpha: T); + /*///Linear RGB from sRGB with transparency. + srgba(red: T, green: T, blue: T, alpha: T);*/ ///Linear RGB from 8 bit sRGB. - srgb8(red: u8, green: u8, blue: u8); + srgb_u8(red: u8, green: u8, blue: u8); - ///Linear RGB from 8 bit sRGB with transparency. - srgba8(red: u8, green: u8, blue: u8, alpha: u8); + /*///Linear RGB from 8 bit sRGB with transparency. + srgba8(red: u8, green: u8, blue: u8, alpha: u8);*/ ///Linear RGB from an sRGB pixel value. srgb_pixel >(pixel: &P); ///Linear RGB from gamma corrected RGB. - gamma_rgb(red: T, green: T, blue: T, gamma: T); + gamma(red: T, green: T, blue: T, gamma: T); - ///Linear RGB from gamma corrected RGB with transparency. - gamma_rgba(red: T, green: T, blue: T, alpha: T, gamma: T); + /*///Linear RGB from gamma corrected RGB with transparency. + gamma_rgba(red: T, green: T, blue: T, alpha: T, gamma: T);*/ ///Linear RGB from 8 bit gamma corrected RGB. - gamma_rgb8(red: u8, green: u8, blue: u8, gamma: T); + gamma_u8(red: u8, green: u8, blue: u8, gamma: T); - ///Linear RGB from 8 bit gamma corrected RGB with transparency. - gamma_rgba8(red: u8, green: u8, blue: u8, alpha: u8, gamma: T); + /*///Linear RGB from 8 bit gamma corrected RGB with transparency. + gamma_rgba8(red: u8, green: u8, blue: u8, alpha: u8, gamma: T);*/ ///Linear RGB from a gamma corrected pixel value. gamma_pixel >(pixel: &P, gamma: T); @@ -244,8 +266,8 @@ make_color! { ///CIE XYZ. xyz(x: T, y: T, z: T); - ///CIE XYZ and transparency. - xyza(x: T, y: T, z: T, alpha: T); + /*///CIE XYZ and transparency. + xyza(x: T, y: T, z: T, alpha: T);*/ } ///CIE L*a*b* (CIELAB). @@ -253,8 +275,8 @@ make_color! { ///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); + /*///CIE L*a*b* and transparency. + laba(l: T, a: T, b: T, alpha: T);*/ } ///CIE L*C*h°, a polar version of CIE L*a*b*. @@ -262,8 +284,8 @@ make_color! { ///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); + /*///CIE L*C*h° and transparency. + lcha(l: T, chroma: T, hue: LabHue, alpha: T);*/ } ///Linear HSV, a cylindrical version of RGB. @@ -271,8 +293,8 @@ make_color! { ///Linear HSV. hsv(hue: RgbHue, saturation: T, value: T); - ///Linear HSV and transparency. - hsva(hue: RgbHue, saturation: T, value: T, alpha: T); + /*///Linear HSV and transparency. + hsva(hue: RgbHue, saturation: T, value: T, alpha: T);*/ } ///Linear HSL, a cylindrical version of RGB. @@ -280,8 +302,8 @@ make_color! { ///Linear HSL. hsl(hue: RgbHue, saturation: T, lightness: T); - ///Linear HSL and transparency. - hsla(hue: RgbHue, saturation: T, lightness: T, alpha: T); + /*///Linear HSL and transparency. + hsla(hue: RgbHue, saturation: T, lightness: T, alpha: T);*/ } } @@ -303,11 +325,11 @@ pub trait ColorSpace { ///``` ///use palette::{Rgb, Mix}; /// -///let a = Rgb::linear_rgb(0.0, 0.5, 1.0); -///let b = Rgb::linear_rgb(1.0, 0.5, 0.0); +///let a = Rgb::linear(0.0, 0.5, 1.0); +///let b = Rgb::linear(1.0, 0.5, 0.0); /// ///assert_eq!(a.mix(&b, 0.0), a); -///assert_eq!(a.mix(&b, 0.5), Rgb::linear_rgb(0.5, 0.5, 0.5)); +///assert_eq!(a.mix(&b, 0.5), Rgb::linear(0.5, 0.5, 0.5)); ///assert_eq!(a.mix(&b, 1.0), b); ///``` pub trait Mix { @@ -324,8 +346,8 @@ pub trait Mix { ///``` ///use palette::{Rgb, Shade}; /// -///let a = Rgb::linear_rgb(0.4, 0.4, 0.4); -///let b = Rgb::linear_rgb(0.6, 0.6, 0.6); +///let a = Rgb::linear(0.4, 0.4, 0.4); +///let b = Rgb::linear(0.6, 0.6, 0.6); /// ///assert_eq!(a.lighten(0.1), b.darken(0.1)); ///``` @@ -344,10 +366,10 @@ pub trait Shade: Sized { ///``` ///use palette::{Rgb, GetHue}; /// -///let red = Rgb::linear_rgb(1.0_f32, 0.0, 0.0); -///let green = Rgb::linear_rgb(0.0_f32, 1.0, 0.0); -///let blue = Rgb::linear_rgb(0.0_f32, 0.0, 1.0); -///let gray = Rgb::linear_rgb(0.5_f32, 0.5, 0.5); +///let red = Rgb::linear(1.0f32, 0.0, 0.0); +///let green = Rgb::linear(0.0f32, 1.0, 0.0); +///let blue = Rgb::linear(0.0f32, 0.0, 1.0); +///let gray = Rgb::linear(0.5f32, 0.5, 0.5); /// ///assert_eq!(red.get_hue(), Some(0.0.into())); ///assert_eq!(green.get_hue(), Some(120.0.into())); diff --git a/src/luma.rs b/src/luma.rs index 8de3bc2c8..33291861f 100644 --- a/src/luma.rs +++ b/src/luma.rs @@ -2,7 +2,7 @@ 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. /// @@ -15,10 +15,6 @@ 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 { @@ -26,14 +22,15 @@ impl Luma { pub fn y(luma: T) -> Luma { Luma { luma: luma, - alpha: T::zero(), } } ///Linear luminance with transparency. - pub fn ya(luma: T, alpha: T) -> Luma { - Luma { - luma: luma, + pub fn ya(luma: T, alpha: T) -> Alpha, T> { + Alpha { + color: Luma { + luma: luma, + }, alpha: alpha, } } @@ -42,14 +39,15 @@ impl Luma { pub fn y8(luma: u8) -> Luma { Luma { luma: T::from(luma).unwrap() / T::from(255.0).unwrap(), - alpha: T::zero(), } } ///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 ya8(luma: u8, alpha: u8) -> Alpha, T> { + Alpha { + color: Luma { + luma: T::from(luma).unwrap() / T::from(255.0).unwrap(), + }, alpha: T::from(alpha).unwrap() / T::from(255.0).unwrap(), } } @@ -57,8 +55,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 +66,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()); } } @@ -79,7 +75,6 @@ impl Mix for Luma { Luma { luma: self.luma + factor * (other.luma - self.luma), - alpha: self.alpha + factor * (other.alpha - self.alpha), } } } @@ -88,7 +83,6 @@ impl Shade for Luma { fn lighten(&self, amount: T) -> Luma { Luma { luma: (self.luma + amount).max(T::zero()), - alpha: self.alpha, } } } @@ -105,7 +99,6 @@ impl Add> for Luma { fn add(self, other: Luma) -> Luma { Luma { luma: self.luma + other.luma, - alpha: self.alpha + other.alpha, } } } @@ -116,7 +109,6 @@ impl Add for Luma { fn add(self, c: T) -> Luma { Luma { luma: self.luma + c, - alpha: self.alpha + c, } } } @@ -127,7 +119,6 @@ impl Sub> for Luma { fn sub(self, other: Luma) -> Luma { Luma { luma: self.luma - other.luma, - alpha: self.alpha - other.alpha, } } } @@ -138,7 +129,6 @@ impl Sub for Luma { fn sub(self, c: T) -> Luma { Luma { luma: self.luma - c, - alpha: self.alpha - c, } } } @@ -149,7 +139,6 @@ impl Mul> for Luma { fn mul(self, other: Luma) -> Luma { Luma { luma: self.luma * other.luma, - alpha: self.alpha * other.alpha, } } } @@ -160,7 +149,6 @@ impl Mul for Luma { fn mul(self, c: T) -> Luma { Luma { luma: self.luma * c, - alpha: self.alpha * c, } } } @@ -171,7 +159,6 @@ impl Div> for Luma { fn div(self, other: Luma) -> Luma { Luma { luma: self.luma / other.luma, - alpha: self.alpha / other.alpha, } } } @@ -182,7 +169,6 @@ impl Div for Luma { fn div(self, c: T) -> Luma { Luma { luma: self.luma / c, - alpha: self.alpha / c, } } } @@ -193,7 +179,6 @@ 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 } } } @@ -202,7 +187,6 @@ impl From> for Luma { fn from(xyz: Xyz) -> Luma { Luma { luma: xyz.y, - alpha: xyz.alpha, } } } diff --git a/src/rgb.rs b/src/rgb.rs index 49bfc13d3..0442d300d 100644 --- a/src/rgb.rs +++ b/src/rgb.rs @@ -2,9 +2,12 @@ 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}; -///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 @@ -28,170 +31,199 @@ 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 linear_rgb(red: T, green: T, blue: T) -> Rgb { - Rgb { - red: red, - green: green, - blue: blue, - alpha: T::one(), - } - } - - ///Linear RGB with transparency. - pub fn linear_rgba(red: T, green: T, blue: T, alpha: T) -> Rgb { + pub fn linear(red: T, green: T, blue: T) -> Rgb { Rgb { red: red, green: green, blue: blue, - alpha: alpha, } } ///Linear RGB from 8 bit values. - pub fn linear_rgb8(red: u8, green: u8, blue: u8) -> Rgb { + pub fn linear_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 with transparency from 8 bit values. - pub fn linear_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(), - alpha: T::from(alpha).unwrap() / T::from(255.0).unwrap(), } } ///Linear RGB from a linear pixel value. pub fn linear_pixel>(pixel: &P) -> Rgb { - let (r, g, b, a) = pixel.to_rgba(); - Rgb::linear_rgba(r, g, b, a) + let (r, g, b, _) = pixel.to_rgba(); + Rgb::linear(r, g, b) } -} -///Creation from sRGB. -impl Rgb { ///Linear RGB from sRGB. pub fn srgb(red: T, green: T, blue: T) -> Rgb { Rgb { red: from_srgb(red), green: from_srgb(green), blue: from_srgb(blue), - alpha: T::one(), - } - } - - ///Linear RGB from sRGB with transparency. - pub fn srgba(red: T, green: T, blue: T, alpha: T) -> Rgb { - Rgb { - red: from_srgb(red), - green: from_srgb(green), - blue: from_srgb(blue), - alpha: alpha, } } ///Linear RGB from 8 bit sRGB. - pub fn srgb8(red: u8, green: u8, blue: u8) -> Rgb { - Rgb { - red: from_srgb(T::from(red).unwrap() / T::from(255.0).unwrap()), - green: from_srgb(T::from(green).unwrap() / T::from(255.0).unwrap()), - blue: from_srgb(T::from(blue).unwrap() / T::from(255.0).unwrap()), - alpha: T::one(), - } - } - - ///Linear RGB from 8 bit sRGB with transparency. - pub fn srgba8(red: u8, green: u8, blue: u8, alpha: u8) -> Rgb { + pub fn srgb_u8(red: u8, green: u8, blue: u8) -> Rgb { Rgb { red: from_srgb(T::from(red).unwrap() / T::from(255.0).unwrap()), green: from_srgb(T::from(green).unwrap() / T::from(255.0).unwrap()), blue: from_srgb(T::from(blue).unwrap() / T::from(255.0).unwrap()), - alpha: T::from(alpha).unwrap() / T::from(255.0).unwrap(), } } ///Linear RGB from an sRGB pixel value. pub fn srgb_pixel>(pixel: &P) -> Rgb { - let (r, g, b, a) = pixel.to_rgba(); - Rgb::srgba(r, g, b, a) + let (r, g, b, _) = pixel.to_rgba(); + Rgb::srgb(r, g, b) } -} -///Creation from gamma corrected RGB. -impl Rgb { ///Linear RGB from gamma corrected RGB. - pub fn gamma_rgb(red: T, green: T, blue: T, gamma: T) -> Rgb { + pub fn gamma(red: T, green: T, blue: T, gamma: T) -> Rgb { Rgb { red: from_gamma(red, gamma), green: from_gamma(green, gamma), blue: from_gamma(blue, gamma), - alpha: T::one(), - } - } - - ///Linear RGB from gamma corrected RGB with transparency. - pub fn gamma_rgba(red: T, green: T, blue: T, alpha: T, gamma: T) -> Rgb { - Rgb { - red: from_gamma(red, gamma), - green: from_gamma(green, gamma), - blue: from_gamma(blue, gamma), - alpha: alpha, } } ///Linear RGB from 8 bit gamma corrected RGB. - pub fn gamma_rgb8(red: u8, green: u8, blue: u8, gamma: T) -> Rgb { + pub fn gamma_u8(red: u8, green: u8, blue: u8, gamma: T) -> Rgb { Rgb { red: from_gamma(T::from(red).unwrap() / T::from(255.0).unwrap(), gamma), green: from_gamma(T::from(green).unwrap() / T::from(255.0).unwrap(), gamma), blue: from_gamma(T::from(blue).unwrap() / T::from(255.0).unwrap(), gamma), - alpha: T::one(), + } + } + + ///Linear RGB from a gamma corrected pixel value. + pub fn gamma_pixel>(pixel: &P, gamma: T) -> Rgb { + let (r, g, b, _) = pixel.to_rgba(); + Rgb::gamma(r, g, b, gamma) + } + + ///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::linear(0.5, 0.3, 0.1); + ///assert_eq!((c.red, c.green, c.blue), c.to_linear()); + ///assert_eq!((0.5, 0.3, 0.1), c.to_linear()); + ///``` + pub fn to_linear>(&self) -> P { + Rgba::::from(*self).to_linear() + } + + + ///Convert to an sRGB pixel. + /// + ///``` + ///use palette::Rgb; + /// + ///let c = Rgb::srgb(0.5f32, 0.3, 0.1); + ///assert_eq!((0.5f32, 0.3, 0.1), c.to_srgb()); + ///``` + pub fn to_srgb>(&self) -> P { + Rgba::::from(*self).to_srgb() + } + + ///Convert to a gamma corrected RGB pixel. + /// + ///``` + ///use palette::Rgb; + /// + ///let c = Rgb::gamma_u8(128, 64, 32, 2.2f32); + ///assert_eq!((128, 64, 32), c.to_gamma(2.2)); + ///``` + pub fn to_gamma>(&self, gamma: T) -> P { + Rgba::::from(*self).to_gamma(gamma) + } +} + +///[`Rgba`](type.Rgba.html) implementations. +impl Alpha, T> { + ///Linear RGB with transparency. + pub fn linear(red: T, green: T, blue: T, alpha: T) -> Rgba { + Alpha { + color: Rgb::linear(red, green, blue), + alpha: alpha, + } + } + + ///Linear RGB with transparency from 8 bit values. + pub fn linear_u8(red: u8, green: u8, blue: u8, alpha: u8) -> Rgba { + Alpha { + color: Rgb::linear_u8(red, green, blue), + alpha: T::from(alpha).unwrap() / T::from(255.0).unwrap(), + } + } + + ///Linear RGB from a linear pixel value. + pub fn linear_pixel>(pixel: &P) -> Rgba { + let (r, g, b, a) = pixel.to_rgba(); + Rgba::linear(r, g, b, a) + } + + ///Linear RGB from sRGB with transparency. + pub fn srgb(red: T, green: T, blue: T, alpha: T) -> Rgba { + Alpha { + color: Rgb::srgb(red, green, blue), + alpha: alpha, + } + } + + ///Linear RGB from 8 bit sRGB with transparency. + pub fn srgb_u8(red: u8, green: u8, blue: u8, alpha: u8) -> Rgba { + Alpha { + color: Rgb::srgb_u8(red, green, blue), + alpha: T::from(alpha).unwrap() / T::from(255.0).unwrap(), + } + } + + ///Linear RGB from an sRGB pixel value. + pub fn srgb_pixel>(pixel: &P) -> Rgba { + let (r, g, b, a) = pixel.to_rgba(); + Rgba::srgb(r, g, b, a) + } + + ///Linear RGB from gamma corrected RGB with transparency. + pub fn gamma(red: T, green: T, blue: T, alpha: T, gamma: T) -> Rgba { + Alpha { + color: Rgb::gamma(red, green, blue, gamma), + alpha: alpha, } } ///Linear RGB from 8 bit gamma corrected RGB with transparency. - pub fn gamma_rgba8(red: u8, green: u8, blue: u8, alpha: u8, gamma: T) -> Rgb { - Rgb { - red: from_gamma(T::from(red).unwrap() / T::from(255.0).unwrap(), gamma), - green: from_gamma(T::from(green).unwrap() / T::from(255.0).unwrap(), gamma), - blue: from_gamma(T::from(blue).unwrap() / T::from(255.0).unwrap(), gamma), + pub fn gamma_u8(red: u8, green: u8, blue: u8, alpha: u8, gamma: T) -> Rgba { + Alpha { + color: Rgb::gamma_u8(red, green, blue, gamma), alpha: T::from(alpha).unwrap() / T::from(255.0).unwrap(), } } ///Linear RGB from a gamma corrected pixel value. - pub fn gamma_pixel>(pixel: &P, gamma: T) -> Rgb { + pub fn gamma_pixel>(pixel: &P, gamma: T) -> Rgba { let (r, g, b, a) = pixel.to_rgba(); - Rgb::gamma_rgba(r, g, b, a, gamma) + Rgba::gamma(r, g, b, a, gamma) } -} -///Conversion to "pixel space". -impl Rgb { ///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::linear_rgb(0.5, 0.3, 0.1); - ///assert_eq!((c.red, c.green, c.blue), c.to_linear()); - ///assert_eq!((0.5, 0.3, 0.1), c.to_linear()); + ///let c = Rgba::linear(0.5, 0.3, 0.1, 0.5); + ///assert_eq!((c.red, c.green, c.blue, c.alpha), c.to_linear()); + ///assert_eq!((0.5, 0.3, 0.1, 0.5), c.to_linear()); ///``` pub fn to_linear>(&self) -> P { P::from_rgba( @@ -205,10 +237,10 @@ impl Rgb { ///Convert to an sRGB pixel. /// ///``` - ///use palette::Rgb; + ///use palette::Rgba; /// - ///let c = Rgb::srgb(0.5, 0.3, 0.1); - ///assert_eq!((0.5, 0.3, 0.1), c.to_srgb()); + ///let c = Rgba::srgb(0.5f32, 0.3, 0.1, 0.5); + ///assert_eq!((0.5f32, 0.3, 0.1, 0.5), c.to_srgb()); ///``` pub fn to_srgb>(&self) -> P { P::from_rgba( @@ -222,17 +254,18 @@ impl Rgb { ///Convert to a gamma corrected RGB pixel. /// ///``` - ///use palette::Rgb; + ///use palette::Rgba; /// - ///let c = Rgb::::gamma_rgb8(128, 64, 32, 2.2); - ///assert_eq!((128, 64, 32), c.to_gamma(2.2)); + ///let c = Rgba::gamma_u8(128, 64, 32, 128, 2.2f32); + ///assert_eq!((128, 64, 32, 128), c.to_gamma(2.2)); ///``` pub fn to_gamma>(&self, gamma: T) -> P { P::from_rgba( clamp(to_gamma(self.red, gamma), T::zero(), T::one()), clamp(to_gamma(self.green, gamma), T::zero(), T::one()), clamp(to_gamma(self.blue, gamma), T::zero(), T::one()), - clamp(self.alpha, T::zero(), T::one())) + clamp(self.alpha, T::zero(), T::one()) + ) } } @@ -240,8 +273,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 { @@ -254,7 +286,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()); } } @@ -266,7 +297,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), } } } @@ -277,7 +307,6 @@ impl Shade for Rgb { red: self.red + amount, green: self.green + amount, blue: self.blue + amount, - alpha: self.alpha, } } } @@ -298,7 +327,7 @@ impl GetHue for Rgb { impl Default for Rgb { fn default() -> Rgb { - Rgb::linear_rgb(T::zero(), T::zero(), T::zero()) + Rgb::linear(T::zero(), T::zero(), T::zero()) } } @@ -310,7 +339,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, } } } @@ -323,7 +351,6 @@ impl Add for Rgb { red: self.red + c, green: self.green + c, blue: self.blue + c, - alpha: self.alpha + c, } } } @@ -336,7 +363,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, } } } @@ -349,7 +375,6 @@ impl Sub for Rgb { red: self.red - c, green: self.green - c, blue: self.blue - c, - alpha: self.alpha - c, } } } @@ -362,7 +387,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, } } } @@ -375,7 +399,6 @@ impl Mul for Rgb { red: self.red * c, green: self.green * c, blue: self.blue * c, - alpha: self.alpha * c, } } } @@ -388,7 +411,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, } } } @@ -401,20 +423,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, } } } @@ -425,7 +447,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.415).unwrap(), blue: xyz.x * T::from(0.557).unwrap() + xyz.y * T::from(-0.2040).unwrap() + xyz.z * T::from(0.570).unwrap(), - alpha: xyz.alpha, } } } @@ -468,7 +489,6 @@ impl From> for Rgb { red: red + m, green: green + m, blue: blue + m, - alpha: hsv.alpha, } } } @@ -499,13 +519,12 @@ impl From> for Rgb { red: red + m, green: green + m, blue: blue + m, - alpha: hsl.alpha, } } } fn from_srgb(x: T) -> T { - if x <= T::from(0.4045).unwrap() { + if x <= T::from(0.04045).unwrap() { x / T::from(12.92).unwrap() } else { ((x + T::from(0.055).unwrap()) / T::from(1.055).unwrap()).powf(T::from(2.4).unwrap()) @@ -513,7 +532,7 @@ fn from_srgb(x: T) -> T { } fn to_srgb(x: T) -> T { - if x <= T::from(0.031308).unwrap() { + if x <= T::from(0.0031308).unwrap() { T::from(12.92).unwrap() * x } else { T::from(1.055).unwrap() * x.powf(T::from(1.0 / 2.4).unwrap()) - T::from(0.055).unwrap() @@ -543,7 +562,6 @@ pub trait RgbPixel { fn to_rgba(&self) -> (T, T, T, T); } -// } impl RgbPixel for (f32, f32, f32, f32) { fn from_rgba(red: T, green: T, blue: T, alpha: T) -> (f32, f32, f32, f32) { ( red.to_f32().unwrap(), green.to_f32().unwrap(), blue.to_f32().unwrap(), alpha.to_f32().unwrap() ) @@ -647,6 +665,7 @@ impl RgbPixel for [f32; 3] { (T::from(self[0]).unwrap(), T::from(self[1]).unwrap(), T::from(self[2]).unwrap(), T::one()) } } + impl RgbPixel for [f64; 4] { fn from_rgba(red: T, green: T, blue: T, alpha: T) -> [f64; 4] { [red.to_f64().unwrap(), green.to_f64().unwrap(), blue.to_f64().unwrap(), alpha.to_f64().unwrap()] diff --git a/src/xyz.rs b/src/xyz.rs index 2a926c426..bd04a891d 100644 --- a/src/xyz.rs +++ b/src/xyz.rs @@ -2,7 +2,7 @@ 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}; @@ -28,10 +28,6 @@ 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 { @@ -41,16 +37,17 @@ impl Xyz { x: x, y: y, z: z, - alpha: T::one(), } } ///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 xyza(x: T, y: T, z: T, alpha: T) -> Alpha, T> { + Alpha { + color: Xyz { + x: x, + y: y, + z: z, + }, alpha: alpha, } } @@ -58,9 +55,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 +70,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()); } } @@ -85,7 +81,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), } } } @@ -96,7 +91,6 @@ impl Shade for Xyz { x: self.x, y: self.y + amount, z: self.z, - alpha: self.alpha, } } } @@ -115,7 +109,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, } } } @@ -128,7 +121,6 @@ impl Add for Xyz { x: self.x + c, y: self.y + c, z: self.z + c, - alpha: self.alpha + c, } } } @@ -141,7 +133,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, } } } @@ -154,7 +145,6 @@ impl Sub for Xyz { x: self.x - c, y: self.y - c, z: self.z - c, - alpha: self.alpha - c, } } } @@ -167,7 +157,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, } } } @@ -180,7 +169,6 @@ impl Mul for Xyz { x: self.x * c, y: self.y * c, z: self.z * c, - alpha: self.alpha * c, } } } @@ -193,7 +181,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, } } } @@ -206,7 +193,6 @@ impl Div for Xyz { x: self.x / c, y: self.y / c, z: self.z / c, - alpha: self.alpha / c, } } } @@ -219,7 +205,6 @@ impl From> for 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, } } } @@ -230,7 +215,6 @@ impl From> for Xyz { x: T::zero(), y: luma.luma, z: T::zero(), - alpha: luma.alpha, } } } @@ -246,7 +230,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, } } } @@ -288,21 +271,21 @@ mod test { #[test] fn red() { - let a = Xyz::from(Rgb::linear_rgb(1.0, 0.0, 0.0)); + let a = Xyz::from(Rgb::linear(1.0, 0.0, 0.0)); let b = Xyz::xyz(0.41240, 0.21260, 0.01930); assert_approx_eq!(a, b, [x, y, z]); } #[test] fn green() { - let a = Xyz::from(Rgb::linear_rgb(0.0, 1.0, 0.0)); + let a = Xyz::from(Rgb::linear(0.0, 1.0, 0.0)); let b = Xyz::xyz(0.35760, 0.71520, 0.11920); assert_approx_eq!(a, b, [x, y, z]); } #[test] fn blue() { - let a = Xyz::from(Rgb::linear_rgb(0.0, 0.0, 1.0)); + let a = Xyz::from(Rgb::linear(0.0, 0.0, 1.0)); let b = Xyz::xyz(0.18050, 0.07220, 0.95050); assert_approx_eq!(a, b, [x, y, z]); }