rawshift_image/data/
wb.rs1#[derive(Debug, Clone, Copy)]
8pub struct Illuminant {
9 pub name: &'static str,
11 pub exif_code: u16,
13 pub cct: u32,
15 pub x: f64,
17 pub y: f64,
19}
20
21impl Illuminant {
22 pub fn xyz(&self) -> [f64; 3] {
24 let big_x = self.x / self.y;
25 let big_z = (1.0 - self.x - self.y) / self.y;
26 [big_x, 1.0, big_z]
27 }
28}
29
30pub const ILLUMINANT_A: Illuminant = Illuminant {
32 name: "Standard Illuminant A",
33 exif_code: 17,
34 cct: 2856,
35 x: 0.44757,
36 y: 0.40745,
37};
38
39pub const D50: Illuminant = Illuminant {
41 name: "D50",
42 exif_code: 23,
43 cct: 5003,
44 x: 0.34567,
45 y: 0.35850,
46};
47
48pub const D55: Illuminant = Illuminant {
50 name: "D55",
51 exif_code: 20,
52 cct: 5503,
53 x: 0.33242,
54 y: 0.34743,
55};
56
57pub const D65: Illuminant = Illuminant {
59 name: "D65",
60 exif_code: 21,
61 cct: 6504,
62 x: 0.31271,
63 y: 0.32902,
64};
65
66pub const D75: Illuminant = Illuminant {
68 name: "D75",
69 exif_code: 22,
70 cct: 7504,
71 x: 0.29902,
72 y: 0.31485,
73};
74
75pub const COOL_WHITE_FLUORESCENT: Illuminant = Illuminant {
77 name: "Cool White Fluorescent",
78 exif_code: 15,
79 cct: 4150,
80 x: 0.37510,
81 y: 0.36714,
82};
83
84static ILLUMINANTS: &[Illuminant] = &[ILLUMINANT_A, COOL_WHITE_FLUORESCENT, D50, D55, D65, D75];
86
87pub fn from_exif_code(code: u16) -> Option<&'static Illuminant> {
89 ILLUMINANTS.iter().find(|i| i.exif_code == code)
90}
91
92pub fn from_name(name: &str) -> Option<&'static Illuminant> {
94 let lower = name.to_lowercase();
95 ILLUMINANTS.iter().find(|i| i.name.to_lowercase() == lower)
96}
97
98pub fn nearest_by_cct(cct: u32) -> &'static Illuminant {
100 ILLUMINANTS
101 .iter()
102 .min_by_key(|i| (i.cct as i64 - cct as i64).unsigned_abs())
103 .expect("illuminant table is non-empty")
104}
105
106pub fn all_illuminants() -> &'static [Illuminant] {
108 ILLUMINANTS
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn test_d65_values() {
117 assert_eq!(D65.cct, 6504);
118 assert!((D65.x - 0.31271).abs() < 1e-5);
119 assert!((D65.y - 0.32902).abs() < 1e-5);
120 }
121
122 #[test]
123 fn test_d50_xyz() {
124 let xyz = D50.xyz();
125 assert!((xyz[0] - 0.9642).abs() < 0.01);
127 assert!((xyz[1] - 1.0).abs() < 1e-10);
128 assert!((xyz[2] - 0.8251).abs() < 0.01);
129 }
130
131 #[test]
132 fn test_exif_lookup() {
133 let d65 = from_exif_code(21).unwrap();
134 assert_eq!(d65.name, "D65");
135
136 let std_a = from_exif_code(17).unwrap();
137 assert_eq!(std_a.name, "Standard Illuminant A");
138 }
139
140 #[test]
141 fn test_unknown_exif_code() {
142 assert!(from_exif_code(255).is_none());
143 }
144
145 #[test]
146 fn test_nearest_cct() {
147 let nearest = nearest_by_cct(6500);
148 assert_eq!(nearest.name, "D65");
149
150 let nearest = nearest_by_cct(3000);
151 assert_eq!(nearest.name, "Standard Illuminant A");
152
153 let nearest = nearest_by_cct(5000);
154 assert_eq!(nearest.name, "D50");
155 }
156
157 #[test]
158 fn test_all_illuminants() {
159 let all = all_illuminants();
160 assert_eq!(all.len(), 6);
161 }
162}