diff --git a/palette/examples/struct_of_arrays.rs b/palette/examples/struct_of_arrays.rs new file mode 100644 index 000000000..a8ecec905 --- /dev/null +++ b/palette/examples/struct_of_arrays.rs @@ -0,0 +1,50 @@ +use palette::{cast, color_difference::EuclideanDistance, IntoColor, Oklab, Srgb}; + +fn main() { + let image = image::open("example-data/input/fruits.png") + .expect("could not open 'example-data/input/fruits.png'") + .to_rgb8(); + + let image = cast::from_component_slice::>(image.as_raw()); + + // Convert and collect the colors in a struct-of-arrays (SoA) format, where + // each component is a Vec of all the pixels' component values. + let oklab_image: Oklab> = image + .iter() + .map(|rgb| rgb.into_linear::().into_color()) + .collect(); + + // Find the min, max and average of each component. We are doing it by + // iterating over each component Vec separately, starting with l... + let (min_l, max_l, average_l) = get_min_max_average(oklab_image.l.iter().copied()); + let (min_a, max_a, average_a) = get_min_max_average(oklab_image.a.iter().copied()); + let (min_b, max_b, average_b) = get_min_max_average(oklab_image.b.iter().copied()); + + // Find out how far the colors in the image are from the average color. In + // this case, we can just iterate over all of the colors and consume the + // collection(s). + let average_color = Oklab::new(average_l, average_a, average_b); + let (min_d, max_d, average_d) = get_min_max_average( + oklab_image + .into_iter() + .map(|color| average_color.distance(color)), + ); + + // Print the stats. + println!("Oklab l: min {min_l}, average {average_l}, max {max_l}"); + println!("Oklab a: min {min_a}, average {average_a}, max {max_a}"); + println!("Oklab b: min {min_b}, average {average_b}, max {max_b}"); + println!("Distance from average color: min {min_d}, average {average_d}, max {max_d}"); +} + +/// Calculates the min, max and average of the iterator's values. +fn get_min_max_average(iter: impl ExactSizeIterator) -> (f32, f32, f32) { + let length = iter.len(); + + let (min, max, sum) = iter.fold( + (f32::INFINITY, f32::NEG_INFINITY, 0.0), + |(min, max, sum), value| (min.min(value), max.max(value), sum + value), + ); + + (min, max, sum / length as f32) +} diff --git a/palette/src/alpha.rs b/palette/src/alpha.rs index d4afc2f4a..f99763b8e 100644 --- a/palette/src/alpha.rs +++ b/palette/src/alpha.rs @@ -1,3 +1,5 @@ +//! Types related to transparent colors. + #[doc(hidden)] pub use palette_derive::WithAlpha; @@ -5,6 +7,9 @@ use crate::{num::Zero, stimulus::Stimulus}; pub use self::alpha::*; +#[doc(no_inline)] +pub use crate::blend::PreAlpha; // Cross-link for visibility. + mod alpha; /// A trait for color types that can have or be given transparency (alpha channel). diff --git a/palette/src/alpha/alpha.rs b/palette/src/alpha/alpha.rs index 7d5a14f2b..c42ec11fc 100644 --- a/palette/src/alpha/alpha.rs +++ b/palette/src/alpha/alpha.rs @@ -1,5 +1,6 @@ use core::{ fmt, + iter::FromIterator, ops::{ Add, AddAssign, BitAnd, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Sub, SubAssign, }, @@ -40,6 +41,24 @@ pub struct Alpha { pub alpha: T, } +impl Alpha { + /// Return an iterator over the colors in the wrapped collections. + pub fn iter<'a>(&'a self) -> <&'a Self as IntoIterator>::IntoIter + where + &'a Self: IntoIterator, + { + self.into_iter() + } + + /// Return an iterator that allows modifying the colors in the wrapped collections. + pub fn iter_mut<'a>(&'a mut self) -> <&'a mut Self as IntoIterator>::IntoIter + where + &'a mut Self: IntoIterator, + { + self.into_iter() + } +} + impl Alpha { /// Alpha mask the color by its transparency. pub fn premultiply(self) -> PreAlpha { @@ -716,6 +735,172 @@ where } } +impl Extend> for Alpha +where + C: Extend, + A: Extend, +{ + fn extend>>(&mut self, iter: T) { + for color in iter { + self.color.extend(core::iter::once(color.color)); + self.alpha.extend(core::iter::once(color.alpha)); + } + } +} + +impl FromIterator> for Alpha +where + C: Extend + FromIterator, + A: Extend + Default, +{ + fn from_iter>>(iter: T) -> Self { + let mut result = Self { + color: C::from_iter(None), // Default is currently a used for black, meaning it's not always what we want here. + alpha: A::default(), + }; + + for color in iter { + result.color.extend(core::iter::once(color.color)); + result.alpha.extend(core::iter::once(color.alpha)); + } + + result + } +} + +impl IntoIterator for Alpha +where + C: IntoIterator, + A: IntoIterator, +{ + type Item = Alpha; + + type IntoIter = Iter; + + fn into_iter(self) -> Self::IntoIter { + Iter { + color: self.color.into_iter(), + alpha: self.alpha.into_iter(), + } + } +} + +impl<'a, C, A> IntoIterator for &'a Alpha +where + &'a C: IntoIterator, + &'a A: IntoIterator, +{ + type Item = Alpha<<&'a C as IntoIterator>::Item, <&'a A as IntoIterator>::Item>; + + type IntoIter = Iter<<&'a C as IntoIterator>::IntoIter, <&'a A as IntoIterator>::IntoIter>; + + fn into_iter(self) -> Self::IntoIter { + Iter { + color: (&self.color).into_iter(), + alpha: (&self.alpha).into_iter(), + } + } +} + +impl<'a, C, A> IntoIterator for &'a mut Alpha +where + &'a mut C: IntoIterator, + &'a mut A: IntoIterator, +{ + type Item = Alpha<<&'a mut C as IntoIterator>::Item, <&'a mut A as IntoIterator>::Item>; + + type IntoIter = + Iter<<&'a mut C as IntoIterator>::IntoIter, <&'a mut A as IntoIterator>::IntoIter>; + + fn into_iter(self) -> Self::IntoIter { + Iter { + color: (&mut self.color).into_iter(), + alpha: (&mut self.alpha).into_iter(), + } + } +} + +/// An iterator for transparent colors. +pub struct Iter { + pub(crate) color: C, + pub(crate) alpha: A, +} + +impl Iterator for Iter +where + C: Iterator, + A: Iterator, +{ + type Item = Alpha; + + fn next(&mut self) -> Option { + let color = self.color.next(); + let alpha = self.alpha.next(); + + if let (Some(color), Some(alpha)) = (color, alpha) { + Some(Alpha { color, alpha }) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + let hint = self.color.size_hint(); + debug_assert_eq!( + self.alpha.size_hint(), + hint, + "the color and alpha iterators have different size hints" + ); + + hint + } + + fn count(self) -> usize { + let count = self.color.count(); + debug_assert_eq!( + self.alpha.count(), + count, + "the color and alpha iterators have different counts" + ); + + count + } +} + +impl DoubleEndedIterator for Iter +where + C: DoubleEndedIterator, + A: DoubleEndedIterator, +{ + fn next_back(&mut self) -> Option { + let color = self.color.next_back(); + let alpha = self.alpha.next_back(); + + if let (Some(color), Some(alpha)) = (color, alpha) { + Some(Alpha { color, alpha }) + } else { + None + } + } +} + +impl ExactSizeIterator for Iter +where + C: ExactSizeIterator, + A: ExactSizeIterator, +{ + fn len(&self) -> usize { + let len = self.color.len(); + debug_assert_eq!( + self.alpha.len(), + len, + "the color and alpha iterators have different lengths" + ); + + len + } +} + #[cfg(feature = "serializing")] impl serde::Serialize for Alpha where @@ -771,6 +956,7 @@ where } } +/// Sample transparent colors uniformly. #[cfg(feature = "random")] pub struct UniformAlpha where diff --git a/palette/src/hsl.rs b/palette/src/hsl.rs index 6bfa3fb86..c9bec6240 100644 --- a/palette/src/hsl.rs +++ b/palette/src/hsl.rs @@ -1,3 +1,5 @@ +//! Types for the HSL color space. + use core::{ any::TypeId, marker::PhantomData, @@ -24,6 +26,7 @@ use crate::{ clamp, clamp_assign, contrast_ratio, convert::FromColorUnclamped, encoding::Srgb, + hues::RgbHueIter, num::{ self, Arithmetics, FromScalarArray, IntoScalarArray, IsValidDivisor, MinMax, One, PartialCmp, Real, Zero, @@ -276,6 +279,9 @@ impl Alpha, A> { } } +impl_reference_component_methods_hue!(Hsl, [saturation, lightness], standard); +impl_struct_of_arrays_methods_hue!(Hsl, [saturation, lightness], standard); + impl FromColorUnclamped> for Hsl where S1: RgbStandard + 'static, @@ -628,6 +634,7 @@ impl_color_sub!(Hsl, [hue, saturation, lightness], standard); impl_array_casts!(Hsl, [T; 3]); impl_simd_array_conversion_hue!(Hsl, [saturation, lightness], standard); +impl_struct_of_array_traits_hue!(Hsl, RgbHueIter, [saturation, lightness], standard); impl_eq_hue!(Hsl, RgbHue, [hue, saturation, lightness]); @@ -661,6 +668,7 @@ where } } +/// Sample HSL colors uniformly. #[cfg(feature = "random")] pub struct UniformHsl where @@ -835,6 +843,24 @@ mod test { assert_relative_eq!(Hsl::::max_lightness(), 1.0); } + struct_of_arrays_tests!( + Hsl, + Hsl::new(0.1f32, 0.2, 0.3), + Hsl::new(0.2, 0.3, 0.4), + Hsl::new(0.3, 0.4, 0.5) + ); + + mod alpha { + use crate::{encoding::Srgb, hsl::Hsla}; + + struct_of_arrays_tests!( + Hsla, + Hsla::new(0.1f32, 0.2, 0.3, 0.4), + Hsla::new(0.2, 0.3, 0.4, 0.5), + Hsla::new(0.3, 0.4, 0.5, 0.6) + ); + } + #[cfg(feature = "serializing")] #[test] fn serialize() { diff --git a/palette/src/hsluv.rs b/palette/src/hsluv.rs index 5baed582e..a77e2c9f4 100644 --- a/palette/src/hsluv.rs +++ b/palette/src/hsluv.rs @@ -1,3 +1,5 @@ +//! Types for the HSLuv color space. + use core::{ marker::PhantomData, ops::{Add, AddAssign, BitAnd, Sub, SubAssign}, @@ -23,6 +25,7 @@ use crate::{ bool_mask::{HasBoolMask, LazySelect}, clamp, clamp_assign, contrast_ratio, convert::FromColorUnclamped, + hues::LuvHueIter, luv_bounds::LuvBounds, num::{ self, Arithmetics, FromScalarArray, IntoScalarArray, MinMax, One, PartialCmp, Powi, Real, @@ -180,6 +183,9 @@ impl Alpha, A> { } } +impl_reference_component_methods_hue!(Hsluv, [saturation, l], white_point); +impl_struct_of_arrays_methods_hue!(Hsluv, [saturation, l], white_point); + impl FromColorUnclamped> for Hsluv { fn from_color_unclamped(hsluv: Hsluv) -> Self { hsluv @@ -353,6 +359,7 @@ impl_color_sub!(Hsluv, [hue, saturation, l], white_point); impl_array_casts!(Hsluv, [T; 3]); impl_simd_array_conversion_hue!(Hsluv, [saturation, l], white_point); +impl_struct_of_array_traits_hue!(Hsluv, LuvHueIter, [saturation, l], white_point); impl_eq_hue!(Hsluv, LuvHue, [hue, saturation, l]); @@ -385,6 +392,7 @@ where } } +/// Sample HSLuv colors uniformly. #[cfg(feature = "random")] pub struct UniformHsluv where @@ -557,6 +565,24 @@ mod test { assert_relative_eq!(Hsluv::::max_l(), 100.0); } + struct_of_arrays_tests!( + Hsluv, + Hsluv::new(0.1f32, 0.2, 0.3), + Hsluv::new(0.2, 0.3, 0.4), + Hsluv::new(0.3, 0.4, 0.5) + ); + + mod alpha { + use crate::{hsluv::Hsluva, white_point::D65}; + + struct_of_arrays_tests!( + Hsluva, + Hsluva::new(0.1f32, 0.2, 0.3, 0.4), + Hsluva::new(0.2, 0.3, 0.4, 0.5), + Hsluva::new(0.3, 0.4, 0.5, 0.6) + ); + } + #[cfg(feature = "serializing")] #[test] fn serialize() { diff --git a/palette/src/hsv.rs b/palette/src/hsv.rs index 0503dfef2..bea15ba91 100644 --- a/palette/src/hsv.rs +++ b/palette/src/hsv.rs @@ -1,3 +1,5 @@ +//! Types for the HSV color space. + use core::{ any::TypeId, marker::PhantomData, @@ -24,6 +26,7 @@ use crate::{ clamp, clamp_assign, contrast_ratio, convert::FromColorUnclamped, encoding::Srgb, + hues::RgbHueIter, num::{ self, Arithmetics, FromScalarArray, IntoScalarArray, IsValidDivisor, MinMax, One, PartialCmp, Real, Zero, @@ -279,6 +282,9 @@ impl Alpha, A> { } } +impl_reference_component_methods_hue!(Hsv, [saturation, value], standard); +impl_struct_of_arrays_methods_hue!(Hsv, [saturation, value], standard); + impl FromColorUnclamped> for Hsv where S1: RgbStandard + 'static, @@ -634,6 +640,7 @@ impl_color_sub!(Hsv, [hue, saturation, value], standard); impl_array_casts!(Hsv, [T; 3]); impl_simd_array_conversion_hue!(Hsv, [saturation, value], standard); +impl_struct_of_array_traits_hue!(Hsv, RgbHueIter, [saturation, value], standard); impl_eq_hue!(Hsv, RgbHue, [hue, saturation, value]); @@ -666,6 +673,7 @@ where } } +/// Sample HSV colors uniformly. #[cfg(feature = "random")] pub struct UniformHsv where @@ -834,6 +842,24 @@ mod test { assert_relative_eq!(Hsv::::max_value(), 1.0,); } + struct_of_arrays_tests!( + Hsv, + Hsv::new(0.1f32, 0.2, 0.3), + Hsv::new(0.2, 0.3, 0.4), + Hsv::new(0.3, 0.4, 0.5) + ); + + mod alpha { + use crate::{encoding::Srgb, hsv::Hsva}; + + struct_of_arrays_tests!( + Hsva, + Hsva::new(0.1f32, 0.2, 0.3, 0.4), + Hsva::new(0.2, 0.3, 0.4, 0.5), + Hsva::new(0.3, 0.4, 0.5, 0.6) + ); + } + #[cfg(feature = "serializing")] #[test] fn serialize() { diff --git a/palette/src/hues.rs b/palette/src/hues.rs index b9be904fa..0e74bf53b 100644 --- a/palette/src/hues.rs +++ b/palette/src/hues.rs @@ -1,3 +1,5 @@ +//! Hues and hue related types. + #[cfg(any(feature = "approx", feature = "random"))] use core::ops::Mul; @@ -30,7 +32,7 @@ use crate::{ }; macro_rules! make_hues { - ($($(#[$doc:meta])+ struct $name:ident;)+) => ($( + ($($(#[$doc:meta])+ struct $name:ident; $iter_name:ident)+) => ($( $(#[$doc])+ /// /// The hue is a circular type, where `0` and `360` is the same, and @@ -159,6 +161,125 @@ macro_rules! make_hues { } } + impl $name<&T> { + /// Get an owned, copied version of this hue. + #[inline(always)] + pub fn copied(&self) -> $name + where + T: Copy, + { + $name(*self.0) + } + + /// Get an owned, cloned version of this hue. + #[inline(always)] + pub fn cloned(&self) -> $name + where + T: Clone, + { + $name(self.0.clone()) + } + } + + impl $name<&mut T> { + /// Update this hue with a new value. + #[inline(always)] + pub fn set(&mut self, value: $name) { + *self.0 = value.0; + } + + /// Borrow this hue's value as shared references. + #[inline(always)] + pub fn as_ref(&self) -> $name<&T> { + $name(&*self.0) + } + + /// Get an owned, copied version of this hue. + #[inline(always)] + pub fn copied(&self) -> $name + where + T: Copy, + { + $name(*self.0) + } + + /// Get an owned, cloned version of this hue. + #[inline(always)] + pub fn cloned(&self) -> $name + where + T: Clone, + { + $name(self.0.clone()) + } + } + + impl $name { + /// Return an iterator over the hues in the wrapped collection. + #[inline(always)] + pub fn iter<'a>(&'a self) -> <&'a Self as IntoIterator>::IntoIter where &'a Self: IntoIterator { + self.into_iter() + } + + /// Return an iterator that allows modifying the hues in the wrapped collection. + #[inline(always)] + pub fn iter_mut<'a>(&'a mut self) -> <&'a mut Self as IntoIterator>::IntoIter where &'a mut Self: IntoIterator { + self.into_iter() + } + + /// Get a hue, or slice of hues, with references to the values at `index`. See [`slice::get`] for details. + #[inline(always)] + pub fn get<'a, I, T>(&'a self, index: I) -> Option<$name<&>::Output>> + where + T: 'a, + C: AsRef<[T]>, + I: core::slice::SliceIndex<[T]> + Clone, + { + self.0.as_ref().get(index).map($name) + } + + /// Get a hue, or slice of hues, that allows modifying the values at `index`. See [`slice::get_mut`] for details. + #[inline(always)] + pub fn get_mut<'a, I, T>(&'a mut self, index: I) -> Option<$name<&mut >::Output>> + where + T: 'a, + C: AsMut<[T]>, + I: core::slice::SliceIndex<[T]> + Clone, + { + self.0.as_mut().get_mut(index).map($name) + } + } + + #[cfg(feature = "std")] + impl $name> { + /// Create a struct with a vector with a minimum capacity. See [`Vec::with_capacity`] for details. + pub fn with_capacity(capacity: usize) -> Self { + Self(Vec::with_capacity(capacity)) + } + + /// Push an additional hue onto the hue vector. See [`Vec::push`] for details. + pub fn push(&mut self, value: $name) { + self.0.push(value.0); + } + + /// Pop a hue from the hue vector. See [`Vec::pop`] for details. + pub fn pop(&mut self) -> Option<$name> { + self.0.pop().map($name) + } + + /// Clear the hue vector. See [`Vec::clear`] for details. + pub fn clear(&mut self) { + self.0.clear(); + } + + /// Return an iterator that moves hues out of the specified range. + pub fn drain(&mut self, range: R) -> $iter_name> + where + R: core::ops::RangeBounds + Clone, + { + $iter_name(self.0.drain(range)) + } + } + impl From for $name { #[inline] fn from(degrees: T) -> $name { @@ -447,12 +568,95 @@ macro_rules! make_hues { } } + impl Extend for $name where C: Extend { + #[inline(always)] + fn extend>(&mut self, iter: I) { + self.0.extend(iter); + } + } + + impl IntoIterator for $name where C: IntoIterator { + type Item = $name; + type IntoIter = $iter_name; + + #[inline(always)] + fn into_iter(self) -> Self::IntoIter { + $iter_name(self.0.into_iter()) + } + } + + impl<'a, C> IntoIterator for &'a $name where &'a C: IntoIterator { + type Item = $name<<&'a C as IntoIterator>::Item>; + type IntoIter = $iter_name<<&'a C as IntoIterator>::IntoIter>; + + #[inline(always)] + fn into_iter(self) -> Self::IntoIter { + $iter_name(self.0.into_iter()) + } + } + + impl<'a, C> IntoIterator for &'a mut $name where &'a mut C: IntoIterator { + type Item = $name<<&'a mut C as IntoIterator>::Item>; + type IntoIter = $iter_name<<&'a mut C as IntoIterator>::IntoIter>; + + #[inline(always)] + fn into_iter(self) -> Self::IntoIter { + $iter_name(self.0.into_iter()) + } + } + + #[doc = concat!("Iterator over [`", stringify!($name), "`] values.")] + pub struct $iter_name(I); + + impl Iterator for $iter_name + where + I: Iterator, + { + type Item = $name; + + #[inline(always)] + fn next(&mut self) -> Option { + self.0.next().map($name) + } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + + #[inline(always)] + fn count(self) -> usize { + self.0.count() + } + } + + impl DoubleEndedIterator for $iter_name + where + I: DoubleEndedIterator, + { + #[inline(always)] + fn next_back(&mut self) -> Option { + self.0.next_back().map($name) + } + } + + impl ExactSizeIterator for $iter_name + where + I: ExactSizeIterator, + { + #[inline(always)] + fn len(&self) -> usize { + self.0.len() + } + } + #[cfg(feature = "random")] impl Distribution<$name> for Standard where T: RealAngle + FullRotation + Mul, Standard: Distribution, { + #[inline(always)] fn sample(&self, rng: &mut R) -> $name { $name::from_degrees(rng.gen() * T::full_rotation()) } @@ -471,25 +675,26 @@ make_hues! { /// It's measured in degrees and it's based on the four physiological /// elementary colors _red_, _yellow_, _green_ and _blue_. This makes it /// different from the hue of RGB based color spaces. - struct LabHue; + struct LabHue; LabHueIter /// A hue type for the CIE L\*u\*v\* family of color spaces. - struct LuvHue; + struct LuvHue; LuvHueIter /// A hue type for the RGB family of color spaces. /// /// It's measured in degrees and uses the three additive primaries _red_, /// _green_ and _blue_. - struct RgbHue; + struct RgbHue; RgbHueIter /// A hue type for the Oklab color space. /// /// It's measured in degrees. - struct OklabHue; + struct OklabHue; OklabHueIter } macro_rules! impl_uniform { ( $uni_ty: ident , $base_ty: ident) => { + #[doc = concat!("Sample [`", stringify!($base_ty), "`] uniformly.")] #[cfg(feature = "random")] pub struct $uni_ty where diff --git a/palette/src/hwb.rs b/palette/src/hwb.rs index 0c14b4c93..d8c65a9bc 100644 --- a/palette/src/hwb.rs +++ b/palette/src/hwb.rs @@ -1,3 +1,5 @@ +//! Types for the HWB color space. + use core::{ any::TypeId, marker::PhantomData, @@ -21,6 +23,7 @@ use crate::{ clamp, clamp_min, clamp_min_assign, contrast_ratio, convert::FromColorUnclamped, encoding::Srgb, + hues::RgbHueIter, num::{ self, Arithmetics, FromScalarArray, IntoScalarArray, MinMax, One, PartialCmp, Real, Zero, }, @@ -296,6 +299,9 @@ impl Alpha, A> { } } +impl_reference_component_methods_hue!(Hwb, [whiteness, blackness], standard); +impl_struct_of_arrays_methods_hue!(Hwb, [whiteness, blackness], standard); + impl FromColorUnclamped> for Hwb where S1: RgbStandard + 'static, @@ -576,6 +582,7 @@ impl_color_sub!(Hwb, [hue, whiteness, blackness], standard); impl_array_casts!(Hwb, [T; 3]); impl_simd_array_conversion_hue!(Hwb, [whiteness, blackness], standard); +impl_struct_of_array_traits_hue!(Hwb, RgbHueIter, [whiteness, blackness], standard); #[cfg(feature = "approx")] impl AbsDiffEq for Hwb @@ -697,6 +704,7 @@ where } } +/// Sample HWB colors uniformly. #[cfg(feature = "random")] pub struct UniformHwb where @@ -863,6 +871,24 @@ mod test { assert_relative_eq!(Hwb::::max_blackness(), 1.0,); } + struct_of_arrays_tests!( + Hwb, + Hwb::new(0.1f32, 0.2, 0.3), + Hwb::new(0.2, 0.3, 0.4), + Hwb::new(0.3, 0.4, 0.5) + ); + + mod alpha { + use crate::{encoding::Srgb, hwb::Hwba}; + + struct_of_arrays_tests!( + Hwba, + Hwba::new(0.1f32, 0.2, 0.3, 0.4), + Hwba::new(0.2, 0.3, 0.4, 0.5), + Hwba::new(0.3, 0.4, 0.5, 0.6) + ); + } + #[cfg(feature = "serializing")] #[test] fn serialize() { diff --git a/palette/src/lab.rs b/palette/src/lab.rs index bbe430172..ab4ad7010 100644 --- a/palette/src/lab.rs +++ b/palette/src/lab.rs @@ -1,3 +1,5 @@ +//! Types for the CIE L\*a\*b\* (CIELAB) color space. + use core::{ marker::PhantomData, ops::{Add, AddAssign, BitAnd, BitOr, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, @@ -174,6 +176,9 @@ impl Alpha, A> { } } +impl_reference_component_methods!(Lab, [l, a, b], white_point); +impl_struct_of_arrays_methods!(Lab, [l, a, b], white_point); + impl FromColorUnclamped> for Lab { fn from_color_unclamped(color: Lab) -> Self { color @@ -379,6 +384,7 @@ impl_color_div!(Lab, [l, a, b], white_point); impl_array_casts!(Lab, [T; 3]); impl_simd_array_conversion!(Lab, [l, a, b], white_point); +impl_struct_of_array_traits!(Lab, [l, a, b], white_point); impl_eq!(Lab, [l, a, b]); @@ -416,6 +422,7 @@ where } } +/// Sample CIE L\*a\*b\* (CIELAB) colors uniformly. #[cfg(feature = "random")] pub struct UniformLab where @@ -546,6 +553,24 @@ mod test { assert_relative_eq!(Lab::::max_b(), 127.0); } + struct_of_arrays_tests!( + Lab, + Lab::new(0.1f32, 0.2, 0.3), + Lab::new(0.2, 0.3, 0.4), + Lab::new(0.3, 0.4, 0.5) + ); + + mod alpha { + use crate::{lab::Laba, white_point::D65}; + + struct_of_arrays_tests!( + Laba, + Laba::new(0.1f32, 0.2, 0.3, 0.4), + Laba::new(0.2, 0.3, 0.4, 0.5), + Laba::new(0.3, 0.4, 0.5, 0.6) + ); + } + #[cfg(feature = "serializing")] #[test] fn serialize() { diff --git a/palette/src/lch.rs b/palette/src/lch.rs index bb026a06c..04c97ed13 100644 --- a/palette/src/lch.rs +++ b/palette/src/lch.rs @@ -1,3 +1,5 @@ +//! Types for the CIE L\*C\*h° color space. + use core::{ marker::PhantomData, ops::{Add, AddAssign, BitAnd, BitOr, Sub, SubAssign}, @@ -21,6 +23,7 @@ use crate::{ color_difference::{get_ciede2000_difference, Ciede2000, LabColorDiff}, contrast_ratio, convert::{FromColorUnclamped, IntoColorUnclamped}, + hues::LabHueIter, num::{ self, Abs, Arithmetics, Exp, FromScalarArray, Hypot, IntoScalarArray, MinMax, One, PartialCmp, Powi, Real, Sqrt, Trigonometry, Zero, @@ -179,6 +182,9 @@ impl Alpha, A> { } } +impl_reference_component_methods_hue!(Lch, [l, chroma], white_point); +impl_struct_of_arrays_methods_hue!(Lch, [l, chroma], white_point); + impl FromColorUnclamped> for Lch { fn from_color_unclamped(color: Lch) -> Self { color @@ -398,6 +404,7 @@ impl_color_sub!(Lch, [l, chroma, hue], white_point); impl_array_casts!(Lch, [T; 3]); impl_simd_array_conversion_hue!(Lch, [l, chroma], white_point); +impl_struct_of_array_traits_hue!(Lch, LabHueIter, [l, chroma], white_point); impl_eq_hue!(Lch, LabHue, [l, chroma, hue]); @@ -434,6 +441,7 @@ where } } +/// Sample CIE L\*C\*h° colors uniformly. #[cfg(feature = "random")] pub struct UniformLch where @@ -553,6 +561,24 @@ mod test { assert_relative_eq!(Lch::::max_extended_chroma(), 181.01933598375618); } + struct_of_arrays_tests!( + Lch, + Lch::new(0.1f32, 0.2, 0.3), + Lch::new(0.2, 0.3, 0.4), + Lch::new(0.3, 0.4, 0.5) + ); + + mod alpha { + use crate::{lch::Lcha, white_point::D65}; + + struct_of_arrays_tests!( + Lcha, + Lcha::new(0.1f32, 0.2, 0.3, 0.4), + Lcha::new(0.2, 0.3, 0.4, 0.5), + Lcha::new(0.3, 0.4, 0.5, 0.6) + ); + } + #[cfg(feature = "serializing")] #[test] fn serialize() { diff --git a/palette/src/lchuv.rs b/palette/src/lchuv.rs index 4db2911e3..6d0ce4f89 100644 --- a/palette/src/lchuv.rs +++ b/palette/src/lchuv.rs @@ -1,3 +1,5 @@ +//! Types for the CIE L\*C\*uv h°uv color space. + use core::{ marker::PhantomData, ops::{Add, AddAssign, BitAnd, Mul, Sub, SubAssign}, @@ -22,6 +24,7 @@ use crate::{ bool_mask::{HasBoolMask, LazySelect}, clamp, clamp_assign, contrast_ratio, convert::FromColorUnclamped, + hues::LuvHueIter, luv_bounds::LuvBounds, num::{ self, Arithmetics, FromScalarArray, Hypot, IntoScalarArray, MinMax, One, PartialCmp, Powi, @@ -174,6 +177,9 @@ impl Alpha, A> { } } +impl_reference_component_methods_hue!(Lchuv, [l, chroma], white_point); +impl_struct_of_arrays_methods_hue!(Lchuv, [l, chroma], white_point); + impl FromColorUnclamped> for Lchuv { fn from_color_unclamped(color: Lchuv) -> Self { color @@ -354,6 +360,7 @@ impl_color_sub!(Lchuv, [l, chroma, hue], white_point); impl_array_casts!(Lchuv, [T; 3]); impl_simd_array_conversion_hue!(Lchuv, [l, chroma], white_point); +impl_struct_of_array_traits_hue!(Lchuv, LuvHueIter, [l, chroma], white_point); impl_eq_hue!(Lchuv, LuvHue, [l, chroma, hue]); @@ -390,6 +397,7 @@ where } } +/// Sample CIE L\*C\*uv h°uv colors uniformly. #[cfg(feature = "random")] pub struct UniformLchuv where @@ -525,6 +533,24 @@ mod test { assert_relative_eq!(Lchuv::::max_chroma(), 180.0); } + struct_of_arrays_tests!( + Lchuv, + Lchuv::new(0.1f32, 0.2, 0.3), + Lchuv::new(0.2, 0.3, 0.4), + Lchuv::new(0.3, 0.4, 0.5) + ); + + mod alpha { + use crate::{lchuv::Lchuva, white_point::D65}; + + struct_of_arrays_tests!( + Lchuva, + Lchuva::new(0.1f32, 0.2, 0.3, 0.4), + Lchuva::new(0.2, 0.3, 0.4, 0.5), + Lchuva::new(0.3, 0.4, 0.5, 0.6) + ); + } + #[cfg(feature = "serializing")] #[test] fn serialize() { diff --git a/palette/src/lib.rs b/palette/src/lib.rs index 84ab88bd2..c4e0306a5 100644 --- a/palette/src/lib.rs +++ b/palette/src/lib.rs @@ -256,30 +256,50 @@ use core::ops::{BitAndAssign, Neg}; use bool_mask::{BoolMask, HasBoolMask}; use luma::Luma; +#[doc(inline)] pub use alpha::{Alpha, WithAlpha}; +#[doc(inline)] pub use hsl::{Hsl, Hsla}; +#[doc(inline)] pub use hsluv::{Hsluv, Hsluva}; +#[doc(inline)] pub use hsv::{Hsv, Hsva}; +#[doc(inline)] pub use hwb::{Hwb, Hwba}; +#[doc(inline)] pub use lab::{Lab, Laba}; +#[doc(inline)] pub use lch::{Lch, Lcha}; +#[doc(inline)] pub use lchuv::{Lchuv, Lchuva}; +#[doc(inline)] pub use luma::{GammaLuma, GammaLumaa, LinLuma, LinLumaa, SrgbLuma, SrgbLumaa}; +#[doc(inline)] pub use luv::{Luv, Luva}; +#[doc(inline)] pub use okhsl::{Okhsl, Okhsla}; +#[doc(inline)] pub use okhsv::{Okhsv, Okhsva}; +#[doc(inline)] pub use okhwb::{Okhwb, Okhwba}; +#[doc(inline)] pub use oklab::{Oklab, Oklaba}; +#[doc(inline)] pub use oklch::{Oklch, Oklcha}; +#[doc(inline)] pub use rgb::{GammaSrgb, GammaSrgba, LinSrgb, LinSrgba, Srgb, Srgba}; +#[doc(inline)] pub use xyz::{Xyz, Xyza}; +#[doc(inline)] pub use yxy::{Yxy, Yxya}; +#[doc(inline)] +pub use hues::{LabHue, LuvHue, OklabHue, RgbHue}; + #[allow(deprecated)] pub use color_difference::ColorDifference; pub use convert::{FromColor, FromColorMut, FromColorMutGuard, IntoColor, IntoColorMut}; -pub use hues::{LabHue, LuvHue, OklabHue, RgbHue}; pub use matrix::Mat3; pub use relative_contrast::{contrast_ratio, RelativeContrast}; @@ -451,7 +471,7 @@ mod random_sampling; #[cfg(feature = "serializing")] pub mod serde; -mod alpha; +pub mod alpha; pub mod angle; pub mod blend; pub mod bool_mask; @@ -460,30 +480,30 @@ pub mod chromatic_adaptation; pub mod color_difference; pub mod convert; pub mod encoding; -mod hsl; -mod hsluv; -mod hsv; -mod hues; -mod hwb; -mod lab; -mod lch; -mod lchuv; +pub mod hsl; +pub mod hsluv; +pub mod hsv; +pub mod hues; +pub mod hwb; +pub mod lab; +pub mod lch; +pub mod lchuv; pub mod luma; -mod luv; +pub mod luv; mod luv_bounds; pub mod num; mod ok_utils; -mod okhsl; -mod okhsv; -mod okhwb; -mod oklab; -mod oklch; +pub mod okhsl; +pub mod okhsv; +pub mod okhwb; +pub mod oklab; +pub mod oklch; mod relative_contrast; pub mod rgb; pub mod stimulus; pub mod white_point; -mod xyz; -mod yxy; +pub mod xyz; +pub mod yxy; #[cfg(test)] #[cfg(feature = "approx")] diff --git a/palette/src/luma.rs b/palette/src/luma.rs index c36ada64f..b9e123022 100644 --- a/palette/src/luma.rs +++ b/palette/src/luma.rs @@ -1,4 +1,4 @@ -//! Luminance types. +//! Types for luma and luminance (grayscale) values. pub mod channels; mod luma; @@ -6,7 +6,7 @@ mod luma; use crate::encoding::{Gamma, Linear, Srgb}; use crate::white_point::D65; -pub use self::luma::{Luma, Lumaa}; +pub use self::luma::{Iter, Luma, Lumaa}; /// sRGB encoded luminance. pub type SrgbLuma = Luma; diff --git a/palette/src/luma/luma.rs b/palette/src/luma/luma.rs index da1dfa519..527ae86f8 100644 --- a/palette/src/luma/luma.rs +++ b/palette/src/luma/luma.rs @@ -547,6 +547,9 @@ impl Alpha, T>, A> { } } +impl_reference_component_methods!(Luma, [luma], standard); +impl_struct_of_arrays_methods!(Luma, [luma], standard); + impl FromColorUnclamped> for Luma where S1: LumaStandard + 'static, @@ -823,6 +826,7 @@ impl From> for u16 { } impl_simd_array_conversion!(Luma, [luma], standard); +impl_struct_of_array_traits!(Luma, [luma], standard); #[cfg(feature = "approx")] impl AbsDiffEq for Luma @@ -1065,6 +1069,24 @@ mod test { assert_relative_eq!(Luma::::max_luma(), 1.0); } + struct_of_arrays_tests!( + Luma, + Luma::new(0.1f32), + Luma::new(0.2), + Luma::new(0.3) + ); + + mod alpha { + use crate::{encoding::Srgb, luma::Lumaa}; + + struct_of_arrays_tests!( + Lumaa, + Lumaa::new(0.1f32, 0.4), + Lumaa::new(0.2, 0.5), + Lumaa::new(0.3, 0.6) + ); + } + #[cfg(feature = "serializing")] #[test] fn serialize() { diff --git a/palette/src/luv.rs b/palette/src/luv.rs index dd5b577ae..0a801146e 100644 --- a/palette/src/luv.rs +++ b/palette/src/luv.rs @@ -1,3 +1,5 @@ +//! Types for the CIE L\*u\*v\* (CIELUV) color space. + use core::{ marker::PhantomData, ops::{Add, AddAssign, BitAnd, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, @@ -169,6 +171,9 @@ impl Alpha, A> { } } +impl_reference_component_methods!(Luv, [l, u, v], white_point); +impl_struct_of_arrays_methods!(Luv, [l, u, v], white_point); + impl FromColorUnclamped> for Luv { fn from_color_unclamped(color: Luv) -> Self { color @@ -335,6 +340,7 @@ impl_color_div!(Luv, [l, u, v], white_point); impl_array_casts!(Luv, [T; 3]); impl_simd_array_conversion!(Luv, [l, u, v], white_point); +impl_struct_of_array_traits!(Luv, [l, u, v], white_point); impl_eq!(Luv, [l, u, v]); @@ -372,6 +378,7 @@ where } } +/// Sample CIE L\*u\*v\* (CIELUV) colors uniformly. #[cfg(feature = "random")] pub struct UniformLuv where @@ -518,6 +525,24 @@ mod test { assert_relative_eq!(Luv::::max_v(), 108.0); } + struct_of_arrays_tests!( + Luv, + Luv::new(0.1f32, 0.2, 0.3), + Luv::new(0.2, 0.3, 0.4), + Luv::new(0.3, 0.4, 0.5) + ); + + mod alpha { + use crate::{luv::Luva, white_point::D65}; + + struct_of_arrays_tests!( + Luva, + Luva::new(0.1f32, 0.2, 0.3, 0.4), + Luva::new(0.2, 0.3, 0.4, 0.5), + Luva::new(0.3, 0.4, 0.5, 0.6) + ); + } + #[cfg(feature = "serializing")] #[test] fn serialize() { diff --git a/palette/src/macros.rs b/palette/src/macros.rs index 1aa73c00b..9b8d391ed 100644 --- a/palette/src/macros.rs +++ b/palette/src/macros.rs @@ -33,6 +33,12 @@ mod convert; #[macro_use] mod color_difference; +#[macro_use] +mod struct_of_arrays; + +#[macro_use] +mod reference_component; + #[cfg(feature = "random")] #[macro_use] mod random; diff --git a/palette/src/macros/reference_component.rs b/palette/src/macros/reference_component.rs new file mode 100644 index 000000000..0541dfd79 --- /dev/null +++ b/palette/src/macros/reference_component.rs @@ -0,0 +1,267 @@ +macro_rules! impl_reference_component_methods { + ( $self_ty: ident , [$($element: ident),+] $(, $phantom: ident)?) => { + impl_reference_component_methods!($self_ty<>, [$($element),+] $(, $phantom)?); + }; + ( $self_ty: ident < $($phantom_ty: ident)? > , [$($element: ident),+] $(, $phantom: ident)?) => { + impl<$($phantom_ty,)? T> $self_ty<$($phantom_ty,)? &T> { + /// Get an owned, copied version of this color. + #[inline] + pub fn copied(&self) -> $self_ty<$($phantom_ty,)? T> + where + T: Copy, + { + $self_ty { + $($element: *self.$element,)+ + $($phantom: PhantomData,)? + } + } + + /// Get an owned, cloned version of this color. + #[inline] + pub fn cloned(&self) -> $self_ty<$($phantom_ty,)? T> + where + T: Clone, + { + $self_ty { + $($element: self.$element.clone(),)+ + $($phantom: PhantomData,)? + } + } + } + + impl<$($phantom_ty,)? T> $self_ty<$($phantom_ty,)? &mut T> { + /// Update this color with new values. + #[inline] + pub fn set(&mut self, value: $self_ty<$($phantom_ty,)? T>) { + $(*self.$element = value.$element;)+ + } + + /// Borrow this color's components as shared references. + #[inline] + pub fn as_refs(&self) -> $self_ty<$($phantom_ty,)? &T> { + $self_ty { + $($element: &*self.$element,)+ + $($phantom: PhantomData,)? + } + } + + /// Get an owned, copied version of this color. + #[inline] + pub fn copied(&self) -> $self_ty<$($phantom_ty,)? T> + where + T: Copy, + { + $self_ty { + $($element: *self.$element,)+ + $($phantom: PhantomData,)? + } + } + + /// Get an owned, cloned version of this color. + #[inline] + pub fn cloned(&self) -> $self_ty<$($phantom_ty,)? T> + where + T: Clone, + { + $self_ty { + $($element: self.$element.clone(),)+ + $($phantom: PhantomData,)? + } + } + } + + impl<$($phantom_ty,)? T, A> crate::Alpha<$self_ty<$($phantom_ty,)? &T>, &A> { + /// Get an owned, copied version of this color. + #[inline] + pub fn copied(&self) -> crate::Alpha<$self_ty<$($phantom_ty,)? T>, A> + where + T: Copy, + A: Copy, + { + crate::Alpha{color: self.color.copied(), alpha: *self.alpha} + } + + /// Get an owned, cloned version of this color. + #[inline] + pub fn cloned(&self) -> crate::Alpha<$self_ty<$($phantom_ty,)? T>, A> + where + T: Clone, + A: Clone, + { + crate::Alpha{color: self.color.cloned(), alpha: self.alpha.clone()} + } + } + + impl<$($phantom_ty,)? T, A> crate::Alpha<$self_ty<$($phantom_ty,)? &mut T>, &mut A> { + /// Update this color with new values. + #[inline] + pub fn set(&mut self, value: crate::Alpha<$self_ty<$($phantom_ty,)? T>, A>) { + self.color.set(value.color); + *self.alpha = value.alpha; + } + + /// Borrow this color's components as shared references. + #[inline] + pub fn as_refs(&self) -> crate::Alpha<$self_ty<$($phantom_ty,)? &T>, &A>{ + crate::Alpha{color: self.color.as_refs(), alpha: &*self.alpha} + } + + /// Get an owned, copied version of this color. + #[inline] + pub fn copied(&self) -> crate::Alpha<$self_ty<$($phantom_ty,)? T>, A> + where + T: Copy, + A: Copy, + { + crate::Alpha{color: self.color.copied(), alpha: *self.alpha} + } + + /// Get an owned, cloned version of this color. + #[inline] + pub fn cloned(&self) -> crate::Alpha<$self_ty<$($phantom_ty,)? T>, A> + where + T: Clone, + A: Clone, + { + crate::Alpha{color: self.color.cloned(), alpha: self.alpha.clone()} + } + } + } +} + +macro_rules! impl_reference_component_methods_hue { + ( $self_ty: ident , [$($element: ident),+] $(, $phantom: ident)?) => { + impl_reference_component_methods_hue!($self_ty<>, [$($element),+] $(, $phantom)?); + }; + ( $self_ty: ident < $($phantom_ty: ident)? > , [$($element: ident),+] $(, $phantom: ident)?) => { + impl<$($phantom_ty,)? T> $self_ty<$($phantom_ty,)? &T> { + /// Get an owned, copied version of this color. + #[inline] + pub fn copied(&self) -> $self_ty<$($phantom_ty,)? T> + where + T: Copy, + { + $self_ty { + hue: self.hue.copied(), + $($element: *self.$element,)+ + $($phantom: PhantomData,)? + } + } + + /// Get an owned, cloned version of this color. + #[inline] + pub fn cloned(&self) -> $self_ty<$($phantom_ty,)? T> + where + T: Clone, + { + $self_ty { + hue: self.hue.cloned(), + $($element: self.$element.clone(),)+ + $($phantom: PhantomData,)? + } + } + } + + impl<$($phantom_ty,)? T> $self_ty<$($phantom_ty,)? &mut T> { + /// Update this color with new values. + #[inline] + pub fn set(&mut self, value: $self_ty<$($phantom_ty,)? T>) { + self.hue.set(value.hue); + $(*self.$element = value.$element;)+ + } + + /// Borrow this color's components as shared references. + #[inline] + pub fn as_refs(&self) -> $self_ty<$($phantom_ty,)? &T> { + $self_ty { + hue: self.hue.as_ref(), + $($element: &*self.$element,)+ + $($phantom: PhantomData,)? + } + } + + /// Get an owned, copied version of this color. + #[inline] + pub fn copied(&self) -> $self_ty<$($phantom_ty,)? T> + where + T: Copy, + { + $self_ty { + hue: self.hue.copied(), + $($element: *self.$element,)+ + $($phantom: PhantomData,)? + } + } + + /// Get an owned, cloned version of this color. + #[inline] + pub fn cloned(&self) -> $self_ty<$($phantom_ty,)? T> + where + T: Clone, + { + $self_ty { + hue: self.hue.cloned(), + $($element: self.$element.clone(),)+ + $($phantom: PhantomData,)? + } + } + } + + impl<$($phantom_ty,)? T, A> crate::Alpha<$self_ty<$($phantom_ty,)? &T>, &A> { + /// Get an owned, copied version of this color. + #[inline] + pub fn copied(&self) -> crate::Alpha<$self_ty<$($phantom_ty,)? T>, A> + where + T: Copy, + A: Copy, + { + crate::Alpha{color: self.color.copied(), alpha: *self.alpha} + } + + /// Get an owned, cloned version of this color. + #[inline] + pub fn cloned(&self) -> crate::Alpha<$self_ty<$($phantom_ty,)? T>, A> + where + T: Clone, + A: Clone, + { + crate::Alpha{color: self.color.cloned(), alpha: self.alpha.clone()} + } + } + + impl<$($phantom_ty,)? T, A> crate::Alpha<$self_ty<$($phantom_ty,)? &mut T>, &mut A> { + /// Update this color with new values. + #[inline] + pub fn set(&mut self, value: crate::Alpha<$self_ty<$($phantom_ty,)? T>, A>) { + self.color.set(value.color); + *self.alpha = value.alpha; + } + + /// Borrow this color's components as shared references. + #[inline] + pub fn as_refs(&self) -> crate::Alpha<$self_ty<$($phantom_ty,)? &T>, &A>{ + crate::Alpha{color: self.color.as_refs(), alpha: &*self.alpha} + } + + /// Get an owned, copied version of this color. + #[inline] + pub fn copied(&self) -> crate::Alpha<$self_ty<$($phantom_ty,)? T>, A> + where + T: Copy, + A: Copy, + { + crate::Alpha{color: self.color.copied(), alpha: *self.alpha} + } + + /// Get an owned, cloned version of this color. + #[inline] + pub fn cloned(&self) -> crate::Alpha<$self_ty<$($phantom_ty,)? T>, A> + where + T: Clone, + A: Clone, + { + crate::Alpha{color: self.color.cloned(), alpha: self.alpha.clone()} + } + } + } +} diff --git a/palette/src/macros/struct_of_arrays.rs b/palette/src/macros/struct_of_arrays.rs new file mode 100644 index 000000000..bbc436c3a --- /dev/null +++ b/palette/src/macros/struct_of_arrays.rs @@ -0,0 +1,835 @@ +macro_rules! first { + (($($first: tt)+) $(, ($($rest: tt)+))*) => { + $($first)+ + }; +} + +macro_rules! skip_first { + (($($first: tt)+) $(, ($($rest: tt)+))*) => { + $($($rest)+)* + }; +} + +macro_rules! impl_struct_of_array_traits { + ( $self_ty: ident , [$($element: ident),+] $(, $phantom: ident)?) => { + impl_struct_of_array_traits!($self_ty<>, [$($element),+] $(, $phantom)?); + }; + ( $self_ty: ident < $($phantom_ty: ident)? > , [$($element: ident),+] $(, $phantom: ident)?) => { + impl<$($phantom_ty,)? T, C> Extend<$self_ty<$($phantom_ty,)? T>> for $self_ty<$($phantom_ty,)? C> + where + C: Extend, + { + #[inline(always)] + fn extend>>(&mut self, iter: I) { + let iter = iter.into_iter(); + + for color in iter { + $(self.$element.extend(core::iter::once(color.$element));)+ + } + } + } + + impl<$($phantom_ty,)? T, C> core::iter::FromIterator<$self_ty<$($phantom_ty,)? T>> for $self_ty<$($phantom_ty,)? C> + where + Self: Extend<$self_ty<$($phantom_ty,)? T>>, + C: Default, + { + #[inline(always)] + fn from_iter>>(iter: I) -> Self { + let mut result = Self { + $($element: C::default(),)+ + $($phantom: PhantomData)? + }; + result.extend(iter); + + result + } + } + + impl<$($phantom_ty,)? C> IntoIterator for $self_ty<$($phantom_ty,)? C> + where + C: IntoIterator, + { + type Item = $self_ty<$($phantom_ty,)? C::Item>; + + type IntoIter = Iter; + + #[inline(always)] + fn into_iter(self) -> Self::IntoIter { + Iter { + $($element: self.$element.into_iter(),)+ + $($phantom: PhantomData)? + } + } + } + + impl<'a, $($phantom_ty,)? C> IntoIterator for &'a $self_ty<$($phantom_ty,)? C> + where + &'a C: IntoIterator, + { + type Item = $self_ty<$($phantom_ty,)? <&'a C as IntoIterator>::Item>; + + type IntoIter = Iter<<&'a C as IntoIterator>::IntoIter $(,$phantom_ty)?>; + + #[inline(always)] + fn into_iter(self) -> Self::IntoIter { + Iter { + $($element: (&self.$element).into_iter(),)+ + $($phantom: PhantomData)? + } + } + } + + impl<'a, $($phantom_ty,)? C> IntoIterator for &'a mut $self_ty<$($phantom_ty,)? C> + where + &'a mut C: IntoIterator, + { + type Item = $self_ty<$($phantom_ty,)? <&'a mut C as IntoIterator>::Item>; + + type IntoIter = Iter<<&'a mut C as IntoIterator>::IntoIter $(,$phantom_ty)?>; + + #[inline(always)] + fn into_iter(self) -> Self::IntoIter { + Iter { + $($element: (&mut self.$element).into_iter(),)+ + $($phantom: PhantomData)? + } + } + } + + #[doc = concat!("An iterator for [`", stringify!($self_ty), "`] values.")] + pub struct Iter { + $(pub(crate) $element: I,)+ + $(pub(crate) $phantom: PhantomData<$phantom_ty>)? + } + + impl Iterator for Iter + where + I: Iterator, + { + type Item = $self_ty<$($phantom_ty,)? I::Item>; + + #[inline(always)] + fn next(&mut self) -> Option { + $(let $element = self.$element.next();)+ + + if let ($(Some($element),)+) = ($($element,)+) { + Some($self_ty { + $($element,)+ + $($phantom: PhantomData,)? + }) + } else { + None + } + } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + let hint = first!($((self.$element)),+).size_hint(); + skip_first!($((debug_assert_eq!(self.$element.size_hint(), hint, "the component iterators have different size hints");)),+); + + hint + } + + #[inline(always)] + fn count(self) -> usize { + let count = first!($((self.$element)),+).count(); + skip_first!($((debug_assert_eq!(self.$element.count(), count, "the component iterators have different counts");)),+); + + count + } + } + + impl DoubleEndedIterator for Iter + where + I: DoubleEndedIterator, + { + #[inline(always)] + fn next_back(&mut self) -> Option { + $(let $element = self.$element.next_back();)+ + + if let ($(Some($element),)+) = ($($element,)+) { + Some($self_ty { + $($element,)+ + $($phantom: PhantomData,)? + }) + } else { + None + } + } + } + + impl ExactSizeIterator for Iter + where + I: ExactSizeIterator, + { + #[inline(always)] + fn len(&self) -> usize { + let len = first!($((self.$element)),+).len(); + skip_first!($((debug_assert_eq!(self.$element.len(), len, "the component iterators have different lengths");)),+); + + len + } + } + } +} + +macro_rules! impl_struct_of_array_traits_hue { + ( $self_ty: ident, $hue_iter_ty: ident, [$($element: ident),+] $(, $phantom: ident)?) => { + impl_struct_of_array_traits_hue!($self_ty<>, $hue_iter_ty, [$($element),+] $(, $phantom)?); + }; + ( $self_ty: ident < $($phantom_ty: ident)? > , $hue_iter_ty: ident, [$($element: ident),+] $(, $phantom: ident)?) => { + impl<$($phantom_ty,)? T, C> Extend<$self_ty<$($phantom_ty,)? T>> for $self_ty<$($phantom_ty,)? C> + where + C: Extend, + { + #[inline(always)] + fn extend>>(&mut self, iter: I) { + let iter = iter.into_iter(); + + for color in iter { + self.hue.extend(core::iter::once(color.hue.into_inner())); + $(self.$element.extend(core::iter::once(color.$element));)+ + } + } + } + + impl<$($phantom_ty,)? T, C> core::iter::FromIterator<$self_ty<$($phantom_ty,)? T>> for $self_ty<$($phantom_ty,)? C> + where + Self: Extend<$self_ty<$($phantom_ty,)? T>>, + C: Default, + { + #[inline(always)] + fn from_iter>>(iter: I) -> Self { + let mut result = Self { + hue: C::default().into(), + $($element: C::default(),)+ + $($phantom: PhantomData)? + }; + result.extend(iter); + + result + } + } + + impl<$($phantom_ty,)? C> IntoIterator for $self_ty<$($phantom_ty,)? C> + where + C: IntoIterator, + { + type Item = $self_ty<$($phantom_ty,)? C::Item>; + + type IntoIter = Iter; + + #[inline(always)] + fn into_iter(self) -> Self::IntoIter { + Iter { + hue: self.hue.into_iter(), + $($element: self.$element.into_iter(),)+ + $($phantom: PhantomData)? + } + } + } + + impl<'a, $($phantom_ty,)? C> IntoIterator for &'a $self_ty<$($phantom_ty,)? C> + where + &'a C: IntoIterator + 'a, + { + type Item = $self_ty<$($phantom_ty,)? <&'a C as IntoIterator>::Item>; + + type IntoIter = Iter<<&'a C as IntoIterator>::IntoIter $(,$phantom_ty)?>; + + #[inline(always)] + fn into_iter(self) -> Self::IntoIter { + Iter { + hue: (&self.hue).into_iter(), + $($element: (&self.$element).into_iter(),)+ + $($phantom: PhantomData)? + } + } + } + + impl<'a, $($phantom_ty,)? C> IntoIterator for &'a mut $self_ty<$($phantom_ty,)? C> + where + &'a mut C: IntoIterator + 'a, + { + type Item = $self_ty<$($phantom_ty,)? <&'a mut C as IntoIterator>::Item>; + + type IntoIter = Iter<<&'a mut C as IntoIterator>::IntoIter $(,$phantom_ty)?>; + + #[inline(always)] + fn into_iter(self) -> Self::IntoIter { + Iter { + hue: (&mut self.hue).into_iter(), + $($element: (&mut self.$element).into_iter(),)+ + $($phantom: PhantomData)? + } + } + } + + #[doc = concat!("An iterator for [`", stringify!($self_ty), "`] values.")] + pub struct Iter { + pub(crate) hue: $hue_iter_ty, + $(pub(crate) $element: I,)+ + $(pub(crate) $phantom: PhantomData<$phantom_ty>)? + } + + impl Iterator for Iter + where + I: Iterator, + { + type Item = $self_ty<$($phantom_ty,)? I::Item>; + + #[inline(always)] + fn next(&mut self) -> Option { + let hue = self.hue.next(); + $(let $element = self.$element.next();)+ + + if let (Some(hue), $(Some($element),)+) = (hue, $($element,)+) { + Some($self_ty {hue $(, $element)+ $(, $phantom: PhantomData)?}) + } else { + None + } + } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + let hint = self.hue.size_hint(); + $(debug_assert_eq!(self.$element.size_hint(), hint, "the component iterators have different size hints");)+ + + hint + } + + #[inline(always)] + fn count(self) -> usize { + let count = self.hue.count(); + $(debug_assert_eq!(self.$element.count(), count, "the component iterators have different counts");)+ + + count + } + } + + impl DoubleEndedIterator for Iter + where + I: DoubleEndedIterator, + { + #[inline(always)] + fn next_back(&mut self) -> Option { + let hue = self.hue.next_back(); + $(let $element = self.$element.next_back();)+ + + if let (Some(hue), $(Some($element),)+) = (hue, $($element,)+) { + Some($self_ty {hue $(, $element)+ $(, $phantom: PhantomData)?}) + } else { + None + } + } + } + + impl ExactSizeIterator for Iter + where + I: ExactSizeIterator, + { + #[inline(always)] + fn len(&self) -> usize { + let len = self.hue.len(); + $(debug_assert_eq!(self.$element.len(), len, "the component iterators have different lengths");)+ + + len + } + } + } +} + +macro_rules! impl_struct_of_arrays_methods { + ( $self_ty: ident , [$($element: ident),+] $(, $phantom: ident)?) => { + impl_struct_of_arrays_methods!($self_ty<>, [$($element),+] $(, $phantom)?); + }; + ( $self_ty: ident < $($phantom_ty: ident)? > , [$($element: ident),+] $(, $phantom: ident)?) => { + impl<$($phantom_ty,)? C> $self_ty<$($phantom_ty,)? C> { + /// Return an iterator over the colors in the wrapped collections. + #[inline(always)] + pub fn iter<'a>(&'a self) -> <&'a Self as IntoIterator>::IntoIter where &'a Self: IntoIterator { + self.into_iter() + } + + /// Return an iterator that allows modifying the colors in the wrapped collections. + #[inline(always)] + pub fn iter_mut<'a>(&'a mut self) -> <&'a mut Self as IntoIterator>::IntoIter where &'a mut Self: IntoIterator { + self.into_iter() + } + + /// Get a color, or slice of colors, with references to the components at `index`. See [`slice::get`] for details. + #[inline(always)] + pub fn get<'a, I, T>(&'a self, index: I) -> Option<$self_ty<$($phantom_ty,)? &>::Output>> + where + T: 'a, + C: AsRef<[T]>, + I: core::slice::SliceIndex<[T]> + Clone, + { + $(let $element = self.$element.as_ref().get(index.clone());)+ + + if let ($(Some($element),)+) = ($($element,)+) { + Some($self_ty { + $($element,)+ + $($phantom: PhantomData,)? + }) + } else { + None + } + } + + /// Get a color, or slice of colors, that allows modifying the components at `index`. See [`slice::get_mut`] for details. + #[inline(always)] + pub fn get_mut<'a, I, T>(&'a mut self, index: I) -> Option<$self_ty<$($phantom_ty,)? &mut >::Output>> + where + T: 'a, + C: AsMut<[T]>, + I: core::slice::SliceIndex<[T]> + Clone, + { + $(let $element = self.$element.as_mut().get_mut(index.clone());)+ + + if let ($(Some($element),)+) = ($($element,)+) { + Some($self_ty { + $($element,)+ + $($phantom: PhantomData,)? + }) + } else { + None + } + } + } + + #[cfg(feature = "std")] + impl<$($phantom_ty,)? T> $self_ty<$($phantom_ty,)? Vec> { + /// Create a struct of vectors with a minimum capacity. See [`Vec::with_capacity`] for details. + #[inline(always)] + pub fn with_capacity(capacity: usize) -> Self { + $(let $element = Vec::with_capacity(capacity);)+ + + Self { + $($element,)+ + $($phantom: PhantomData,)? + } + } + + /// Push an additional color's components onto the component vectors. See [`Vec::push`] for details. + #[inline(always)] + pub fn push(&mut self, value: $self_ty<$($phantom_ty,)? T>) { + $(self.$element.push(value.$element);)+ + } + + /// Pop a color's components from the component vectors. See [`Vec::pop`] for details. + #[inline(always)] + pub fn pop(&mut self) -> Option<$self_ty<$($phantom_ty,)? T>> { + $(let $element = self.$element.pop();)+ + + Some($self_ty { + $($element: $element?,)+ + $($phantom: PhantomData,)? + }) + } + + /// Clear the component vectors. See [`Vec::clear`] for details. + #[inline(always)] + pub fn clear(&mut self) { + $(self.$element.clear();)+ + } + + /// Return an iterator that moves colors out of the specified range. + #[inline(always)] + pub fn drain(&mut self, range: R) -> Iter $(, $phantom_ty)?> + where + R: core::ops::RangeBounds + Clone, + { + Iter { + $($element: self.$element.drain(range.clone()),)+ + $($phantom: PhantomData,)? + } + } + } + + impl<$($phantom_ty,)? Ct, Ca> crate::Alpha<$self_ty<$($phantom_ty,)? Ct>, Ca> { + /// Get a color, or slice of colors, with references to the components at `index`. See [`slice::get`] for details. + #[inline(always)] + pub fn get<'a, I, T, A>(&'a self, index: I) -> Option>::Output>, + &>::Output + >> + where + T: 'a, + A: 'a, + Ct: AsRef<[T]>, + Ca: AsRef<[A]>, + I: core::slice::SliceIndex<[T]> + core::slice::SliceIndex<[A]> + Clone + { + let color = self.color.get(index.clone()); + let alpha = self.alpha.as_ref().get(index); + + if let (Some(color), Some(alpha)) = (color, alpha) { + Some(crate::Alpha{color, alpha}) + } else { + None + } + } + + /// Get a color, or slice of colors, that allows modifying the components at `index`. See [`slice::get_mut`] for details. + #[inline(always)] + pub fn get_mut<'a, I, T, A>(&'a mut self, index: I) -> Option>::Output>, + &mut >::Output + >> + where + T: 'a, + A: 'a, + Ct: AsMut<[T]>, + Ca: AsMut<[A]>, + I: core::slice::SliceIndex<[T]> + core::slice::SliceIndex<[A]> + Clone + { + let color = self.color.get_mut(index.clone()); + let alpha = self.alpha.as_mut().get_mut(index); + + if let (Some(color), Some(alpha)) = (color, alpha) { + Some(crate::Alpha{color, alpha}) + } else { + None + } + } + } + + #[cfg(feature = "std")] + impl<$($phantom_ty,)? T, A> crate::Alpha<$self_ty<$($phantom_ty,)? Vec>, Vec> { + /// Create a struct of vectors with a minimum capacity. See [`Vec::with_capacity`] for details. + #[inline(always)] + pub fn with_capacity(capacity: usize) -> Self { + crate::Alpha { + color: $self_ty::with_capacity(capacity), + alpha: Vec::with_capacity(capacity), + } + } + + /// Push an additional color's components onto the component vectors. See [`Vec::push`] for details. + #[inline(always)] + pub fn push(&mut self, value: crate::Alpha<$self_ty<$($phantom_ty,)? T>, A>) { + self.color.push(value.color); + self.alpha.push(value.alpha); + } + + /// Pop a color's components from the component vectors. See [`Vec::pop`] for details. + #[inline(always)] + pub fn pop(&mut self) -> Option, A>> { + let color = self.color.pop(); + let alpha = self.alpha.pop(); + + Some(crate::Alpha { + color: color?, + alpha: alpha?, + }) + } + + /// Clear the component vectors. See [`Vec::clear`] for details. + #[inline(always)] + pub fn clear(&mut self) { + self.color.clear(); + self.alpha.clear(); + } + + /// Return an iterator that moves colors out of the specified range. + #[inline(always)] + pub fn drain(&mut self, range: R) -> crate::alpha::Iter $(, $phantom_ty)?>, std::vec::Drain> + where + R: core::ops::RangeBounds + Clone, + { + crate::alpha::Iter { + color: self.color.drain(range.clone()), + alpha: self.alpha.drain(range), + } + } + } + }; +} + +macro_rules! impl_struct_of_arrays_methods_hue { + ( $self_ty: ident , [$($element: ident),+] $(, $phantom: ident)?) => { + impl_struct_of_arrays_methods_hue!($self_ty<>, [$($element),+] $(, $phantom)?); + }; + ( $self_ty: ident < $($phantom_ty: ident)? > , [$($element: ident),+] $(, $phantom: ident)?) => { + impl<$($phantom_ty,)? C> $self_ty<$($phantom_ty,)? C> { + /// Return an iterator over the colors in the wrapped collections. + #[inline(always)] + pub fn iter<'a>(&'a self) -> <&'a Self as IntoIterator>::IntoIter where &'a Self: IntoIterator { + self.into_iter() + } + + /// Return an iterator that allows modifying the colors in the wrapped collections. + #[inline(always)] + pub fn iter_mut<'a>(&'a mut self) -> <&'a mut Self as IntoIterator>::IntoIter where &'a mut Self: IntoIterator { + self.into_iter() + } + + /// Get a color, or slice of colors, with references to the components at `index`. See [`slice::get`] for details. + #[inline(always)] + pub fn get<'a, I, T>(&'a self, index: I) -> Option<$self_ty<$($phantom_ty,)? &>::Output>> + where + T: 'a, + C: AsRef<[T]>, + I: core::slice::SliceIndex<[T]> + Clone, + { + let hue = self.hue.get(index.clone()); + $(let $element = self.$element.as_ref().get(index.clone());)+ + + if let (Some(hue) $(, Some($element))+) = (hue $(,$element)+) { + Some($self_ty {hue $(, $element)+ $(, $phantom: PhantomData)?}) + } else { + None + } + } + + /// Get a color, or slice of colors, that allows modifying the components at `index`. See [`slice::get_mut`] for details. + #[inline(always)] + pub fn get_mut<'a, I, T>(&'a mut self, index: I) -> Option<$self_ty<$($phantom_ty,)? &mut >::Output>> + where + T: 'a, + C: AsMut<[T]>, + I: core::slice::SliceIndex<[T]> + Clone, + { + let hue = self.hue.get_mut(index.clone()); + $(let $element = self.$element.as_mut().get_mut(index.clone());)+ + + if let (Some(hue) $(, Some($element))+) = (hue $(,$element)+) { + Some($self_ty {hue $(, $element)+ $(, $phantom: PhantomData)?}) + } else { + None + } + } + } + + #[cfg(feature = "std")] + impl<$($phantom_ty,)? T> $self_ty<$($phantom_ty,)? Vec> { + /// Create a struct of vectors with a minimum capacity. See [`Vec::with_capacity`] for details. + #[inline(always)] + pub fn with_capacity(capacity: usize) -> Self { + let hue = Vec::with_capacity(capacity); + $(let $element = Vec::with_capacity(capacity);)+ + + Self {hue: hue.into() $(, $element)+ $(, $phantom: PhantomData)?} + } + + /// Push an additional color's components onto the component vectors. See [`Vec::push`] for details. + #[inline(always)] + pub fn push(&mut self, value: $self_ty<$($phantom_ty,)? T>) { + self.hue.push(value.hue); + $(self.$element.push(value.$element);)+ + } + + /// Pop a color's components from the component vectors. See [`Vec::pop`] for details. + #[inline(always)] + pub fn pop(&mut self) -> Option<$self_ty<$($phantom_ty,)? T>> { + let hue = self.hue.pop(); + $(let $element = self.$element.pop();)+ + + Some($self_ty { + hue: hue?, + $($element: $element?,)+ + $($phantom: PhantomData,)? + }) + } + + /// Clear the component vectors. See [`Vec::clear`] for details. + #[inline(always)] + pub fn clear(&mut self) { + self.hue.clear(); + $(self.$element.clear();)+ + } + + /// Return an iterator that moves colors out of the specified range. + #[inline(always)] + pub fn drain(&mut self, range: R) -> Iter $(, $phantom_ty)?> + where + R: core::ops::RangeBounds + Clone, + { + Iter { + hue: self.hue.drain(range.clone()), + $($element: self.$element.drain(range.clone()),)+ + $($phantom: PhantomData,)? + } + } + } + + impl<$($phantom_ty,)? Ct, Ca> crate::Alpha<$self_ty<$($phantom_ty,)? Ct>, Ca> { + /// Get a color, or slice of colors, with references to the components at `index`. See [`slice::get`] for details. + #[inline(always)] + pub fn get<'a, I, T, A>(&'a self, index: I) -> Option>::Output>, + &>::Output + >> + where + T: 'a, + A: 'a, + Ct: AsRef<[T]>, + Ca: AsRef<[A]>, + I: core::slice::SliceIndex<[T]> + core::slice::SliceIndex<[A]> + Clone + { + let color = self.color.get(index.clone()); + let alpha = self.alpha.as_ref().get(index); + + if let (Some(color), Some(alpha)) = (color, alpha) { + Some(crate::Alpha{color, alpha}) + } else { + None + } + } + + /// Get a color, or slice of colors, that allows modifying the components at `index`. See [`slice::get_mut`] for details. + #[inline(always)] + pub fn get_mut<'a, I, T, A>(&'a mut self, index: I) -> Option>::Output>, + &mut >::Output + >> + where + T: 'a, + A: 'a, + Ct: AsMut<[T]>, + Ca: AsMut<[A]>, + I: core::slice::SliceIndex<[T]> + core::slice::SliceIndex<[A]> + Clone + { + let color = self.color.get_mut(index.clone()); + let alpha = self.alpha.as_mut().get_mut(index); + + if let (Some(color), Some(alpha)) = (color, alpha) { + Some(crate::Alpha{color, alpha}) + } else { + None + } + } + } + + #[cfg(feature = "std")] + impl<$($phantom_ty,)? T, A> crate::Alpha<$self_ty<$($phantom_ty,)? Vec>, Vec> { + /// Create a struct of vectors with a minimum capacity. See [`Vec::with_capacity`] for details. + #[inline(always)] + pub fn with_capacity(capacity: usize) -> Self { + crate::Alpha { + color: $self_ty::with_capacity(capacity), + alpha: Vec::with_capacity(capacity), + } + } + + /// Push an additional color's components onto the component vectors. See [`Vec::push`] for details. + #[inline(always)] + pub fn push(&mut self, value: crate::Alpha<$self_ty<$($phantom_ty,)? T>, A>) { + self.color.push(value.color); + self.alpha.push(value.alpha); + } + + /// Pop a color's components from the component vectors. See [`Vec::pop`] for details. + #[inline(always)] + pub fn pop(&mut self) -> Option, A>> { + let color = self.color.pop(); + let alpha = self.alpha.pop(); + + Some(crate::Alpha { + color: color?, + alpha: alpha?, + }) + } + + /// Clear the component vectors. See [`Vec::clear`] for details. + #[inline(always)] + pub fn clear(&mut self) { + self.color.clear(); + self.alpha.clear(); + } + + /// Return an iterator that moves colors out of the specified range. + #[inline(always)] + pub fn drain(&mut self, range: R) -> crate::alpha::Iter $(, $phantom_ty)?>, std::vec::Drain> + where + R: core::ops::RangeBounds + Clone, + { + crate::alpha::Iter { + color: self.color.drain(range.clone()), + alpha: self.alpha.drain(range), + } + } + } + }; +} + +#[cfg(test)] +macro_rules! struct_of_arrays_tests { + ($color_ty: ident $(<$phantom_ty:ident>)?, $($values:expr),+) => { + #[test] + fn collect() { + let vec_of_colors = vec![$($values),+]; + let color_of_vecs: $color_ty<$($phantom_ty,)? Vec<_>> = vec_of_colors.into_iter().collect(); + let vec_of_colors: Vec<_> = color_of_vecs.into_iter().collect(); + + assert_eq!(vec_of_colors, vec![$($values),+]); + } + + #[test] + fn extend() { + let vec_of_colors = vec![$($values),+]; + + let mut color_of_vecs: $color_ty<$($phantom_ty,)? Vec<_>> = $color_ty::with_capacity(vec_of_colors.len()); + color_of_vecs.extend(vec_of_colors); + + let vec_of_colors: Vec<_> = color_of_vecs.into_iter().collect(); + + assert_eq!(vec_of_colors, vec![$($values),+]); + } + + #[test] + fn pop_push() { + let vec_of_colors = vec![$($values),+]; + + let mut color_of_vecs: $color_ty<$($phantom_ty,)? Vec<_>> = vec_of_colors.into_iter().collect(); + let last = color_of_vecs.pop().unwrap(); + color_of_vecs.push(last); + + let vec_of_colors: Vec<_> = color_of_vecs.into_iter().collect(); + + assert_eq!(vec_of_colors, vec![$($values),+]); + } + + #[test] + fn clear() { + let vec_of_colors = vec![$($values),+]; + + let mut color_of_vecs: $color_ty<$($phantom_ty,)? Vec<_>> = vec_of_colors.into_iter().collect(); + color_of_vecs.clear(); + + let vec_of_colors: Vec<_> = color_of_vecs.into_iter().collect(); + + assert_eq!(vec_of_colors, vec![]); + } + + #[test] + fn drain() { + let vec_of_colors = vec![$($values),+]; + + let mut color_of_vecs: $color_ty<$($phantom_ty,)? Vec<_>> = vec_of_colors.into_iter().collect(); + + let vec_of_colors1: Vec<_> = color_of_vecs.drain(..).collect(); + let vec_of_colors2: Vec<_> = color_of_vecs.into_iter().collect(); + + assert_eq!(vec_of_colors1, vec![$($values),+]); + assert_eq!(vec_of_colors2, vec![]); + } + + #[test] + fn modify() { + let vec_of_colors = vec![$($values),+]; + + let mut color_of_vecs: $color_ty<$($phantom_ty,)? Vec<_>> = vec_of_colors.into_iter().collect(); + + for mut color in &mut color_of_vecs { + color.set(color.copied() + 2.0); + } + + let vec_of_colors: Vec<_> = color_of_vecs.into_iter().collect(); + + assert_eq!(vec_of_colors, vec![$($values + 2.0),+]); + } + } +} diff --git a/palette/src/okhsl.rs b/palette/src/okhsl.rs index e2ee01e08..2663049b2 100644 --- a/palette/src/okhsl.rs +++ b/palette/src/okhsl.rs @@ -1,3 +1,5 @@ +//! Types for the Okhsl color space. + pub use alpha::Okhsla; use crate::{ @@ -10,6 +12,11 @@ use crate::{ GetHue, HasBoolMask, LinSrgb, Oklab, OklabHue, }; +pub use self::properties::Iter; + +#[cfg(feature = "random")] +pub use self::random::UniformOkhsl; + mod alpha; mod properties; #[cfg(feature = "random")] @@ -146,6 +153,9 @@ where } } +impl_reference_component_methods_hue!(Okhsl, [saturation, lightness]); +impl_struct_of_arrays_methods_hue!(Okhsl, [saturation, lightness]); + /// # See /// See [`srgb_to_okhsl`](https://bottosson.github.io/posts/colorpicker/#hsl-2) impl FromColorUnclamped> for Okhsl @@ -379,4 +389,22 @@ mod tests { let hex_str = format!("{:x}", rgb8); assert_eq!(hex_str, "aa5a74"); } + + struct_of_arrays_tests!( + Okhsl, + Okhsl::new(0.1f32, 0.2, 0.3), + Okhsl::new(0.2, 0.3, 0.4), + Okhsl::new(0.3, 0.4, 0.5) + ); + + mod alpha { + use crate::okhsl::Okhsla; + + struct_of_arrays_tests!( + Okhsla, + Okhsla::new(0.1f32, 0.2, 0.3, 0.4), + Okhsla::new(0.2, 0.3, 0.4, 0.5), + Okhsla::new(0.3, 0.4, 0.5, 0.6) + ); + } } diff --git a/palette/src/okhsl/properties.rs b/palette/src/okhsl/properties.rs index b27df7e1c..e81ec186e 100644 --- a/palette/src/okhsl/properties.rs +++ b/palette/src/okhsl/properties.rs @@ -3,7 +3,7 @@ use core::ops::{Add, AddAssign, BitAnd, Sub, SubAssign}; #[cfg(feature = "approx")] use approx::{AbsDiffEq, RelativeEq, UlpsEq}; -use crate::white_point::D65; +use crate::{hues::OklabHueIter, white_point::D65}; use crate::{ angle::{RealAngle, SignedAngle}, @@ -135,6 +135,7 @@ impl_color_sub!(Okhsl, [hue, saturation, lightness]); impl_array_casts!(Okhsl, [T; 3]); impl_simd_array_conversion_hue!(Okhsl, [saturation, lightness]); +impl_struct_of_array_traits_hue!(Okhsl, OklabHueIter, [saturation, lightness]); impl_eq_hue!(Okhsl, OklabHue, [hue, saturation, lightness]); diff --git a/palette/src/okhsl/random.rs b/palette/src/okhsl/random.rs index bd9f88550..8c1b19cf9 100644 --- a/palette/src/okhsl/random.rs +++ b/palette/src/okhsl/random.rs @@ -26,6 +26,7 @@ where } } +/// Sample Okhsl colors uniformly. pub struct UniformOkhsl where T: SampleUniform, diff --git a/palette/src/okhsv.rs b/palette/src/okhsv.rs index 8194d90aa..bb53d1c44 100644 --- a/palette/src/okhsv.rs +++ b/palette/src/okhsv.rs @@ -1,3 +1,5 @@ +//! Types for the Okhsv color space. + use core::fmt::Debug; pub use alpha::Okhsva; @@ -17,6 +19,8 @@ use crate::{ GetHue, HasBoolMask, LinSrgb, Okhwb, Oklab, OklabHue, }; +pub use self::properties::Iter; + mod alpha; mod properties; #[cfg(feature = "random")] @@ -126,6 +130,9 @@ where } } +impl_reference_component_methods_hue!(Okhsv, [saturation, value]); +impl_struct_of_arrays_methods_hue!(Okhsv, [saturation, value]); + impl Okhsv { /// Create an `Okhsv` color. pub fn new>>(hue: H, saturation: T, value: T) -> Self { @@ -535,4 +542,22 @@ mod tests { ); } } + + struct_of_arrays_tests!( + Okhsv, + Okhsv::new(0.1f32, 0.2, 0.3), + Okhsv::new(0.2, 0.3, 0.4), + Okhsv::new(0.3, 0.4, 0.5) + ); + + mod alpha { + use crate::okhsv::Okhsva; + + struct_of_arrays_tests!( + Okhsva, + Okhsva::new(0.1f32, 0.2, 0.3, 0.4), + Okhsva::new(0.2, 0.3, 0.4, 0.5), + Okhsva::new(0.3, 0.4, 0.5, 0.6) + ); + } } diff --git a/palette/src/okhsv/properties.rs b/palette/src/okhsv/properties.rs index 717706dd2..ad484547e 100644 --- a/palette/src/okhsv/properties.rs +++ b/palette/src/okhsv/properties.rs @@ -3,10 +3,10 @@ use core::ops::{Add, AddAssign, BitAnd, Sub, SubAssign}; #[cfg(feature = "approx")] use approx::{AbsDiffEq, RelativeEq, UlpsEq}; -use crate::angle::SignedAngle; use crate::num::{ self, Arithmetics, FromScalarArray, IntoScalarArray, MinMax, One, PartialCmp, Real, Zero, }; +use crate::{angle::SignedAngle, hues::OklabHueIter}; use crate::{angle::RealAngle, clamp_assign, ok_utils, Alpha, IsWithinBounds, OklabHue}; use crate::{ @@ -125,5 +125,6 @@ impl_color_sub!(Okhsv, [hue, saturation, value]); impl_array_casts!(Okhsv, [T; 3]); impl_simd_array_conversion_hue!(Okhsv, [saturation, value]); +impl_struct_of_array_traits_hue!(Okhsv, OklabHueIter, [saturation, value]); impl_eq_hue!(Okhsv, OklabHue, [hue, saturation, value]); diff --git a/palette/src/okhsv/random.rs b/palette/src/okhsv/random.rs index 2954cff07..3f40795db 100644 --- a/palette/src/okhsv/random.rs +++ b/palette/src/okhsv/random.rs @@ -23,6 +23,7 @@ where } } +/// Sample Okhsv colors uniformly. #[cfg(feature = "random")] pub struct UniformOkhsv where diff --git a/palette/src/okhwb.rs b/palette/src/okhwb.rs index 31e4e1f29..c7b273d9e 100644 --- a/palette/src/okhwb.rs +++ b/palette/src/okhwb.rs @@ -1,3 +1,5 @@ +//! Types for the Okhwb color space. + use core::fmt::Debug; pub use alpha::Okhwba; @@ -11,6 +13,11 @@ use crate::{ HasBoolMask, Okhsv, OklabHue, }; +pub use self::properties::Iter; + +#[cfg(feature = "random")] +pub use self::random::UniformOkhwb; + mod alpha; mod properties; #[cfg(feature = "random")] @@ -119,6 +126,9 @@ where } } +impl_reference_component_methods_hue!(Okhwb, [whiteness, blackness]); +impl_struct_of_arrays_methods_hue!(Okhwb, [whiteness, blackness]); + impl FromColorUnclamped> for Okhwb where T: One + Arithmetics, @@ -264,4 +274,22 @@ mod tests { ); } } + + struct_of_arrays_tests!( + Okhwb, + Okhwb::new(0.1f32, 0.2, 0.3), + Okhwb::new(0.2, 0.3, 0.4), + Okhwb::new(0.3, 0.4, 0.5) + ); + + mod alpha { + use crate::okhwb::Okhwba; + + struct_of_arrays_tests!( + Okhwba, + Okhwba::new(0.1f32, 0.2, 0.3, 0.4), + Okhwba::new(0.2, 0.3, 0.4, 0.5), + Okhwba::new(0.3, 0.4, 0.5, 0.6) + ); + } } diff --git a/palette/src/okhwb/properties.rs b/palette/src/okhwb/properties.rs index 132ab9e3e..d8e326e8f 100644 --- a/palette/src/okhwb/properties.rs +++ b/palette/src/okhwb/properties.rs @@ -1,9 +1,12 @@ use core::ops::{Add, AddAssign, BitAnd, DivAssign, Sub, SubAssign}; -use crate::angle::{RealAngle, SignedAngle}; 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, contrast_ratio, ClampAssign, FromColor, GetHue, @@ -221,6 +224,7 @@ impl_color_sub!(Okhwb, [hue, whiteness, blackness]); impl_array_casts!(Okhwb, [T; 3]); impl_simd_array_conversion_hue!(Okhwb, [whiteness, blackness]); +impl_struct_of_array_traits_hue!(Okhwb, OklabHueIter, [whiteness, blackness]); impl RelativeContrast for Okhwb where diff --git a/palette/src/okhwb/random.rs b/palette/src/okhwb/random.rs index bfc15519a..38e008a42 100644 --- a/palette/src/okhwb/random.rs +++ b/palette/src/okhwb/random.rs @@ -21,6 +21,7 @@ where } } +/// Sample Okhwb colors uniformly. pub struct UniformOkhwb where T: SampleUniform, diff --git a/palette/src/oklab.rs b/palette/src/oklab.rs index c033edf54..8d84b6909 100644 --- a/palette/src/oklab.rs +++ b/palette/src/oklab.rs @@ -1,3 +1,5 @@ +//! Types for the Oklab color space. + use core::{any::TypeId, fmt::Debug, ops::Mul}; pub use alpha::Oklaba; @@ -15,6 +17,11 @@ use crate::{ LinSrgb, Mat3, Okhsl, Okhsv, Oklch, Xyz, }; +pub use self::properties::Iter; + +#[cfg(feature = "random")] +pub use self::random::UniformOklab; + mod alpha; mod properties; #[cfg(feature = "random")] @@ -233,6 +240,9 @@ where } } +impl_reference_component_methods!(Oklab, [l, a, b]); +impl_struct_of_arrays_methods!(Oklab, [l, a, b]); + impl Oklab where T: Hypot + Clone, @@ -668,6 +678,24 @@ mod test { assert_eq!(Oklab::::max_l(), 1.0); } + struct_of_arrays_tests!( + Oklab, + Oklab::new(0.1f32, 0.2, 0.3), + Oklab::new(0.2, 0.3, 0.4), + Oklab::new(0.3, 0.4, 0.5) + ); + + mod alpha { + use crate::oklab::Oklaba; + + struct_of_arrays_tests!( + Oklaba, + Oklaba::new(0.1f32, 0.2, 0.3, 0.4), + Oklaba::new(0.2, 0.3, 0.4, 0.5), + Oklaba::new(0.3, 0.4, 0.5, 0.6) + ); + } + #[cfg(feature = "serializing")] #[test] fn serialize() { diff --git a/palette/src/oklab/properties.rs b/palette/src/oklab/properties.rs index f504a236a..fcd7e9210 100644 --- a/palette/src/oklab/properties.rs +++ b/palette/src/oklab/properties.rs @@ -73,6 +73,7 @@ impl_color_div!(Oklab, [l, a, b]); impl_array_casts!(Oklab, [T; 3]); impl_simd_array_conversion!(Oklab, [l, a, b]); +impl_struct_of_array_traits!(Oklab, [l, a, b]); impl_eq!(Oklab, [l, a, b]); diff --git a/palette/src/oklab/random.rs b/palette/src/oklab/random.rs index 4afd57f95..0c6b151a6 100644 --- a/palette/src/oklab/random.rs +++ b/palette/src/oklab/random.rs @@ -21,6 +21,7 @@ where { } } +/// Sample Oklab colors uniformly. pub struct UniformOklab where T: SampleUniform, diff --git a/palette/src/oklch.rs b/palette/src/oklch.rs index b1c700334..3a306a0c3 100644 --- a/palette/src/oklch.rs +++ b/palette/src/oklch.rs @@ -1,3 +1,5 @@ +//! Types for the Oklch color space. + pub use alpha::Oklcha; use crate::{ @@ -8,6 +10,11 @@ use crate::{ GetHue, Oklab, OklabHue, }; +pub use self::properties::Iter; + +#[cfg(feature = "random")] +pub use self::random::UniformOklch; + mod alpha; mod properties; #[cfg(feature = "random")] @@ -95,6 +102,9 @@ where } } +impl_reference_component_methods_hue!(Oklch, [l, chroma]); +impl_struct_of_arrays_methods_hue!(Oklch, [l, chroma]); + impl FromColorUnclamped> for Oklch { fn from_color_unclamped(color: Oklch) -> Self { color @@ -193,6 +203,24 @@ mod test { assert_eq!(deserialized, Oklch::new(0.3, 0.8, 0.1)); } + struct_of_arrays_tests!( + Oklch, + Oklch::new(0.1f32, 0.2, 0.3), + Oklch::new(0.2, 0.3, 0.4), + Oklch::new(0.3, 0.4, 0.5) + ); + + mod alpha { + use crate::oklch::Oklcha; + + struct_of_arrays_tests!( + Oklcha, + Oklcha::new(0.1f32, 0.2, 0.3, 0.4), + Oklcha::new(0.2, 0.3, 0.4, 0.5), + Oklcha::new(0.3, 0.4, 0.5, 0.6) + ); + } + #[cfg(feature = "random")] test_uniform_distribution! { Oklch as crate::Oklab { diff --git a/palette/src/oklch/properties.rs b/palette/src/oklch/properties.rs index 43464361d..77d3eb2dc 100644 --- a/palette/src/oklch/properties.rs +++ b/palette/src/oklch/properties.rs @@ -7,6 +7,7 @@ use crate::{ angle::{RealAngle, SignedAngle}, bool_mask::LazySelect, clamp, clamp_assign, clamp_min, clamp_min_assign, contrast_ratio, + hues::OklabHueIter, num::{ self, Arithmetics, FromScalarArray, IntoScalarArray, MinMax, One, PartialCmp, Real, Zero, }, @@ -116,6 +117,7 @@ impl_color_sub!(Oklch, [l, chroma, hue]); impl_array_casts!(Oklch, [T; 3]); impl_simd_array_conversion_hue!(Oklch, [l, chroma]); +impl_struct_of_array_traits_hue!(Oklch, OklabHueIter, [l, chroma]); impl_eq_hue!(Oklch, OklabHue, [l, chroma, hue]); diff --git a/palette/src/oklch/random.rs b/palette/src/oklch/random.rs index 8a8a3551b..1f19ef4b4 100644 --- a/palette/src/oklch/random.rs +++ b/palette/src/oklch/random.rs @@ -24,6 +24,7 @@ where } } +/// Sample Oklch colors uniformly. pub struct UniformOklch where T: SampleUniform, diff --git a/palette/src/rgb.rs b/palette/src/rgb.rs index a90296dc9..dc4a9f7d0 100644 --- a/palette/src/rgb.rs +++ b/palette/src/rgb.rs @@ -1,4 +1,4 @@ -//! RGB types, spaces and standards. +//! Types for the RGB color space, including spaces and standards. //! //! # Linear And Non-linear RGB //! @@ -67,7 +67,7 @@ use crate::{ Mat3, Yxy, }; -pub use self::rgb::{FromHexError, Rgb, Rgba}; +pub use self::rgb::{FromHexError, Iter, Rgb, Rgba}; pub mod channels; mod rgb; diff --git a/palette/src/rgb/rgb.rs b/palette/src/rgb/rgb.rs index 4f50c9e91..510e5eb97 100644 --- a/palette/src/rgb/rgb.rs +++ b/palette/src/rgb/rgb.rs @@ -560,6 +560,9 @@ impl Alpha, T>, A> { } } +impl_reference_component_methods!(Rgb, [red, green, blue], standard); +impl_struct_of_arrays_methods!(Rgb, [red, green, blue], standard); + impl FromColorUnclamped> for Rgb where S1: RgbStandard + 'static, @@ -887,6 +890,7 @@ impl From, A>> for (T, T, T, A) { impl_array_casts!(Rgb, [T; 3]); impl_simd_array_conversion!(Rgb, [red, green, blue], standard); +impl_struct_of_array_traits!(Rgb, [red, green, blue], standard); impl_eq!(Rgb, [red, green, blue]); @@ -1385,6 +1389,24 @@ mod test { assert_relative_eq!(Rgb::::max_blue(), 1.0); } + struct_of_arrays_tests!( + Rgb, + Rgb::new(0.1f32, 0.2, 0.3), + Rgb::new(0.2, 0.3, 0.4), + Rgb::new(0.3, 0.4, 0.5) + ); + + mod alpha { + use crate::{encoding::Srgb, rgb::Rgba}; + + struct_of_arrays_tests!( + Rgba, + Rgba::new(0.1f32, 0.2, 0.3, 0.4), + Rgba::new(0.2, 0.3, 0.4, 0.5), + Rgba::new(0.3, 0.4, 0.5, 0.6) + ); + } + #[cfg(feature = "random")] test_uniform_distribution! { Rgb { diff --git a/palette/src/xyz.rs b/palette/src/xyz.rs index 03f663c84..b611fef1a 100644 --- a/palette/src/xyz.rs +++ b/palette/src/xyz.rs @@ -1,3 +1,5 @@ +//! Types for the CIE 1931 XYZ color space. + use core::{ marker::PhantomData, ops::{Add, AddAssign, BitAnd, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}, @@ -200,6 +202,9 @@ impl Alpha, A> { } } +impl_reference_component_methods!(Xyz, [x, y, z], white_point); +impl_struct_of_arrays_methods!(Xyz, [x, y, z], white_point); + impl FromColorUnclamped> for Xyz { fn from_color_unclamped(color: Xyz) -> Self { color @@ -452,6 +457,7 @@ impl_color_div!(Xyz, [x, y, z], white_point); impl_array_casts!(Xyz, [T; 3]); impl_simd_array_conversion!(Xyz, [x, y, z], white_point); +impl_struct_of_array_traits!(Xyz, [x, y, z], white_point); impl_eq!(Xyz, [x, y, z]); @@ -486,6 +492,7 @@ where } } +/// Sample CIE 1931 XYZ colors uniformly. #[cfg(feature = "random")] pub struct UniformXyz where @@ -630,6 +637,24 @@ mod test { assert_relative_eq!(Xyz::::max_z(), Z_N); } + struct_of_arrays_tests!( + Xyz, + Xyz::new(0.1f32, 0.2, 0.3), + Xyz::new(0.2, 0.3, 0.4), + Xyz::new(0.3, 0.4, 0.5) + ); + + mod alpha { + use crate::{white_point::D65, xyz::Xyza}; + + struct_of_arrays_tests!( + Xyza, + Xyza::new(0.1f32, 0.2, 0.3, 0.4), + Xyza::new(0.2, 0.3, 0.4, 0.5), + Xyza::new(0.3, 0.4, 0.5, 0.6) + ); + } + #[cfg(feature = "serializing")] #[test] fn serialize() { diff --git a/palette/src/yxy.rs b/palette/src/yxy.rs index 2dfa7b809..cb54f27b9 100644 --- a/palette/src/yxy.rs +++ b/palette/src/yxy.rs @@ -1,3 +1,5 @@ +//! Types for the CIE 1931 Yxy (xyY) color space. + use core::{ marker::PhantomData, ops::{Add, AddAssign, BitAnd, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}, @@ -35,7 +37,7 @@ use crate::{ /// in `Alpha`](crate::Alpha#Yxya). pub type Yxya = Alpha, T>; -/// The CIE 1931 Yxy (xyY) color space. +/// The CIE 1931 Yxy (xyY) color space. /// /// Yxy is a luminance-chromaticity color space derived from the CIE XYZ /// color space. It is widely used to define colors. The chromaticity diagrams @@ -195,6 +197,9 @@ impl Alpha, A> { } } +impl_reference_component_methods!(Yxy, [x, y, luma], white_point); +impl_struct_of_arrays_methods!(Yxy, [x, y, luma], white_point); + impl From<(T, T, T)> for Yxy { fn from(components: (T, T, T)) -> Self { Self::from_components(components) @@ -338,6 +343,7 @@ impl_color_div!(Yxy, [x, y, luma], white_point); impl_array_casts!(Yxy, [T; 3]); impl_simd_array_conversion!(Yxy, [x, y, luma], white_point); +impl_struct_of_array_traits!(Yxy, [x, y, luma], white_point); impl_eq!(Yxy, [y, x, luma]); @@ -369,6 +375,7 @@ where } } +/// Sample CIE 1931 Yxy (xyY) colors uniformly. #[cfg(feature = "random")] pub struct UniformYxy where @@ -506,6 +513,24 @@ mod test { assert_relative_eq!(Yxy::::max_luma(), 1.0); } + struct_of_arrays_tests!( + Yxy, + Yxy::new(0.1f32, 0.2, 0.3), + Yxy::new(0.2, 0.3, 0.4), + Yxy::new(0.3, 0.4, 0.5) + ); + + mod alpha { + use crate::{white_point::D65, yxy::Yxya}; + + struct_of_arrays_tests!( + Yxya, + Yxya::new(0.1f32, 0.2, 0.3, 0.4), + Yxya::new(0.2, 0.3, 0.4, 0.5), + Yxya::new(0.3, 0.4, 0.5, 0.6) + ); + } + #[cfg(feature = "serializing")] #[test] fn serialize() {