moxcms/conversions/
rgb_xyz_factory.rs

1/*
2 * // Copyright (c) Radzivon Bartoshyk 4/2025. All rights reserved.
3 * //
4 * // Redistribution and use in source and binary forms, with or without modification,
5 * // are permitted provided that the following conditions are met:
6 * //
7 * // 1.  Redistributions of source code must retain the above copyright notice, this
8 * // list of conditions and the following disclaimer.
9 * //
10 * // 2.  Redistributions in binary form must reproduce the above copyright notice,
11 * // this list of conditions and the following disclaimer in the documentation
12 * // and/or other materials provided with the distribution.
13 * //
14 * // 3.  Neither the name of the copyright holder nor the names of its
15 * // contributors may be used to endorse or promote products derived from
16 * // this software without specific prior written permission.
17 * //
18 * // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29use crate::conversions::TransformMatrixShaper;
30use crate::conversions::rgbxyz::{
31    TransformMatrixShaperOptimized, make_rgb_xyz_rgb_transform, make_rgb_xyz_rgb_transform_opt,
32};
33use crate::conversions::rgbxyz_fixed::{make_rgb_xyz_q2_13, make_rgb_xyz_q2_13_opt};
34use crate::{CmsError, Layout, TransformExecutor, TransformOptions};
35use num_traits::AsPrimitive;
36
37const FIXED_POINT_SCALE: i32 = 13; // Q2.13;
38
39pub(crate) trait RgbXyzFactory<T: Clone + AsPrimitive<usize> + Default> {
40    fn make_transform<const LINEAR_CAP: usize, const GAMMA_LUT: usize, const BIT_DEPTH: usize>(
41        src_layout: Layout,
42        dst_layout: Layout,
43        profile: TransformMatrixShaper<T, LINEAR_CAP>,
44        transform_options: TransformOptions,
45    ) -> Result<Box<dyn TransformExecutor<T> + Send + Sync>, CmsError>;
46}
47
48pub(crate) trait RgbXyzFactoryOpt<T: Clone + AsPrimitive<usize> + Default> {
49    fn make_optimized_transform<
50        const LINEAR_CAP: usize,
51        const GAMMA_LUT: usize,
52        const BIT_DEPTH: usize,
53    >(
54        src_layout: Layout,
55        dst_layout: Layout,
56        profile: TransformMatrixShaperOptimized<T, LINEAR_CAP>,
57        transform_options: TransformOptions,
58    ) -> Result<Box<dyn TransformExecutor<T> + Send + Sync>, CmsError>;
59}
60
61impl RgbXyzFactory<u16> for u16 {
62    fn make_transform<const LINEAR_CAP: usize, const GAMMA_LUT: usize, const BIT_DEPTH: usize>(
63        src_layout: Layout,
64        dst_layout: Layout,
65        profile: TransformMatrixShaper<u16, LINEAR_CAP>,
66        transform_options: TransformOptions,
67    ) -> Result<Box<dyn TransformExecutor<u16> + Send + Sync>, CmsError> {
68        if BIT_DEPTH < 16 && transform_options.prefer_fixed_point {
69            #[cfg(all(target_arch = "x86_64", feature = "avx"))]
70            {
71                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_avx2;
72                if std::arch::is_x86_feature_detected!("avx2") {
73                    return make_rgb_xyz_q2_13_transform_avx2::<u16, LINEAR_CAP, FIXED_POINT_SCALE>(
74                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
75                    );
76                }
77            }
78            #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
79            {
80                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_sse_41;
81                if std::arch::is_x86_feature_detected!("sse4.1") {
82                    return make_rgb_xyz_q2_13_transform_sse_41::<u16, LINEAR_CAP, FIXED_POINT_SCALE>(
83                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
84                    );
85                }
86            }
87            #[cfg(all(target_arch = "aarch64", target_feature = "neon", feature = "neon"))]
88            {
89                return make_rgb_xyz_q2_13::<u16, LINEAR_CAP, FIXED_POINT_SCALE>(
90                    src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
91                );
92            }
93        }
94        make_rgb_xyz_rgb_transform::<u16, LINEAR_CAP>(
95            src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
96        )
97    }
98}
99
100impl RgbXyzFactory<f32> for f32 {
101    fn make_transform<const LINEAR_CAP: usize, const GAMMA_LUT: usize, const BIT_DEPTH: usize>(
102        src_layout: Layout,
103        dst_layout: Layout,
104        profile: TransformMatrixShaper<f32, LINEAR_CAP>,
105        transform_options: TransformOptions,
106    ) -> Result<Box<dyn TransformExecutor<f32> + Send + Sync>, CmsError> {
107        if transform_options.prefer_fixed_point {
108            #[cfg(all(target_arch = "x86_64", feature = "avx"))]
109            {
110                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_avx2;
111                if std::arch::is_x86_feature_detected!("avx2") {
112                    return make_rgb_xyz_q2_13_transform_avx2::<f32, LINEAR_CAP, FIXED_POINT_SCALE>(
113                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
114                    );
115                }
116            }
117            #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
118            {
119                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_sse_41;
120                if std::arch::is_x86_feature_detected!("sse4.1") {
121                    return make_rgb_xyz_q2_13_transform_sse_41::<f32, LINEAR_CAP, FIXED_POINT_SCALE>(
122                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
123                    );
124                }
125            }
126            #[cfg(all(target_arch = "aarch64", target_feature = "neon", feature = "neon"))]
127            {
128                return make_rgb_xyz_q2_13::<f32, LINEAR_CAP, FIXED_POINT_SCALE>(
129                    src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
130                );
131            }
132        }
133        make_rgb_xyz_rgb_transform::<f32, LINEAR_CAP>(
134            src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
135        )
136    }
137}
138
139impl RgbXyzFactory<f64> for f64 {
140    fn make_transform<const LINEAR_CAP: usize, const GAMMA_LUT: usize, const BIT_DEPTH: usize>(
141        src_layout: Layout,
142        dst_layout: Layout,
143        profile: TransformMatrixShaper<f64, LINEAR_CAP>,
144        _: TransformOptions,
145    ) -> Result<Box<dyn TransformExecutor<f64> + Send + Sync>, CmsError> {
146        make_rgb_xyz_rgb_transform::<f64, LINEAR_CAP>(
147            src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
148        )
149    }
150}
151
152impl RgbXyzFactory<u8> for u8 {
153    fn make_transform<const LINEAR_CAP: usize, const GAMMA_LUT: usize, const BIT_DEPTH: usize>(
154        src_layout: Layout,
155        dst_layout: Layout,
156        profile: TransformMatrixShaper<u8, LINEAR_CAP>,
157        transform_options: TransformOptions,
158    ) -> Result<Box<dyn TransformExecutor<u8> + Send + Sync>, CmsError> {
159        if transform_options.prefer_fixed_point {
160            #[cfg(all(target_arch = "x86_64", feature = "avx"))]
161            {
162                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_avx2;
163                if std::arch::is_x86_feature_detected!("avx2") {
164                    return make_rgb_xyz_q2_13_transform_avx2::<u8, LINEAR_CAP, FIXED_POINT_SCALE>(
165                        src_layout, dst_layout, profile, GAMMA_LUT, 8,
166                    );
167                }
168            }
169            #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
170            {
171                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_sse_41;
172                if std::arch::is_x86_feature_detected!("sse4.1") {
173                    return make_rgb_xyz_q2_13_transform_sse_41::<u8, LINEAR_CAP, FIXED_POINT_SCALE>(
174                        src_layout, dst_layout, profile, GAMMA_LUT, 8,
175                    );
176                }
177            }
178            make_rgb_xyz_q2_13::<u8, LINEAR_CAP, FIXED_POINT_SCALE>(
179                src_layout, dst_layout, profile, GAMMA_LUT, 8,
180            )
181        } else {
182            make_rgb_xyz_rgb_transform::<u8, LINEAR_CAP>(
183                src_layout, dst_layout, profile, GAMMA_LUT, 8,
184            )
185        }
186    }
187}
188
189// Optimized factories
190
191impl RgbXyzFactoryOpt<u16> for u16 {
192    fn make_optimized_transform<
193        const LINEAR_CAP: usize,
194        const GAMMA_LUT: usize,
195        const BIT_DEPTH: usize,
196    >(
197        src_layout: Layout,
198        dst_layout: Layout,
199        profile: TransformMatrixShaperOptimized<u16, LINEAR_CAP>,
200        transform_options: TransformOptions,
201    ) -> Result<Box<dyn TransformExecutor<u16> + Send + Sync>, CmsError> {
202        if BIT_DEPTH >= 12 && transform_options.prefer_fixed_point {
203            #[cfg(all(target_arch = "aarch64", target_feature = "neon", feature = "neon"))]
204            {
205                if std::arch::is_aarch64_feature_detected!("rdm") {
206                    use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q1_30_opt;
207                    return make_rgb_xyz_q1_30_opt::<u16, LINEAR_CAP, 30>(
208                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
209                    );
210                }
211            }
212        }
213        if BIT_DEPTH < 16 && transform_options.prefer_fixed_point {
214            #[cfg(all(target_arch = "x86_64", feature = "avx"))]
215            {
216                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_avx2_opt;
217                if std::arch::is_x86_feature_detected!("avx2") {
218                    return make_rgb_xyz_q2_13_transform_avx2_opt::<
219                        u16,
220                        LINEAR_CAP,
221                        FIXED_POINT_SCALE,
222                    >(
223                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH
224                    );
225                }
226            }
227            #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
228            {
229                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_sse_41_opt;
230                if std::arch::is_x86_feature_detected!("sse4.1") {
231                    return make_rgb_xyz_q2_13_transform_sse_41_opt::<
232                        u16,
233                        LINEAR_CAP,
234                        FIXED_POINT_SCALE,
235                    >(
236                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH
237                    );
238                }
239            }
240            #[cfg(all(target_arch = "aarch64", target_feature = "neon", feature = "neon"))]
241            {
242                return make_rgb_xyz_q2_13_opt::<u16, LINEAR_CAP, FIXED_POINT_SCALE>(
243                    src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
244                );
245            }
246        }
247        make_rgb_xyz_rgb_transform_opt::<u16, LINEAR_CAP>(
248            src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
249        )
250    }
251}
252
253impl RgbXyzFactoryOpt<f32> for f32 {
254    fn make_optimized_transform<
255        const LINEAR_CAP: usize,
256        const GAMMA_LUT: usize,
257        const BIT_DEPTH: usize,
258    >(
259        src_layout: Layout,
260        dst_layout: Layout,
261        profile: TransformMatrixShaperOptimized<f32, LINEAR_CAP>,
262        transform_options: TransformOptions,
263    ) -> Result<Box<dyn TransformExecutor<f32> + Send + Sync>, CmsError> {
264        if transform_options.prefer_fixed_point {
265            #[cfg(all(target_arch = "x86_64", feature = "avx"))]
266            {
267                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_avx2_opt;
268                if std::arch::is_x86_feature_detected!("avx2") {
269                    return make_rgb_xyz_q2_13_transform_avx2_opt::<
270                        f32,
271                        LINEAR_CAP,
272                        FIXED_POINT_SCALE,
273                    >(
274                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH
275                    );
276                }
277            }
278            #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
279            {
280                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_sse_41_opt;
281                if std::arch::is_x86_feature_detected!("sse4.1") {
282                    return make_rgb_xyz_q2_13_transform_sse_41_opt::<
283                        f32,
284                        LINEAR_CAP,
285                        FIXED_POINT_SCALE,
286                    >(
287                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH
288                    );
289                }
290            }
291            #[cfg(all(target_arch = "aarch64", target_feature = "neon", feature = "neon"))]
292            {
293                return if std::arch::is_aarch64_feature_detected!("rdm") {
294                    use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q1_30_opt;
295                    make_rgb_xyz_q1_30_opt::<f32, LINEAR_CAP, 30>(
296                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
297                    )
298                } else {
299                    make_rgb_xyz_q2_13_opt::<f32, LINEAR_CAP, FIXED_POINT_SCALE>(
300                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
301                    )
302                };
303            }
304        }
305        make_rgb_xyz_rgb_transform_opt::<f32, LINEAR_CAP>(
306            src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
307        )
308    }
309}
310
311impl RgbXyzFactoryOpt<f64> for f64 {
312    fn make_optimized_transform<
313        const LINEAR_CAP: usize,
314        const GAMMA_LUT: usize,
315        const BIT_DEPTH: usize,
316    >(
317        src_layout: Layout,
318        dst_layout: Layout,
319        profile: TransformMatrixShaperOptimized<f64, LINEAR_CAP>,
320        transform_options: TransformOptions,
321    ) -> Result<Box<dyn TransformExecutor<f64> + Send + Sync>, CmsError> {
322        if transform_options.prefer_fixed_point {
323            #[cfg(all(target_arch = "aarch64", target_feature = "neon", feature = "neon"))]
324            {
325                if std::arch::is_aarch64_feature_detected!("rdm") {
326                    use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q1_30_opt;
327                    return make_rgb_xyz_q1_30_opt::<f64, LINEAR_CAP, 30>(
328                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
329                    );
330                }
331            }
332        }
333        make_rgb_xyz_rgb_transform_opt::<f64, LINEAR_CAP>(
334            src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
335        )
336    }
337}
338
339impl RgbXyzFactoryOpt<u8> for u8 {
340    fn make_optimized_transform<
341        const LINEAR_CAP: usize,
342        const GAMMA_LUT: usize,
343        const BIT_DEPTH: usize,
344    >(
345        src_layout: Layout,
346        dst_layout: Layout,
347        profile: TransformMatrixShaperOptimized<u8, LINEAR_CAP>,
348        transform_options: TransformOptions,
349    ) -> Result<Box<dyn TransformExecutor<u8> + Send + Sync>, CmsError> {
350        if transform_options.prefer_fixed_point {
351            #[cfg(all(target_arch = "x86_64", feature = "avx512"))]
352            {
353                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_avx512_opt;
354                if std::arch::is_x86_feature_detected!("avx512bw")
355                    && std::arch::is_x86_feature_detected!("avx512vl")
356                {
357                    return make_rgb_xyz_q2_13_transform_avx512_opt::<
358                        u8,
359                        LINEAR_CAP,
360                        FIXED_POINT_SCALE,
361                    >(src_layout, dst_layout, profile, GAMMA_LUT, 8);
362                }
363            }
364            #[cfg(all(target_arch = "x86_64", feature = "avx"))]
365            {
366                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_avx2_opt;
367                if std::arch::is_x86_feature_detected!("avx2") {
368                    return make_rgb_xyz_q2_13_transform_avx2_opt::<
369                        u8,
370                        LINEAR_CAP,
371                        FIXED_POINT_SCALE,
372                    >(src_layout, dst_layout, profile, GAMMA_LUT, 8);
373                }
374            }
375            #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
376            {
377                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_sse_41_opt;
378                if std::arch::is_x86_feature_detected!("sse4.1") {
379                    return make_rgb_xyz_q2_13_transform_sse_41_opt::<
380                        u8,
381                        LINEAR_CAP,
382                        FIXED_POINT_SCALE,
383                    >(src_layout, dst_layout, profile, GAMMA_LUT, 8);
384                }
385            }
386            make_rgb_xyz_q2_13_opt::<u8, LINEAR_CAP, FIXED_POINT_SCALE>(
387                src_layout, dst_layout, profile, GAMMA_LUT, 8,
388            )
389        } else {
390            make_rgb_xyz_rgb_transform_opt::<u8, LINEAR_CAP>(
391                src_layout, dst_layout, profile, GAMMA_LUT, 8,
392            )
393        }
394    }
395}