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