1use num_traits::{cast, Bounded, Float, NumCast};
2use rgb::RGB;
3use std::ops::Div;
4
5pub fn luminance<T: Bounded + NumCast, F: Float + NumCast + Div>(color: RGB<T>) -> F {
9 let channels: [T; 3] = color.into();
10 let [r, g, b] = channels;
11 scale_channel::<T, F>(r) * cast(0.2126).unwrap()
12 + scale_channel::<T, F>(g) * cast(0.7152).unwrap()
13 + scale_channel::<T, F>(b) * cast(0.0722).unwrap()
14}
15
16fn scale_channel<T: Bounded + NumCast, F: Float + NumCast + Div>(channel: T) -> F {
17 let float: F = cast(channel).expect("Failed to convert rgb channel into float");
18 let relative =
19 float / cast(T::max_value()).expect("Max value to rgb channel doesn't fit into float");
20 if relative < cast(0.03928).unwrap() {
21 relative / cast(12.92).unwrap()
22 } else {
23 F::powf(
24 (relative + cast(0.055).unwrap()) / cast(1.055).unwrap(),
25 cast(2.4).unwrap(),
26 )
27 }
28}
29
30pub fn contrast<T: Bounded + NumCast, F: Float + NumCast + Div>(a: RGB<T>, b: RGB<T>) -> F {
42 let luminance_a: F = luminance::<T, F>(a) + cast::<_, F>(0.05).unwrap();
43 let luminance_b: F = luminance::<T, F>(b) + cast::<_, F>(0.05).unwrap();
44
45 if luminance_a > luminance_b {
46 luminance_a / luminance_b
47 } else {
48 luminance_b / luminance_a
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55 use rgb::RGB8;
56
57 #[test]
58 fn test_contrast() {
59 let white = RGB8::from([255, 255, 255]);
60 let black = RGB8::from([0, 0, 0]);
61 let red = RGB8::from([255, 0, 0]);
62 let green = RGB8::from([0, 255, 0]);
63 let blue = RGB8::from([0, 0, 255]);
64 let yellow = RGB8::from([255, 255, 0]);
65
66 assert_eq!(luminance::<_, f32>(white), 1.0);
67 assert_eq!(luminance::<_, f32>(black), 0.0);
68
69 assert_eq!(contrast::<_, f32>(white, white), 1.0);
70 assert_eq!(contrast::<_, f32>(white, black), 20.999998);
71 assert_eq!(contrast::<_, f32>(black, white), 20.999998);
72
73 assert_eq!(contrast::<_, f32>(white, red), 3.9984765);
74 assert_eq!(contrast::<_, f32>(white, green), 1.3721902);
75 assert_eq!(contrast::<_, f32>(white, blue), 8.592471);
76 assert_eq!(contrast::<_, f32>(white, yellow), 1.0738392);
77 assert_eq!(contrast::<_, f32>(black, yellow), 19.556);
78 }
79}