1use crate::mlaf::mlaf;
30use crate::transform::PointeeSizeExpressible;
31use crate::{CmsError, Layout, TransformExecutor, Vector3f};
32use num_traits::AsPrimitive;
33
34#[derive(Clone)]
35pub(crate) struct ToneReproductionRgbToGray<T, const BUCKET: usize> {
36 pub(crate) r_linear: Box<[f32; BUCKET]>,
37 pub(crate) g_linear: Box<[f32; BUCKET]>,
38 pub(crate) b_linear: Box<[f32; BUCKET]>,
39 pub(crate) gray_gamma: Box<[T; 65536]>,
40}
41
42#[derive(Clone)]
43struct TransformRgbToGrayExecutor<
44 T,
45 const SRC_LAYOUT: u8,
46 const DST_LAYOUT: u8,
47 const BUCKET: usize,
48 const GAMMA_LUT: usize,
49> {
50 trc_box: ToneReproductionRgbToGray<T, BUCKET>,
51 weights: Vector3f,
52 bit_depth: usize,
53}
54
55pub(crate) fn make_rgb_to_gray<
56 T: Copy + Default + PointeeSizeExpressible + Send + Sync + 'static,
57 const BUCKET: usize,
58 const BIT_DEPTH: usize,
59 const GAMMA_LUT: usize,
60>(
61 src_layout: Layout,
62 dst_layout: Layout,
63 trc: ToneReproductionRgbToGray<T, BUCKET>,
64 weights: Vector3f,
65) -> Box<dyn TransformExecutor<T> + Send + Sync>
66where
67 u32: AsPrimitive<T>,
68{
69 match src_layout {
70 Layout::Rgb => match dst_layout {
71 Layout::Rgb => unreachable!(),
72 Layout::Rgba => unreachable!(),
73 Layout::Gray => Box::new(TransformRgbToGrayExecutor::<
74 T,
75 { Layout::Rgb as u8 },
76 { Layout::Gray as u8 },
77 BUCKET,
78 GAMMA_LUT,
79 > {
80 trc_box: trc,
81 weights,
82 bit_depth: BIT_DEPTH,
83 }),
84 Layout::GrayAlpha => Box::new(TransformRgbToGrayExecutor::<
85 T,
86 { Layout::Rgb as u8 },
87 { Layout::GrayAlpha as u8 },
88 BUCKET,
89 GAMMA_LUT,
90 > {
91 trc_box: trc,
92 weights,
93 bit_depth: BIT_DEPTH,
94 }),
95 _ => unreachable!(),
96 },
97 Layout::Rgba => match dst_layout {
98 Layout::Rgb => unreachable!(),
99 Layout::Rgba => unreachable!(),
100 Layout::Gray => Box::new(TransformRgbToGrayExecutor::<
101 T,
102 { Layout::Rgba as u8 },
103 { Layout::Gray as u8 },
104 BUCKET,
105 GAMMA_LUT,
106 > {
107 trc_box: trc,
108 weights,
109 bit_depth: BIT_DEPTH,
110 }),
111 Layout::GrayAlpha => Box::new(TransformRgbToGrayExecutor::<
112 T,
113 { Layout::Rgba as u8 },
114 { Layout::GrayAlpha as u8 },
115 BUCKET,
116 GAMMA_LUT,
117 > {
118 trc_box: trc,
119 weights,
120 bit_depth: BIT_DEPTH,
121 }),
122 _ => unreachable!(),
123 },
124 Layout::Gray => unreachable!(),
125 Layout::GrayAlpha => unreachable!(),
126 _ => unreachable!(),
127 }
128}
129
130impl<
131 T: Copy + Default + PointeeSizeExpressible + 'static,
132 const SRC_LAYOUT: u8,
133 const DST_LAYOUT: u8,
134 const BUCKET: usize,
135 const GAMMA_LUT: usize,
136> TransformExecutor<T> for TransformRgbToGrayExecutor<T, SRC_LAYOUT, DST_LAYOUT, BUCKET, GAMMA_LUT>
137where
138 u32: AsPrimitive<T>,
139{
140 fn transform(&self, src: &[T], dst: &mut [T]) -> Result<(), CmsError> {
141 let src_cn = Layout::from(SRC_LAYOUT);
142 let dst_cn = Layout::from(DST_LAYOUT);
143 let src_channels = src_cn.channels();
144 let dst_channels = dst_cn.channels();
145
146 if src.len() / src_channels != dst.len() / dst_channels {
147 return Err(CmsError::LaneSizeMismatch);
148 }
149 if src.len() % src_channels != 0 {
150 return Err(CmsError::LaneMultipleOfChannels);
151 }
152 if dst.len() % dst_channels != 0 {
153 return Err(CmsError::LaneMultipleOfChannels);
154 }
155
156 let scale_value = (GAMMA_LUT - 1) as f32;
157 let max_value = ((1u32 << self.bit_depth) - 1).as_();
158
159 for (src, dst) in src
160 .chunks_exact(src_channels)
161 .zip(dst.chunks_exact_mut(dst_channels))
162 {
163 let r = self.trc_box.r_linear[src[src_cn.r_i()]._as_usize()];
164 let g = self.trc_box.g_linear[src[src_cn.g_i()]._as_usize()];
165 let b = self.trc_box.b_linear[src[src_cn.b_i()]._as_usize()];
166 let a = if src_channels == 4 {
167 src[src_cn.a_i()]
168 } else {
169 max_value
170 };
171 let grey = mlaf(
172 0.5,
173 mlaf(
174 mlaf(self.weights.v[0] * r, self.weights.v[1], g),
175 self.weights.v[2],
176 b,
177 )
178 .min(1.)
179 .max(0.),
180 scale_value,
181 );
182 dst[0] = self.trc_box.gray_gamma[(grey as u16) as usize];
183 if dst_channels == 2 {
184 dst[1] = a;
185 }
186 }
187
188 Ok(())
189 }
190}