#![allow(clippy::upper_case_acronyms)]
#[must_use]
pub fn srgb_to_linear(u: u8) -> f32 {
let u = f32::from(u) * 0.003_921_569;
if u <= 0.04045 {
u * 0.077_399_38
} else {
((u + 0.055) * 0.947_867_3).powf(2.4)
}
}
#[must_use]
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
pub fn linear_to_srgb(u: f32) -> u8 {
let u = if u <= 0.003_130_8 {
12.92 * u
} else {
1.055 * u.powf(0.416_666_66) - 0.055
};
if u >= 1.0 {
return 255;
}
if u < 0.0 {
return 0;
}
(255.0 * u).round() as u8
}
#[must_use]
#[allow(clippy::mistyped_literal_suffixes)]
pub fn linear_to_gray(rgb: [f32; 3]) -> f32 {
0.212_639 * rgb[0] + 0.715_168_7 * rgb[1] + 0.072_192_32 * rgb[2]
}
#[must_use]
#[allow(clippy::float_cmp, clippy::many_single_char_names)]
pub fn linear_to_hsv([r, g, b]: [f32; 3]) -> [f32; 3] {
debug_assert!((0.0..=1.0).contains(&r));
debug_assert!((0.0..=1.0).contains(&g));
debug_assert!((0.0..=1.0).contains(&b));
let x_max = r.max(g).max(b);
let x_min = r.min(g).min(b);
let c = x_max - x_min;
let v = x_max;
let h = if c == 0.0 {
0.0
} else if v == r {
60.0 * (0.0 + (g - b) / c)
} else if v == g {
60.0 * (2.0 + (b - r) / c)
} else if v == b {
60.0 * (4.0 + (r - g) / c)
} else {
unsafe { std::hint::unreachable_unchecked() };
};
let s = if v == 0.0 { 0.0 } else { c / v };
let h = if h < 0.0 { 360.0 + h } else { h };
[h, s, v]
}
#[must_use]
#[allow(clippy::many_single_char_names, clippy::manual_range_contains)]
pub fn hsv_to_linear([h, s, v]: [f32; 3]) -> [f32; 3] {
debug_assert!((0.0..=360.0).contains(&h));
debug_assert!((0.0..=1.0).contains(&s));
debug_assert!((0.0..=1.0).contains(&v));
let c = s * v;
let h = h / 60.0;
let x = c * (1.0 - (h % 2.0 - 1.0).abs());
let (r, g, b) = if c == 0.0 {
(0.0, 0.0, 0.0)
} else if h >= 0.0 && h <= 1.0 {
(c, x, 0.0)
} else if h > 1.0 && h <= 2.0 {
(x, c, 0.0)
} else if h > 2.0 && h <= 3.0 {
(0.0, c, x)
} else if h > 3.0 && h <= 4.0 {
(0.0, x, c)
} else if h > 4.0 && h <= 5.0 {
(x, 0.0, c)
} else if h > 5.0 && h <= 6.0 {
(c, 0.0, x)
} else {
std::unreachable!();
};
let m = v - c;
[r + m, g + m, b + m]
}
pub mod iter {
use super::{hsv_to_linear, linear_to_gray, linear_to_hsv, linear_to_srgb, srgb_to_linear};
#[allow(clippy::type_complexity)]
pub struct SRGBToLinear<Iter>
where
Iter: std::iter::Iterator<Item = [u8; 3]>,
{
iter: Iter,
}
impl<Iter> std::iter::Iterator for SRGBToLinear<Iter>
where
Iter: std::iter::Iterator<Item = [u8; 3]>,
{
type Item = [f32; 3];
fn next(&mut self) -> Option<Self::Item> {
self
.iter
.next()
.map(|[r, g, b]| [srgb_to_linear(r), srgb_to_linear(g), srgb_to_linear(b)])
}
}
pub trait SRGBLinearIterator: std::iter::Iterator<Item = [u8; 3]>
where
Self: Sized,
{
fn srgb_to_linear(self) -> SRGBToLinear<Self> {
SRGBToLinear { iter: self }
}
}
impl<Iter> SRGBLinearIterator for Iter where Iter: std::iter::Iterator<Item = [u8; 3]> {}
pub struct LinearToSRGB<Iter>
where
Iter: std::iter::Iterator<Item = [f32; 3]>,
{
iter: Iter,
}
impl<Iter> std::iter::Iterator for LinearToSRGB<Iter>
where
Iter: std::iter::Iterator<Item = [f32; 3]>,
{
type Item = [u8; 3];
fn next(&mut self) -> Option<Self::Item> {
self
.iter
.next()
.map(|[r, g, b]| [linear_to_srgb(r), linear_to_srgb(g), linear_to_srgb(b)])
}
}
#[allow(clippy::type_complexity)]
pub trait LinearSRGBIterator: std::iter::Iterator<Item = [f32; 3]>
where
Self: Sized,
{
fn linear_to_srgb(self) -> LinearToSRGB<Self> {
LinearToSRGB { iter: self }
}
}
impl<Iter> LinearSRGBIterator for Iter where Iter: std::iter::Iterator<Item = [f32; 3]> {}
pub struct LinearToGray<Iter>
where
Iter: std::iter::Iterator<Item = [f32; 3]>,
{
iter: Iter,
}
impl<Iter> std::iter::Iterator for LinearToGray<Iter>
where
Iter: std::iter::Iterator<Item = [f32; 3]>,
{
type Item = f32;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(linear_to_gray)
}
}
pub trait LinearGrayIterator: std::iter::Iterator<Item = [f32; 3]>
where
Self: Sized,
{
fn linear_to_gray(self) -> LinearToGray<Self> {
LinearToGray { iter: self }
}
}
impl<Iter> LinearGrayIterator for Iter where Iter: std::iter::Iterator<Item = [f32; 3]> {}
pub struct LinearToHSV<Iter>
where
Iter: std::iter::Iterator<Item = [f32; 3]>,
{
iter: Iter,
}
impl<Iter> std::iter::Iterator for LinearToHSV<Iter>
where
Iter: std::iter::Iterator<Item = [f32; 3]>,
{
type Item = [f32; 3];
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(linear_to_hsv)
}
}
pub trait LinearHSVIterator: std::iter::Iterator<Item = [f32; 3]>
where
Self: Sized,
{
fn linear_to_hsv(self) -> LinearToHSV<Self> {
LinearToHSV { iter: self }
}
}
impl<Iter> LinearHSVIterator for Iter where Iter: std::iter::Iterator<Item = [f32; 3]> {}
pub struct HSVToLinear<Iter>
where
Iter: std::iter::Iterator<Item = [f32; 3]>,
{
iter: Iter,
}
impl<Iter> std::iter::Iterator for HSVToLinear<Iter>
where
Iter: std::iter::Iterator<Item = [f32; 3]>,
{
type Item = [f32; 3];
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(hsv_to_linear)
}
}
pub trait HSVLinearIterator: std::iter::Iterator<Item = [f32; 3]>
where
Self: Sized,
{
fn hsv_to_linear(self) -> HSVToLinear<Self> {
HSVToLinear { iter: self }
}
}
impl<Iter> HSVLinearIterator for Iter where Iter: std::iter::Iterator<Item = [f32; 3]> {}
}