Skip to content

Commit

Permalink
Merge #106
Browse files Browse the repository at this point in the history
106: Add Extended Conversion Trait  r=Ogeon a=Veykril

This commit tries to implement a conversion trait similar to the one mentioned in #41 mimicking the `From`/`Into` traits of the `std` library.

`convert_*` clamps the resulting color to its color space limitations.
`convert_unclamped_*` simply converts the color.
`try_convert_*` converts the color and returns it in a `Result` depending on it being valid or not.

Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
  • Loading branch information
bors[bot] and Veykril committed Jun 10, 2018
2 parents 420457c + 79c848c commit 5574374
Show file tree
Hide file tree
Showing 2 changed files with 169 additions and 2 deletions.
169 changes: 168 additions & 1 deletion palette/src/convert.rs
@@ -1,6 +1,8 @@
use num_traits::Float;

use {Component, Hsl, Hsv, Hwb, Lab, Lch, Xyz, Yxy};
use std::error::Error;
use std::fmt::{self, Debug, Display, Formatter};
use {Component, Limited, Hsl, Hsv, Hwb, Lab, Lch, Xyz, Yxy};
use white_point::{D65, WhitePoint};
use rgb::{Rgb, RgbSpace};
use luma::Luma;
Expand Down Expand Up @@ -499,6 +501,171 @@ where
}
}

///The error type for a color conversion that converted a color into a color with invalid values.
#[derive(Debug)]
pub struct OutOfBounds<T> {
color: T,
}

impl<T> OutOfBounds<T> {
///Create a new error wrapping a color
#[inline]
fn new(color: T) -> Self {
OutOfBounds { color }
}

///Consume this error and return the wrapped color
#[inline]
pub fn color(self) -> T {
self.color
}
}

impl<T: Debug> Error for OutOfBounds<T> {
fn description(&self) -> &str {
"Color conversion is out of bounds"
}
}

impl<T> Display for OutOfBounds<T> {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
write!(fmt, "Color conversion is out of bounds")
}
}

///A trait for converting a color into another.
pub trait ConvertInto<T>: Into<T> {
///Convert into T with values clamped to the color defined bounds
///
///```
///use palette::ConvertInto;
///use palette::Limited;
///use palette::{Srgb, Lch};
///
///
///let rgb: Srgb = Lch::new(50.0, 100.0, -175.0).convert_into();
///assert!(rgb.is_valid());
///```
fn convert_into(self) -> T;

///Convert into T. The resulting color might be invalid in its color space
///
///```
///use palette::ConvertInto;
///use palette::Limited;
///use palette::{Srgb, Lch};
///
///let rgb: Srgb = Lch::new(50.0, 100.0, -175.0).convert_unclamped_into();
///assert!(!rgb.is_valid());
///```
#[inline]
fn convert_unclamped_into(self) -> T;

///Convert into T, returning ok if the color is inside of its defined range,
///otherwise an `OutOfBounds` error is returned which contains the unclamped color.
///
///```
///use palette::ConvertInto;
///use palette::{Srgb, Hsl};
///
///let rgb: Srgb = match Hsl::new(150.0, 1.0, 1.1).try_convert_into() {
/// Ok(color) => color,
/// Err(err) => {
/// println!("Color is out of bounds");
/// err.color()
/// },
///};
///```
fn try_convert_into(self) -> Result<T, OutOfBounds<T>>;
}

///A trait for converting one color from another.
///
///`convert_unclamped` currently wraps the underlying `From` implementation.
pub trait ConvertFrom<T>: From<T> {
///Convert from T with values clamped to the color defined bounds
///
///```
///use palette::ConvertFrom;
///use palette::Limited;
///use palette::{Srgb, Lch};
///
///
///let rgb = Srgb::convert_from(Lch::new(50.0, 100.0, -175.0));
///assert!(rgb.is_valid());
///```
fn convert_from(_: T) -> Self;

///Convert from T. The resulting color might be invalid in its color space
///
///```
///use palette::ConvertFrom;
///use palette::Limited;
///use palette::{Srgb, Lch};
///
///let rgb = Srgb::convert_unclamped_from(Lch::new(50.0, 100.0, -175.0));
///assert!(!rgb.is_valid());
///```
#[inline]
fn convert_unclamped_from(val: T) -> Self {
Self::from(val)
}

///Convert from T, returning ok if the color is inside of its defined range,
///otherwise an `OutOfBounds` error is returned which contains the unclamped color.
///
///```
///use palette::ConvertFrom;
///use palette::{Srgb, Hsl};
///
///let rgb = match Srgb::try_convert_from(Hsl::new(150.0, 1.0, 1.1)) {
/// Ok(color) => color,
/// Err(err) => {
/// println!("Color is out of bounds");
/// err.color()
/// },
///};
///```
fn try_convert_from(_: T) -> Result<Self, OutOfBounds<Self>>;
}

impl<T, U> ConvertFrom<T> for U where U: From<T> + Limited {
fn convert_from(t: T) -> U {
let mut this = U::from(t);
if !this.is_valid() {
this.clamp_self();
}
this
}

fn try_convert_from(t: T) -> Result<U, OutOfBounds<U>> {
let this = U::from(t);
if this.is_valid() {
Ok(this)
} else {
Err(OutOfBounds::new(this))
}
}
}

// ConvertFrom implies ConvertInto
impl<T, U> ConvertInto<U> for T where U: ConvertFrom<T> {
#[inline]
fn convert_into(self) -> U {
U::convert_from(self)
}

#[inline]
fn convert_unclamped_into(self) -> U {
U::convert_unclamped_from(self)
}

#[inline]
fn try_convert_into(self) -> Result<U, OutOfBounds<U>> {
U::try_convert_from(self)
}
}

macro_rules! impl_into_color {
($self_ty: ident, $from_fn: ident) => {
impl<Wp, T> IntoColor<Wp, T> for $self_ty<Wp, T>
Expand Down
2 changes: 1 addition & 1 deletion palette/src/lib.rs
Expand Up @@ -182,7 +182,7 @@ pub use rgb::{GammaSrgb, GammaSrgba, LinSrgb, LinSrgba, Srgb, Srgba};
pub use xyz::{Xyz, Xyza};
pub use yxy::{Yxy, Yxya};

pub use convert::{FromColor, IntoColor};
pub use convert::{ConvertFrom, ConvertInto, OutOfBounds, FromColor, IntoColor};
pub use encoding::pixel::Pixel;
pub use hues::{LabHue, RgbHue};
pub use matrix::Mat3;
Expand Down

0 comments on commit 5574374

Please sign in to comment.