Coverage Report

Created: 2025-10-14 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/moxcms-0.7.7/src/conversions/lut3x3.rs
Line
Count
Source
1
/*
2
 * // Copyright (c) Radzivon Bartoshyk 3/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
use crate::conversions::katana::{KatanaFinalStage, KatanaInitialStage};
30
use crate::err::{MalformedSize, try_vec};
31
use crate::profile::LutDataType;
32
use crate::safe_math::{SafeMul, SafePowi};
33
use crate::trc::lut_interp_linear_float;
34
use crate::{
35
    CmsError, Cube, DataColorSpace, InterpolationMethod, PointeeSizeExpressible, Stage,
36
    TransformOptions, Vector3f,
37
};
38
use num_traits::AsPrimitive;
39
40
#[derive(Default)]
41
struct Lut3x3 {
42
    input: [Vec<f32>; 3],
43
    clut: Vec<f32>,
44
    grid_size: u8,
45
    gamma: [Vec<f32>; 3],
46
    interpolation_method: InterpolationMethod,
47
    pcs: DataColorSpace,
48
}
49
50
#[derive(Default)]
51
struct KatanaLut3x3<T: Copy + Default> {
52
    input: [Vec<f32>; 3],
53
    clut: Vec<f32>,
54
    grid_size: u8,
55
    gamma: [Vec<f32>; 3],
56
    interpolation_method: InterpolationMethod,
57
    pcs: DataColorSpace,
58
    _phantom: std::marker::PhantomData<T>,
59
    bit_depth: usize,
60
}
61
62
0
fn make_lut_3x3(
63
0
    lut: &LutDataType,
64
0
    options: TransformOptions,
65
0
    pcs: DataColorSpace,
66
0
) -> Result<Lut3x3, CmsError> {
67
0
    let clut_length: usize = (lut.num_clut_grid_points as usize)
68
0
        .safe_powi(lut.num_input_channels as u32)?
69
0
        .safe_mul(lut.num_output_channels as usize)?;
70
71
0
    let lin_table = lut.input_table.to_clut_f32();
72
73
0
    if lin_table.len() < lut.num_input_table_entries as usize * 3 {
74
0
        return Err(CmsError::MalformedCurveLutTable(MalformedSize {
75
0
            size: lin_table.len(),
76
0
            expected: lut.num_input_table_entries as usize * 3,
77
0
        }));
78
0
    }
79
80
0
    let lin_curve0 = lin_table[..lut.num_input_table_entries as usize].to_vec();
81
0
    let lin_curve1 = lin_table
82
0
        [lut.num_input_table_entries as usize..lut.num_input_table_entries as usize * 2]
83
0
        .to_vec();
84
0
    let lin_curve2 = lin_table
85
0
        [lut.num_input_table_entries as usize * 2..lut.num_input_table_entries as usize * 3]
86
0
        .to_vec();
87
88
0
    let clut_table = lut.clut_table.to_clut_f32();
89
0
    if clut_table.len() != clut_length {
90
0
        return Err(CmsError::MalformedClut(MalformedSize {
91
0
            size: clut_table.len(),
92
0
            expected: clut_length,
93
0
        }));
94
0
    }
95
96
0
    let gamma_curves = lut.output_table.to_clut_f32();
97
98
0
    if gamma_curves.len() < lut.num_output_table_entries as usize * 3 {
99
0
        return Err(CmsError::MalformedCurveLutTable(MalformedSize {
100
0
            size: gamma_curves.len(),
101
0
            expected: lut.num_output_table_entries as usize * 3,
102
0
        }));
103
0
    }
104
105
0
    let gamma_curve0 = gamma_curves[..lut.num_output_table_entries as usize].to_vec();
106
0
    let gamma_curve1 = gamma_curves
107
0
        [lut.num_output_table_entries as usize..lut.num_output_table_entries as usize * 2]
108
0
        .to_vec();
109
0
    let gamma_curve2 = gamma_curves
110
0
        [lut.num_output_table_entries as usize * 2..lut.num_output_table_entries as usize * 3]
111
0
        .to_vec();
112
113
0
    let transform = Lut3x3 {
114
0
        input: [lin_curve0, lin_curve1, lin_curve2],
115
0
        gamma: [gamma_curve0, gamma_curve1, gamma_curve2],
116
0
        interpolation_method: options.interpolation_method,
117
0
        clut: clut_table,
118
0
        grid_size: lut.num_clut_grid_points,
119
0
        pcs,
120
0
    };
121
122
0
    Ok(transform)
123
0
}
124
125
0
fn stage_lut_3x3(
126
0
    lut: &LutDataType,
127
0
    options: TransformOptions,
128
0
    pcs: DataColorSpace,
129
0
) -> Result<Box<dyn Stage>, CmsError> {
130
0
    let lut = make_lut_3x3(lut, options, pcs)?;
131
132
0
    let transform = Lut3x3 {
133
0
        input: lut.input,
134
0
        gamma: lut.gamma,
135
0
        interpolation_method: lut.interpolation_method,
136
0
        clut: lut.clut,
137
0
        grid_size: lut.grid_size,
138
0
        pcs: lut.pcs,
139
0
    };
140
141
0
    Ok(Box::new(transform))
142
0
}
143
144
0
pub(crate) fn katana_input_stage_lut_3x3<
145
0
    T: Copy + Default + AsPrimitive<f32> + PointeeSizeExpressible + Send + Sync,
146
0
>(
147
0
    lut: &LutDataType,
148
0
    options: TransformOptions,
149
0
    pcs: DataColorSpace,
150
0
    bit_depth: usize,
151
0
) -> Result<Box<dyn KatanaInitialStage<f32, T> + Send + Sync>, CmsError>
152
0
where
153
0
    f32: AsPrimitive<T>,
154
{
155
0
    let lut = make_lut_3x3(lut, options, pcs)?;
156
157
0
    let transform = KatanaLut3x3::<T> {
158
0
        input: lut.input,
159
0
        gamma: lut.gamma,
160
0
        interpolation_method: lut.interpolation_method,
161
0
        clut: lut.clut,
162
0
        grid_size: lut.grid_size,
163
0
        pcs: lut.pcs,
164
0
        _phantom: std::marker::PhantomData,
165
0
        bit_depth,
166
0
    };
167
168
0
    Ok(Box::new(transform))
169
0
}
Unexecuted instantiation: moxcms::conversions::lut3x3::katana_input_stage_lut_3x3::<f64>
Unexecuted instantiation: moxcms::conversions::lut3x3::katana_input_stage_lut_3x3::<f32>
Unexecuted instantiation: moxcms::conversions::lut3x3::katana_input_stage_lut_3x3::<u8>
Unexecuted instantiation: moxcms::conversions::lut3x3::katana_input_stage_lut_3x3::<u16>
170
171
0
pub(crate) fn katana_output_stage_lut_3x3<
172
0
    T: Copy + Default + AsPrimitive<f32> + PointeeSizeExpressible + Send + Sync,
173
0
>(
174
0
    lut: &LutDataType,
175
0
    options: TransformOptions,
176
0
    pcs: DataColorSpace,
177
0
    bit_depth: usize,
178
0
) -> Result<Box<dyn KatanaFinalStage<f32, T> + Send + Sync>, CmsError>
179
0
where
180
0
    f32: AsPrimitive<T>,
181
{
182
0
    let lut = make_lut_3x3(lut, options, pcs)?;
183
184
0
    let transform = KatanaLut3x3::<T> {
185
0
        input: lut.input,
186
0
        gamma: lut.gamma,
187
0
        interpolation_method: lut.interpolation_method,
188
0
        clut: lut.clut,
189
0
        grid_size: lut.grid_size,
190
0
        pcs: lut.pcs,
191
0
        _phantom: std::marker::PhantomData,
192
0
        bit_depth,
193
0
    };
194
195
0
    Ok(Box::new(transform))
196
0
}
Unexecuted instantiation: moxcms::conversions::lut3x3::katana_output_stage_lut_3x3::<f64>
Unexecuted instantiation: moxcms::conversions::lut3x3::katana_output_stage_lut_3x3::<f32>
Unexecuted instantiation: moxcms::conversions::lut3x3::katana_output_stage_lut_3x3::<u8>
Unexecuted instantiation: moxcms::conversions::lut3x3::katana_output_stage_lut_3x3::<u16>
197
198
impl Lut3x3 {
199
0
    fn transform_impl<Fetch: Fn(f32, f32, f32) -> Vector3f>(
200
0
        &self,
201
0
        src: &[f32],
202
0
        dst: &mut [f32],
203
0
        fetch: Fetch,
204
0
    ) -> Result<(), CmsError> {
205
0
        let linearization_0 = &self.input[0];
206
0
        let linearization_1 = &self.input[1];
207
0
        let linearization_2 = &self.input[2];
208
0
        for (dest, src) in dst.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
209
0
            debug_assert!(self.grid_size as i32 >= 1);
210
0
            let linear_x = lut_interp_linear_float(src[0], linearization_0);
211
0
            let linear_y = lut_interp_linear_float(src[1], linearization_1);
212
0
            let linear_z = lut_interp_linear_float(src[2], linearization_2);
213
214
0
            let clut = fetch(linear_x, linear_y, linear_z);
215
216
0
            let pcs_x = lut_interp_linear_float(clut.v[0], &self.gamma[0]);
217
0
            let pcs_y = lut_interp_linear_float(clut.v[1], &self.gamma[1]);
218
0
            let pcs_z = lut_interp_linear_float(clut.v[2], &self.gamma[2]);
219
0
            dest[0] = pcs_x;
220
0
            dest[1] = pcs_y;
221
0
            dest[2] = pcs_z;
222
        }
223
0
        Ok(())
224
0
    }
Unexecuted instantiation: <moxcms::conversions::lut3x3::Lut3x3>::transform_impl::<<moxcms::conversions::lut3x3::Lut3x3 as moxcms::transform::Stage>::transform::{closure#0}>
Unexecuted instantiation: <moxcms::conversions::lut3x3::Lut3x3>::transform_impl::<<moxcms::conversions::lut3x3::Lut3x3 as moxcms::transform::Stage>::transform::{closure#1}>
225
}
226
227
impl Stage for Lut3x3 {
228
0
    fn transform(&self, src: &[f32], dst: &mut [f32]) -> Result<(), CmsError> {
229
0
        let l_tbl = Cube::new(&self.clut, self.grid_size as usize);
230
231
        // If PCS is LAB then linear interpolation should be used
232
0
        if self.pcs == DataColorSpace::Lab || self.pcs == DataColorSpace::Xyz {
233
0
            return self.transform_impl(src, dst, |x, y, z| l_tbl.trilinear_vec3(x, y, z));
234
0
        }
235
236
0
        match self.interpolation_method {
237
            #[cfg(feature = "options")]
238
            InterpolationMethod::Tetrahedral => {
239
                self.transform_impl(src, dst, |x, y, z| l_tbl.tetra_vec3(x, y, z))?;
240
            }
241
            #[cfg(feature = "options")]
242
            InterpolationMethod::Pyramid => {
243
                self.transform_impl(src, dst, |x, y, z| l_tbl.pyramid_vec3(x, y, z))?;
244
            }
245
            #[cfg(feature = "options")]
246
            InterpolationMethod::Prism => {
247
                self.transform_impl(src, dst, |x, y, z| l_tbl.prism_vec3(x, y, z))?;
248
            }
249
            InterpolationMethod::Linear => {
250
0
                self.transform_impl(src, dst, |x, y, z| l_tbl.trilinear_vec3(x, y, z))?;
251
            }
252
        }
253
0
        Ok(())
254
0
    }
255
}
256
257
impl<T: Copy + Default + PointeeSizeExpressible + AsPrimitive<f32>> KatanaLut3x3<T>
258
where
259
    f32: AsPrimitive<T>,
260
{
261
0
    fn to_pcs_impl<Fetch: Fn(f32, f32, f32) -> Vector3f>(
262
0
        &self,
263
0
        input: &[T],
264
0
        fetch: Fetch,
265
0
    ) -> Result<Vec<f32>, CmsError> {
266
0
        if input.len() % 3 != 0 {
267
0
            return Err(CmsError::LaneMultipleOfChannels);
268
0
        }
269
0
        let normalizing_value = if T::FINITE {
270
0
            1.0 / ((1u32 << self.bit_depth) - 1) as f32
271
        } else {
272
0
            1.0
273
        };
274
0
        let mut dst = try_vec![0.; input.len()];
275
0
        let linearization_0 = &self.input[0];
276
0
        let linearization_1 = &self.input[1];
277
0
        let linearization_2 = &self.input[2];
278
0
        for (dest, src) in dst.chunks_exact_mut(3).zip(input.chunks_exact(3)) {
279
0
            let linear_x =
280
0
                lut_interp_linear_float(src[0].as_() * normalizing_value, linearization_0);
281
0
            let linear_y =
282
0
                lut_interp_linear_float(src[1].as_() * normalizing_value, linearization_1);
283
0
            let linear_z =
284
0
                lut_interp_linear_float(src[2].as_() * normalizing_value, linearization_2);
285
0
286
0
            let clut = fetch(linear_x, linear_y, linear_z);
287
0
288
0
            let pcs_x = lut_interp_linear_float(clut.v[0], &self.gamma[0]);
289
0
            let pcs_y = lut_interp_linear_float(clut.v[1], &self.gamma[1]);
290
0
            let pcs_z = lut_interp_linear_float(clut.v[2], &self.gamma[2]);
291
0
            dest[0] = pcs_x;
292
0
            dest[1] = pcs_y;
293
0
            dest[2] = pcs_z;
294
0
        }
295
0
        Ok(dst)
296
0
    }
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f64>>::to_pcs_impl::<<moxcms::conversions::lut3x3::KatanaLut3x3<f64> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, f64>>::to_pcs::{closure#0}>
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f64>>::to_pcs_impl::<<moxcms::conversions::lut3x3::KatanaLut3x3<f64> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, f64>>::to_pcs::{closure#1}>
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f32>>::to_pcs_impl::<<moxcms::conversions::lut3x3::KatanaLut3x3<f32> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, f32>>::to_pcs::{closure#0}>
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f32>>::to_pcs_impl::<<moxcms::conversions::lut3x3::KatanaLut3x3<f32> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, f32>>::to_pcs::{closure#1}>
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u8>>::to_pcs_impl::<<moxcms::conversions::lut3x3::KatanaLut3x3<u8> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, u8>>::to_pcs::{closure#0}>
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u8>>::to_pcs_impl::<<moxcms::conversions::lut3x3::KatanaLut3x3<u8> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, u8>>::to_pcs::{closure#1}>
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u16>>::to_pcs_impl::<<moxcms::conversions::lut3x3::KatanaLut3x3<u16> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, u16>>::to_pcs::{closure#0}>
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u16>>::to_pcs_impl::<<moxcms::conversions::lut3x3::KatanaLut3x3<u16> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, u16>>::to_pcs::{closure#1}>
297
298
0
    fn to_output<Fetch: Fn(f32, f32, f32) -> Vector3f>(
299
0
        &self,
300
0
        src: &[f32],
301
0
        dst: &mut [T],
302
0
        fetch: Fetch,
303
0
    ) -> Result<(), CmsError> {
304
0
        if src.len() % 3 != 0 {
305
0
            return Err(CmsError::LaneMultipleOfChannels);
306
0
        }
307
0
        if dst.len() % 3 != 0 {
308
0
            return Err(CmsError::LaneMultipleOfChannels);
309
0
        }
310
0
        if dst.len() != src.len() {
311
0
            return Err(CmsError::LaneSizeMismatch);
312
0
        }
313
0
        let norm_value = if T::FINITE {
314
0
            ((1u32 << self.bit_depth) - 1) as f32
315
        } else {
316
0
            1.0
317
        };
318
319
0
        let linearization_0 = &self.input[0];
320
0
        let linearization_1 = &self.input[1];
321
0
        let linearization_2 = &self.input[2];
322
0
        for (dest, src) in dst.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
323
0
            let linear_x = lut_interp_linear_float(src[0], linearization_0);
324
0
            let linear_y = lut_interp_linear_float(src[1], linearization_1);
325
0
            let linear_z = lut_interp_linear_float(src[2], linearization_2);
326
327
0
            let clut = fetch(linear_x, linear_y, linear_z);
328
329
0
            let pcs_x = lut_interp_linear_float(clut.v[0], &self.gamma[0]);
330
0
            let pcs_y = lut_interp_linear_float(clut.v[1], &self.gamma[1]);
331
0
            let pcs_z = lut_interp_linear_float(clut.v[2], &self.gamma[2]);
332
333
0
            if T::FINITE {
334
0
                dest[0] = (pcs_x * norm_value).round().max(0.0).min(norm_value).as_();
335
0
                dest[1] = (pcs_y * norm_value).round().max(0.0).min(norm_value).as_();
336
0
                dest[2] = (pcs_z * norm_value).round().max(0.0).min(norm_value).as_();
337
0
            } else {
338
0
                dest[0] = pcs_x.as_();
339
0
                dest[1] = pcs_y.as_();
340
0
                dest[2] = pcs_z.as_();
341
0
            }
342
        }
343
0
        Ok(())
344
0
    }
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f64>>::to_output::<<moxcms::conversions::lut3x3::KatanaLut3x3<f64> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, f64>>::to_output::{closure#0}>
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f64>>::to_output::<<moxcms::conversions::lut3x3::KatanaLut3x3<f64> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, f64>>::to_output::{closure#1}>
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f32>>::to_output::<<moxcms::conversions::lut3x3::KatanaLut3x3<f32> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, f32>>::to_output::{closure#0}>
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f32>>::to_output::<<moxcms::conversions::lut3x3::KatanaLut3x3<f32> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, f32>>::to_output::{closure#1}>
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u8>>::to_output::<<moxcms::conversions::lut3x3::KatanaLut3x3<u8> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, u8>>::to_output::{closure#0}>
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u8>>::to_output::<<moxcms::conversions::lut3x3::KatanaLut3x3<u8> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, u8>>::to_output::{closure#1}>
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u16>>::to_output::<<moxcms::conversions::lut3x3::KatanaLut3x3<u16> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, u16>>::to_output::{closure#0}>
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u16>>::to_output::<<moxcms::conversions::lut3x3::KatanaLut3x3<u16> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, u16>>::to_output::{closure#1}>
345
}
346
347
impl<T: Copy + Default + PointeeSizeExpressible + AsPrimitive<f32>> KatanaInitialStage<f32, T>
348
    for KatanaLut3x3<T>
349
where
350
    f32: AsPrimitive<T>,
351
{
352
0
    fn to_pcs(&self, input: &[T]) -> Result<Vec<f32>, CmsError> {
353
0
        let l_tbl = Cube::new(&self.clut, self.grid_size as usize);
354
355
        // If PCS is LAB then linear interpolation should be used
356
0
        if self.pcs == DataColorSpace::Lab || self.pcs == DataColorSpace::Xyz {
357
0
            return self.to_pcs_impl(input, |x, y, z| l_tbl.trilinear_vec3(x, y, z));
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f64> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, f64>>::to_pcs::{closure#0}
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f32> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, f32>>::to_pcs::{closure#0}
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u8> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, u8>>::to_pcs::{closure#0}
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u16> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, u16>>::to_pcs::{closure#0}
358
0
        }
359
360
0
        match self.interpolation_method {
361
            #[cfg(feature = "options")]
362
            InterpolationMethod::Tetrahedral => {
363
                self.to_pcs_impl(input, |x, y, z| l_tbl.tetra_vec3(x, y, z))
364
            }
365
            #[cfg(feature = "options")]
366
            InterpolationMethod::Pyramid => {
367
                self.to_pcs_impl(input, |x, y, z| l_tbl.pyramid_vec3(x, y, z))
368
            }
369
            #[cfg(feature = "options")]
370
            InterpolationMethod::Prism => {
371
                self.to_pcs_impl(input, |x, y, z| l_tbl.prism_vec3(x, y, z))
372
            }
373
            InterpolationMethod::Linear => {
374
0
                self.to_pcs_impl(input, |x, y, z| l_tbl.trilinear_vec3(x, y, z))
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f64> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, f64>>::to_pcs::{closure#1}
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f32> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, f32>>::to_pcs::{closure#1}
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u8> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, u8>>::to_pcs::{closure#1}
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u16> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, u16>>::to_pcs::{closure#1}
375
            }
376
        }
377
0
    }
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f64> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, f64>>::to_pcs
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f32> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, f32>>::to_pcs
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u8> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, u8>>::to_pcs
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u16> as moxcms::conversions::katana::stages::KatanaInitialStage<f32, u16>>::to_pcs
378
}
379
380
impl<T: Copy + Default + PointeeSizeExpressible + AsPrimitive<f32>> KatanaFinalStage<f32, T>
381
    for KatanaLut3x3<T>
382
where
383
    f32: AsPrimitive<T>,
384
{
385
0
    fn to_output(&self, src: &mut [f32], dst: &mut [T]) -> Result<(), CmsError> {
386
0
        let l_tbl = Cube::new(&self.clut, self.grid_size as usize);
387
388
        // If PCS is LAB then linear interpolation should be used
389
0
        if self.pcs == DataColorSpace::Lab || self.pcs == DataColorSpace::Xyz {
390
0
            return self.to_output(src, dst, |x, y, z| l_tbl.trilinear_vec3(x, y, z));
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f64> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, f64>>::to_output::{closure#0}
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f32> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, f32>>::to_output::{closure#0}
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u8> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, u8>>::to_output::{closure#0}
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u16> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, u16>>::to_output::{closure#0}
391
0
        }
392
393
0
        match self.interpolation_method {
394
            #[cfg(feature = "options")]
395
            InterpolationMethod::Tetrahedral => {
396
                self.to_output(src, dst, |x, y, z| l_tbl.tetra_vec3(x, y, z))
397
            }
398
            #[cfg(feature = "options")]
399
            InterpolationMethod::Pyramid => {
400
                self.to_output(src, dst, |x, y, z| l_tbl.pyramid_vec3(x, y, z))
401
            }
402
            #[cfg(feature = "options")]
403
            InterpolationMethod::Prism => {
404
                self.to_output(src, dst, |x, y, z| l_tbl.prism_vec3(x, y, z))
405
            }
406
            InterpolationMethod::Linear => {
407
0
                self.to_output(src, dst, |x, y, z| l_tbl.trilinear_vec3(x, y, z))
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f64> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, f64>>::to_output::{closure#1}
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f32> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, f32>>::to_output::{closure#1}
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u8> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, u8>>::to_output::{closure#1}
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u16> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, u16>>::to_output::{closure#1}
408
            }
409
        }
410
0
    }
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f64> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, f64>>::to_output
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<f32> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, f32>>::to_output
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u8> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, u8>>::to_output
Unexecuted instantiation: <moxcms::conversions::lut3x3::KatanaLut3x3<u16> as moxcms::conversions::katana::stages::KatanaFinalStage<f32, u16>>::to_output
411
}
412
413
0
pub(crate) fn create_lut3x3(
414
0
    lut: &LutDataType,
415
0
    src: &[f32],
416
0
    options: TransformOptions,
417
0
    pcs: DataColorSpace,
418
0
) -> Result<Vec<f32>, CmsError> {
419
0
    if lut.num_input_channels != 3 || lut.num_output_channels != 3 {
420
0
        return Err(CmsError::UnsupportedProfileConnection);
421
0
    }
422
423
0
    let mut dest = try_vec![0.; src.len()];
424
425
0
    let lut_stage = stage_lut_3x3(lut, options, pcs)?;
426
0
    lut_stage.transform(src, &mut dest)?;
427
0
    Ok(dest)
428
0
}