1#![allow(clippy::doc_markdown)]
12
13pub(crate) struct GammaCurve8 {
15 lut: Vec<u8>,
17}
18
19#[allow(
20 clippy::cast_possible_truncation,
21 clippy::cast_precision_loss,
22 clippy::cast_sign_loss
23)]
24impl GammaCurve8 {
25 const LUT_BITS: u32 = 12;
27
28 #[must_use]
30 pub(crate) fn new() -> Self {
31 let size = 1u32 << Self::LUT_BITS;
32
33 let lut_fn = |i| {
34 let x = (i as f32) / ((size - 1) as f32);
35
36 let gamma = if x <= 0.003_130_8 {
38 12.92 * x
40 } else {
41 1.055 * x.powf(1.0 / 2.4) - 0.055
43 };
44
45 (gamma * f32::from(u8::MAX) + 0.5) as u8
46 };
47
48 let lut = (0..size).map(lut_fn).collect();
49
50 GammaCurve8 { lut }
51 }
52
53 #[must_use]
56 pub(crate) fn transform(&self, x: u16) -> u8 {
57 let shift = 16 - Self::LUT_BITS;
58 let i = (x >> shift) as usize;
59
60 debug_assert!(i < self.lut.len());
61 unsafe { *self.lut.get_unchecked(i) }
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn transform() {
71 let lut = GammaCurve8::new();
72
73 assert_eq!(lut.transform(0), 0);
74 assert_eq!(lut.transform(16), 1);
75 assert_eq!(lut.transform(32), 2);
76 assert_eq!(lut.transform(64), 3);
77 assert_eq!(lut.transform(80), 4);
78 assert_eq!(lut.transform(96), 5);
79
80 assert_eq!(lut.transform(256), 13);
81 assert_eq!(lut.transform(1024), 34);
82 assert_eq!(lut.transform(16384), 137);
83 assert_eq!(lut.transform(32768), 188);
84 assert_eq!(lut.transform(65535), 255);
85 }
86}