#![allow(clippy::float_cmp)]
use crate::*;
pub type Channel = usize;
pub trait Color:
Unpin + PartialEq + Eq + PartialOrd + Ord + Clone + Sync + Send + std::fmt::Debug
{
const NAME: &'static str;
const CHANNELS: Channel;
const ALPHA: Option<Channel> = None;
fn to_rgb(src: &Pixel<Self>, dest: &mut Pixel<Rgb>);
fn from_rgb(pixel: &Pixel<Rgb>, dest: &mut Pixel<Self>);
fn convert<ToColor: Color>(src: &Pixel<Self>, dest: &mut Pixel<ToColor>) {
src.convert_to(dest);
}
}
macro_rules! color {
($t:ident, $doc:expr) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[doc = $doc]
pub struct $t;
unsafe impl Sync for $t {}
unsafe impl Send for $t {}
};
}
color!(Gray, "Single-channel grayscale");
impl Color for Gray {
const NAME: &'static str = "gray";
const CHANNELS: Channel = 1;
fn to_rgb(src: &Pixel<Self>, pixel: &mut Pixel<Rgb>) {
pixel.fill(src[0]);
}
fn from_rgb(src: &Pixel<Rgb>, mut dest: &mut Pixel<Self>) {
dest[0] = src[0] * 0.21 + src[1] * 0.72 + src[2] * 0.7;
}
}
color!(Rgb, "Three-channel red, green, blue");
impl Color for Rgb {
const NAME: &'static str = "rgb";
const CHANNELS: Channel = 3;
fn to_rgb(rgb: &Pixel<Self>, pixel: &mut Pixel<Rgb>) {
pixel.copy_from(rgb);
}
fn from_rgb(rgb: &Pixel<Rgb>, pixel: &mut Pixel<Self>) {
pixel.copy_from(rgb);
}
}
color!(Srgb, "Log space, three-channel red, green, blue");
impl Color for Srgb {
const NAME: &'static str = "rgb";
const CHANNELS: Channel = 3;
fn to_rgb(rgb: &Pixel<Self>, pixel: &mut Pixel<Rgb>) {
pixel.copy_from_slice(rgb);
pixel.gamma_lin();
}
fn from_rgb(rgb: &Pixel<Rgb>, pixel: &mut Pixel<Self>) {
pixel.copy_from_slice(rgb);
pixel.gamma_log();
}
}
color!(Rgba, "Four-channel red, green, blue with alpha channel");
impl Color for Rgba {
const NAME: &'static str = "rgba";
const CHANNELS: Channel = 4;
const ALPHA: Option<Channel> = Some(3);
fn to_rgb(pixel: &Pixel<Self>, mut rgb: &mut Pixel<Rgb>) {
rgb[0] = pixel[0] * pixel[3];
rgb[1] = pixel[1] * pixel[3];
rgb[2] = pixel[2] * pixel[3];
}
fn from_rgb(rgb: &Pixel<Rgb>, mut pixel: &mut Pixel<Self>) {
pixel[0] = rgb[0];
pixel[1] = rgb[1];
pixel[2] = rgb[2];
pixel[3] = 1.0;
}
}
color!(
Srgba,
"Log space, four-channel red, green, blue with alpha channel"
);
impl Color for Srgba {
const NAME: &'static str = "rgba";
const CHANNELS: Channel = 4;
const ALPHA: Option<Channel> = Some(3);
fn to_rgb(pixel: &Pixel<Self>, mut rgb: &mut Pixel<Rgb>) {
rgb[0] = pixel[0] * pixel[3];
rgb[1] = pixel[1] * pixel[3];
rgb[2] = pixel[2] * pixel[3];
rgb.gamma_lin();
}
fn from_rgb(rgb: &Pixel<Rgb>, mut pixel: &mut Pixel<Self>) {
pixel[0] = rgb[0];
pixel[1] = rgb[1];
pixel[2] = rgb[2];
pixel[3] = 1.0;
pixel.gamma_log();
}
}
color!(Xyz, "Three-channel CIE-XYZ");
impl Color for Xyz {
const NAME: &'static str = "xyz";
const CHANNELS: Channel = 3;
fn from_rgb(rgb: &Pixel<Rgb>, mut pixel: &mut Pixel<Self>) {
let mut r = rgb[0];
let mut g = rgb[1];
let mut b = rgb[2];
if r > 0.04045 {
r = ((r + 0.055) / 1.055).powf(2.4)
} else {
r /= 12.92
}
if g > 0.04045 {
g = ((g + 0.055) / 1.055).powf(2.4);
} else {
g /= 12.92
}
if b > 0.04045 {
b = ((b + 0.055) / 1.055).powf(2.4)
} else {
b /= 12.92
}
pixel[0] = r * 0.4124 + g * 0.3576 + b * 0.1805;
pixel[1] = r * 0.2126 + g * 0.7152 + b * 0.0722;
pixel[2] = r * 0.0193 + g * 0.1192 + b * 0.9505;
}
fn to_rgb(px: &Pixel<Xyz>, mut rgb: &mut Pixel<Rgb>) {
let x = px[0];
let y = px[1];
let z = px[2];
rgb[0] = {
let var_r = x * 3.2406 + y * -1.5372 + z * -0.4986;
if var_r > 0.0031308 {
1.055 * (var_r.powf(1.0 / 2.4)) - 0.055
} else {
12.92 * var_r
}
};
rgb[1] = {
let var_g = x * -0.9689 + y * 1.8758 + z * 0.0415;
if var_g > 0.0031308 {
1.055 * (var_g.powf(1. / 2.4)) - 0.055
} else {
12.92 * var_g
}
};
rgb[2] = {
let var_b = x * 0.0557 + y * -0.2040 + z * 1.0570;
if var_b > 0.0031308 {
1.055 * (var_b.powf(1. / 2.4)) - 0.055
} else {
12.92 * var_b
}
};
}
}
color!(Hsv, "Three-channel hue, saturation and value color");
impl Color for Hsv {
const NAME: &'static str = "hsv";
const CHANNELS: Channel = 3;
fn from_rgb(rgb: &Pixel<Rgb>, mut pixel: &mut Pixel<Self>) {
let r = rgb[0];
let g = rgb[1];
let b = rgb[2];
let cmax = r.max(g).max(b);
let cmin = r.min(g).min(b);
let delta = cmax - cmin;
let del_r = (((cmax - r) / 6.) + (delta / 2.)) / delta;
let del_g = (((cmax - g) / 6.) + (delta / 2.)) / delta;
let del_b = (((cmax - b) / 6.) + (delta / 2.)) / delta;
pixel[0] = {
let x = if cmin == cmax {
0.0
} else if cmax == r {
del_b - del_g
} else if cmax == g {
(1. / 3.) + del_r - del_b
} else if cmax == b {
(2. / 3.) + del_g - del_r
} else {
-1.0
};
if x < 0. {
x + 1.
} else if x > 1. {
x - 1.
} else {
x
}
};
pixel[1] = {
if cmax == 0.0 {
0.0
} else {
delta / cmax
}
};
pixel[2] = cmax;
}
fn to_rgb(px: &Pixel<Hsv>, mut rgb: &mut Pixel<Rgb>) {
if px[1] == 0. {
rgb.fill(px[2]);
return;
}
let (h, s, v) = (px[0], px[1], px[2]);
let mut var_h = h * 6.;
if var_h == 6. {
var_h = 0.0;
}
let var_i = var_h.floor();
let var_1 = v * (1. - s);
let var_2 = v * (1. - s * (var_h - var_i));
let var_3 = v * (1. - s * (1. - (var_h - var_i)));
if var_i == 0. {
rgb[0] = v;
rgb[1] = var_3;
rgb[2] = var_1;
} else if var_i == 1. {
rgb[0] = var_2;
rgb[1] = v;
rgb[2] = var_1;
} else if var_i == 2. {
rgb[0] = var_1;
rgb[1] = v;
rgb[2] = var_3;
} else if var_i == 3. {
rgb[0] = var_1;
rgb[1] = var_2;
rgb[2] = v;
} else if var_i == 4. {
rgb[0] = var_3;
rgb[1] = var_1;
rgb[2] = v;
} else {
rgb[0] = v;
rgb[1] = var_1;
rgb[2] = var_2;
}
}
}
color!(
Yuv,
"Three-channel, luma, blue projection and red projection"
);
impl Color for Yuv {
const NAME: &'static str = "yuv";
const CHANNELS: Channel = 3;
fn from_rgb(rgb: &Pixel<Rgb>, mut pixel: &mut Pixel<Self>) {
let r = rgb[0];
let g = rgb[1];
let b = rgb[2];
pixel[0] = 0.299 * r + 0.587 * g + 0.114 * b;
pixel[1] = -0.147 * r + 0.289 + g + 0.436 * b;
pixel[2] = 0.615 * r + 0.515 * g + 0.1 * b;
}
fn to_rgb(px: &Pixel<Self>, mut rgb: &mut Pixel<Rgb>) {
let y = px[0];
let u = px[1];
let v = px[2];
rgb[0] = y + 1.14 * v;
rgb[1] = y - 0.395 * u - 0.581 * v;
rgb[2] = y + 2.032 * u;
}
}
color!(Cmyk, "Four-channel, cyan, magenta, yellow and black");
impl Color for Cmyk {
const NAME: &'static str = "cmyk";
const CHANNELS: Channel = 4;
#[allow(clippy::many_single_char_names)]
fn from_rgb(rgb: &Pixel<Rgb>, mut pixel: &mut Pixel<Self>) {
let r = rgb[0];
let g = rgb[1];
let b = rgb[2];
let c = 1.0 - r;
let m = 1.0 - g;
let y = 1.0 - b;
let mut k = 1.0;
if c < k {
k = c
}
if m < k {
k = m
}
if y < k {
k = y
}
if k == 1. {
pixel[0] = 0.;
pixel[1] = 0.;
pixel[2] = 0.;
pixel[3] = k;
} else {
pixel[0] = (c - k) / (1. - k);
pixel[1] = (m - k) / (1. - k);
pixel[2] = (y - k) / (1. - k);
pixel[3] = k;
}
}
fn to_rgb(cmyk: &Pixel<Cmyk>, mut rgb: &mut Pixel<Rgb>) {
let mut c = cmyk[0];
let mut m = cmyk[1];
let mut y = cmyk[2];
let k = cmyk[3];
c = c * (1.0 - k) + k;
m = m * (1.0 - k) + k;
y = y * (1.0 - k) + k;
rgb[0] = 1.0 - c;
rgb[1] = 1.0 - m;
rgb[2] = 1.0 - y;
}
}