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