use super::{
conversion::{calculate_channels, clamp_levels, convert_levels},
Color,
};
use std::{
fmt::{self, LowerHex, UpperHex},
hash::{Hash, Hasher},
ops::{Add, AddAssign, Deref, Div, DivAssign, Index, Mul, MulAssign, Sub, SubAssign},
};
impl LowerHex for Color {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let [red, green, blue, alpha] = self.channels();
write!(f, "#{red:x}{green:x}{blue:x}{alpha:x}")
}
}
impl UpperHex for Color {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let [red, green, blue, alpha] = self.channels();
write!(f, "#{red:X}{green:X}{blue:X}{alpha:X}")
}
}
impl Index<usize> for Color {
type Output = u8;
fn index(&self, idx: usize) -> &Self::Output {
&self.channels[idx]
}
}
impl PartialEq for Color {
fn eq(&self, other: &Self) -> bool {
self.channels.eq(&other.channels)
}
}
impl Eq for Color {}
impl Hash for Color {
fn hash<H: Hasher>(&self, state: &mut H) {
self.channels.hash(state);
}
}
impl Add for Color {
type Output = Self;
fn add(self, other: Color) -> Self::Output {
if self.mode == other.mode {
let [r, g, b, a] = self.channels();
let [or, og, ob, _] = other.channels();
Self {
mode: self.mode,
channels: [
r.saturating_add(or),
g.saturating_add(og),
b.saturating_add(ob),
a,
],
}
} else {
let [v1, v2, v3, a] = self.levels();
let [ov1, ov2, ov3, _] = convert_levels(other.levels(), other.mode, self.mode);
let levels = clamp_levels([v1 + ov1, v2 + ov2, v3 + ov3, a]);
Self {
mode: self.mode,
channels: calculate_channels(levels),
}
}
}
}
impl Add<u8> for Color {
type Output = Self;
fn add(self, val: u8) -> Self::Output {
let [r, g, b, _] = self.channels;
Self::rgb(
r.saturating_add(val),
g.saturating_add(val),
b.saturating_add(val),
)
}
}
impl AddAssign for Color {
fn add_assign(&mut self, other: Color) {
if self.mode == other.mode {
for (i, v) in self.channels.iter_mut().enumerate().take(3) {
*v = v.saturating_add(other[i]);
}
} else {
let [v1, v2, v3, a] = self.levels();
let [ov1, ov2, ov3, _] = convert_levels(other.levels(), other.mode, self.mode);
let levels = clamp_levels([v1 + ov1, v2 + ov2, v3 + ov3, a]);
self.update_channels(levels, self.mode);
}
}
}
impl AddAssign<u8> for Color {
fn add_assign(&mut self, val: u8) {
for v in &mut self.channels {
*v = v.saturating_add(val);
}
}
}
impl Sub for Color {
type Output = Self;
fn sub(self, other: Color) -> Self::Output {
if self.mode == other.mode {
let [r, g, b, a] = self.channels();
let [or, og, ob, _] = other.channels();
Self {
mode: self.mode,
channels: [
r.saturating_sub(or),
g.saturating_sub(og),
b.saturating_sub(ob),
a,
],
}
} else {
let [v1, v2, v3, a] = self.levels();
let [ov1, ov2, ov3, _] = convert_levels(other.levels(), other.mode, self.mode);
let levels = clamp_levels([v1 - ov1, v2 - ov2, v3 - ov3, a]);
Self {
mode: self.mode,
channels: calculate_channels(levels),
}
}
}
}
impl Sub<u8> for Color {
type Output = Self;
fn sub(self, val: u8) -> Self::Output {
let [r, g, b, a] = self.channels;
Self::rgba(
r.saturating_sub(val),
g.saturating_sub(val),
b.saturating_sub(val),
a,
)
}
}
impl SubAssign for Color {
fn sub_assign(&mut self, other: Color) {
if self.mode == other.mode {
for (i, v) in self.channels.iter_mut().enumerate().take(3) {
*v = v.saturating_sub(other[i]);
}
} else {
let [v1, v2, v3, a] = self.levels();
let [ov1, ov2, ov3, _] = convert_levels(other.levels(), other.mode, self.mode);
let levels = clamp_levels([v1 - ov1, v2 - ov2, v3 - ov3, a]);
self.update_channels(levels, self.mode);
}
}
}
impl SubAssign<u8> for Color {
fn sub_assign(&mut self, val: u8) {
for v in self.channels.iter_mut().take(3) {
*v = v.saturating_sub(val);
}
}
}
impl Deref for Color {
type Target = [u8; 4];
fn deref(&self) -> &Self::Target {
&self.channels
}
}
macro_rules! impl_ops {
($($target:ty),*) => {
$(
impl Mul<$target> for Color where $target: Into<f64> {
type Output = Self;
fn mul(self, s: $target) -> Self::Output {
let [v1, v2, v3, a] = self.levels();
let s = f64::from(s);
let levels = clamp_levels([v1 * s, v2 * s, v3 * s, a]);
Self {
mode: self.mode,
channels: calculate_channels(levels),
}
}
}
impl Mul<Color> for $target where $target: Into<f64> {
type Output = Color;
fn mul(self, c: Color) -> Self::Output {
let [v1, v2, v3, a] = c.levels();
let s = f64::from(self);
let levels = clamp_levels([v1 * s, v2 * s, v3 * s, a]);
Color {
mode: c.mode,
channels: calculate_channels(levels),
}
}
}
impl MulAssign<$target> for Color where $target: Into<f64> {
fn mul_assign(&mut self, s: $target) {
let [v1, v2, v3, a] = self.levels();
let s = f64::from(s);
let levels = clamp_levels([v1 * s, v2 * s, v3 * s, a]);
self.update_channels(levels, self.mode);
}
}
impl Div<$target> for Color where $target: Into<f64> {
type Output = Self;
fn div(self, s: $target) -> Self::Output {
let [v1, v2, v3, a] = self.levels();
let s = f64::from(s);
let levels = clamp_levels([v1 / s, v2 / s, v3 / s, a]);
Self {
mode: self.mode,
channels: calculate_channels(levels),
}
}
}
impl DivAssign<$target> for Color where $target: Into<f64> {
fn div_assign(&mut self, s: $target) {
let [v1, v2, v3, a] = self.levels();
let s = f64::from(s);
let levels = clamp_levels([v1 / s, v2 / s, v3 / s, a]);
self.update_channels(levels, self.mode);
}
}
)*
};
}
macro_rules! impl_as_ops {
($($target:ty),*) => {
$(
impl Mul<$target> for Color {
type Output = Self;
fn mul(self, s: $target) -> Self::Output {
let [v1, v2, v3, a] = self.levels();
let s = s as f64;
let levels = clamp_levels([v1 * s, v2 * s, v3 * s, a]);
Self {
mode: self.mode,
channels: calculate_channels(levels),
}
}
}
impl Mul<Color> for $target {
type Output = Color;
fn mul(self, c: Color) -> Self::Output {
let [v1, v2, v3, a] = c.levels();
let s = self as f64;
let levels = clamp_levels([v1 * s, v2 * s, v3 * s, a]);
Color {
mode: c.mode,
channels: calculate_channels(levels),
}
}
}
impl MulAssign<$target> for Color {
fn mul_assign(&mut self, s: $target) {
let [v1, v2, v3, a] = self.levels();
let s = s as f64;
let levels = clamp_levels([v1 * s, v2 * s, v3 * s, a]);
self.update_channels(levels, self.mode);
}
}
impl Div<$target> for Color {
type Output = Self;
fn div(self, s: $target) -> Self::Output {
let [v1, v2, v3, a] = self.levels();
let s = s as f64;
let levels = clamp_levels([v1 / s, v2 / s, v3 / s, a]);
Self {
mode: self.mode,
channels: calculate_channels(levels),
}
}
}
impl DivAssign<$target> for Color {
fn div_assign(&mut self, s: $target) {
let [v1, v2, v3, a] = self.levels();
let s = s as f64;
let levels = clamp_levels([v1 / s, v2 / s, v3 / s, a]);
self.update_channels(levels, self.mode);
}
}
)*
}
}
impl_ops!(i8, u8, i16, u16, f32);
impl_ops!(i32, u32, f64);
impl_as_ops!(isize, usize, i64, u64, i128, u128);
#[cfg(test)]
mod tests {
use crate::prelude::*;
macro_rules! test_ops {
($($val: expr),*) => {
$(
let c = color!(200, 50, 10, 100) * $val;
assert_eq!(c.channels(), [255, 100, 20, 100]);
let c: Color = $val * color!(200, 50, 10, 100);
assert_eq!(c.channels(), [255, 100, 20, 100]);
let mut c = color!(200, 50, 10, 100);
c *= $val;
assert_eq!(c.channels(), [255, 100, 20, 100]);
let c: Color = color!(100, 255, 0, 100) / $val;
assert_eq!(c.channels(), [50, 128, 0, 100]);
let mut c = color!(200, 50, 10, 100);
c /= $val;
assert_eq!(c.channels(), [100, 25, 5, 100]);
)*
};
}
#[test]
fn test_ops() {
let c1 = color!(200, 50, 10, 100);
let c2 = color!(100, 50, 10, 100);
let c3 = c1 + c2;
assert_eq!(c3.channels(), [255, 100, 20, 100]);
let mut c1 = color!(200, 50, 10, 100);
let c2 = color!(100, 50, 10, 100);
c1 += c2;
assert_eq!(c1.channels(), [255, 100, 20, 100]);
let c1 = color!(200, 100, 20, 200);
let c2 = color!(100, 50, 30, 100);
let c3 = c1 - c2;
assert_eq!(c3.channels(), [100, 50, 0, 200]);
let mut c1 = color!(200, 100, 20, 200);
let c2 = color!(100, 50, 30, 100);
c1 -= c2;
assert_eq!(c1.channels(), [100, 50, 0, 200]);
test_ops!(2i8, 2u8, 2i16, 2u16, 2i32, 2u32, 2f32, 2f64);
}
}