1use crate::conversions::mab::{BCurves3, MCurves3};
30use crate::err::try_vec;
31use crate::safe_math::SafeMul;
32use crate::{
33 CmsError, Cube, DataColorSpace, InPlaceStage, InterpolationMethod, LutMultidimensionalType,
34 MalformedSize, Matrix3d, Stage, TransformOptions, Vector3d, Vector4f,
35};
36
37struct ACurves3x4Inverse<'a> {
38 curve0: Box<[f32; 65536]>,
39 curve1: Box<[f32; 65536]>,
40 curve2: Box<[f32; 65536]>,
41 curve3: Box<[f32; 65536]>,
42 clut: &'a [f32],
43 grid_size: [u8; 3],
44 interpolation_method: InterpolationMethod,
45 pcs: DataColorSpace,
46 depth: usize,
47}
48
49struct ACurves3x4InverseOptimized<'a> {
50 clut: &'a [f32],
51 grid_size: [u8; 3],
52 interpolation_method: InterpolationMethod,
53 pcs: DataColorSpace,
54}
55
56impl ACurves3x4Inverse<'_> {
57 fn transform_impl<Fetch: Fn(f32, f32, f32) -> Vector4f>(
58 &self,
59 src: &[f32],
60 dst: &mut [f32],
61 fetch: Fetch,
62 ) -> Result<(), CmsError> {
63 let scale_value = (self.depth as u32 - 1u32) as f32;
64
65 assert_eq!(src.len() / 3, dst.len() / 4);
66
67 for (src, dst) in src.chunks_exact(3).zip(dst.chunks_exact_mut(4)) {
68 let interpolated = fetch(src[0], src[1], src[2]);
69 let a0 = (interpolated.v[0] * scale_value).round().min(scale_value) as u16;
70 let a1 = (interpolated.v[1] * scale_value).round().min(scale_value) as u16;
71 let a2 = (interpolated.v[2] * scale_value).round().min(scale_value) as u16;
72 let a3 = (interpolated.v[3] * scale_value).round().min(scale_value) as u16;
73 let b0 = self.curve0[a0 as usize];
74 let b1 = self.curve1[a1 as usize];
75 let b2 = self.curve2[a2 as usize];
76 let b3 = self.curve3[a3 as usize];
77 dst[0] = b0;
78 dst[1] = b1;
79 dst[2] = b2;
80 dst[3] = b3;
81 }
82 Ok(())
83 }
84}
85
86impl ACurves3x4InverseOptimized<'_> {
87 fn transform_impl<Fetch: Fn(f32, f32, f32) -> Vector4f>(
88 &self,
89 src: &[f32],
90 dst: &mut [f32],
91 fetch: Fetch,
92 ) -> Result<(), CmsError> {
93 assert_eq!(src.len() / 3, dst.len() / 4);
94
95 for (src, dst) in src.chunks_exact(3).zip(dst.chunks_exact_mut(4)) {
96 let interpolated = fetch(src[0], src[1], src[2]);
97 let b0 = interpolated.v[0];
98 let b1 = interpolated.v[1];
99 let b2 = interpolated.v[2];
100 let b3 = interpolated.v[3];
101 dst[0] = b0;
102 dst[1] = b1;
103 dst[2] = b2;
104 dst[3] = b3;
105 }
106 Ok(())
107 }
108}
109
110impl Stage for ACurves3x4Inverse<'_> {
111 fn transform(&self, src: &[f32], dst: &mut [f32]) -> Result<(), CmsError> {
112 let lut = Cube::new_checked_cube(self.clut, self.grid_size, 4)?;
113
114 if self.pcs == DataColorSpace::Lab || self.pcs == DataColorSpace::Xyz {
116 return self.transform_impl(src, dst, |x, y, z| lut.trilinear_vec4(x, y, z));
117 }
118
119 match self.interpolation_method {
120 #[cfg(feature = "options")]
121 InterpolationMethod::Tetrahedral => {
122 self.transform_impl(src, dst, |x, y, z| lut.tetra_vec4(x, y, z))?;
123 }
124 #[cfg(feature = "options")]
125 InterpolationMethod::Pyramid => {
126 self.transform_impl(src, dst, |x, y, z| lut.pyramid_vec4(x, y, z))?;
127 }
128 #[cfg(feature = "options")]
129 InterpolationMethod::Prism => {
130 self.transform_impl(src, dst, |x, y, z| lut.prism_vec4(x, y, z))?;
131 }
132 InterpolationMethod::Linear => {
133 self.transform_impl(src, dst, |x, y, z| lut.trilinear_vec4(x, y, z))?;
134 }
135 }
136 Ok(())
137 }
138}
139
140impl Stage for ACurves3x4InverseOptimized<'_> {
141 fn transform(&self, src: &[f32], dst: &mut [f32]) -> Result<(), CmsError> {
142 let lut = Cube::new_checked_cube(self.clut, self.grid_size, 4)?;
143
144 if self.pcs == DataColorSpace::Lab || self.pcs == DataColorSpace::Xyz {
146 return self.transform_impl(src, dst, |x, y, z| lut.trilinear_vec4(x, y, z));
147 }
148
149 match self.interpolation_method {
150 #[cfg(feature = "options")]
151 InterpolationMethod::Tetrahedral => {
152 self.transform_impl(src, dst, |x, y, z| lut.tetra_vec4(x, y, z))?;
153 }
154 #[cfg(feature = "options")]
155 InterpolationMethod::Pyramid => {
156 self.transform_impl(src, dst, |x, y, z| lut.pyramid_vec4(x, y, z))?;
157 }
158 #[cfg(feature = "options")]
159 InterpolationMethod::Prism => {
160 self.transform_impl(src, dst, |x, y, z| lut.prism_vec4(x, y, z))?;
161 }
162 InterpolationMethod::Linear => {
163 self.transform_impl(src, dst, |x, y, z| lut.trilinear_vec4(x, y, z))?;
164 }
165 }
166 Ok(())
167 }
168}
169
170pub(crate) fn prepare_mba_3x4(
171 mab: &LutMultidimensionalType,
172 lut: &mut [f32],
173 options: TransformOptions,
174 pcs: DataColorSpace,
175) -> Result<Vec<f32>, CmsError> {
176 if mab.num_input_channels != 3 || mab.num_output_channels != 4 {
177 return Err(CmsError::UnsupportedProfileConnection);
178 }
179
180 const LERP_DEPTH: usize = 65536;
181 const BP: usize = 13;
182 const DEPTH: usize = 8192;
183
184 if mab.b_curves.len() == 3 {
185 let all_curves_linear = mab.b_curves.iter().all(|curve| curve.is_linear());
186
187 if !all_curves_linear {
188 let curves: Result<Vec<_>, _> = mab
189 .b_curves
190 .iter()
191 .map(|c| {
192 c.build_linearize_table::<u16, LERP_DEPTH, BP>()
193 .ok_or(CmsError::InvalidTrcCurve)
194 })
195 .collect();
196
197 let [curve0, curve1, curve2] =
198 curves?.try_into().map_err(|_| CmsError::InvalidTrcCurve)?;
199 let b_curves = BCurves3::<DEPTH> {
200 curve0,
201 curve1,
202 curve2,
203 };
204 b_curves.transform(lut)?;
205 }
206 } else {
207 return Err(CmsError::InvalidAtoBLut);
208 }
209
210 if mab.m_curves.len() == 3 {
211 let all_curves_linear = mab.m_curves.iter().all(|curve| curve.is_linear());
212 if !all_curves_linear
213 || !mab.matrix.test_equality(Matrix3d::IDENTITY)
214 || mab.bias.ne(&Vector3d::default())
215 {
216 let curves: Result<Vec<_>, _> = mab
217 .m_curves
218 .iter()
219 .map(|c| {
220 c.build_linearize_table::<u16, LERP_DEPTH, BP>()
221 .ok_or(CmsError::InvalidTrcCurve)
222 })
223 .collect();
224
225 let [curve0, curve1, curve2] =
226 curves?.try_into().map_err(|_| CmsError::InvalidTrcCurve)?;
227
228 let matrix = mab.matrix.to_f32();
229 let bias = mab.bias.cast();
230 let m_curves = MCurves3 {
231 curve0,
232 curve1,
233 curve2,
234 matrix,
235 bias,
236 inverse: true,
237 depth: DEPTH,
238 };
239 m_curves.transform(lut)?;
240 }
241 }
242
243 let mut new_lut = try_vec![0f32; (lut.len() / 3) * 4];
244
245 if mab.a_curves.len() == 4 && mab.clut.is_some() {
246 let clut = &mab.clut.as_ref().map(|x| x.to_clut_f32()).unwrap();
247
248 let lut_grid = (mab.grid_points[0] as usize)
249 .safe_mul(mab.grid_points[1] as usize)?
250 .safe_mul(mab.grid_points[2] as usize)?
251 .safe_mul(mab.num_output_channels as usize)?;
252 if clut.len() != lut_grid {
253 return Err(CmsError::MalformedClut(MalformedSize {
254 size: clut.len(),
255 expected: lut_grid,
256 }));
257 }
258
259 let grid_size = [mab.grid_points[0], mab.grid_points[1], mab.grid_points[2]];
260
261 let all_curves_linear = mab.a_curves.iter().all(|curve| curve.is_linear());
262
263 if all_curves_linear {
264 let a_curves = ACurves3x4InverseOptimized {
265 clut,
266 grid_size: [mab.grid_points[0], mab.grid_points[1], mab.grid_points[2]],
267 interpolation_method: options.interpolation_method,
268 pcs,
269 };
270 a_curves.transform(lut, &mut new_lut)?;
271 } else {
272 let curves: Result<Vec<_>, _> = mab
273 .a_curves
274 .iter()
275 .map(|c| {
276 c.build_linearize_table::<u16, LERP_DEPTH, BP>()
277 .ok_or(CmsError::InvalidTrcCurve)
278 })
279 .collect();
280
281 let [curve0, curve1, curve2, curve3] =
282 curves?.try_into().map_err(|_| CmsError::InvalidTrcCurve)?;
283
284 let a_curves = ACurves3x4Inverse {
285 curve0,
286 curve1,
287 curve2,
288 curve3,
289 clut,
290 grid_size,
291 interpolation_method: options.interpolation_method,
292 depth: DEPTH,
293 pcs,
294 };
295 a_curves.transform(lut, &mut new_lut)?;
296 }
297 } else {
298 return Err(CmsError::UnsupportedProfileConnection);
299 }
300
301 Ok(new_lut)
302}