devela/media/color/
namespace.rs

1// devela::media::color::namespace
2//
3//! Standalone color functions and constants.
4//
5
6#[allow(unused_imports)]
7use crate::code::{iif, paste, sf, CONST};
8
9#[cfg(all(_float··, not(feature = "std")))]
10#[allow(unused_imports, reason = "!std: powf, powi")]
11use crate::num::ExtFloat;
12
13#[doc = crate::TAG_NAMESPACE!()]
14/// Color namespaced constants and methods.
15pub struct Color;
16
17// RETHINK
18CONST! { pub(crate),
19    /// Returns the RGB luminance factors for the Rec. 709 color space.
20    ///
21    /// # Details
22    /// Rec. 709 is used for sRGB and HDTV color spaces.
23    // #[macro_export]
24    LUMINANCE_REC_709 = {[0.212_639, 0.715_169, 0.072_192]}
25}
26
27impl Color {
28    /// Returns the coefficients used for calculating the R,G,B luminances.
29    pub const fn luminances_rgb_f32() -> [f32; 3] {
30        LUMINANCE_REC_709![]
31    }
32    /// Returns the coefficients used for calculating the R,G,B luminances.
33    pub const fn luminances_rgb_f64() -> [f64; 3] {
34        LUMINANCE_REC_709![]
35    }
36}
37
38///
39///
40/// # Args
41/// $t:   the floating-point primitive
42/// $cap: the capability feature that enables the given implementation. E.g "_f32".
43macro_rules! color_gamma_fns {
44    ($($t:ty : $cap:literal),+) => { $( color_gamma_fns![@$t:$cap]; )+ };
45    (@$t:ty : $cap:literal) => { paste! {
46        impl Color {
47            #[doc = "Applies the `gamma` *(γ)* to a linear `" $t "` channel to make it non-linear."]
48            ///
49            /// # Algorithm
50            /// $$
51            /// \begin{align}
52            /// \notag f_\text{apply}(c) = \begin{cases}
53            /// 12.92c,
54            ///   & \text{if } c <= 0.0031308 \cr
55            /// 1.055c^{1/\gamma} - 0.055,
56            ///   & \text{if } c > 0.0031308 \end{cases} \cr
57            /// \end{align}
58            /// $$
59            #[cfg(any(feature = "std", feature = $cap))]
60            #[cfg_attr(feature = "nightly_doc", doc(cfg(any(feature = "std", feature = $cap))))]
61            pub fn [<gamma_apply_ $t>](c: $t, gamma: $t) -> $t {
62                iif![c <= 0.003_130_8; 12.92 * c; 1.055 * c.powf(1.0 / gamma) - 0.055]
63            }
64
65            #[doc = "Removes the `gamma` *(γ)* from a non-linear `" $t "` channel to make it linear."]
66            ///
67            /// # Algorithm
68            /// $$
69            /// \begin{align}
70            /// \notag f_\text{remove}(c) = \begin{cases}
71            /// c / 12.92,
72            ///   & \normalsize\text{if } c <= 0.04045 \cr
73            /// \left(\Large\frac{c+0.055}{1.055} - \normalsize 0.055\right)^{\gamma},
74            ///   & \normalsize \text{if } c > 0.04045 \end{cases} \cr
75            /// \end{align}
76            /// $$
77            #[cfg(any(feature = "std", feature = $cap))]
78            #[cfg_attr(feature = "nightly_doc", doc(cfg(any(feature = "std", feature = $cap))))]
79            pub fn [<gamma_remove_ $t>](c: $t, gamma: $t) -> $t {
80                iif![c <= 0.040_45; c / 12.92; ((c + 0.055) / (1.055)).powf(gamma)]
81            }
82        }
83    }};
84}
85color_gamma_fns![f32:"_float_f32", f64:"_float_f64"];
86
87// TODO SACAR (needs std or _float_f…
88
89/*
90/// Convert an f64 \[0‥1\] component to a u8 \[0‥255\] component
91#[inline] #[must_use] #[rustfmt::skip]
92fn cu8(v: f64) -> u8 { (v * 255.0).round() as u8 }
93
94/// Convert from sRGB to RGB for a single component
95#[inline] #[must_use] #[rustfmt::skip]
96fn srgb_to_rgb(x: f64) -> f64 {
97    if x <= 0.04045 { x / 12.92 } else { ((x + 0.055) / 1.055).powf(2.4) }
98}
99/// Convert from RGB to sRGB for a single component
100#[inline] #[must_use] #[rustfmt::skip]
101fn rgb_to_srgb(x: f64) -> f64 {
102    if x <= 0.003_130_8 { x * 12.92 } else { 1.055 * x.powf(1.0 / 2.4) - 0.055 }
103}
104
105// Converts a `u8` color component to `f64` in the range [0.0, 1.0].
106#[inline] #[must_use] #[rustfmt::skip]
107pub(crate) fn color_u8_to_f64(x: u8) -> f64 { f64::from(x) / 255.0 }
108
109// Computes the luminance of an RGB color in `u8` and returns it as `u8`.
110#[inline] #[must_use] #[rustfmt::skip]
111fn luminance_u8(red: u8, green: u8, blue: u8) -> u8 {
112    (luminance(color_u8_to_f64(red), color_u8_to_f64(green), color_u8_to_f64(blue)) * 255.0).round()
113        as u8
114}
115
116/// Returns the luminance.
117#[inline] #[must_use] #[rustfmt::skip]
118pub const fn luminance(red: f64, green: f64, blue: f64) -> f64 {
119    0.2126 * red + 0.7152 * green + 0.0722 * blue
120}
121
122/// Returns the lightness.
123///
124/// $ (\text{max}(R, G, B) + \text{min}(R, G, B)) / 2 $
125#[inline] #[must_use] #[rustfmt::skip]
126pub const fn lightness(red: f64, green: f64, blue: f64) -> f64 {
127    let (mut cmax, mut cmin) = (red, red);
128    if green > cmax { cmax = green; }
129    if blue > cmax { cmax = blue; }
130    if green < cmin { cmin = green; }
131    if blue < cmin { cmin = blue; }
132    (cmax + cmin) / 2.0
133}
134
135/// Returns the average.
136#[inline] #[must_use] #[rustfmt::skip]
137pub const fn average(red: f64, green: f64, blue: f64) -> f64 {
138    (red + green + blue) / 3.0
139}
140*/