#![forbid(unsafe_code)]
use crate::mlaf::mlaf;
use crate::saturate_narrow::SaturateNarrow;
use num_traits::{FromPrimitive, MulAdd, WrappingAdd, WrappingMul};
use std::ops::{Add, AddAssign, Mul, Shr, ShrAssign, Sub, SubAssign};
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub(crate) struct ColorGroup<const CN: usize, J: Copy> {
pub(crate) r: J,
pub(crate) g: J,
pub(crate) b: J,
pub(crate) a: J,
}
impl<const CN: usize, J> ColorGroup<CN, J>
where
J: Copy + Default,
{
#[inline(always)]
pub(crate) fn new() -> ColorGroup<CN, J> {
ColorGroup {
r: J::default(),
g: J::default(),
b: J::default(),
a: J::default(),
}
}
#[inline(always)]
pub(crate) fn from_components(r: J, g: J, b: J, a: J) -> ColorGroup<CN, J> {
ColorGroup { r, g, b, a }
}
#[inline(always)]
pub(crate) fn dup(v: J) -> ColorGroup<CN, J> {
ColorGroup {
r: v,
g: v,
b: v,
a: v,
}
}
}
impl<const CN: usize, J> Mul<J> for ColorGroup<CN, J>
where
J: Copy + Mul<Output = J> + Default + 'static,
{
type Output = Self;
#[inline(always)]
fn mul(self, rhs: J) -> Self::Output {
if CN == 1 {
ColorGroup::from_components(self.r * rhs, self.g, self.b, self.a)
} else if CN == 2 {
ColorGroup::from_components(self.r * rhs, self.g * rhs, self.b, self.a)
} else if CN == 3 {
ColorGroup::from_components(self.r * rhs, self.g * rhs, self.b * rhs, self.a)
} else if CN == 4 {
ColorGroup::from_components(self.r * rhs, self.g * rhs, self.b * rhs, self.a * rhs)
} else {
unimplemented!();
}
}
}
impl<const CN: usize, J> ColorGroup<CN, J>
where
J: Copy + WrappingMul<Output = J> + Default + 'static,
{
#[inline(always)]
pub(crate) fn trunc_mul(self, rhs: J) -> ColorGroup<CN, J> {
if CN == 1 {
ColorGroup::from_components(self.r.wrapping_mul(&rhs), self.g, self.b, self.a)
} else if CN == 2 {
ColorGroup::from_components(
self.r.wrapping_mul(&rhs),
self.g.wrapping_mul(&rhs),
self.b,
self.a,
)
} else if CN == 3 {
ColorGroup::from_components(
self.r.wrapping_mul(&rhs),
self.g.wrapping_mul(&rhs),
self.b.wrapping_mul(&rhs),
self.a,
)
} else if CN == 4 {
ColorGroup::from_components(
self.r.wrapping_mul(&rhs),
self.g.wrapping_mul(&rhs),
self.b.wrapping_mul(&rhs),
self.a.wrapping_mul(&rhs),
)
} else {
unimplemented!();
}
}
}
impl<const CN: usize, J> ColorGroup<CN, J>
where
J: Copy + WrappingAdd<Output = J> + Default + 'static,
{
#[inline(always)]
pub(crate) fn trunc_add(self, rhs: &ColorGroup<CN, J>) -> ColorGroup<CN, J> {
if CN == 1 {
ColorGroup::from_components(self.r.wrapping_add(&rhs.r), self.g, self.b, self.a)
} else if CN == 2 {
ColorGroup::from_components(
self.r.wrapping_add(&rhs.r),
self.g.wrapping_add(&rhs.g),
self.b,
self.a,
)
} else if CN == 3 {
ColorGroup::from_components(
self.r.wrapping_add(&rhs.r),
self.g.wrapping_add(&rhs.g),
self.b.wrapping_add(&rhs.b),
self.a,
)
} else if CN == 4 {
ColorGroup::from_components(
self.r.wrapping_add(&rhs.r),
self.g.wrapping_add(&rhs.g),
self.b.wrapping_add(&rhs.b),
self.a.wrapping_add(&rhs.a),
)
} else {
unimplemented!();
}
}
}
impl<const CN: usize, J> ColorGroup<CN, J>
where
J: Copy + Default + 'static,
{
#[inline(always)]
pub(crate) fn saturate_narrow<V>(&self, bit_depth: u32) -> ColorGroup<CN, V>
where
V: Copy + Default,
J: SaturateNarrow<V>,
{
if CN == 1 {
ColorGroup::<CN, V>::from_components(
self.r.saturate_narrow(bit_depth),
V::default(),
V::default(),
V::default(),
)
} else if CN == 2 {
ColorGroup::<CN, V>::from_components(
self.r.saturate_narrow(bit_depth),
self.g.saturate_narrow(bit_depth),
V::default(),
V::default(),
)
} else if CN == 3 {
ColorGroup::<CN, V>::from_components(
self.r.saturate_narrow(bit_depth),
self.g.saturate_narrow(bit_depth),
self.b.saturate_narrow(bit_depth),
V::default(),
)
} else {
ColorGroup::<CN, V>::from_components(
self.r.saturate_narrow(bit_depth),
self.g.saturate_narrow(bit_depth),
self.b.saturate_narrow(bit_depth),
self.a.saturate_narrow(bit_depth),
)
}
}
}
impl<const CN: usize, J> Mul<ColorGroup<CN, J>> for ColorGroup<CN, J>
where
J: Copy + Mul<Output = J> + Default + 'static,
{
type Output = Self;
#[inline(always)]
fn mul(self, rhs: ColorGroup<CN, J>) -> Self::Output {
if CN == 1 {
ColorGroup::from_components(self.r * rhs.r, self.g, self.b, self.a)
} else if CN == 2 {
ColorGroup::from_components(self.r * rhs.r, self.g * rhs.g, self.b, self.a)
} else if CN == 3 {
ColorGroup::from_components(self.r * rhs.r, self.g * rhs.g, self.b * rhs.b, self.a)
} else if CN == 4 {
ColorGroup::from_components(
self.r * rhs.r,
self.g * rhs.g,
self.b * rhs.b,
self.a * rhs.b,
)
} else {
unimplemented!();
}
}
}
impl<const CN: usize, J> Sub<J> for ColorGroup<CN, J>
where
J: Copy + Sub<Output = J> + Default + 'static,
{
type Output = Self;
#[inline(always)]
fn sub(self, rhs: J) -> Self::Output {
if CN == 1 {
ColorGroup::from_components(self.r - rhs, self.g, self.b, self.a)
} else if CN == 2 {
ColorGroup::from_components(self.r - rhs, self.g - rhs, self.b, self.a)
} else if CN == 3 {
ColorGroup::from_components(self.r - rhs, self.g - rhs, self.b - rhs, self.a)
} else if CN == 4 {
ColorGroup::from_components(self.r - rhs, self.g - rhs, self.b - rhs, self.a - rhs)
} else {
unimplemented!();
}
}
}
impl<const CN: usize, J> Sub<ColorGroup<CN, J>> for ColorGroup<CN, J>
where
J: Copy + Sub<Output = J> + Default + 'static,
{
type Output = Self;
#[inline(always)]
fn sub(self, rhs: ColorGroup<CN, J>) -> Self::Output {
if CN == 1 {
ColorGroup::from_components(self.r - rhs.r, self.g, self.b, self.a)
} else if CN == 2 {
ColorGroup::from_components(self.r - rhs.r, self.g - rhs.g, self.b, self.a)
} else if CN == 3 {
ColorGroup::from_components(self.r - rhs.r, self.g - rhs.g, self.b - rhs.b, self.a)
} else if CN == 4 {
ColorGroup::from_components(
self.r - rhs.r,
self.g - rhs.g,
self.b - rhs.b,
self.a - rhs.a,
)
} else {
unimplemented!("Not implemented.");
}
}
}
impl<const CN: usize, J> Add<ColorGroup<CN, J>> for ColorGroup<CN, J>
where
J: Copy + Add<Output = J> + Default + 'static,
{
type Output = Self;
#[inline(always)]
fn add(self, rhs: ColorGroup<CN, J>) -> Self::Output {
if CN == 1 {
ColorGroup::from_components(self.r + rhs.r, self.g, self.b, self.a)
} else if CN == 2 {
ColorGroup::from_components(self.r + rhs.r, self.g + rhs.g, self.b, self.a)
} else if CN == 3 {
ColorGroup::from_components(self.r + rhs.r, self.g + rhs.g, self.b + rhs.b, self.a)
} else if CN == 4 {
ColorGroup::from_components(
self.r + rhs.r,
self.g + rhs.g,
self.b + rhs.b,
self.a + rhs.a,
)
} else {
unimplemented!("Not implemented.");
}
}
}
impl<const CN: usize, J> Add<J> for ColorGroup<CN, J>
where
J: Copy + Add<Output = J> + Default + 'static,
{
type Output = Self;
#[inline(always)]
fn add(self, rhs: J) -> Self::Output {
if CN == 1 {
ColorGroup::from_components(self.r + rhs, self.g, self.b, self.a)
} else if CN == 2 {
ColorGroup::from_components(self.r + rhs, self.g + rhs, self.b, self.a)
} else if CN == 3 {
ColorGroup::from_components(self.r + rhs, self.g + rhs, self.b + rhs, self.a)
} else if CN == 4 {
ColorGroup::from_components(self.r + rhs, self.g + rhs, self.b + rhs, self.a + rhs)
} else {
unimplemented!("Not implemented.");
}
}
}
impl<const CN: usize, J> Shr<J> for ColorGroup<CN, J>
where
J: Copy + Shr<J, Output = J> + Default + 'static,
{
type Output = Self;
#[inline(always)]
fn shr(self, rhs: J) -> Self::Output {
if CN == 1 {
ColorGroup::from_components(self.r >> rhs, self.g, self.b, self.a)
} else if CN == 2 {
ColorGroup::from_components(self.r >> rhs, self.g >> rhs, self.b, self.a)
} else if CN == 3 {
ColorGroup::from_components(self.r >> rhs, self.g >> rhs, self.b >> rhs, self.a)
} else if CN == 4 {
ColorGroup::from_components(self.r >> rhs, self.g >> rhs, self.b >> rhs, self.a >> rhs)
} else {
unimplemented!("Not implemented.");
}
}
}
impl<const CN: usize, J> ShrAssign<J> for ColorGroup<CN, J>
where
J: Copy + ShrAssign<J> + Default + 'static,
{
#[inline(always)]
fn shr_assign(&mut self, rhs: J) {
if CN == 1 {
self.r >>= rhs;
} else if CN == 2 {
self.r >>= rhs;
self.g >>= rhs;
} else if CN == 3 {
self.r >>= rhs;
self.g >>= rhs;
self.b >>= rhs;
} else if CN == 4 {
self.r >>= rhs;
self.g >>= rhs;
self.b >>= rhs;
self.a >>= rhs;
}
}
}
impl<const CN: usize, J> MulAdd<ColorGroup<CN, J>, J> for ColorGroup<CN, J>
where
J: Copy + MulAdd<J, Output = J> + Default + 'static + Mul<J, Output = J> + Add<J, Output = J>,
{
type Output = Self;
#[inline(always)]
fn mul_add(self, a: ColorGroup<CN, J>, b: J) -> Self::Output {
if CN == 1 {
ColorGroup::from_components(mlaf(self.r, a.r, b), self.g, self.b, self.a)
} else if CN == 2 {
ColorGroup::from_components(mlaf(self.r, a.r, b), mlaf(self.g, a.g, b), self.b, self.a)
} else if CN == 3 {
ColorGroup::from_components(
mlaf(self.r, a.r, b),
mlaf(self.g, a.g, b),
mlaf(self.b, a.b, b),
self.a,
)
} else if CN == 4 {
ColorGroup::from_components(
mlaf(self.r, a.r, b),
mlaf(self.g, a.g, b),
mlaf(self.b, a.b, b),
mlaf(self.a, a.a, b),
)
} else {
unimplemented!();
}
}
}
impl<const CN: usize, J> AddAssign<ColorGroup<CN, J>> for ColorGroup<CN, J>
where
J: Copy + AddAssign,
{
#[inline(always)]
fn add_assign(&mut self, rhs: ColorGroup<CN, J>) {
if CN == 1 {
self.r += rhs.r;
} else if CN == 2 {
self.r += rhs.r;
self.g += rhs.g;
} else if CN == 3 {
self.r += rhs.r;
self.g += rhs.g;
self.b += rhs.b;
} else if CN == 4 {
self.r += rhs.r;
self.g += rhs.g;
self.b += rhs.b;
self.a += rhs.a;
}
}
}
impl<const CN: usize, J> SubAssign<ColorGroup<CN, J>> for ColorGroup<CN, J>
where
J: Copy + SubAssign,
{
#[inline(always)]
fn sub_assign(&mut self, rhs: ColorGroup<CN, J>) {
if CN == 1 {
self.r -= rhs.r;
} else if CN == 2 {
self.r -= rhs.r;
self.g -= rhs.g;
} else if CN == 3 {
self.r -= rhs.r;
self.g -= rhs.g;
self.b -= rhs.b;
} else if CN == 4 {
self.r -= rhs.r;
self.g -= rhs.g;
self.b -= rhs.b;
self.a -= rhs.a;
}
}
}
impl<const CN: usize, J> Default for ColorGroup<CN, J>
where
J: Copy + FromPrimitive + Default,
{
#[inline(always)]
fn default() -> Self {
ColorGroup::new()
}
}
macro_rules! ld_g {
($store: expr, $channels: expr, $vtype: ty) => {{
if $channels == 1 {
ColorGroup::<$channels, $vtype> {
r: $store[0].as_(),
g: 0.as_(),
b: 0.as_(),
a: 0.as_(),
}
} else if $channels == 2 {
ColorGroup::<$channels, $vtype> {
r: $store[0].as_(),
g: $store[1].as_(),
b: 0.as_(),
a: 0.as_(),
}
} else if $channels == 3 {
ColorGroup::<$channels, $vtype> {
r: $store[0].as_(),
g: $store[1].as_(),
b: $store[2].as_(),
a: 0.as_(),
}
} else if $channels == 4 {
ColorGroup::<$channels, $vtype> {
r: $store[0].as_(),
g: $store[1].as_(),
b: $store[2].as_(),
a: $store[3].as_(),
}
} else {
unimplemented!()
}
}};
}
pub(crate) use ld_g;
macro_rules! ldg_with_offset {
($store: expr, $channels: expr, $offset: expr, $vtype: ty) => {{
if $channels == 1 {
ColorGroup::<$channels, $vtype> {
r: $store[$offset].as_(),
g: 0.as_(),
b: 0.as_(),
a: 0.as_(),
}
} else if $channels == 2 {
ColorGroup::<$channels, $vtype> {
r: $store[$offset].as_(),
g: $store[$offset + 1].as_(),
b: 0.as_(),
a: 0.as_(),
}
} else if $channels == 3 {
ColorGroup::<$channels, $vtype> {
r: $store[$offset].as_(),
g: $store[$offset + 1].as_(),
b: $store[$offset + 2].as_(),
a: 0.as_(),
}
} else if $channels == 4 {
ColorGroup::<$channels, $vtype> {
r: $store[$offset].as_(),
g: $store[$offset + 1].as_(),
b: $store[$offset + 2].as_(),
a: $store[$offset + 3].as_(),
}
} else {
unimplemented!()
}
}};
}
pub(crate) use ldg_with_offset;
macro_rules! st_g {
($color_group: expr, $store: expr, $components: expr) => {{
$store[0] = $color_group.r;
if $components > 1 {
$store[1] = $color_group.g;
}
if $components > 2 {
$store[2] = $color_group.b;
}
if $components == 4 {
$store[3] = $color_group.a;
}
}};
}
pub(crate) use st_g;
macro_rules! st_g_mixed {
($color_group: expr, $store: expr, $components: expr, $bit_depth: expr) => {{
$store[0] = $color_group.r.to_mixed($bit_depth);
if $components > 1 {
$store[1] = $color_group.g.to_mixed($bit_depth);
}
if $components > 2 {
$store[2] = $color_group.b.to_mixed($bit_depth);
}
if $components == 4 {
$store[3] = $color_group.a.to_mixed($bit_depth);
}
}};
}
pub(crate) use st_g_mixed;