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