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*/