Coverage Report

Created: 2026-09-01 07:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/moxcms-0.8.1/src/conversions/gray2rgb.rs
Line
Count
Source
1
/*
2
 * // Copyright (c) Radzivon Bartoshyk 2/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")]
30
use crate::InPlaceTransformExecutor;
31
use crate::transform::PointeeSizeExpressible;
32
use crate::{CmsError, Layout, TransformExecutor};
33
use num_traits::AsPrimitive;
34
use std::sync::Arc;
35
36
#[derive(Clone)]
37
struct TransformGray2RgbFusedExecutor<T, const SRC_LAYOUT: u8, const DEST_LAYOUT: u8> {
38
    fused_gamma: Box<[T; 65536]>,
39
    bit_depth: usize,
40
}
41
42
0
pub(crate) fn make_gray_to_x<
43
0
    T: Copy + Default + PointeeSizeExpressible + 'static + Send + Sync,
44
0
    const BUCKET: usize,
45
0
>(
46
0
    src_layout: Layout,
47
0
    dst_layout: Layout,
48
0
    gray_linear: &[f32; BUCKET],
49
0
    gray_gamma: &[T; 65536],
50
0
    bit_depth: usize,
51
0
    gamma_lut: usize,
52
0
) -> Result<Arc<dyn TransformExecutor<T> + Sync + Send>, CmsError>
53
0
where
54
0
    u32: AsPrimitive<T>,
55
{
56
0
    if src_layout != Layout::Gray && src_layout != Layout::GrayAlpha {
57
0
        return Err(CmsError::UnsupportedProfileConnection);
58
0
    }
59
60
0
    let mut fused_gamma = Box::new([T::default(); 65536]);
61
0
    let max_lut_size = (gamma_lut - 1) as f32;
62
0
    for (&src, dst) in gray_linear.iter().zip(fused_gamma.iter_mut()) {
63
0
        let possible_value = ((src * max_lut_size).round() as u32).min(max_lut_size as u32) as u16;
64
0
        *dst = gray_gamma[possible_value as usize];
65
0
    }
66
67
0
    match src_layout {
68
0
        Layout::Gray => match dst_layout {
69
0
            Layout::Rgb => Ok(Arc::new(TransformGray2RgbFusedExecutor::<
70
0
                T,
71
0
                { Layout::Gray as u8 },
72
0
                { Layout::Rgb as u8 },
73
0
            > {
74
0
                fused_gamma,
75
0
                bit_depth,
76
0
            })),
77
0
            Layout::Rgba => Ok(Arc::new(TransformGray2RgbFusedExecutor::<
78
0
                T,
79
0
                { Layout::Gray as u8 },
80
0
                { Layout::Rgba as u8 },
81
0
            > {
82
0
                fused_gamma,
83
0
                bit_depth,
84
0
            })),
85
0
            Layout::Gray => Ok(Arc::new(TransformGray2RgbFusedExecutor::<
86
0
                T,
87
0
                { Layout::Gray as u8 },
88
0
                { Layout::Gray as u8 },
89
0
            > {
90
0
                fused_gamma,
91
0
                bit_depth,
92
0
            })),
93
0
            Layout::GrayAlpha => Ok(Arc::new(TransformGray2RgbFusedExecutor::<
94
0
                T,
95
0
                { Layout::Gray as u8 },
96
0
                { Layout::GrayAlpha as u8 },
97
0
            > {
98
0
                fused_gamma,
99
0
                bit_depth,
100
0
            })),
101
0
            _ => Err(CmsError::UnsupportedProfileConnection),
102
        },
103
0
        Layout::GrayAlpha => match dst_layout {
104
0
            Layout::Rgb => Ok(Arc::new(TransformGray2RgbFusedExecutor::<
105
0
                T,
106
0
                { Layout::GrayAlpha as u8 },
107
0
                { Layout::Rgb as u8 },
108
0
            > {
109
0
                fused_gamma,
110
0
                bit_depth,
111
0
            })),
112
0
            Layout::Rgba => Ok(Arc::new(TransformGray2RgbFusedExecutor::<
113
0
                T,
114
0
                { Layout::GrayAlpha as u8 },
115
0
                { Layout::Rgba as u8 },
116
0
            > {
117
0
                fused_gamma,
118
0
                bit_depth,
119
0
            })),
120
0
            Layout::Gray => Ok(Arc::new(TransformGray2RgbFusedExecutor::<
121
0
                T,
122
0
                { Layout::GrayAlpha as u8 },
123
0
                { Layout::Gray as u8 },
124
0
            > {
125
0
                fused_gamma,
126
0
                bit_depth,
127
0
            })),
128
0
            Layout::GrayAlpha => Ok(Arc::new(TransformGray2RgbFusedExecutor::<
129
0
                T,
130
0
                { Layout::GrayAlpha as u8 },
131
0
                { Layout::GrayAlpha as u8 },
132
0
            > {
133
0
                fused_gamma,
134
0
                bit_depth,
135
0
            })),
136
0
            _ => Err(CmsError::UnsupportedProfileConnection),
137
        },
138
0
        _ => Err(CmsError::UnsupportedProfileConnection),
139
    }
140
0
}
Unexecuted instantiation: moxcms::conversions::gray2rgb::make_gray_to_x::<f64, 65536>
Unexecuted instantiation: moxcms::conversions::gray2rgb::make_gray_to_x::<f32, 65536>
Unexecuted instantiation: moxcms::conversions::gray2rgb::make_gray_to_x::<u8, 256>
Unexecuted instantiation: moxcms::conversions::gray2rgb::make_gray_to_x::<u16, 65536>
141
142
#[cfg(feature = "in_place")]
143
pub(crate) fn make_gray_to_gray_in_place<
144
    T: Copy + Default + PointeeSizeExpressible + 'static + Send + Sync,
145
    const BUCKET: usize,
146
>(
147
    layout: Layout,
148
    gray_linear: &[f32; BUCKET],
149
    gray_gamma: &[T; 65536],
150
    bit_depth: usize,
151
    gamma_lut: usize,
152
) -> Result<Arc<dyn InPlaceTransformExecutor<T> + Sync + Send>, CmsError>
153
where
154
    u32: AsPrimitive<T>,
155
{
156
    if layout != Layout::Gray && layout != Layout::GrayAlpha {
157
        return Err(CmsError::UnsupportedProfileConnection);
158
    }
159
160
    let mut fused_gamma = Box::new([T::default(); 65536]);
161
    let max_lut_size = (gamma_lut - 1) as f32;
162
    for (&src, dst) in gray_linear.iter().zip(fused_gamma.iter_mut()) {
163
        let possible_value = ((src * max_lut_size).round() as u32).min(max_lut_size as u32) as u16;
164
        *dst = gray_gamma[possible_value as usize];
165
    }
166
167
    match layout {
168
        Layout::Gray => Ok(Arc::new(TransformGray2RgbFusedExecutor::<
169
            T,
170
            { Layout::Gray as u8 },
171
            { Layout::Gray as u8 },
172
        > {
173
            fused_gamma,
174
            bit_depth,
175
        })),
176
        Layout::GrayAlpha => Ok(Arc::new(TransformGray2RgbFusedExecutor::<
177
            T,
178
            { Layout::GrayAlpha as u8 },
179
            { Layout::GrayAlpha as u8 },
180
        > {
181
            fused_gamma,
182
            bit_depth,
183
        })),
184
        _ => Err(CmsError::UnsupportedProfileConnection),
185
    }
186
}
187
188
impl<
189
    T: Copy + Default + PointeeSizeExpressible + 'static,
190
    const SRC_LAYOUT: u8,
191
    const DST_LAYOUT: u8,
192
> TransformExecutor<T> for TransformGray2RgbFusedExecutor<T, SRC_LAYOUT, DST_LAYOUT>
193
where
194
    u32: AsPrimitive<T>,
195
{
196
0
    fn transform(&self, src: &[T], dst: &mut [T]) -> Result<(), CmsError> {
197
0
        let src_cn = Layout::from(SRC_LAYOUT);
198
0
        let dst_cn = Layout::from(DST_LAYOUT);
199
0
        let src_channels = src_cn.channels();
200
0
        let dst_channels = dst_cn.channels();
201
202
0
        if src.len() / src_channels != dst.len() / dst_channels {
203
0
            return Err(CmsError::LaneSizeMismatch);
204
0
        }
205
0
        if src.len() % src_channels != 0 {
206
0
            return Err(CmsError::LaneMultipleOfChannels);
207
0
        }
208
0
        if dst.len() % dst_channels != 0 {
209
0
            return Err(CmsError::LaneMultipleOfChannels);
210
0
        }
211
212
0
        let is_gray_alpha = src_cn == Layout::GrayAlpha;
213
214
0
        let max_value: T = ((1u32 << self.bit_depth as u32) - 1u32).as_();
215
216
0
        for (src, dst) in src
217
0
            .chunks_exact(src_channels)
218
0
            .zip(dst.chunks_exact_mut(dst_channels))
219
        {
220
0
            let g = self.fused_gamma[src[0]._as_usize()];
221
0
            let a = if is_gray_alpha { src[1] } else { max_value };
222
223
0
            dst[0] = g;
224
0
            if dst_cn == Layout::GrayAlpha {
225
0
                dst[1] = a;
226
0
            } else if dst_cn == Layout::Rgb {
227
0
                dst[1] = g;
228
0
                dst[2] = g;
229
0
            } else if dst_cn == Layout::Rgba {
230
0
                dst[1] = g;
231
0
                dst[2] = g;
232
0
                dst[3] = a;
233
0
            }
234
        }
235
236
0
        Ok(())
237
0
    }
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<f64, 2, 2> as moxcms::transform::TransformExecutor<f64>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<f64, 2, 0> as moxcms::transform::TransformExecutor<f64>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<f64, 2, 1> as moxcms::transform::TransformExecutor<f64>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<f64, 2, 3> as moxcms::transform::TransformExecutor<f64>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<f64, 3, 3> as moxcms::transform::TransformExecutor<f64>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<f64, 3, 0> as moxcms::transform::TransformExecutor<f64>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<f64, 3, 1> as moxcms::transform::TransformExecutor<f64>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<f64, 3, 2> as moxcms::transform::TransformExecutor<f64>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<f32, 2, 2> as moxcms::transform::TransformExecutor<f32>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<f32, 2, 0> as moxcms::transform::TransformExecutor<f32>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<f32, 2, 1> as moxcms::transform::TransformExecutor<f32>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<f32, 2, 3> as moxcms::transform::TransformExecutor<f32>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<f32, 3, 3> as moxcms::transform::TransformExecutor<f32>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<f32, 3, 0> as moxcms::transform::TransformExecutor<f32>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<f32, 3, 1> as moxcms::transform::TransformExecutor<f32>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<f32, 3, 2> as moxcms::transform::TransformExecutor<f32>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<u8, 2, 2> as moxcms::transform::TransformExecutor<u8>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<u8, 2, 0> as moxcms::transform::TransformExecutor<u8>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<u8, 2, 1> as moxcms::transform::TransformExecutor<u8>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<u8, 2, 3> as moxcms::transform::TransformExecutor<u8>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<u8, 3, 3> as moxcms::transform::TransformExecutor<u8>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<u8, 3, 0> as moxcms::transform::TransformExecutor<u8>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<u8, 3, 1> as moxcms::transform::TransformExecutor<u8>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<u8, 3, 2> as moxcms::transform::TransformExecutor<u8>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<u16, 2, 2> as moxcms::transform::TransformExecutor<u16>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<u16, 2, 0> as moxcms::transform::TransformExecutor<u16>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<u16, 2, 1> as moxcms::transform::TransformExecutor<u16>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<u16, 2, 3> as moxcms::transform::TransformExecutor<u16>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<u16, 3, 3> as moxcms::transform::TransformExecutor<u16>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<u16, 3, 0> as moxcms::transform::TransformExecutor<u16>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<u16, 3, 1> as moxcms::transform::TransformExecutor<u16>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGray2RgbFusedExecutor<u16, 3, 2> as moxcms::transform::TransformExecutor<u16>>::transform
238
}
239
240
#[cfg(feature = "in_place")]
241
impl<
242
    T: Copy + Default + PointeeSizeExpressible + 'static,
243
    const SRC_LAYOUT: u8,
244
    const DST_LAYOUT: u8,
245
> InPlaceTransformExecutor<T> for TransformGray2RgbFusedExecutor<T, SRC_LAYOUT, DST_LAYOUT>
246
where
247
    u32: AsPrimitive<T>,
248
{
249
    fn transform(&self, in_out: &mut [T]) -> Result<(), CmsError> {
250
        assert_eq!(
251
            SRC_LAYOUT, DST_LAYOUT,
252
            "This is in-place transform, layout must not diverge"
253
        );
254
        let src_cn = Layout::from(SRC_LAYOUT);
255
        let src_channels = src_cn.channels();
256
257
        if in_out.len() % src_channels != 0 {
258
            return Err(CmsError::LaneMultipleOfChannels);
259
        }
260
261
        let is_gray_alpha = src_cn == Layout::GrayAlpha;
262
263
        let max_value: T = ((1u32 << self.bit_depth as u32) - 1u32).as_();
264
265
        for dst in in_out.chunks_exact_mut(src_channels) {
266
            let g = self.fused_gamma[dst[0]._as_usize()];
267
            let a = if is_gray_alpha { dst[1] } else { max_value };
268
269
            dst[0] = g;
270
            if src_cn == Layout::GrayAlpha {
271
                dst[1] = a;
272
            }
273
        }
274
275
        Ok(())
276
    }
277
}
278
279
#[derive(Clone)]
280
struct TransformGrayToRgbExecutor<T, const SRC_LAYOUT: u8, const DEST_LAYOUT: u8> {
281
    gray_linear: Box<[f32; 65536]>,
282
    red_gamma: Box<[T; 65536]>,
283
    green_gamma: Box<[T; 65536]>,
284
    blue_gamma: Box<[T; 65536]>,
285
    bit_depth: usize,
286
    gamma_lut: usize,
287
}
288
289
#[allow(clippy::too_many_arguments)]
290
0
pub(crate) fn make_gray_to_unfused<
291
0
    T: Copy + Default + PointeeSizeExpressible + 'static + Send + Sync,
292
0
    const BUCKET: usize,
293
0
>(
294
0
    src_layout: Layout,
295
0
    dst_layout: Layout,
296
0
    gray_linear: Box<[f32; 65536]>,
297
0
    red_gamma: Box<[T; 65536]>,
298
0
    green_gamma: Box<[T; 65536]>,
299
0
    blue_gamma: Box<[T; 65536]>,
300
0
    bit_depth: usize,
301
0
    gamma_lut: usize,
302
0
) -> Result<Arc<dyn TransformExecutor<T> + Sync + Send>, CmsError>
303
0
where
304
0
    u32: AsPrimitive<T>,
305
{
306
0
    if src_layout != Layout::Gray && src_layout != Layout::GrayAlpha {
307
0
        return Err(CmsError::UnsupportedProfileConnection);
308
0
    }
309
0
    if dst_layout != Layout::Rgb && dst_layout != Layout::Rgba {
310
0
        return Err(CmsError::UnsupportedProfileConnection);
311
0
    }
312
0
    match src_layout {
313
0
        Layout::Gray => match dst_layout {
314
0
            Layout::Rgb => Ok(Arc::new(TransformGrayToRgbExecutor::<
315
0
                T,
316
0
                { Layout::Gray as u8 },
317
0
                { Layout::Rgb as u8 },
318
0
            > {
319
0
                gray_linear,
320
0
                red_gamma,
321
0
                green_gamma,
322
0
                blue_gamma,
323
0
                bit_depth,
324
0
                gamma_lut,
325
0
            })),
326
0
            Layout::Rgba => Ok(Arc::new(TransformGrayToRgbExecutor::<
327
0
                T,
328
0
                { Layout::Gray as u8 },
329
0
                { Layout::Rgba as u8 },
330
0
            > {
331
0
                gray_linear,
332
0
                red_gamma,
333
0
                green_gamma,
334
0
                blue_gamma,
335
0
                bit_depth,
336
0
                gamma_lut,
337
0
            })),
338
0
            Layout::Gray => Ok(Arc::new(TransformGrayToRgbExecutor::<
339
0
                T,
340
0
                { Layout::Gray as u8 },
341
0
                { Layout::Gray as u8 },
342
0
            > {
343
0
                gray_linear,
344
0
                red_gamma,
345
0
                green_gamma,
346
0
                blue_gamma,
347
0
                bit_depth,
348
0
                gamma_lut,
349
0
            })),
350
0
            Layout::GrayAlpha => Ok(Arc::new(TransformGrayToRgbExecutor::<
351
0
                T,
352
0
                { Layout::Gray as u8 },
353
0
                { Layout::GrayAlpha as u8 },
354
0
            > {
355
0
                gray_linear,
356
0
                red_gamma,
357
0
                green_gamma,
358
0
                blue_gamma,
359
0
                bit_depth,
360
0
                gamma_lut,
361
0
            })),
362
0
            _ => Err(CmsError::UnsupportedProfileConnection),
363
        },
364
0
        Layout::GrayAlpha => match dst_layout {
365
0
            Layout::Rgb => Ok(Arc::new(TransformGrayToRgbExecutor::<
366
0
                T,
367
0
                { Layout::GrayAlpha as u8 },
368
0
                { Layout::Rgb as u8 },
369
0
            > {
370
0
                gray_linear,
371
0
                red_gamma,
372
0
                green_gamma,
373
0
                blue_gamma,
374
0
                bit_depth,
375
0
                gamma_lut,
376
0
            })),
377
0
            Layout::Rgba => Ok(Arc::new(TransformGrayToRgbExecutor::<
378
0
                T,
379
0
                { Layout::GrayAlpha as u8 },
380
0
                { Layout::Rgba as u8 },
381
0
            > {
382
0
                gray_linear,
383
0
                red_gamma,
384
0
                green_gamma,
385
0
                blue_gamma,
386
0
                bit_depth,
387
0
                gamma_lut,
388
0
            })),
389
0
            Layout::Gray => Ok(Arc::new(TransformGrayToRgbExecutor::<
390
0
                T,
391
0
                { Layout::GrayAlpha as u8 },
392
0
                { Layout::Gray as u8 },
393
0
            > {
394
0
                gray_linear,
395
0
                red_gamma,
396
0
                green_gamma,
397
0
                blue_gamma,
398
0
                bit_depth,
399
0
                gamma_lut,
400
0
            })),
401
0
            Layout::GrayAlpha => Ok(Arc::new(TransformGrayToRgbExecutor::<
402
0
                T,
403
0
                { Layout::GrayAlpha as u8 },
404
0
                { Layout::GrayAlpha as u8 },
405
0
            > {
406
0
                gray_linear,
407
0
                red_gamma,
408
0
                green_gamma,
409
0
                blue_gamma,
410
0
                bit_depth,
411
0
                gamma_lut,
412
0
            })),
413
0
            _ => Err(CmsError::UnsupportedProfileConnection),
414
        },
415
0
        _ => Err(CmsError::UnsupportedProfileConnection),
416
    }
417
0
}
Unexecuted instantiation: moxcms::conversions::gray2rgb::make_gray_to_unfused::<f64, 65536>
Unexecuted instantiation: moxcms::conversions::gray2rgb::make_gray_to_unfused::<f32, 65536>
Unexecuted instantiation: moxcms::conversions::gray2rgb::make_gray_to_unfused::<u8, 256>
Unexecuted instantiation: moxcms::conversions::gray2rgb::make_gray_to_unfused::<u16, 65536>
418
419
impl<
420
    T: Copy + Default + PointeeSizeExpressible + 'static,
421
    const SRC_LAYOUT: u8,
422
    const DST_LAYOUT: u8,
423
> TransformExecutor<T> for TransformGrayToRgbExecutor<T, SRC_LAYOUT, DST_LAYOUT>
424
where
425
    u32: AsPrimitive<T>,
426
{
427
0
    fn transform(&self, src: &[T], dst: &mut [T]) -> Result<(), CmsError> {
428
0
        let src_cn = Layout::from(SRC_LAYOUT);
429
0
        let dst_cn = Layout::from(DST_LAYOUT);
430
0
        let src_channels = src_cn.channels();
431
0
        let dst_channels = dst_cn.channels();
432
433
0
        if src.len() / src_channels != dst.len() / dst_channels {
434
0
            return Err(CmsError::LaneSizeMismatch);
435
0
        }
436
0
        if src.len() % src_channels != 0 {
437
0
            return Err(CmsError::LaneMultipleOfChannels);
438
0
        }
439
0
        if dst.len() % dst_channels != 0 {
440
0
            return Err(CmsError::LaneMultipleOfChannels);
441
0
        }
442
443
0
        let is_gray_alpha = src_cn == Layout::GrayAlpha;
444
445
0
        let max_value: T = ((1u32 << self.bit_depth as u32) - 1u32).as_();
446
0
        let max_lut_size = (self.gamma_lut - 1) as f32;
447
448
0
        for (src, dst) in src
449
0
            .chunks_exact(src_channels)
450
0
            .zip(dst.chunks_exact_mut(dst_channels))
451
        {
452
0
            let g = self.gray_linear[src[0]._as_usize()];
453
0
            let a = if is_gray_alpha { src[1] } else { max_value };
454
455
0
            let possible_value = ((g * max_lut_size).round() as u16) as usize;
456
0
            let red_value = self.red_gamma[possible_value];
457
0
            let green_value = self.green_gamma[possible_value];
458
0
            let blue_value = self.blue_gamma[possible_value];
459
460
0
            if dst_cn == Layout::Rgb {
461
0
                dst[0] = red_value;
462
0
                dst[1] = green_value;
463
0
                dst[2] = blue_value;
464
0
            } else if dst_cn == Layout::Rgba {
465
0
                dst[0] = red_value;
466
0
                dst[1] = green_value;
467
0
                dst[2] = blue_value;
468
0
                dst[3] = a;
469
0
            } else {
470
0
                return Err(CmsError::UnsupportedProfileConnection);
471
            }
472
        }
473
474
0
        Ok(())
475
0
    }
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<f64, 2, 2> as moxcms::transform::TransformExecutor<f64>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<f64, 2, 0> as moxcms::transform::TransformExecutor<f64>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<f64, 2, 1> as moxcms::transform::TransformExecutor<f64>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<f64, 2, 3> as moxcms::transform::TransformExecutor<f64>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<f64, 3, 3> as moxcms::transform::TransformExecutor<f64>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<f64, 3, 0> as moxcms::transform::TransformExecutor<f64>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<f64, 3, 1> as moxcms::transform::TransformExecutor<f64>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<f64, 3, 2> as moxcms::transform::TransformExecutor<f64>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<f32, 2, 2> as moxcms::transform::TransformExecutor<f32>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<f32, 2, 0> as moxcms::transform::TransformExecutor<f32>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<f32, 2, 1> as moxcms::transform::TransformExecutor<f32>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<f32, 2, 3> as moxcms::transform::TransformExecutor<f32>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<f32, 3, 3> as moxcms::transform::TransformExecutor<f32>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<f32, 3, 0> as moxcms::transform::TransformExecutor<f32>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<f32, 3, 1> as moxcms::transform::TransformExecutor<f32>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<f32, 3, 2> as moxcms::transform::TransformExecutor<f32>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<u8, 2, 2> as moxcms::transform::TransformExecutor<u8>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<u8, 2, 0> as moxcms::transform::TransformExecutor<u8>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<u8, 2, 1> as moxcms::transform::TransformExecutor<u8>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<u8, 2, 3> as moxcms::transform::TransformExecutor<u8>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<u8, 3, 3> as moxcms::transform::TransformExecutor<u8>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<u8, 3, 0> as moxcms::transform::TransformExecutor<u8>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<u8, 3, 1> as moxcms::transform::TransformExecutor<u8>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<u8, 3, 2> as moxcms::transform::TransformExecutor<u8>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<u16, 2, 2> as moxcms::transform::TransformExecutor<u16>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<u16, 2, 0> as moxcms::transform::TransformExecutor<u16>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<u16, 2, 1> as moxcms::transform::TransformExecutor<u16>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<u16, 2, 3> as moxcms::transform::TransformExecutor<u16>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<u16, 3, 3> as moxcms::transform::TransformExecutor<u16>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<u16, 3, 0> as moxcms::transform::TransformExecutor<u16>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<u16, 3, 1> as moxcms::transform::TransformExecutor<u16>>::transform
Unexecuted instantiation: <moxcms::conversions::gray2rgb::TransformGrayToRgbExecutor<u16, 3, 2> as moxcms::transform::TransformExecutor<u16>>::transform
476
}
477
478
#[cfg(test)]
479
mod tests {
480
    use super::*;
481
    use crate::{ColorProfile, TransformOptions};
482
483
    #[test]
484
    fn gray_rgb_roundtrip() {
485
        let gray = ColorProfile::new_gray_with_gamma(2.2);
486
        let srgb = ColorProfile::new_srgb();
487
488
        let src_layout = [Layout::Gray, Layout::GrayAlpha];
489
        let dst_layout = [Layout::Rgb, Layout::GrayAlpha, Layout::Rgba, Layout::Gray];
490
        for src in src_layout {
491
            for dst in dst_layout {
492
                let transform = gray
493
                    .create_transform_8bit(src, &srgb, dst, TransformOptions::default())
494
                    .unwrap();
495
                let mut in_px = vec![0u8; src.channels()];
496
                let mut out_px = vec![0u8; dst.channels()];
497
                transform.transform(&mut in_px, &mut out_px).unwrap();
498
            }
499
        }
500
    }
501
}