1use crate::image::{RGBAPLU, RGBLU};
2use rgb::alt::*;
3use rgb::*;
4
5#[doc(hidden)]
7pub trait GammaComponent {
8 type Lut;
9 fn max_value() -> usize;
10 fn to_linear(&self, lut: &Self::Lut) -> f32;
11 fn make_lut() -> Self::Lut;
12}
13
14#[doc(hidden)]
21pub trait GammaPixel {
22 type Component: GammaComponent;
23 type Output;
24
25 fn to_linear(&self, gamma_lut: &<Self::Component as GammaComponent>::Lut) -> Self::Output;
26
27 #[inline(always)]
28 #[must_use]
29 fn make_lut() -> <Self::Component as GammaComponent>::Lut {
30 <Self::Component as GammaComponent>::make_lut()
31 }
32}
33
34#[inline]
35fn to_linear(s: f32) -> f32 {
36 if s <= 0.04045 {
37 s / 12.92
38 } else {
39 ((s + 0.055) / 1.055).powf(2.4)
40 }
41}
42
43pub trait ToRGBAPLU {
47 fn to_rgbaplu(&self) -> Vec<RGBAPLU>;
49 fn to_rgblu(&self) -> Vec<RGBLU>;
51}
52
53impl GammaComponent for u8 {
54 type Lut = [f32; 256];
55 fn max_value() -> usize { 255 }
56 #[inline(always)]
57 fn to_linear(&self, lut: &Self::Lut) -> f32 {
58 lut[*self as usize]
59 }
60
61 #[inline(always)]
62 fn make_lut() -> Self::Lut {
63 let mut out = [0.; 256];
64 for (i, o) in out.iter_mut().enumerate() {
65 *o = to_linear(i as f32 / f32::from(Self::MAX));
66 }
67 out
68 }
69}
70
71impl GammaComponent for u16 {
72 type Lut = [f32; 65536];
73
74 fn max_value() -> usize { 65535 }
75
76 #[inline(always)]
77 fn to_linear(&self, lut: &Self::Lut) -> f32 {
78 lut[*self as usize]
79 }
80
81 #[inline(always)]
82 fn make_lut() -> Self::Lut {
83 let mut out = [0.; 65536];
84 for (i, o) in out.iter_mut().enumerate() {
85 *o = to_linear(i as f32 / f32::from(Self::MAX));
86 }
87 out
88 }
89}
90
91impl<M> GammaPixel for RGBA<M> where M: Clone + Into<f32> + GammaComponent {
92 type Component = M;
93 type Output = RGBAPLU;
94 #[inline]
95 fn to_linear(&self, gamma_lut: &M::Lut) -> RGBAPLU {
96 let a_unit = self.a.clone().into() / M::max_value() as f32;
97 RGBAPLU {
98 r: self.r.to_linear(gamma_lut) * a_unit,
99 g: self.g.to_linear(gamma_lut) * a_unit,
100 b: self.b.to_linear(gamma_lut) * a_unit,
101 a: a_unit,
102 }
103 }
104}
105
106impl<M> GammaPixel for BGRA<M> where M: Clone + Into<f32> + GammaComponent {
107 type Component = M;
108 type Output = RGBAPLU;
109
110 #[inline]
111 fn to_linear(&self, gamma_lut: &M::Lut) -> RGBAPLU {
112 let a_unit = self.a.clone().into() / M::max_value() as f32;
113 RGBAPLU {
114 r: self.r.to_linear(gamma_lut) * a_unit,
115 g: self.g.to_linear(gamma_lut) * a_unit,
116 b: self.b.to_linear(gamma_lut) * a_unit,
117 a: a_unit,
118 }
119 }
120}
121
122impl<M> GammaPixel for RGB<M> where M: GammaComponent {
123 type Component = M;
124 type Output = RGBAPLU;
125 #[inline]
126 fn to_linear(&self, gamma_lut: &M::Lut) -> RGBAPLU {
127 RGBAPLU {
128 r: self.r.to_linear(gamma_lut),
129 g: self.g.to_linear(gamma_lut),
130 b: self.b.to_linear(gamma_lut),
131 a: 1.0,
132 }
133 }
134}
135
136impl<M> GammaPixel for BGR<M> where M: GammaComponent {
137 type Component = M;
138 type Output = RGBAPLU;
139
140 #[inline]
141 fn to_linear(&self, gamma_lut: &M::Lut) -> RGBAPLU {
142 RGBAPLU {
143 r: self.r.to_linear(gamma_lut),
144 g: self.g.to_linear(gamma_lut),
145 b: self.b.to_linear(gamma_lut),
146 a: 1.0,
147 }
148 }
149}
150
151impl<M> GammaPixel for GrayAlpha<M> where M: Copy + Clone + Into<f32> + GammaComponent {
152 type Component = M;
153 type Output = RGBAPLU;
154
155 fn to_linear(&self, gamma_lut: &M::Lut) -> RGBAPLU {
156 let a_unit = self.value().into() / M::max_value() as f32;
157 let g = self.value().to_linear(gamma_lut);
158 RGBAPLU {
159 r: g * a_unit,
160 g: g * a_unit,
161 b: g * a_unit,
162 a: a_unit,
163 }
164 }
165}
166
167impl<M> GammaPixel for M where M: GammaComponent {
168 type Component = M;
169 type Output = f32;
170
171 #[inline(always)]
172 fn to_linear(&self, gamma_lut: &M::Lut) -> f32 {
173 self.to_linear(gamma_lut)
174 }
175}
176
177impl<M> GammaPixel for Gray<M> where M: Copy + GammaComponent {
178 type Component = M;
179 type Output = RGBAPLU;
180
181 #[inline(always)]
182 fn to_linear(&self, gamma_lut: &M::Lut) -> RGBAPLU {
183 let g = self.value().to_linear(gamma_lut);
184 RGBAPLU { r: g, g, b: g, a: 1.0 }
185 }
186}
187
188impl<P> ToRGBAPLU for [P] where P: GammaPixel<Output=RGBAPLU> {
189 fn to_rgbaplu(&self) -> Vec<RGBAPLU> {
190 let gamma_lut = P::make_lut();
191 self.iter().map(|px| px.to_linear(&gamma_lut)).collect()
192 }
193
194 fn to_rgblu(&self) -> Vec<RGBLU> {
195 let gamma_lut = P::make_lut();
196 self.iter().map(|px| px.to_linear(&gamma_lut).rgb()).collect()
197 }
198}