Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split Clamp into smaller traits and implement for [T] #247

Merged
merged 1 commit into from
Jul 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions palette/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,17 +306,8 @@ where
}
}

// Add the required clamping and validation.
// Add the required clamping.
impl Clamp for Color {
fn is_within_bounds(&self) -> bool {
let zero_to_one = 0.0..=1.0;

zero_to_one.contains(&self.r)
&& zero_to_one.contains(&self.g)
&& zero_to_one.contains(&self.b)
&& zero_to_one.contains(&self.a)
}

fn clamp(self) -> Self {
Color {
r: self.r.min(1.0).max(0.0),
Expand Down
22 changes: 17 additions & 5 deletions palette/src/alpha/alpha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ use crate::convert::{FromColorUnclamped, IntoColorUnclamped};
use crate::encoding::pixel::RawPixel;
use crate::float::Float;
use crate::{
clamp, Blend, Clamp, Component, ComponentWise, GetHue, Hue, Mix, Pixel, Saturate, Shade,
WithAlpha,
clamp, clamp_assign, Blend, Clamp, ClampAssign, Component, ComponentWise, GetHue, Hue,
IsWithinBounds, Mix, Pixel, Saturate, Shade, WithAlpha,
};

/// An alpha component wrapper for colors.
Expand Down Expand Up @@ -186,21 +186,33 @@ impl<C: Saturate> Saturate for Alpha<C, C::Scalar> {
}
}

impl<C: Clamp, T: Component> Clamp for Alpha<C, T> {
impl<C: IsWithinBounds, T: Component> IsWithinBounds for Alpha<C, T> {
#[inline]
fn is_within_bounds(&self) -> bool {
self.color.is_within_bounds() && self.alpha >= T::zero() && self.alpha <= T::max_intensity()
self.color.is_within_bounds()
&& self.alpha >= Self::min_alpha()
&& self.alpha <= Self::max_alpha()
}
}

impl<C: Clamp, T: Component> Clamp for Alpha<C, T> {
#[inline]
fn clamp(self) -> Self {
Alpha {
color: self.color.clamp(),
alpha: clamp(self.alpha, T::zero(), T::max_intensity()),
alpha: clamp(self.alpha, Self::min_alpha(), Self::max_alpha()),
}
}
}

impl<C: ClampAssign, T: Component> ClampAssign for Alpha<C, T> {
#[inline]
fn clamp_assign(&mut self) {
self.color.clamp_assign();
clamp_assign(&mut self.alpha, Self::min_alpha(), Self::max_alpha());
}
}

impl<C: Blend, T: Float> Blend for Alpha<C, T>
where
C::Color: ComponentWise<Scalar = T>,
Expand Down
70 changes: 38 additions & 32 deletions palette/src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ use core::fmt::{self, Display, Formatter};
#[doc(hidden)]
pub use palette_derive::FromColorUnclamped;

use crate::Clamp;
use crate::{Clamp, IsWithinBounds};

/// The error type for a color conversion that converted a color into a color
/// with invalid values.
Expand Down Expand Up @@ -326,7 +326,7 @@ pub trait IntoColor<T>: Sized {
/// Convert into T with values clamped to the color defined bounds
///
/// ```
/// use palette::{Clamp, IntoColor, Lch, Srgb};
/// use palette::{IsWithinBounds, IntoColor, Lch, Srgb};
///
/// let rgb: Srgb = Lch::new(50.0, 100.0, -175.0).into_color();
/// assert!(rgb.is_within_bounds());
Expand All @@ -345,7 +345,7 @@ pub trait IntoColorUnclamped<T>: Sized {
///
/// ```
/// use palette::convert::IntoColorUnclamped;
/// use palette::{Clamp, Lch, Srgb};
/// use palette::{IsWithinBounds, Lch, Srgb};
///
///let rgb: Srgb = Lch::new(50.0, 100.0, -175.0).into_color_unclamped();
///assert!(!rgb.is_within_bounds());
Expand Down Expand Up @@ -382,34 +382,39 @@ pub trait TryIntoColor<T>: Sized {

///A trait for converting one color from another, in a possibly lossy way.
///
/// `U: FromColor<T>` is implemented for every type `U: FromColorUnclamped<T> + Clamp`.
/// `U: FromColor<T>` is implemented for every type `U: FromColorUnclamped<T> +
/// Clamp`.
///
/// See [`FromColorUnclamped`](crate::convert::FromColorUnclamped) for a lossless version of this trait.
/// See [`TryFromColor`](crate::convert::TryFromColor) for a trait that gives an error when the result
/// is out of bounds.
/// See [`FromColorUnclamped`](crate::convert::FromColorUnclamped) for a
/// lossless version of this trait. See
/// [`TryFromColor`](crate::convert::TryFromColor) for a trait that gives an
/// error when the result is out of bounds.
///
/// # The Difference Between FromColor and From
///
/// The conversion traits, including `FromColor`, were added to gain even more flexibility
/// than what `From` and the other standard library traits can give. There are a few subtle,
/// but important, differences in their semantics:
/// The conversion traits, including `FromColor`, were added to gain even more
/// flexibility than what `From` and the other standard library traits can give.
/// There are a few subtle, but important, differences in their semantics:
///
/// * `FromColor` and `IntoColor` are allowed to be lossy, meaning converting `A -> B -> A`
/// may result in a different value than the original. This applies to `A -> A` as well.
/// * `From<Self>` and `Into<Self>` are blanket implemented, while `FromColor<Self>` and
/// `IntoColor<Self>` have to be manually implemented. This allows additional flexibility,
/// such as allowing implementing `FromColor<Rgb<S2, T>> for Rgb<S1, T>`.
/// * Implementing `FromColorUnclamped` and `Clamp` is enough to get all the other conversion
/// traits, while `From` and `Into` would not be possible to blanket implement in the same way.
/// This also reduces the work that needs to be done by macros.
/// * `FromColor` and `IntoColor` are allowed to be lossy, meaning converting `A
/// -> B -> A` may result in a different value than the original. This applies
/// to `A -> A` as well.
/// * `From<Self>` and `Into<Self>` are blanket implemented, while
/// `FromColor<Self>` and `IntoColor<Self>` have to be manually implemented.
/// This allows additional flexibility, such as allowing implementing
/// `FromColor<Rgb<S2, T>> for Rgb<S1, T>`.
/// * Implementing `FromColorUnclamped`, [`IsWithinBounds`] and [`Clamp`] is
/// enough to get all the other conversion traits, while `From` and `Into`
/// would not be possible to blanket implement in the same way. This also
/// reduces the work that needs to be done by macros.
///
/// See the [`convert`](crate::convert) module for how to implement `FromColorUnclamped` for
/// custom colors.
/// See the [`convert`](crate::convert) module for how to implement
/// `FromColorUnclamped` for custom colors.
pub trait FromColor<T>: Sized {
/// Convert from T with values clamped to the color defined bounds.
///
/// ```
/// use palette::{Clamp, FromColor, Lch, Srgb};
/// use palette::{IsWithinBounds, FromColor, Lch, Srgb};
///
/// let rgb = Srgb::from_color(Lch::new(50.0, 100.0, -175.0));
/// assert!(rgb.is_within_bounds());
Expand All @@ -431,7 +436,7 @@ pub trait FromColorUnclamped<T>: Sized {
///
/// ```
/// use palette::convert::FromColorUnclamped;
/// use palette::{Clamp, Lch, Srgb};
/// use palette::{IsWithinBounds, Lch, Srgb};
///
/// let rgb = Srgb::from_color_unclamped(Lch::new(50.0, 100.0, -175.0));
/// assert!(!rgb.is_within_bounds());
Expand Down Expand Up @@ -476,17 +481,13 @@ where
{
#[inline]
fn from_color(t: T) -> Self {
let mut this = Self::from_color_unclamped(t);
if !this.is_within_bounds() {
this = this.clamp();
}
this
Self::from_color_unclamped(t).clamp()
}
}

impl<T, U> TryFromColor<T> for U
where
U: FromColorUnclamped<T> + Clamp,
U: FromColorUnclamped<T> + IsWithinBounds,
{
#[inline]
fn try_from_color(t: T) -> Result<Self, OutOfBounds<Self>> {
Expand Down Expand Up @@ -537,8 +538,9 @@ mod tests {
use crate::encoding::linear::Linear;
use crate::luma::{Luma, LumaStandard};
use crate::rgb::{Rgb, RgbSpace};
use crate::{Alpha, Hsl, Hsluv, Hsv, Hwb, Lab, Lch, Luv, Xyz, Yxy};
use crate::{Clamp, FloatComponent};
use crate::{
Alpha, Clamp, FloatComponent, Hsl, Hsluv, Hsv, Hwb, IsWithinBounds, Lab, Lch, Luv, Xyz, Yxy,
};

#[derive(FromColorUnclamped, WithAlpha)]
#[palette(
Expand All @@ -558,11 +560,13 @@ mod tests {

impl<S> Copy for WithXyz<S> {}

impl<S> Clamp for WithXyz<S> {
impl<S> IsWithinBounds for WithXyz<S> {
fn is_within_bounds(&self) -> bool {
true
}
}

impl<S> Clamp for WithXyz<S> {
fn clamp(self) -> Self {
self
}
Expand Down Expand Up @@ -617,11 +621,13 @@ mod tests {
)]
struct WithoutXyz<T: FloatComponent>(PhantomData<T>);

impl<T: FloatComponent> Clamp for WithoutXyz<T> {
impl<T: FloatComponent> IsWithinBounds for WithoutXyz<T> {
fn is_within_bounds(&self) -> bool {
true
}
}

impl<T: FloatComponent> Clamp for WithoutXyz<T> {
fn clamp(self) -> Self {
self
}
Expand Down
23 changes: 20 additions & 3 deletions palette/src/hsl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ use crate::encoding::pixel::RawPixel;
use crate::encoding::Srgb;
use crate::rgb::{Rgb, RgbSpace, RgbStandard};
use crate::{
clamp, contrast_ratio, from_f64, Alpha, Clamp, Component, FloatComponent, GetHue, Hsv, Hue,
Mix, Pixel, RelativeContrast, RgbHue, Saturate, Shade, Xyz,
clamp, clamp_assign, contrast_ratio, from_f64, Alpha, Clamp, ClampAssign, Component,
FloatComponent, GetHue, Hsv, Hue, IsWithinBounds, Mix, Pixel, RelativeContrast, RgbHue,
Saturate, Shade, Xyz,
};
#[cfg(feature = "random")]
use crate::{float::Float, FromF64};
Expand Down Expand Up @@ -349,7 +350,7 @@ impl<S, T, A> From<Alpha<Hsl<S, T>, A>> for (RgbHue<T>, T, T, A) {
}
}

impl<S, T> Clamp for Hsl<S, T>
impl<S, T> IsWithinBounds for Hsl<S, T>
where
T: Component,
{
Expand All @@ -359,7 +360,12 @@ where
self.saturation >= T::zero() && self.saturation <= T::max_intensity() &&
self.lightness >= T::zero() && self.lightness <= T::max_intensity()
}
}

impl<S, T> Clamp for Hsl<S, T>
where
T: Component,
{
#[inline]
fn clamp(self) -> Self {
Self::new(
Expand All @@ -370,6 +376,17 @@ where
}
}

impl<S, T> ClampAssign for Hsl<S, T>
where
T: Component,
{
#[inline]
fn clamp_assign(&mut self) {
clamp_assign(&mut self.saturation, T::zero(), T::max_intensity());
clamp_assign(&mut self.lightness, T::zero(), T::max_intensity());
}
}

impl<S, T> Mix for Hsl<S, T>
where
T: FloatComponent,
Expand Down
23 changes: 22 additions & 1 deletion palette/src/hsluv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::{
Alpha, Clamp, FloatComponent, FromF64, GetHue, Hue, Lchuv, LuvHue, Mix, Pixel,
RelativeContrast, Saturate, Shade, Xyz,
};
use crate::{clamp_assign, ClampAssign, IsWithinBounds};

/// HSLuv with an alpha component. See the [`Hsluva` implementation in
/// `Alpha`](crate::Alpha#Hsluva).
Expand Down Expand Up @@ -214,7 +215,7 @@ impl<Wp, T, A> From<Alpha<Hsluv<Wp, T>, A>> for (LuvHue<T>, T, T, A) {
}
}

impl<Wp, T> Clamp for Hsluv<Wp, T>
impl<Wp, T> IsWithinBounds for Hsluv<Wp, T>
where
T: Zero + FromF64 + PartialOrd,
{
Expand All @@ -224,7 +225,12 @@ where
self.saturation >= Self::min_saturation() && self.saturation <= Self::max_saturation() &&
self.l >= Self::min_l() && self.l <= Self::max_l()
}
}

impl<Wp, T> Clamp for Hsluv<Wp, T>
where
T: Zero + FromF64 + PartialOrd,
{
#[inline]
fn clamp(self) -> Self {
Self::new(
Expand All @@ -239,6 +245,21 @@ where
}
}

impl<Wp, T> ClampAssign for Hsluv<Wp, T>
where
T: Zero + FromF64 + PartialOrd,
{
#[inline]
fn clamp_assign(&mut self) {
clamp_assign(
&mut self.saturation,
Self::min_saturation(),
Self::max_saturation(),
);
clamp_assign(&mut self.l, Self::min_l(), Self::max_l());
}
}

impl<Wp, T> Mix for Hsluv<Wp, T>
where
T: FloatComponent,
Expand Down
27 changes: 24 additions & 3 deletions palette/src/hsv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ use crate::encoding::pixel::RawPixel;
use crate::encoding::Srgb;
use crate::rgb::{Rgb, RgbSpace, RgbStandard};
use crate::{
clamp, contrast_ratio, from_f64, Alpha, Clamp, Component, FloatComponent, FromColor, GetHue,
Hsl, Hue, Hwb, Mix, Pixel, RelativeContrast, RgbHue, Saturate, Shade, Xyz,
clamp, clamp_assign, contrast_ratio, from_f64, Alpha, Clamp, ClampAssign, Component,
FloatComponent, FromColor, GetHue, Hsl, Hue, Hwb, IsWithinBounds, Mix, Pixel, RelativeContrast,
RgbHue, Saturate, Shade, Xyz,
};
#[cfg(feature = "random")]
use crate::{float::Float, FromF64};
Expand Down Expand Up @@ -360,7 +361,7 @@ impl<S, T, A> From<Alpha<Hsv<S, T>, A>> for (RgbHue<T>, T, T, A) {
}
}

impl<S, T> Clamp for Hsv<S, T>
impl<S, T> IsWithinBounds for Hsv<S, T>
where
T: Component,
{
Expand All @@ -370,7 +371,12 @@ where
self.saturation >= Self::min_saturation() && self.saturation <= Self::max_saturation() &&
self.value >= Self::min_value() && self.value <= Self::max_value()
}
}

impl<S, T> Clamp for Hsv<S, T>
where
T: Component,
{
#[inline]
fn clamp(self) -> Self {
Self::new(
Expand All @@ -385,6 +391,21 @@ where
}
}

impl<S, T> ClampAssign for Hsv<S, T>
where
T: Component,
{
#[inline]
fn clamp_assign(&mut self) {
clamp_assign(
&mut self.saturation,
Self::min_saturation(),
Self::max_saturation(),
);
clamp_assign(&mut self.value, Self::min_value(), Self::max_value());
}
}

impl<S, T> Mix for Hsv<S, T>
where
T: FloatComponent,
Expand Down
Loading