diff options
Diffstat (limited to 'vendor/image/src/color.rs')
-rw-r--r-- | vendor/image/src/color.rs | 985 |
1 files changed, 0 insertions, 985 deletions
diff --git a/vendor/image/src/color.rs b/vendor/image/src/color.rs deleted file mode 100644 index 57a8511..0000000 --- a/vendor/image/src/color.rs +++ /dev/null @@ -1,985 +0,0 @@ -use std::ops::{Index, IndexMut}; - -use num_traits::{NumCast, ToPrimitive, Zero}; - -use crate::traits::{Enlargeable, Pixel, Primitive}; - -/// An enumeration over supported color types and bit depths -#[derive(Copy, PartialEq, Eq, Debug, Clone, Hash)] -#[non_exhaustive] -pub enum ColorType { - /// Pixel is 8-bit luminance - L8, - /// Pixel is 8-bit luminance with an alpha channel - La8, - /// Pixel contains 8-bit R, G and B channels - Rgb8, - /// Pixel is 8-bit RGB with an alpha channel - Rgba8, - - /// Pixel is 16-bit luminance - L16, - /// Pixel is 16-bit luminance with an alpha channel - La16, - /// Pixel is 16-bit RGB - Rgb16, - /// Pixel is 16-bit RGBA - Rgba16, - - /// Pixel is 32-bit float RGB - Rgb32F, - /// Pixel is 32-bit float RGBA - Rgba32F, -} - -impl ColorType { - /// Returns the number of bytes contained in a pixel of `ColorType` ```c``` - pub fn bytes_per_pixel(self) -> u8 { - match self { - ColorType::L8 => 1, - ColorType::L16 | ColorType::La8 => 2, - ColorType::Rgb8 => 3, - ColorType::Rgba8 | ColorType::La16 => 4, - ColorType::Rgb16 => 6, - ColorType::Rgba16 => 8, - ColorType::Rgb32F => 3 * 4, - ColorType::Rgba32F => 4 * 4, - } - } - - /// Returns if there is an alpha channel. - pub fn has_alpha(self) -> bool { - use ColorType::*; - match self { - L8 | L16 | Rgb8 | Rgb16 | Rgb32F => false, - La8 | Rgba8 | La16 | Rgba16 | Rgba32F => true, - } - } - - /// Returns false if the color scheme is grayscale, true otherwise. - pub fn has_color(self) -> bool { - use ColorType::*; - match self { - L8 | L16 | La8 | La16 => false, - Rgb8 | Rgb16 | Rgba8 | Rgba16 | Rgb32F | Rgba32F => true, - } - } - - /// Returns the number of bits contained in a pixel of `ColorType` ```c``` (which will always be - /// a multiple of 8). - pub fn bits_per_pixel(self) -> u16 { - <u16 as From<u8>>::from(self.bytes_per_pixel()) * 8 - } - - /// Returns the number of color channels that make up this pixel - pub fn channel_count(self) -> u8 { - let e: ExtendedColorType = self.into(); - e.channel_count() - } -} - -/// An enumeration of color types encountered in image formats. -/// -/// This is not exhaustive over all existing image formats but should be granular enough to allow -/// round tripping of decoding and encoding as much as possible. The variants will be extended as -/// necessary to enable this. -/// -/// Another purpose is to advise users of a rough estimate of the accuracy and effort of the -/// decoding from and encoding to such an image format. -#[derive(Copy, PartialEq, Eq, Debug, Clone, Hash)] -#[non_exhaustive] -pub enum ExtendedColorType { - /// Pixel is 8-bit alpha - A8, - /// Pixel is 1-bit luminance - L1, - /// Pixel is 1-bit luminance with an alpha channel - La1, - /// Pixel contains 1-bit R, G and B channels - Rgb1, - /// Pixel is 1-bit RGB with an alpha channel - Rgba1, - /// Pixel is 2-bit luminance - L2, - /// Pixel is 2-bit luminance with an alpha channel - La2, - /// Pixel contains 2-bit R, G and B channels - Rgb2, - /// Pixel is 2-bit RGB with an alpha channel - Rgba2, - /// Pixel is 4-bit luminance - L4, - /// Pixel is 4-bit luminance with an alpha channel - La4, - /// Pixel contains 4-bit R, G and B channels - Rgb4, - /// Pixel is 4-bit RGB with an alpha channel - Rgba4, - /// Pixel is 8-bit luminance - L8, - /// Pixel is 8-bit luminance with an alpha channel - La8, - /// Pixel contains 8-bit R, G and B channels - Rgb8, - /// Pixel is 8-bit RGB with an alpha channel - Rgba8, - /// Pixel is 16-bit luminance - L16, - /// Pixel is 16-bit luminance with an alpha channel - La16, - /// Pixel contains 16-bit R, G and B channels - Rgb16, - /// Pixel is 16-bit RGB with an alpha channel - Rgba16, - /// Pixel contains 8-bit B, G and R channels - Bgr8, - /// Pixel is 8-bit BGR with an alpha channel - Bgra8, - - // TODO f16 types? - /// Pixel is 32-bit float RGB - Rgb32F, - /// Pixel is 32-bit float RGBA - Rgba32F, - - /// Pixel is of unknown color type with the specified bits per pixel. This can apply to pixels - /// which are associated with an external palette. In that case, the pixel value is an index - /// into the palette. - Unknown(u8), -} - -impl ExtendedColorType { - /// Get the number of channels for colors of this type. - /// - /// Note that the `Unknown` variant returns a value of `1` since pixels can only be treated as - /// an opaque datum by the library. - pub fn channel_count(self) -> u8 { - match self { - ExtendedColorType::A8 - | ExtendedColorType::L1 - | ExtendedColorType::L2 - | ExtendedColorType::L4 - | ExtendedColorType::L8 - | ExtendedColorType::L16 - | ExtendedColorType::Unknown(_) => 1, - ExtendedColorType::La1 - | ExtendedColorType::La2 - | ExtendedColorType::La4 - | ExtendedColorType::La8 - | ExtendedColorType::La16 => 2, - ExtendedColorType::Rgb1 - | ExtendedColorType::Rgb2 - | ExtendedColorType::Rgb4 - | ExtendedColorType::Rgb8 - | ExtendedColorType::Rgb16 - | ExtendedColorType::Rgb32F - | ExtendedColorType::Bgr8 => 3, - ExtendedColorType::Rgba1 - | ExtendedColorType::Rgba2 - | ExtendedColorType::Rgba4 - | ExtendedColorType::Rgba8 - | ExtendedColorType::Rgba16 - | ExtendedColorType::Rgba32F - | ExtendedColorType::Bgra8 => 4, - } - } -} -impl From<ColorType> for ExtendedColorType { - fn from(c: ColorType) -> Self { - match c { - ColorType::L8 => ExtendedColorType::L8, - ColorType::La8 => ExtendedColorType::La8, - ColorType::Rgb8 => ExtendedColorType::Rgb8, - ColorType::Rgba8 => ExtendedColorType::Rgba8, - ColorType::L16 => ExtendedColorType::L16, - ColorType::La16 => ExtendedColorType::La16, - ColorType::Rgb16 => ExtendedColorType::Rgb16, - ColorType::Rgba16 => ExtendedColorType::Rgba16, - ColorType::Rgb32F => ExtendedColorType::Rgb32F, - ColorType::Rgba32F => ExtendedColorType::Rgba32F, - } - } -} - -macro_rules! define_colors { - {$( - $(#[$doc:meta])* - pub struct $ident:ident<T: $($bound:ident)*>([T; $channels:expr, $alphas:expr]) - = $interpretation:literal; - )*} => { - -$( // START Structure definitions - -$(#[$doc])* -#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash)] -#[repr(C)] -#[allow(missing_docs)] -pub struct $ident<T> (pub [T; $channels]); - -impl<T: $($bound+)*> Pixel for $ident<T> { - type Subpixel = T; - - const CHANNEL_COUNT: u8 = $channels; - - #[inline(always)] - fn channels(&self) -> &[T] { - &self.0 - } - - #[inline(always)] - fn channels_mut(&mut self) -> &mut [T] { - &mut self.0 - } - - const COLOR_MODEL: &'static str = $interpretation; - - fn channels4(&self) -> (T, T, T, T) { - const CHANNELS: usize = $channels; - let mut channels = [T::DEFAULT_MAX_VALUE; 4]; - channels[0..CHANNELS].copy_from_slice(&self.0); - (channels[0], channels[1], channels[2], channels[3]) - } - - fn from_channels(a: T, b: T, c: T, d: T,) -> $ident<T> { - const CHANNELS: usize = $channels; - *<$ident<T> as Pixel>::from_slice(&[a, b, c, d][..CHANNELS]) - } - - fn from_slice(slice: &[T]) -> &$ident<T> { - assert_eq!(slice.len(), $channels); - unsafe { &*(slice.as_ptr() as *const $ident<T>) } - } - fn from_slice_mut(slice: &mut [T]) -> &mut $ident<T> { - assert_eq!(slice.len(), $channels); - unsafe { &mut *(slice.as_mut_ptr() as *mut $ident<T>) } - } - - fn to_rgb(&self) -> Rgb<T> { - let mut pix = Rgb([Zero::zero(), Zero::zero(), Zero::zero()]); - pix.from_color(self); - pix - } - - fn to_rgba(&self) -> Rgba<T> { - let mut pix = Rgba([Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero()]); - pix.from_color(self); - pix - } - - fn to_luma(&self) -> Luma<T> { - let mut pix = Luma([Zero::zero()]); - pix.from_color(self); - pix - } - - fn to_luma_alpha(&self) -> LumaA<T> { - let mut pix = LumaA([Zero::zero(), Zero::zero()]); - pix.from_color(self); - pix - } - - fn map<F>(& self, f: F) -> $ident<T> where F: FnMut(T) -> T { - let mut this = (*self).clone(); - this.apply(f); - this - } - - fn apply<F>(&mut self, mut f: F) where F: FnMut(T) -> T { - for v in &mut self.0 { - *v = f(*v) - } - } - - fn map_with_alpha<F, G>(&self, f: F, g: G) -> $ident<T> where F: FnMut(T) -> T, G: FnMut(T) -> T { - let mut this = (*self).clone(); - this.apply_with_alpha(f, g); - this - } - - fn apply_with_alpha<F, G>(&mut self, mut f: F, mut g: G) where F: FnMut(T) -> T, G: FnMut(T) -> T { - const ALPHA: usize = $channels - $alphas; - for v in self.0[..ALPHA].iter_mut() { - *v = f(*v) - } - // The branch of this match is `const`. This way ensures that no subexpression fails the - // `const_err` lint (the expression `self.0[ALPHA]` would). - if let Some(v) = self.0.get_mut(ALPHA) { - *v = g(*v) - } - } - - fn map2<F>(&self, other: &Self, f: F) -> $ident<T> where F: FnMut(T, T) -> T { - let mut this = (*self).clone(); - this.apply2(other, f); - this - } - - fn apply2<F>(&mut self, other: &$ident<T>, mut f: F) where F: FnMut(T, T) -> T { - for (a, &b) in self.0.iter_mut().zip(other.0.iter()) { - *a = f(*a, b) - } - } - - fn invert(&mut self) { - Invert::invert(self) - } - - fn blend(&mut self, other: &$ident<T>) { - Blend::blend(self, other) - } -} - -impl<T> Index<usize> for $ident<T> { - type Output = T; - #[inline(always)] - fn index(&self, _index: usize) -> &T { - &self.0[_index] - } -} - -impl<T> IndexMut<usize> for $ident<T> { - #[inline(always)] - fn index_mut(&mut self, _index: usize) -> &mut T { - &mut self.0[_index] - } -} - -impl<T> From<[T; $channels]> for $ident<T> { - fn from(c: [T; $channels]) -> Self { - Self(c) - } -} - -)* // END Structure definitions - - } -} - -define_colors! { - /// RGB colors. - /// - /// For the purpose of color conversion, as well as blending, the implementation of `Pixel` - /// assumes an `sRGB` color space of its data. - pub struct Rgb<T: Primitive Enlargeable>([T; 3, 0]) = "RGB"; - /// Grayscale colors. - pub struct Luma<T: Primitive>([T; 1, 0]) = "Y"; - /// RGB colors + alpha channel - pub struct Rgba<T: Primitive Enlargeable>([T; 4, 1]) = "RGBA"; - /// Grayscale colors + alpha channel - pub struct LumaA<T: Primitive>([T; 2, 1]) = "YA"; -} - -/// Convert from one pixel component type to another. For example, convert from `u8` to `f32` pixel values. -pub trait FromPrimitive<Component> { - /// Converts from any pixel component type to this type. - fn from_primitive(component: Component) -> Self; -} - -impl<T: Primitive> FromPrimitive<T> for T { - fn from_primitive(sample: T) -> Self { - sample - } -} - -// from f32: -// Note that in to-integer-conversion we are performing rounding but NumCast::from is implemented -// as truncate towards zero. We emulate rounding by adding a bias. - -impl FromPrimitive<f32> for u8 { - fn from_primitive(float: f32) -> Self { - let inner = (float.clamp(0.0, 1.0) * u8::MAX as f32).round(); - NumCast::from(inner).unwrap() - } -} - -impl FromPrimitive<f32> for u16 { - fn from_primitive(float: f32) -> Self { - let inner = (float.clamp(0.0, 1.0) * u16::MAX as f32).round(); - NumCast::from(inner).unwrap() - } -} - -// from u16: - -impl FromPrimitive<u16> for u8 { - fn from_primitive(c16: u16) -> Self { - fn from(c: impl Into<u32>) -> u32 { - c.into() - } - // The input c is the numerator of `c / u16::MAX`. - // Derive numerator of `num / u8::MAX`, with rounding. - // - // This method is based on the inverse (see FromPrimitive<u8> for u16) and was tested - // exhaustively in Python. It's the same as the reference function: - // round(c * (2**8 - 1) / (2**16 - 1)) - NumCast::from((from(c16) + 128) / 257).unwrap() - } -} - -impl FromPrimitive<u16> for f32 { - fn from_primitive(int: u16) -> Self { - (int as f32 / u16::MAX as f32).clamp(0.0, 1.0) - } -} - -// from u8: - -impl FromPrimitive<u8> for f32 { - fn from_primitive(int: u8) -> Self { - (int as f32 / u8::MAX as f32).clamp(0.0, 1.0) - } -} - -impl FromPrimitive<u8> for u16 { - fn from_primitive(c8: u8) -> Self { - let x = c8.to_u64().unwrap(); - NumCast::from((x << 8) | x).unwrap() - } -} - -/// Provides color conversions for the different pixel types. -pub trait FromColor<Other> { - /// Changes `self` to represent `Other` in the color space of `Self` - fn from_color(&mut self, _: &Other); -} - -/// Copy-based conversions to target pixel types using `FromColor`. -// FIXME: this trait should be removed and replaced with real color space models -// rather than assuming sRGB. -pub(crate) trait IntoColor<Other> { - /// Constructs a pixel of the target type and converts this pixel into it. - fn into_color(&self) -> Other; -} - -impl<O, S> IntoColor<O> for S -where - O: Pixel + FromColor<S>, -{ - fn into_color(&self) -> O { - // Note we cannot use Pixel::CHANNELS_COUNT here to directly construct - // the pixel due to a current bug/limitation of consts. - #[allow(deprecated)] - let mut pix = O::from_channels(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero()); - pix.from_color(self); - pix - } -} - -/// Coefficients to transform from sRGB to a CIE Y (luminance) value. -const SRGB_LUMA: [u32; 3] = [2126, 7152, 722]; -const SRGB_LUMA_DIV: u32 = 10000; - -#[inline] -fn rgb_to_luma<T: Primitive + Enlargeable>(rgb: &[T]) -> T { - let l = <T::Larger as NumCast>::from(SRGB_LUMA[0]).unwrap() * rgb[0].to_larger() - + <T::Larger as NumCast>::from(SRGB_LUMA[1]).unwrap() * rgb[1].to_larger() - + <T::Larger as NumCast>::from(SRGB_LUMA[2]).unwrap() * rgb[2].to_larger(); - T::clamp_from(l / <T::Larger as NumCast>::from(SRGB_LUMA_DIV).unwrap()) -} - -// `FromColor` for Luma -impl<S: Primitive, T: Primitive> FromColor<Luma<S>> for Luma<T> -where - T: FromPrimitive<S>, -{ - fn from_color(&mut self, other: &Luma<S>) { - let own = self.channels_mut(); - let other = other.channels(); - own[0] = T::from_primitive(other[0]); - } -} - -impl<S: Primitive, T: Primitive> FromColor<LumaA<S>> for Luma<T> -where - T: FromPrimitive<S>, -{ - fn from_color(&mut self, other: &LumaA<S>) { - self.channels_mut()[0] = T::from_primitive(other.channels()[0]) - } -} - -impl<S: Primitive + Enlargeable, T: Primitive> FromColor<Rgb<S>> for Luma<T> -where - T: FromPrimitive<S>, -{ - fn from_color(&mut self, other: &Rgb<S>) { - let gray = self.channels_mut(); - let rgb = other.channels(); - gray[0] = T::from_primitive(rgb_to_luma(rgb)); - } -} - -impl<S: Primitive + Enlargeable, T: Primitive> FromColor<Rgba<S>> for Luma<T> -where - T: FromPrimitive<S>, -{ - fn from_color(&mut self, other: &Rgba<S>) { - let gray = self.channels_mut(); - let rgb = other.channels(); - let l = rgb_to_luma(rgb); - gray[0] = T::from_primitive(l); - } -} - -// `FromColor` for LumaA - -impl<S: Primitive, T: Primitive> FromColor<LumaA<S>> for LumaA<T> -where - T: FromPrimitive<S>, -{ - fn from_color(&mut self, other: &LumaA<S>) { - let own = self.channels_mut(); - let other = other.channels(); - own[0] = T::from_primitive(other[0]); - own[1] = T::from_primitive(other[1]); - } -} - -impl<S: Primitive + Enlargeable, T: Primitive> FromColor<Rgb<S>> for LumaA<T> -where - T: FromPrimitive<S>, -{ - fn from_color(&mut self, other: &Rgb<S>) { - let gray_a = self.channels_mut(); - let rgb = other.channels(); - gray_a[0] = T::from_primitive(rgb_to_luma(rgb)); - gray_a[1] = T::DEFAULT_MAX_VALUE; - } -} - -impl<S: Primitive + Enlargeable, T: Primitive> FromColor<Rgba<S>> for LumaA<T> -where - T: FromPrimitive<S>, -{ - fn from_color(&mut self, other: &Rgba<S>) { - let gray_a = self.channels_mut(); - let rgba = other.channels(); - gray_a[0] = T::from_primitive(rgb_to_luma(rgba)); - gray_a[1] = T::from_primitive(rgba[3]); - } -} - -impl<S: Primitive, T: Primitive> FromColor<Luma<S>> for LumaA<T> -where - T: FromPrimitive<S>, -{ - fn from_color(&mut self, other: &Luma<S>) { - let gray_a = self.channels_mut(); - gray_a[0] = T::from_primitive(other.channels()[0]); - gray_a[1] = T::DEFAULT_MAX_VALUE; - } -} - -// `FromColor` for RGBA - -impl<S: Primitive, T: Primitive> FromColor<Rgba<S>> for Rgba<T> -where - T: FromPrimitive<S>, -{ - fn from_color(&mut self, other: &Rgba<S>) { - let own = &mut self.0; - let other = &other.0; - own[0] = T::from_primitive(other[0]); - own[1] = T::from_primitive(other[1]); - own[2] = T::from_primitive(other[2]); - own[3] = T::from_primitive(other[3]); - } -} - -impl<S: Primitive, T: Primitive> FromColor<Rgb<S>> for Rgba<T> -where - T: FromPrimitive<S>, -{ - fn from_color(&mut self, other: &Rgb<S>) { - let rgba = &mut self.0; - let rgb = &other.0; - rgba[0] = T::from_primitive(rgb[0]); - rgba[1] = T::from_primitive(rgb[1]); - rgba[2] = T::from_primitive(rgb[2]); - rgba[3] = T::DEFAULT_MAX_VALUE; - } -} - -impl<S: Primitive, T: Primitive> FromColor<LumaA<S>> for Rgba<T> -where - T: FromPrimitive<S>, -{ - fn from_color(&mut self, gray: &LumaA<S>) { - let rgba = &mut self.0; - let gray = &gray.0; - rgba[0] = T::from_primitive(gray[0]); - rgba[1] = T::from_primitive(gray[0]); - rgba[2] = T::from_primitive(gray[0]); - rgba[3] = T::from_primitive(gray[1]); - } -} - -impl<S: Primitive, T: Primitive> FromColor<Luma<S>> for Rgba<T> -where - T: FromPrimitive<S>, -{ - fn from_color(&mut self, gray: &Luma<S>) { - let rgba = &mut self.0; - let gray = gray.0[0]; - rgba[0] = T::from_primitive(gray); - rgba[1] = T::from_primitive(gray); - rgba[2] = T::from_primitive(gray); - rgba[3] = T::DEFAULT_MAX_VALUE; - } -} - -// `FromColor` for RGB - -impl<S: Primitive, T: Primitive> FromColor<Rgb<S>> for Rgb<T> -where - T: FromPrimitive<S>, -{ - fn from_color(&mut self, other: &Rgb<S>) { - let own = &mut self.0; - let other = &other.0; - own[0] = T::from_primitive(other[0]); - own[1] = T::from_primitive(other[1]); - own[2] = T::from_primitive(other[2]); - } -} - -impl<S: Primitive, T: Primitive> FromColor<Rgba<S>> for Rgb<T> -where - T: FromPrimitive<S>, -{ - fn from_color(&mut self, other: &Rgba<S>) { - let rgb = &mut self.0; - let rgba = &other.0; - rgb[0] = T::from_primitive(rgba[0]); - rgb[1] = T::from_primitive(rgba[1]); - rgb[2] = T::from_primitive(rgba[2]); - } -} - -impl<S: Primitive, T: Primitive> FromColor<LumaA<S>> for Rgb<T> -where - T: FromPrimitive<S>, -{ - fn from_color(&mut self, other: &LumaA<S>) { - let rgb = &mut self.0; - let gray = other.0[0]; - rgb[0] = T::from_primitive(gray); - rgb[1] = T::from_primitive(gray); - rgb[2] = T::from_primitive(gray); - } -} - -impl<S: Primitive, T: Primitive> FromColor<Luma<S>> for Rgb<T> -where - T: FromPrimitive<S>, -{ - fn from_color(&mut self, other: &Luma<S>) { - let rgb = &mut self.0; - let gray = other.0[0]; - rgb[0] = T::from_primitive(gray); - rgb[1] = T::from_primitive(gray); - rgb[2] = T::from_primitive(gray); - } -} - -/// Blends a color inter another one -pub(crate) trait Blend { - /// Blends a color in-place. - fn blend(&mut self, other: &Self); -} - -impl<T: Primitive> Blend for LumaA<T> { - fn blend(&mut self, other: &LumaA<T>) { - let max_t = T::DEFAULT_MAX_VALUE; - let max_t = max_t.to_f32().unwrap(); - let (bg_luma, bg_a) = (self.0[0], self.0[1]); - let (fg_luma, fg_a) = (other.0[0], other.0[1]); - - let (bg_luma, bg_a) = ( - bg_luma.to_f32().unwrap() / max_t, - bg_a.to_f32().unwrap() / max_t, - ); - let (fg_luma, fg_a) = ( - fg_luma.to_f32().unwrap() / max_t, - fg_a.to_f32().unwrap() / max_t, - ); - - let alpha_final = bg_a + fg_a - bg_a * fg_a; - if alpha_final == 0.0 { - return; - }; - let bg_luma_a = bg_luma * bg_a; - let fg_luma_a = fg_luma * fg_a; - - let out_luma_a = fg_luma_a + bg_luma_a * (1.0 - fg_a); - let out_luma = out_luma_a / alpha_final; - - *self = LumaA([ - NumCast::from(max_t * out_luma).unwrap(), - NumCast::from(max_t * alpha_final).unwrap(), - ]) - } -} - -impl<T: Primitive> Blend for Luma<T> { - fn blend(&mut self, other: &Luma<T>) { - *self = *other - } -} - -impl<T: Primitive> Blend for Rgba<T> { - fn blend(&mut self, other: &Rgba<T>) { - // http://stackoverflow.com/questions/7438263/alpha-compositing-algorithm-blend-modes#answer-11163848 - - if other.0[3].is_zero() { - return; - } - if other.0[3] == T::DEFAULT_MAX_VALUE { - *self = *other; - return; - } - - // First, as we don't know what type our pixel is, we have to convert to floats between 0.0 and 1.0 - let max_t = T::DEFAULT_MAX_VALUE; - let max_t = max_t.to_f32().unwrap(); - let (bg_r, bg_g, bg_b, bg_a) = (self.0[0], self.0[1], self.0[2], self.0[3]); - let (fg_r, fg_g, fg_b, fg_a) = (other.0[0], other.0[1], other.0[2], other.0[3]); - let (bg_r, bg_g, bg_b, bg_a) = ( - bg_r.to_f32().unwrap() / max_t, - bg_g.to_f32().unwrap() / max_t, - bg_b.to_f32().unwrap() / max_t, - bg_a.to_f32().unwrap() / max_t, - ); - let (fg_r, fg_g, fg_b, fg_a) = ( - fg_r.to_f32().unwrap() / max_t, - fg_g.to_f32().unwrap() / max_t, - fg_b.to_f32().unwrap() / max_t, - fg_a.to_f32().unwrap() / max_t, - ); - - // Work out what the final alpha level will be - let alpha_final = bg_a + fg_a - bg_a * fg_a; - if alpha_final == 0.0 { - return; - }; - - // We premultiply our channels by their alpha, as this makes it easier to calculate - let (bg_r_a, bg_g_a, bg_b_a) = (bg_r * bg_a, bg_g * bg_a, bg_b * bg_a); - let (fg_r_a, fg_g_a, fg_b_a) = (fg_r * fg_a, fg_g * fg_a, fg_b * fg_a); - - // Standard formula for src-over alpha compositing - let (out_r_a, out_g_a, out_b_a) = ( - fg_r_a + bg_r_a * (1.0 - fg_a), - fg_g_a + bg_g_a * (1.0 - fg_a), - fg_b_a + bg_b_a * (1.0 - fg_a), - ); - - // Unmultiply the channels by our resultant alpha channel - let (out_r, out_g, out_b) = ( - out_r_a / alpha_final, - out_g_a / alpha_final, - out_b_a / alpha_final, - ); - - // Cast back to our initial type on return - *self = Rgba([ - NumCast::from(max_t * out_r).unwrap(), - NumCast::from(max_t * out_g).unwrap(), - NumCast::from(max_t * out_b).unwrap(), - NumCast::from(max_t * alpha_final).unwrap(), - ]) - } -} - -impl<T: Primitive> Blend for Rgb<T> { - fn blend(&mut self, other: &Rgb<T>) { - *self = *other - } -} - -/// Invert a color -pub(crate) trait Invert { - /// Inverts a color in-place. - fn invert(&mut self); -} - -impl<T: Primitive> Invert for LumaA<T> { - fn invert(&mut self) { - let l = self.0; - let max = T::DEFAULT_MAX_VALUE; - - *self = LumaA([max - l[0], l[1]]) - } -} - -impl<T: Primitive> Invert for Luma<T> { - fn invert(&mut self) { - let l = self.0; - - let max = T::DEFAULT_MAX_VALUE; - let l1 = max - l[0]; - - *self = Luma([l1]) - } -} - -impl<T: Primitive> Invert for Rgba<T> { - fn invert(&mut self) { - let rgba = self.0; - - let max = T::DEFAULT_MAX_VALUE; - - *self = Rgba([max - rgba[0], max - rgba[1], max - rgba[2], rgba[3]]) - } -} - -impl<T: Primitive> Invert for Rgb<T> { - fn invert(&mut self) { - let rgb = self.0; - - let max = T::DEFAULT_MAX_VALUE; - - let r1 = max - rgb[0]; - let g1 = max - rgb[1]; - let b1 = max - rgb[2]; - - *self = Rgb([r1, g1, b1]) - } -} - -#[cfg(test)] -mod tests { - use super::{Luma, LumaA, Pixel, Rgb, Rgba}; - - #[test] - fn test_apply_with_alpha_rgba() { - let mut rgba = Rgba([0, 0, 0, 0]); - rgba.apply_with_alpha(|s| s, |_| 0xFF); - assert_eq!(rgba, Rgba([0, 0, 0, 0xFF])); - } - - #[test] - fn test_apply_with_alpha_rgb() { - let mut rgb = Rgb([0, 0, 0]); - rgb.apply_with_alpha(|s| s, |_| panic!("bug")); - assert_eq!(rgb, Rgb([0, 0, 0])); - } - - #[test] - fn test_map_with_alpha_rgba() { - let rgba = Rgba([0, 0, 0, 0]).map_with_alpha(|s| s, |_| 0xFF); - assert_eq!(rgba, Rgba([0, 0, 0, 0xFF])); - } - - #[test] - fn test_map_with_alpha_rgb() { - let rgb = Rgb([0, 0, 0]).map_with_alpha(|s| s, |_| panic!("bug")); - assert_eq!(rgb, Rgb([0, 0, 0])); - } - - #[test] - fn test_blend_luma_alpha() { - let ref mut a = LumaA([255 as u8, 255]); - let b = LumaA([255 as u8, 255]); - a.blend(&b); - assert_eq!(a.0[0], 255); - assert_eq!(a.0[1], 255); - - let ref mut a = LumaA([255 as u8, 0]); - let b = LumaA([255 as u8, 255]); - a.blend(&b); - assert_eq!(a.0[0], 255); - assert_eq!(a.0[1], 255); - - let ref mut a = LumaA([255 as u8, 255]); - let b = LumaA([255 as u8, 0]); - a.blend(&b); - assert_eq!(a.0[0], 255); - assert_eq!(a.0[1], 255); - - let ref mut a = LumaA([255 as u8, 0]); - let b = LumaA([255 as u8, 0]); - a.blend(&b); - assert_eq!(a.0[0], 255); - assert_eq!(a.0[1], 0); - } - - #[test] - fn test_blend_rgba() { - let ref mut a = Rgba([255 as u8, 255, 255, 255]); - let b = Rgba([255 as u8, 255, 255, 255]); - a.blend(&b); - assert_eq!(a.0, [255, 255, 255, 255]); - - let ref mut a = Rgba([255 as u8, 255, 255, 0]); - let b = Rgba([255 as u8, 255, 255, 255]); - a.blend(&b); - assert_eq!(a.0, [255, 255, 255, 255]); - - let ref mut a = Rgba([255 as u8, 255, 255, 255]); - let b = Rgba([255 as u8, 255, 255, 0]); - a.blend(&b); - assert_eq!(a.0, [255, 255, 255, 255]); - - let ref mut a = Rgba([255 as u8, 255, 255, 0]); - let b = Rgba([255 as u8, 255, 255, 0]); - a.blend(&b); - assert_eq!(a.0, [255, 255, 255, 0]); - } - - #[test] - fn test_apply_without_alpha_rgba() { - let mut rgba = Rgba([0, 0, 0, 0]); - rgba.apply_without_alpha(|s| s + 1); - assert_eq!(rgba, Rgba([1, 1, 1, 0])); - } - - #[test] - fn test_apply_without_alpha_rgb() { - let mut rgb = Rgb([0, 0, 0]); - rgb.apply_without_alpha(|s| s + 1); - assert_eq!(rgb, Rgb([1, 1, 1])); - } - - #[test] - fn test_map_without_alpha_rgba() { - let rgba = Rgba([0, 0, 0, 0]).map_without_alpha(|s| s + 1); - assert_eq!(rgba, Rgba([1, 1, 1, 0])); - } - - #[test] - fn test_map_without_alpha_rgb() { - let rgb = Rgb([0, 0, 0]).map_without_alpha(|s| s + 1); - assert_eq!(rgb, Rgb([1, 1, 1])); - } - - macro_rules! test_lossless_conversion { - ($a:ty, $b:ty, $c:ty) => { - let a: $a = [<$a as Pixel>::Subpixel::DEFAULT_MAX_VALUE >> 2; - <$a as Pixel>::CHANNEL_COUNT as usize] - .into(); - let b: $b = a.into_color(); - let c: $c = b.into_color(); - assert_eq!(a.channels(), c.channels()); - }; - } - - #[test] - fn test_lossless_conversions() { - use super::IntoColor; - use crate::traits::Primitive; - - test_lossless_conversion!(Luma<u8>, Luma<u16>, Luma<u8>); - test_lossless_conversion!(LumaA<u8>, LumaA<u16>, LumaA<u8>); - test_lossless_conversion!(Rgb<u8>, Rgb<u16>, Rgb<u8>); - test_lossless_conversion!(Rgba<u8>, Rgba<u16>, Rgba<u8>); - } - - #[test] - fn accuracy_conversion() { - use super::{Luma, Pixel, Rgb}; - let pixel = Rgb::from([13, 13, 13]); - let Luma([luma]) = pixel.to_luma(); - assert_eq!(luma, 13); - } -} |