planetarium/
gamma.rs

1//! Planetarium
2//! ===========
3//!
4//! Private gamma compression curve definitions
5//! -------------------------------------------
6//!
7//! Defines a new opaque private structure `GammaCurve8`
8//! implementing the sRGB gamma compression curve
9//! with 8-bit output precision.
10
11#![allow(clippy::doc_markdown)]
12
13/// Opaque 16-bit -> 8-bit gamma compression curve LUT object
14pub(crate) struct GammaCurve8 {
15    /// LUT byte vector
16    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    /// Lookup table resolution (bits)
26    const LUT_BITS: u32 = 12;
27
28    /// Allocates and initializes the gamma compression LUT entries.
29    #[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            // sRGB gamma curve function
37            let gamma = if x <= 0.003_130_8 {
38                // Linear segment
39                12.92 * x
40            } else {
41                // Power-law segment
42                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    /// Converts 16-bit linear light raw samples into
54    /// 8-bit gamma-compressed sRGB grayscale samples.
55    #[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}