Skip to main content

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 */
29#[cfg(feature = "in_place")]
30use crate::InPlaceTransformExecutor;
31use crate::conversions::TransformMatrixShaper;
32use crate::conversions::rgbxyz::{
33    TransformMatrixShaperOptimized, make_rgb_xyz_rgb_transform, make_rgb_xyz_rgb_transform_opt,
34};
35use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_opt;
36use crate::{CmsError, Layout, TransformExecutor, TransformOptions};
37use num_traits::AsPrimitive;
38use std::sync::Arc;
39
40const FIXED_POINT_SCALE: i32 = 13; // Q2.13;
41
42pub(crate) trait RgbXyzFactory<T: Clone + AsPrimitive<usize> + Default> {
43    fn make_transform<const LINEAR_CAP: usize, const GAMMA_LUT: usize, const BIT_DEPTH: usize>(
44        src_layout: Layout,
45        dst_layout: Layout,
46        profile: TransformMatrixShaper<T, LINEAR_CAP>,
47        transform_options: TransformOptions,
48    ) -> Result<Arc<dyn TransformExecutor<T> + Send + Sync>, CmsError>;
49
50    #[cfg(feature = "in_place")]
51    fn make_in_place_transform<
52        const LINEAR_CAP: usize,
53        const GAMMA_LUT: usize,
54        const BIT_DEPTH: usize,
55    >(
56        layout: Layout,
57        profile: TransformMatrixShaper<T, LINEAR_CAP>,
58        transform_options: TransformOptions,
59    ) -> Result<Arc<dyn InPlaceTransformExecutor<T> + Send + Sync>, CmsError>;
60}
61
62pub(crate) trait RgbXyzFactoryOpt<T: Clone + AsPrimitive<usize> + Default> {
63    fn make_optimized_transform<
64        const LINEAR_CAP: usize,
65        const GAMMA_LUT: usize,
66        const BIT_DEPTH: usize,
67    >(
68        src_layout: Layout,
69        dst_layout: Layout,
70        profile: TransformMatrixShaperOptimized<T, LINEAR_CAP>,
71        transform_options: TransformOptions,
72    ) -> Result<Arc<dyn TransformExecutor<T> + Send + Sync>, CmsError>;
73
74    #[cfg(feature = "in_place")]
75    fn make_in_place_optimized_transform<
76        const LINEAR_CAP: usize,
77        const GAMMA_LUT: usize,
78        const BIT_DEPTH: usize,
79    >(
80        layout: Layout,
81        profile: TransformMatrixShaperOptimized<T, LINEAR_CAP>,
82        transform_options: TransformOptions,
83    ) -> Result<Arc<dyn InPlaceTransformExecutor<T> + Send + Sync>, CmsError>;
84}
85
86impl RgbXyzFactory<u16> for u16 {
87    fn make_transform<const LINEAR_CAP: usize, const GAMMA_LUT: usize, const BIT_DEPTH: usize>(
88        src_layout: Layout,
89        dst_layout: Layout,
90        profile: TransformMatrixShaper<u16, LINEAR_CAP>,
91        _: TransformOptions,
92    ) -> Result<Arc<dyn TransformExecutor<u16> + Send + Sync>, CmsError> {
93        make_rgb_xyz_rgb_transform::<u16, LINEAR_CAP>(
94            src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
95        )
96    }
97
98    #[cfg(feature = "in_place")]
99    fn make_in_place_transform<
100        const LINEAR_CAP: usize,
101        const GAMMA_LUT: usize,
102        const BIT_DEPTH: usize,
103    >(
104        layout: Layout,
105        profile: TransformMatrixShaper<u16, LINEAR_CAP>,
106        _: TransformOptions,
107    ) -> Result<Arc<dyn InPlaceTransformExecutor<u16> + Send + Sync>, CmsError> {
108        use crate::conversions::rgbxyz::make_in_place_rgb_xyz_transform;
109        make_in_place_rgb_xyz_transform::<u16, LINEAR_CAP>(layout, profile, GAMMA_LUT, BIT_DEPTH)
110    }
111}
112
113impl RgbXyzFactory<f32> for f32 {
114    fn make_transform<const LINEAR_CAP: usize, const GAMMA_LUT: usize, const BIT_DEPTH: usize>(
115        src_layout: Layout,
116        dst_layout: Layout,
117        profile: TransformMatrixShaper<f32, LINEAR_CAP>,
118        _: TransformOptions,
119    ) -> Result<Arc<dyn TransformExecutor<f32> + Send + Sync>, CmsError> {
120        make_rgb_xyz_rgb_transform::<f32, LINEAR_CAP>(
121            src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
122        )
123    }
124
125    #[cfg(feature = "in_place")]
126    fn make_in_place_transform<
127        const LINEAR_CAP: usize,
128        const GAMMA_LUT: usize,
129        const BIT_DEPTH: usize,
130    >(
131        layout: Layout,
132        profile: TransformMatrixShaper<f32, LINEAR_CAP>,
133        _: TransformOptions,
134    ) -> Result<Arc<dyn InPlaceTransformExecutor<f32> + Send + Sync>, CmsError> {
135        use crate::conversions::rgbxyz::make_in_place_rgb_xyz_transform;
136        make_in_place_rgb_xyz_transform::<f32, LINEAR_CAP>(layout, profile, GAMMA_LUT, BIT_DEPTH)
137    }
138}
139
140impl RgbXyzFactory<f64> for f64 {
141    fn make_transform<const LINEAR_CAP: usize, const GAMMA_LUT: usize, const BIT_DEPTH: usize>(
142        src_layout: Layout,
143        dst_layout: Layout,
144        profile: TransformMatrixShaper<f64, LINEAR_CAP>,
145        _: TransformOptions,
146    ) -> Result<Arc<dyn TransformExecutor<f64> + Send + Sync>, CmsError> {
147        make_rgb_xyz_rgb_transform::<f64, LINEAR_CAP>(
148            src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
149        )
150    }
151
152    #[cfg(feature = "in_place")]
153    fn make_in_place_transform<
154        const LINEAR_CAP: usize,
155        const GAMMA_LUT: usize,
156        const BIT_DEPTH: usize,
157    >(
158        layout: Layout,
159        profile: TransformMatrixShaper<f64, LINEAR_CAP>,
160        _: TransformOptions,
161    ) -> Result<Arc<dyn InPlaceTransformExecutor<f64> + Send + Sync>, CmsError> {
162        use crate::conversions::rgbxyz::make_in_place_rgb_xyz_transform;
163        make_in_place_rgb_xyz_transform::<f64, LINEAR_CAP>(layout, profile, GAMMA_LUT, BIT_DEPTH)
164    }
165}
166
167impl RgbXyzFactory<u8> for u8 {
168    fn make_transform<const LINEAR_CAP: usize, const GAMMA_LUT: usize, const BIT_DEPTH: usize>(
169        src_layout: Layout,
170        dst_layout: Layout,
171        profile: TransformMatrixShaper<u8, LINEAR_CAP>,
172        _: TransformOptions,
173    ) -> Result<Arc<dyn TransformExecutor<u8> + Send + Sync>, CmsError> {
174        make_rgb_xyz_rgb_transform::<u8, LINEAR_CAP>(src_layout, dst_layout, profile, GAMMA_LUT, 8)
175    }
176
177    #[cfg(feature = "in_place")]
178    fn make_in_place_transform<
179        const LINEAR_CAP: usize,
180        const GAMMA_LUT: usize,
181        const BIT_DEPTH: usize,
182    >(
183        layout: Layout,
184        profile: TransformMatrixShaper<u8, LINEAR_CAP>,
185        _: TransformOptions,
186    ) -> Result<Arc<dyn InPlaceTransformExecutor<u8> + Send + Sync>, CmsError> {
187        use crate::conversions::rgbxyz::make_in_place_rgb_xyz_transform;
188        make_in_place_rgb_xyz_transform::<u8, LINEAR_CAP>(layout, profile, GAMMA_LUT, BIT_DEPTH)
189    }
190}
191
192// Optimized factories
193
194impl RgbXyzFactoryOpt<u16> for u16 {
195    fn make_optimized_transform<
196        const LINEAR_CAP: usize,
197        const GAMMA_LUT: usize,
198        const BIT_DEPTH: usize,
199    >(
200        src_layout: Layout,
201        dst_layout: Layout,
202        profile: TransformMatrixShaperOptimized<u16, LINEAR_CAP>,
203        transform_options: TransformOptions,
204    ) -> Result<Arc<dyn TransformExecutor<u16> + Send + Sync>, CmsError> {
205        if BIT_DEPTH >= 12 && transform_options.prefer_fixed_point {
206            #[cfg(all(target_arch = "aarch64", feature = "neon_shaper_fixed_point_paths"))]
207            {
208                if std::arch::is_aarch64_feature_detected!("rdm") {
209                    use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q1_30_opt;
210                    return make_rgb_xyz_q1_30_opt::<u16, LINEAR_CAP, 30>(
211                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
212                    );
213                }
214            }
215        }
216        if BIT_DEPTH < 16 && transform_options.prefer_fixed_point {
217            #[cfg(all(target_arch = "x86_64", feature = "avx_shaper_fixed_point_paths"))]
218            {
219                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_avx2_opt;
220                if std::arch::is_x86_feature_detected!("avx2") {
221                    return make_rgb_xyz_q2_13_transform_avx2_opt::<
222                        u16,
223                        LINEAR_CAP,
224                        FIXED_POINT_SCALE,
225                    >(
226                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH
227                    );
228                }
229            }
230            #[cfg(all(
231                any(target_arch = "x86", target_arch = "x86_64"),
232                feature = "sse_shaper_fixed_point_paths"
233            ))]
234            {
235                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_sse_41_opt;
236                if std::arch::is_x86_feature_detected!("sse4.1") {
237                    return make_rgb_xyz_q2_13_transform_sse_41_opt::<
238                        u16,
239                        LINEAR_CAP,
240                        FIXED_POINT_SCALE,
241                    >(
242                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH
243                    );
244                }
245            }
246            #[cfg(all(target_arch = "aarch64", feature = "neon"))]
247            {
248                return make_rgb_xyz_q2_13_opt::<u16, LINEAR_CAP, FIXED_POINT_SCALE>(
249                    src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
250                );
251            }
252        }
253        make_rgb_xyz_rgb_transform_opt::<u16, LINEAR_CAP>(
254            src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
255        )
256    }
257
258    #[cfg(feature = "in_place")]
259    fn make_in_place_optimized_transform<
260        const LINEAR_CAP: usize,
261        const GAMMA_LUT: usize,
262        const BIT_DEPTH: usize,
263    >(
264        layout: Layout,
265        profile: TransformMatrixShaperOptimized<u16, LINEAR_CAP>,
266        transform_options: TransformOptions,
267    ) -> Result<Arc<dyn InPlaceTransformExecutor<u16> + Send + Sync>, CmsError> {
268        if transform_options.prefer_fixed_point && BIT_DEPTH < 16 {
269            #[cfg(all(
270                target_arch = "aarch64",
271                feature = "in_place",
272                feature = "neon_shaper_fixed_point_paths"
273            ))]
274            {
275                use crate::conversions::rgbxyz::make_rgb_xyz_in_place_transform_q2_13_opt;
276                return make_rgb_xyz_in_place_transform_q2_13_opt::<
277                    u16,
278                    LINEAR_CAP,
279                    FIXED_POINT_SCALE,
280                >(layout, profile, GAMMA_LUT, BIT_DEPTH);
281            }
282
283            #[cfg(all(
284                target_arch = "x86_64",
285                feature = "in_place",
286                feature = "avx_shaper_fixed_point_paths"
287            ))]
288            {
289                if std::arch::is_x86_feature_detected!("avx2") {
290                    use crate::conversions::rgbxyz::make_avx_rgb_xyz_in_place_transform_q2_13_opt;
291                    return make_avx_rgb_xyz_in_place_transform_q2_13_opt::<
292                        u16,
293                        LINEAR_CAP,
294                        FIXED_POINT_SCALE,
295                    >(layout, profile, GAMMA_LUT, BIT_DEPTH);
296                }
297            }
298
299            #[cfg(all(
300                any(target_arch = "x86_64", target_arch = "x86"),
301                feature = "in_place",
302                feature = "sse_shaper_fixed_point_paths"
303            ))]
304            {
305                if std::arch::is_x86_feature_detected!("sse4.1") {
306                    use crate::conversions::rgbxyz::make_sse_rgb_xyz_in_place_transform_q2_13_opt;
307                    return make_sse_rgb_xyz_in_place_transform_q2_13_opt::<
308                        u16,
309                        LINEAR_CAP,
310                        FIXED_POINT_SCALE,
311                    >(layout, profile, GAMMA_LUT, BIT_DEPTH);
312                }
313            }
314        }
315        use crate::conversions::rgbxyz::make_rgb_xyz_in_place_transform_opt;
316        make_rgb_xyz_in_place_transform_opt::<u16, LINEAR_CAP>(
317            layout, profile, GAMMA_LUT, BIT_DEPTH,
318        )
319    }
320}
321
322impl RgbXyzFactoryOpt<f32> for f32 {
323    fn make_optimized_transform<
324        const LINEAR_CAP: usize,
325        const GAMMA_LUT: usize,
326        const BIT_DEPTH: usize,
327    >(
328        src_layout: Layout,
329        dst_layout: Layout,
330        profile: TransformMatrixShaperOptimized<f32, LINEAR_CAP>,
331        transform_options: TransformOptions,
332    ) -> Result<Arc<dyn TransformExecutor<f32> + Send + Sync>, CmsError> {
333        if transform_options.prefer_fixed_point {
334            #[cfg(all(target_arch = "x86_64", feature = "avx_shaper_fixed_point_paths"))]
335            {
336                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_avx2_opt;
337                if std::arch::is_x86_feature_detected!("avx2") {
338                    return make_rgb_xyz_q2_13_transform_avx2_opt::<
339                        f32,
340                        LINEAR_CAP,
341                        FIXED_POINT_SCALE,
342                    >(
343                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH
344                    );
345                }
346            }
347            #[cfg(all(
348                any(target_arch = "x86", target_arch = "x86_64"),
349                feature = "sse_shaper_fixed_point_paths"
350            ))]
351            {
352                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_sse_41_opt;
353                if std::arch::is_x86_feature_detected!("sse4.1") {
354                    return make_rgb_xyz_q2_13_transform_sse_41_opt::<
355                        f32,
356                        LINEAR_CAP,
357                        FIXED_POINT_SCALE,
358                    >(
359                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH
360                    );
361                }
362            }
363            #[cfg(all(target_arch = "aarch64", feature = "neon_shaper_fixed_point_paths"))]
364            {
365                return if std::arch::is_aarch64_feature_detected!("rdm") {
366                    use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q1_30_opt;
367                    make_rgb_xyz_q1_30_opt::<f32, LINEAR_CAP, 30>(
368                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
369                    )
370                } else {
371                    make_rgb_xyz_q2_13_opt::<f32, LINEAR_CAP, FIXED_POINT_SCALE>(
372                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
373                    )
374                };
375            }
376        }
377        make_rgb_xyz_rgb_transform_opt::<f32, LINEAR_CAP>(
378            src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
379        )
380    }
381
382    #[cfg(feature = "in_place")]
383    fn make_in_place_optimized_transform<
384        const LINEAR_CAP: usize,
385        const GAMMA_LUT: usize,
386        const BIT_DEPTH: usize,
387    >(
388        layout: Layout,
389        profile: TransformMatrixShaperOptimized<f32, LINEAR_CAP>,
390        _: TransformOptions,
391    ) -> Result<Arc<dyn InPlaceTransformExecutor<f32> + Send + Sync>, CmsError> {
392        use crate::conversions::rgbxyz::make_rgb_xyz_in_place_transform_opt;
393        make_rgb_xyz_in_place_transform_opt::<f32, LINEAR_CAP>(
394            layout, profile, GAMMA_LUT, BIT_DEPTH,
395        )
396    }
397}
398
399impl RgbXyzFactoryOpt<f64> for f64 {
400    fn make_optimized_transform<
401        const LINEAR_CAP: usize,
402        const GAMMA_LUT: usize,
403        const BIT_DEPTH: usize,
404    >(
405        src_layout: Layout,
406        dst_layout: Layout,
407        profile: TransformMatrixShaperOptimized<f64, LINEAR_CAP>,
408        transform_options: TransformOptions,
409    ) -> Result<Arc<dyn TransformExecutor<f64> + Send + Sync>, CmsError> {
410        if transform_options.prefer_fixed_point {
411            #[cfg(all(target_arch = "aarch64", feature = "neon_shaper_fixed_point_paths"))]
412            {
413                if std::arch::is_aarch64_feature_detected!("rdm") {
414                    use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q1_30_opt;
415                    return make_rgb_xyz_q1_30_opt::<f64, LINEAR_CAP, 30>(
416                        src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
417                    );
418                }
419            }
420        }
421        make_rgb_xyz_rgb_transform_opt::<f64, LINEAR_CAP>(
422            src_layout, dst_layout, profile, GAMMA_LUT, BIT_DEPTH,
423        )
424    }
425
426    #[cfg(feature = "in_place")]
427    fn make_in_place_optimized_transform<
428        const LINEAR_CAP: usize,
429        const GAMMA_LUT: usize,
430        const BIT_DEPTH: usize,
431    >(
432        layout: Layout,
433        profile: TransformMatrixShaperOptimized<f64, LINEAR_CAP>,
434        _: TransformOptions,
435    ) -> Result<Arc<dyn InPlaceTransformExecutor<f64> + Send + Sync>, CmsError> {
436        use crate::conversions::rgbxyz::make_rgb_xyz_in_place_transform_opt;
437        make_rgb_xyz_in_place_transform_opt::<f64, LINEAR_CAP>(
438            layout, profile, GAMMA_LUT, BIT_DEPTH,
439        )
440    }
441}
442
443impl RgbXyzFactoryOpt<u8> for u8 {
444    fn make_optimized_transform<
445        const LINEAR_CAP: usize,
446        const GAMMA_LUT: usize,
447        const BIT_DEPTH: usize,
448    >(
449        src_layout: Layout,
450        dst_layout: Layout,
451        profile: TransformMatrixShaperOptimized<u8, LINEAR_CAP>,
452        transform_options: TransformOptions,
453    ) -> Result<Arc<dyn TransformExecutor<u8> + Send + Sync>, CmsError> {
454        if transform_options.prefer_fixed_point {
455            #[cfg(all(target_arch = "x86_64", feature = "avx512_shaper_fixed_point_paths"))]
456            {
457                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_avx512_opt;
458                if std::arch::is_x86_feature_detected!("avx512bw")
459                    && std::arch::is_x86_feature_detected!("avx512vl")
460                {
461                    return make_rgb_xyz_q2_13_transform_avx512_opt::<
462                        u8,
463                        LINEAR_CAP,
464                        FIXED_POINT_SCALE,
465                    >(src_layout, dst_layout, profile, GAMMA_LUT, 8);
466                }
467            }
468            #[cfg(all(target_arch = "x86_64", feature = "avx_shaper_fixed_point_paths"))]
469            {
470                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_avx2_opt;
471                if std::arch::is_x86_feature_detected!("avx2") {
472                    return make_rgb_xyz_q2_13_transform_avx2_opt::<
473                        u8,
474                        LINEAR_CAP,
475                        FIXED_POINT_SCALE,
476                    >(src_layout, dst_layout, profile, GAMMA_LUT, 8);
477                }
478            }
479            #[cfg(all(
480                any(target_arch = "x86", target_arch = "x86_64"),
481                feature = "sse_shaper_fixed_point_paths"
482            ))]
483            {
484                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_sse_41_opt;
485                if std::arch::is_x86_feature_detected!("sse4.1") {
486                    return make_rgb_xyz_q2_13_transform_sse_41_opt::<
487                        u8,
488                        LINEAR_CAP,
489                        FIXED_POINT_SCALE,
490                    >(src_layout, dst_layout, profile, GAMMA_LUT, 8);
491                }
492            }
493            make_rgb_xyz_q2_13_opt::<u8, LINEAR_CAP, FIXED_POINT_SCALE>(
494                src_layout, dst_layout, profile, GAMMA_LUT, 8,
495            )
496        } else {
497            make_rgb_xyz_rgb_transform_opt::<u8, LINEAR_CAP>(
498                src_layout, dst_layout, profile, GAMMA_LUT, 8,
499            )
500        }
501    }
502
503    #[cfg(feature = "in_place")]
504    fn make_in_place_optimized_transform<
505        const LINEAR_CAP: usize,
506        const GAMMA_LUT: usize,
507        const BIT_DEPTH: usize,
508    >(
509        layout: Layout,
510        profile: TransformMatrixShaperOptimized<u8, LINEAR_CAP>,
511        transform_options: TransformOptions,
512    ) -> Result<Arc<dyn InPlaceTransformExecutor<u8> + Send + Sync>, CmsError> {
513        if transform_options.prefer_fixed_point {
514            #[cfg(all(
515                target_arch = "aarch64",
516                feature = "in_place",
517                feature = "neon_shaper_fixed_point_paths"
518            ))]
519            {
520                use crate::conversions::rgbxyz::make_rgb_xyz_in_place_transform_q2_13_opt;
521                return make_rgb_xyz_in_place_transform_q2_13_opt::<
522                    u8,
523                    LINEAR_CAP,
524                    FIXED_POINT_SCALE,
525                >(layout, profile, GAMMA_LUT, 8);
526            }
527
528            #[cfg(all(
529                target_arch = "x86_64",
530                feature = "in_place",
531                feature = "avx_shaper_fixed_point_paths"
532            ))]
533            {
534                if std::arch::is_x86_feature_detected!("avx2") {
535                    use crate::conversions::rgbxyz::make_avx_rgb_xyz_in_place_transform_q2_13_opt;
536                    return make_avx_rgb_xyz_in_place_transform_q2_13_opt::<
537                        u8,
538                        LINEAR_CAP,
539                        FIXED_POINT_SCALE,
540                    >(layout, profile, GAMMA_LUT, 8);
541                }
542            }
543
544            #[cfg(all(
545                any(target_arch = "x86_64", target_arch = "x86"),
546                feature = "in_place",
547                feature = "sse_shaper_fixed_point_paths"
548            ))]
549            {
550                if std::arch::is_x86_feature_detected!("sse4.1") {
551                    use crate::conversions::rgbxyz::make_sse_rgb_xyz_in_place_transform_q2_13_opt;
552                    return make_sse_rgb_xyz_in_place_transform_q2_13_opt::<
553                        u8,
554                        LINEAR_CAP,
555                        FIXED_POINT_SCALE,
556                    >(layout, profile, GAMMA_LUT, 8);
557                }
558            }
559        }
560        use crate::conversions::rgbxyz::make_rgb_xyz_in_place_transform_opt;
561        make_rgb_xyz_in_place_transform_opt::<u8, LINEAR_CAP>(layout, profile, GAMMA_LUT, 8)
562    }
563}