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]
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_value()));
66 }
67 out
68 }
69}
70
71impl GammaComponent for u16 {
72 type Lut = [f32; 65536];
73 fn max_value() -> usize { 65535 }
74 #[inline(always)]
75 fn to_linear(&self, lut: &Self::Lut) -> f32 {
76 lut[*self as usize]
77 }
78
79 #[inline]
80 fn make_lut() -> Self::Lut {
81 let mut out = [0.; 65536];
82 for (i, o) in out.iter_mut().enumerate() {
83 *o = to_linear(i as f32 / f32::from(Self::max_value()));
84 }
85 out
86 }
87}
88
89impl<M> GammaPixel for RGBA<M> where M: Clone + Into<f32> + GammaComponent {
90 type Component = M;
91 type Output = RGBAPLU;
92 #[inline]
93 fn to_linear(&self, gamma_lut: &M::Lut) -> RGBAPLU {
94 let a_unit = self.a.clone().into() / M::max_value() as f32;
95 RGBAPLU {
96 r: self.r.to_linear(gamma_lut) * a_unit,
97 g: self.g.to_linear(gamma_lut) * a_unit,
98 b: self.b.to_linear(gamma_lut) * a_unit,
99 a: a_unit,
100 }
101 }
102}
103
104impl<M> GammaPixel for BGRA<M> where M: Clone + Into<f32> + GammaComponent {
105 type Component = M;
106 type Output = RGBAPLU;
107
108 #[inline]
109 fn to_linear(&self, gamma_lut: &M::Lut) -> RGBAPLU {
110 let a_unit = self.a.clone().into() / M::max_value() as f32;
111 RGBAPLU {
112 r: self.r.to_linear(gamma_lut) * a_unit,
113 g: self.g.to_linear(gamma_lut) * a_unit,
114 b: self.b.to_linear(gamma_lut) * a_unit,
115 a: a_unit,
116 }
117 }
118}
119
120impl<M> GammaPixel for RGB<M> where M: GammaComponent {
121 type Component = M;
122 type Output = RGBAPLU;
123 #[inline]
124 fn to_linear(&self, gamma_lut: &M::Lut) -> RGBAPLU {
125 RGBAPLU {
126 r: self.r.to_linear(gamma_lut),
127 g: self.g.to_linear(gamma_lut),
128 b: self.b.to_linear(gamma_lut),
129 a: 1.0,
130 }
131 }
132}
133
134impl<M> GammaPixel for BGR<M> where M: GammaComponent {
135 type Component = M;
136 type Output = RGBAPLU;
137
138 #[inline]
139 fn to_linear(&self, gamma_lut: &M::Lut) -> RGBAPLU {
140 RGBAPLU {
141 r: self.r.to_linear(gamma_lut),
142 g: self.g.to_linear(gamma_lut),
143 b: self.b.to_linear(gamma_lut),
144 a: 1.0,
145 }
146 }
147}
148
149impl<M> GammaPixel for GrayAlpha<M> where M: Copy + Clone + Into<f32> + GammaComponent {
150 type Component = M;
151 type Output = RGBAPLU;
152
153 fn to_linear(&self, gamma_lut: &M::Lut) -> RGBAPLU {
154 let a_unit = self.1.into() / M::max_value() as f32;
155 let g = self.0.to_linear(gamma_lut);
156 RGBAPLU {
157 r: g * a_unit,
158 g: g * a_unit,
159 b: g * a_unit,
160 a: a_unit,
161 }
162 }
163}
164
165impl<M> GammaPixel for M where M: GammaComponent {
166 type Component = M;
167 type Output = f32;
168
169 #[inline(always)]
170 fn to_linear(&self, gamma_lut: &M::Lut) -> f32 {
171 self.to_linear(gamma_lut)
172 }
173}
174
175impl<M> GammaPixel for Gray<M> where M: Copy + GammaComponent {
176 type Component = M;
177 type Output = RGBAPLU;
178
179 #[inline(always)]
180 fn to_linear(&self, gamma_lut: &M::Lut) -> RGBAPLU {
181 let g = self.0.to_linear(gamma_lut);
182 RGBAPLU { r: g, g, b: g, a: 1.0 }
183 }
184}
185
186impl<P> ToRGBAPLU for [P] where P: GammaPixel<Output=RGBAPLU> {
187 fn to_rgbaplu(&self) -> Vec<RGBAPLU> {
188 let gamma_lut = P::make_lut();
189 self.iter().map(|px| px.to_linear(&gamma_lut)).collect()
190 }
191
192 fn to_rgblu(&self) -> Vec<RGBLU> {
193 let gamma_lut = P::make_lut();
194 self.iter().map(|px| px.to_linear(&gamma_lut).rgb()).collect()
195 }
196}