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::<
74                        u16,
75                        LINEAR_CAP,
76                        GAMMA_LUT,
77                        BIT_DEPTH,
78                        FIXED_POINT_SCALE,
79                    >(src_layout, dst_layout, profile);
80                }
81            }
82            #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
83            {
84                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_sse_41;
85                if std::arch::is_x86_feature_detected!("sse4.1") {
86                    return make_rgb_xyz_q2_13_transform_sse_41::<
87                        u16,
88                        LINEAR_CAP,
89                        GAMMA_LUT,
90                        BIT_DEPTH,
91                        FIXED_POINT_SCALE,
92                    >(src_layout, dst_layout, profile);
93                }
94            }
95            #[cfg(all(target_arch = "aarch64", target_feature = "neon", feature = "neon"))]
96            {
97                return make_rgb_xyz_q2_13::<
98                    u16,
99                    LINEAR_CAP,
100                    GAMMA_LUT,
101                    BIT_DEPTH,
102                    FIXED_POINT_SCALE,
103                >(src_layout, dst_layout, profile);
104            }
105        }
106        make_rgb_xyz_rgb_transform::<u16, LINEAR_CAP, GAMMA_LUT, BIT_DEPTH>(
107            src_layout, dst_layout, profile,
108        )
109    }
110}
111
112impl RgbXyzFactory<f32> for f32 {
113    fn make_transform<const LINEAR_CAP: usize, const GAMMA_LUT: usize, const BIT_DEPTH: usize>(
114        src_layout: Layout,
115        dst_layout: Layout,
116        profile: TransformMatrixShaper<f32, LINEAR_CAP>,
117        transform_options: TransformOptions,
118    ) -> Result<Box<dyn TransformExecutor<f32> + Send + Sync>, CmsError> {
119        if transform_options.prefer_fixed_point {
120            #[cfg(all(target_arch = "x86_64", feature = "avx"))]
121            {
122                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_avx2;
123                if std::arch::is_x86_feature_detected!("avx2") {
124                    return make_rgb_xyz_q2_13_transform_avx2::<
125                        f32,
126                        LINEAR_CAP,
127                        GAMMA_LUT,
128                        BIT_DEPTH,
129                        FIXED_POINT_SCALE,
130                    >(src_layout, dst_layout, profile);
131                }
132            }
133            #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
134            {
135                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_sse_41;
136                if std::arch::is_x86_feature_detected!("sse4.1") {
137                    return make_rgb_xyz_q2_13_transform_sse_41::<
138                        f32,
139                        LINEAR_CAP,
140                        GAMMA_LUT,
141                        BIT_DEPTH,
142                        FIXED_POINT_SCALE,
143                    >(src_layout, dst_layout, profile);
144                }
145            }
146            #[cfg(all(target_arch = "aarch64", target_feature = "neon", feature = "neon"))]
147            {
148                return make_rgb_xyz_q2_13::<
149                    f32,
150                    LINEAR_CAP,
151                    GAMMA_LUT,
152                    BIT_DEPTH,
153                    FIXED_POINT_SCALE,
154                >(src_layout, dst_layout, profile);
155            }
156        }
157        make_rgb_xyz_rgb_transform::<f32, LINEAR_CAP, GAMMA_LUT, BIT_DEPTH>(
158            src_layout, dst_layout, profile,
159        )
160    }
161}
162
163impl RgbXyzFactory<f64> for f64 {
164    fn make_transform<const LINEAR_CAP: usize, const GAMMA_LUT: usize, const BIT_DEPTH: usize>(
165        src_layout: Layout,
166        dst_layout: Layout,
167        profile: TransformMatrixShaper<f64, LINEAR_CAP>,
168        _: TransformOptions,
169    ) -> Result<Box<dyn TransformExecutor<f64> + Send + Sync>, CmsError> {
170        make_rgb_xyz_rgb_transform::<f64, LINEAR_CAP, GAMMA_LUT, BIT_DEPTH>(
171            src_layout, dst_layout, profile,
172        )
173    }
174}
175
176impl RgbXyzFactory<u8> for u8 {
177    fn make_transform<const LINEAR_CAP: usize, const GAMMA_LUT: usize, const BIT_DEPTH: usize>(
178        src_layout: Layout,
179        dst_layout: Layout,
180        profile: TransformMatrixShaper<u8, LINEAR_CAP>,
181        transform_options: TransformOptions,
182    ) -> Result<Box<dyn TransformExecutor<u8> + Send + Sync>, CmsError> {
183        if transform_options.prefer_fixed_point {
184            #[cfg(all(target_arch = "x86_64", feature = "avx"))]
185            {
186                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_avx2;
187                if std::arch::is_x86_feature_detected!("avx2") {
188                    return make_rgb_xyz_q2_13_transform_avx2::<
189                        u8,
190                        LINEAR_CAP,
191                        GAMMA_LUT,
192                        8,
193                        FIXED_POINT_SCALE,
194                    >(src_layout, dst_layout, profile);
195                }
196            }
197            #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
198            {
199                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_sse_41;
200                if std::arch::is_x86_feature_detected!("sse4.1") {
201                    return make_rgb_xyz_q2_13_transform_sse_41::<
202                        u8,
203                        LINEAR_CAP,
204                        GAMMA_LUT,
205                        8,
206                        FIXED_POINT_SCALE,
207                    >(src_layout, dst_layout, profile);
208                }
209            }
210            make_rgb_xyz_q2_13::<u8, LINEAR_CAP, GAMMA_LUT, 8, FIXED_POINT_SCALE>(
211                src_layout, dst_layout, profile,
212            )
213        } else {
214            make_rgb_xyz_rgb_transform::<u8, LINEAR_CAP, GAMMA_LUT, 8>(
215                src_layout, dst_layout, profile,
216            )
217        }
218    }
219}
220
221// Optimized factories
222
223impl RgbXyzFactoryOpt<u16> for u16 {
224    fn make_optimized_transform<
225        const LINEAR_CAP: usize,
226        const GAMMA_LUT: usize,
227        const BIT_DEPTH: usize,
228    >(
229        src_layout: Layout,
230        dst_layout: Layout,
231        profile: TransformMatrixShaperOptimized<u16, LINEAR_CAP>,
232        transform_options: TransformOptions,
233    ) -> Result<Box<dyn TransformExecutor<u16> + Send + Sync>, CmsError> {
234        if BIT_DEPTH >= 12 && transform_options.prefer_fixed_point {
235            #[cfg(all(target_arch = "aarch64", target_feature = "neon", feature = "neon"))]
236            {
237                if std::arch::is_aarch64_feature_detected!("rdm") {
238                    use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q1_30_opt;
239                    return make_rgb_xyz_q1_30_opt::<u16, LINEAR_CAP, GAMMA_LUT, BIT_DEPTH, 30>(
240                        src_layout, dst_layout, profile,
241                    );
242                }
243            }
244        }
245        if BIT_DEPTH < 16 && transform_options.prefer_fixed_point {
246            #[cfg(all(target_arch = "x86_64", feature = "avx"))]
247            {
248                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_avx2_opt;
249                if std::arch::is_x86_feature_detected!("avx2") {
250                    return make_rgb_xyz_q2_13_transform_avx2_opt::<
251                        u16,
252                        LINEAR_CAP,
253                        GAMMA_LUT,
254                        BIT_DEPTH,
255                        FIXED_POINT_SCALE,
256                    >(src_layout, dst_layout, profile);
257                }
258            }
259            #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
260            {
261                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_sse_41_opt;
262                if std::arch::is_x86_feature_detected!("sse4.1") {
263                    return make_rgb_xyz_q2_13_transform_sse_41_opt::<
264                        u16,
265                        LINEAR_CAP,
266                        GAMMA_LUT,
267                        BIT_DEPTH,
268                        FIXED_POINT_SCALE,
269                    >(src_layout, dst_layout, profile);
270                }
271            }
272            #[cfg(all(target_arch = "aarch64", target_feature = "neon", feature = "neon"))]
273            {
274                return make_rgb_xyz_q2_13_opt::<
275                    u16,
276                    LINEAR_CAP,
277                    GAMMA_LUT,
278                    BIT_DEPTH,
279                    FIXED_POINT_SCALE,
280                >(src_layout, dst_layout, profile);
281            }
282        }
283        make_rgb_xyz_rgb_transform_opt::<u16, LINEAR_CAP, GAMMA_LUT, BIT_DEPTH>(
284            src_layout, dst_layout, profile,
285        )
286    }
287}
288
289impl RgbXyzFactoryOpt<f32> for f32 {
290    fn make_optimized_transform<
291        const LINEAR_CAP: usize,
292        const GAMMA_LUT: usize,
293        const BIT_DEPTH: usize,
294    >(
295        src_layout: Layout,
296        dst_layout: Layout,
297        profile: TransformMatrixShaperOptimized<f32, LINEAR_CAP>,
298        transform_options: TransformOptions,
299    ) -> Result<Box<dyn TransformExecutor<f32> + Send + Sync>, CmsError> {
300        if transform_options.prefer_fixed_point {
301            #[cfg(all(target_arch = "x86_64", feature = "avx"))]
302            {
303                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_avx2_opt;
304                if std::arch::is_x86_feature_detected!("avx2") {
305                    return make_rgb_xyz_q2_13_transform_avx2_opt::<
306                        f32,
307                        LINEAR_CAP,
308                        GAMMA_LUT,
309                        BIT_DEPTH,
310                        FIXED_POINT_SCALE,
311                    >(src_layout, dst_layout, profile);
312                }
313            }
314            #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
315            {
316                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_sse_41_opt;
317                if std::arch::is_x86_feature_detected!("sse4.1") {
318                    return make_rgb_xyz_q2_13_transform_sse_41_opt::<
319                        f32,
320                        LINEAR_CAP,
321                        GAMMA_LUT,
322                        BIT_DEPTH,
323                        FIXED_POINT_SCALE,
324                    >(src_layout, dst_layout, profile);
325                }
326            }
327            #[cfg(all(target_arch = "aarch64", target_feature = "neon", feature = "neon"))]
328            {
329                return if std::arch::is_aarch64_feature_detected!("rdm") {
330                    use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q1_30_opt;
331                    make_rgb_xyz_q1_30_opt::<f32, LINEAR_CAP, GAMMA_LUT, BIT_DEPTH, 30>(
332                        src_layout, dst_layout, profile,
333                    )
334                } else {
335                    make_rgb_xyz_q2_13_opt::<f32, LINEAR_CAP, GAMMA_LUT, BIT_DEPTH, FIXED_POINT_SCALE>(
336                        src_layout, dst_layout, profile,
337                    )
338                };
339            }
340        }
341        make_rgb_xyz_rgb_transform_opt::<f32, LINEAR_CAP, GAMMA_LUT, BIT_DEPTH>(
342            src_layout, dst_layout, profile,
343        )
344    }
345}
346
347impl RgbXyzFactoryOpt<f64> for f64 {
348    fn make_optimized_transform<
349        const LINEAR_CAP: usize,
350        const GAMMA_LUT: usize,
351        const BIT_DEPTH: usize,
352    >(
353        src_layout: Layout,
354        dst_layout: Layout,
355        profile: TransformMatrixShaperOptimized<f64, LINEAR_CAP>,
356        transform_options: TransformOptions,
357    ) -> Result<Box<dyn TransformExecutor<f64> + Send + Sync>, CmsError> {
358        if transform_options.prefer_fixed_point {
359            #[cfg(all(target_arch = "aarch64", target_feature = "neon", feature = "neon"))]
360            {
361                if std::arch::is_aarch64_feature_detected!("rdm") {
362                    use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q1_30_opt;
363                    return make_rgb_xyz_q1_30_opt::<f64, LINEAR_CAP, GAMMA_LUT, BIT_DEPTH, 30>(
364                        src_layout, dst_layout, profile,
365                    );
366                }
367            }
368        }
369        make_rgb_xyz_rgb_transform_opt::<f64, LINEAR_CAP, GAMMA_LUT, BIT_DEPTH>(
370            src_layout, dst_layout, profile,
371        )
372    }
373}
374
375impl RgbXyzFactoryOpt<u8> for u8 {
376    fn make_optimized_transform<
377        const LINEAR_CAP: usize,
378        const GAMMA_LUT: usize,
379        const BIT_DEPTH: usize,
380    >(
381        src_layout: Layout,
382        dst_layout: Layout,
383        profile: TransformMatrixShaperOptimized<u8, LINEAR_CAP>,
384        transform_options: TransformOptions,
385    ) -> Result<Box<dyn TransformExecutor<u8> + Send + Sync>, CmsError> {
386        if transform_options.prefer_fixed_point {
387            #[cfg(all(target_arch = "x86_64", feature = "avx512"))]
388            {
389                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_avx512_opt;
390                if std::arch::is_x86_feature_detected!("avx512bw")
391                    && std::arch::is_x86_feature_detected!("avx512vl")
392                {
393                    return make_rgb_xyz_q2_13_transform_avx512_opt::<
394                        u8,
395                        LINEAR_CAP,
396                        GAMMA_LUT,
397                        8,
398                        FIXED_POINT_SCALE,
399                    >(src_layout, dst_layout, profile);
400                }
401            }
402            #[cfg(all(target_arch = "x86_64", feature = "avx"))]
403            {
404                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_avx2_opt;
405                if std::arch::is_x86_feature_detected!("avx2") {
406                    return make_rgb_xyz_q2_13_transform_avx2_opt::<
407                        u8,
408                        LINEAR_CAP,
409                        GAMMA_LUT,
410                        8,
411                        FIXED_POINT_SCALE,
412                    >(src_layout, dst_layout, profile);
413                }
414            }
415            #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
416            {
417                use crate::conversions::rgbxyz_fixed::make_rgb_xyz_q2_13_transform_sse_41_opt;
418                if std::arch::is_x86_feature_detected!("sse4.1") {
419                    return make_rgb_xyz_q2_13_transform_sse_41_opt::<
420                        u8,
421                        LINEAR_CAP,
422                        GAMMA_LUT,
423                        8,
424                        FIXED_POINT_SCALE,
425                    >(src_layout, dst_layout, profile);
426                }
427            }
428            make_rgb_xyz_q2_13_opt::<u8, LINEAR_CAP, GAMMA_LUT, 8, FIXED_POINT_SCALE>(
429                src_layout, dst_layout, profile,
430            )
431        } else {
432            make_rgb_xyz_rgb_transform_opt::<u8, LINEAR_CAP, GAMMA_LUT, 8>(
433                src_layout, dst_layout, profile,
434            )
435        }
436    }
437}