use core::ops::{Add, AddAssign, BitAnd, DivAssign, Sub, SubAssign};
use crate::stimulus::Stimulus;
use crate::white_point::D65;
use crate::HasBoolMask;
use crate::{
angle::{RealAngle, SignedAngle},
hues::OklabHueIter,
};
use crate::{
bool_mask::{LazySelect, Select},
clamp, clamp_min, clamp_min_assign, ClampAssign, FromColor, GetHue, IsWithinBounds, Lighten,
LightenAssign, Mix, MixAssign, OklabHue, SetHue, ShiftHue, ShiftHueAssign, WithHue, Xyz,
};
use crate::{
num::{
self, Arithmetics, FromScalarArray, IntoScalarArray, MinMax, One, PartialCmp, Real, Zero,
},
Alpha, Clamp,
};
use super::Okhwb;
#[cfg(feature = "approx")]
use approx::{AbsDiffEq, RelativeEq, UlpsEq};
impl<T> IsWithinBounds for Okhwb<T>
where
T: Real + Stimulus + PartialCmp + Add<Output = T> + HasBoolMask + Clone,
T::Mask: BitAnd<Output = T::Mask>,
{
#[rustfmt::skip]
#[inline]
fn is_within_bounds(&self) -> T::Mask {
self.blackness.gt_eq(&Self::min_blackness()) & self.blackness.lt_eq(&Self::max_blackness()) &
self.whiteness.gt_eq(&Self::min_whiteness()) & self.whiteness.lt_eq(&Self::max_blackness()) &
(self.whiteness.clone() + self.blackness.clone()).lt_eq(&T::max_intensity())
}
}
impl<T> Clamp for Okhwb<T>
where
T: Real + Stimulus + One + num::Clamp + PartialCmp + Add<Output = T> + DivAssign + Clone,
T::Mask: Select<T>,
{
#[inline]
fn clamp(self) -> Self {
let mut whiteness = clamp_min(self.whiteness.clone(), Self::min_whiteness());
let mut blackness = clamp_min(self.blackness.clone(), Self::min_blackness());
let sum = self.blackness + self.whiteness;
let divisor = sum.gt(&T::max_intensity()).select(sum, T::one());
whiteness /= divisor.clone();
blackness /= divisor;
Self::new(self.hue, whiteness, blackness)
}
}
impl<T> ClampAssign for Okhwb<T>
where
T: Real + Stimulus + One + num::ClampAssign + PartialCmp + Add<Output = T> + DivAssign + Clone,
T::Mask: Select<T>,
{
#[inline]
fn clamp_assign(&mut self) {
clamp_min_assign(&mut self.whiteness, Self::min_whiteness());
clamp_min_assign(&mut self.blackness, Self::min_blackness());
let sum = self.blackness.clone() + self.whiteness.clone();
let divisor = sum.gt(&T::max_intensity()).select(sum, T::one());
self.whiteness /= divisor.clone();
self.blackness /= divisor;
}
}
impl_mix_hue!(Okhwb {
whiteness,
blackness
});
impl<T> Lighten for Okhwb<T>
where
T: Stimulus + Real + Zero + MinMax + Arithmetics + PartialCmp + Clone,
T::Mask: LazySelect<T>,
{
type Scalar = T;
#[inline]
fn lighten(self, factor: T) -> Self {
let difference_whiteness = lazy_select! {
if factor.gt_eq(&T::zero()) => Self::max_whiteness() - &self.whiteness,
else => self.whiteness.clone(),
};
let delta_whiteness = difference_whiteness.max(T::zero()) * &factor;
let difference_blackness = lazy_select! {
if factor.gt_eq(&T::zero()) => self.blackness.clone(),
else => Self::max_blackness() - &self.blackness,
};
let delta_blackness = difference_blackness.max(T::zero()) * factor;
Okhwb {
hue: self.hue,
whiteness: (self.whiteness + delta_whiteness).max(Self::min_whiteness()),
blackness: (self.blackness - delta_blackness).max(Self::min_blackness()),
}
}
#[inline]
fn lighten_fixed(self, amount: T) -> Self {
Okhwb {
hue: self.hue,
whiteness: (self.whiteness + Self::max_whiteness() * &amount)
.max(Self::min_whiteness()),
blackness: (self.blackness - Self::max_blackness() * amount).max(Self::min_blackness()),
}
}
}
impl<T> LightenAssign for Okhwb<T>
where
T: Stimulus
+ Real
+ Zero
+ MinMax
+ num::ClampAssign
+ AddAssign
+ SubAssign
+ Arithmetics
+ PartialCmp
+ Clone,
T::Mask: LazySelect<T>,
{
type Scalar = T;
#[inline]
fn lighten_assign(&mut self, factor: T) {
let difference_whiteness = lazy_select! {
if factor.gt_eq(&T::zero()) => Self::max_whiteness() - &self.whiteness,
else => self.whiteness.clone(),
};
self.whiteness += difference_whiteness.max(T::zero()) * &factor;
clamp_min_assign(&mut self.whiteness, Self::min_whiteness());
let difference_blackness = lazy_select! {
if factor.gt_eq(&T::zero()) => self.blackness.clone(),
else => Self::max_blackness() - &self.blackness,
};
self.blackness -= difference_blackness.max(T::zero()) * factor;
clamp_min_assign(&mut self.blackness, Self::min_blackness());
}
#[inline]
fn lighten_fixed_assign(&mut self, amount: T) {
self.whiteness += Self::max_whiteness() * &amount;
clamp_min_assign(&mut self.whiteness, Self::min_whiteness());
self.blackness -= Self::max_blackness() * amount;
clamp_min_assign(&mut self.blackness, Self::min_blackness());
}
}
impl<T> GetHue for Okhwb<T>
where
T: Clone,
{
type Hue = OklabHue<T>;
#[inline]
fn get_hue(&self) -> OklabHue<T> {
self.hue.clone()
}
}
impl<T, H> WithHue<H> for Okhwb<T>
where
H: Into<OklabHue<T>>,
{
#[inline]
fn with_hue(mut self, hue: H) -> Self {
self.hue = hue.into();
self
}
}
impl<T, H> SetHue<H> for Okhwb<T>
where
H: Into<OklabHue<T>>,
{
#[inline]
fn set_hue(&mut self, hue: H) {
self.hue = hue.into();
}
}
impl<T> ShiftHue for Okhwb<T>
where
T: Add<Output = T>,
{
type Scalar = T;
#[inline]
fn shift_hue(mut self, amount: Self::Scalar) -> Self {
self.hue = self.hue + amount;
self
}
}
impl<T> ShiftHueAssign for Okhwb<T>
where
T: AddAssign,
{
type Scalar = T;
#[inline]
fn shift_hue_assign(&mut self, amount: Self::Scalar) {
self.hue += amount;
}
}
impl_color_add!(Okhwb<T>, [hue, whiteness, blackness]);
impl_color_sub!(Okhwb<T>, [hue, whiteness, blackness]);
impl_array_casts!(Okhwb<T>, [T; 3]);
impl_simd_array_conversion_hue!(Okhwb, [whiteness, blackness]);
impl_struct_of_array_traits_hue!(Okhwb, OklabHueIter, [whiteness, blackness]);
#[allow(deprecated)]
impl<T> crate::RelativeContrast for Okhwb<T>
where
T: Real + Arithmetics + PartialCmp,
T::Mask: LazySelect<T>,
Xyz<D65, T>: FromColor<Self>,
{
type Scalar = T;
#[inline]
fn get_contrast_ratio(self, other: Self) -> T {
let xyz1 = Xyz::from_color(self);
let xyz2 = Xyz::from_color(other);
crate::contrast_ratio(xyz1.y, xyz2.y)
}
}
impl_eq_hue!(Okhwb, OklabHue, [hue, whiteness, blackness]);