Coverage Report

Created: 2025-10-28 08:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/moxcms-0.7.9/src/conversions/lut3x4.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::err::try_vec;
30
use crate::profile::LutDataType;
31
use crate::safe_math::{SafeMul, SafePowi};
32
use crate::trc::lut_interp_linear_float;
33
use crate::{
34
    CmsError, Cube, DataColorSpace, InterpolationMethod, MalformedSize, Stage, TransformOptions,
35
    Vector4f,
36
};
37
use num_traits::AsPrimitive;
38
39
#[derive(Default)]
40
struct Lut3x4 {
41
    input: [Vec<f32>; 3],
42
    clut: Vec<f32>,
43
    grid_size: u8,
44
    gamma: [Vec<f32>; 4],
45
    interpolation_method: InterpolationMethod,
46
    pcs: DataColorSpace,
47
}
48
49
0
fn make_lut_3x4(
50
0
    lut: &LutDataType,
51
0
    options: TransformOptions,
52
0
    pcs: DataColorSpace,
53
0
) -> Result<Lut3x4, CmsError> {
54
0
    let clut_length: usize = (lut.num_clut_grid_points as usize)
55
0
        .safe_powi(lut.num_input_channels as u32)?
56
0
        .safe_mul(lut.num_output_channels as usize)?;
57
58
0
    let clut_table = lut.clut_table.to_clut_f32();
59
0
    if clut_table.len() != clut_length {
60
0
        return Err(CmsError::MalformedClut(MalformedSize {
61
0
            size: clut_table.len(),
62
0
            expected: clut_length,
63
0
        }));
64
0
    }
65
66
0
    let linearization_table = lut.input_table.to_clut_f32();
67
68
0
    if linearization_table.len() < lut.num_input_table_entries as usize * 3 {
69
0
        return Err(CmsError::MalformedCurveLutTable(MalformedSize {
70
0
            size: linearization_table.len(),
71
0
            expected: lut.num_input_table_entries as usize * 3,
72
0
        }));
73
0
    }
74
75
0
    let linear_curve0 = linearization_table[..lut.num_input_table_entries as usize].to_vec();
76
0
    let linear_curve1 = linearization_table
77
0
        [lut.num_input_table_entries as usize..lut.num_input_table_entries as usize * 2]
78
0
        .to_vec();
79
0
    let linear_curve2 = linearization_table
80
0
        [lut.num_input_table_entries as usize * 2..lut.num_input_table_entries as usize * 3]
81
0
        .to_vec();
82
83
0
    let gamma_table = lut.output_table.to_clut_f32();
84
85
0
    if gamma_table.len() < lut.num_output_table_entries as usize * 4 {
86
0
        return Err(CmsError::MalformedCurveLutTable(MalformedSize {
87
0
            size: gamma_table.len(),
88
0
            expected: lut.num_output_table_entries as usize * 4,
89
0
        }));
90
0
    }
91
92
0
    let gamma_curve0 = gamma_table[..lut.num_output_table_entries as usize].to_vec();
93
0
    let gamma_curve1 = gamma_table
94
0
        [lut.num_output_table_entries as usize..lut.num_output_table_entries as usize * 2]
95
0
        .to_vec();
96
0
    let gamma_curve2 = gamma_table
97
0
        [lut.num_output_table_entries as usize * 2..lut.num_output_table_entries as usize * 3]
98
0
        .to_vec();
99
0
    let gamma_curve3 = gamma_table
100
0
        [lut.num_output_table_entries as usize * 3..lut.num_output_table_entries as usize * 4]
101
0
        .to_vec();
102
103
0
    let transform = Lut3x4 {
104
0
        input: [linear_curve0, linear_curve1, linear_curve2],
105
0
        interpolation_method: options.interpolation_method,
106
0
        clut: clut_table,
107
0
        grid_size: lut.num_clut_grid_points,
108
0
        pcs,
109
0
        gamma: [gamma_curve0, gamma_curve1, gamma_curve2, gamma_curve3],
110
0
    };
111
0
    Ok(transform)
112
0
}
113
114
0
fn stage_lut_3x4(
115
0
    lut: &LutDataType,
116
0
    options: TransformOptions,
117
0
    pcs: DataColorSpace,
118
0
) -> Result<Box<dyn Stage>, CmsError> {
119
0
    let lut = make_lut_3x4(lut, options, pcs)?;
120
121
0
    let transform = Lut3x4 {
122
0
        input: lut.input,
123
0
        interpolation_method: lut.interpolation_method,
124
0
        clut: lut.clut,
125
0
        grid_size: lut.grid_size,
126
0
        pcs: lut.pcs,
127
0
        gamma: lut.gamma,
128
0
    };
129
0
    Ok(Box::new(transform))
130
0
}
131
132
impl Lut3x4 {
133
0
    fn transform_impl<Fetch: Fn(f32, f32, f32) -> Vector4f>(
134
0
        &self,
135
0
        src: &[f32],
136
0
        dst: &mut [f32],
137
0
        fetch: Fetch,
138
0
    ) -> Result<(), CmsError> {
139
0
        let linearization_0 = &self.input[0];
140
0
        let linearization_1 = &self.input[1];
141
0
        let linearization_2 = &self.input[2];
142
0
        for (dest, src) in dst.chunks_exact_mut(4).zip(src.chunks_exact(3)) {
143
0
            debug_assert!(self.grid_size as i32 >= 1);
144
0
            let linear_x = lut_interp_linear_float(src[0], linearization_0);
145
0
            let linear_y = lut_interp_linear_float(src[1], linearization_1);
146
0
            let linear_z = lut_interp_linear_float(src[2], linearization_2);
147
148
0
            let clut = fetch(linear_x, linear_y, linear_z);
149
150
0
            let pcs_x = lut_interp_linear_float(clut.v[0], &self.gamma[0]);
151
0
            let pcs_y = lut_interp_linear_float(clut.v[1], &self.gamma[1]);
152
0
            let pcs_z = lut_interp_linear_float(clut.v[2], &self.gamma[2]);
153
0
            let pcs_w = lut_interp_linear_float(clut.v[3], &self.gamma[3]);
154
0
            dest[0] = pcs_x;
155
0
            dest[1] = pcs_y;
156
0
            dest[2] = pcs_z;
157
0
            dest[3] = pcs_w;
158
        }
159
0
        Ok(())
160
0
    }
Unexecuted instantiation: <moxcms::conversions::lut3x4::Lut3x4>::transform_impl::<<moxcms::conversions::lut3x4::Lut3x4 as moxcms::transform::Stage>::transform::{closure#0}>
Unexecuted instantiation: <moxcms::conversions::lut3x4::Lut3x4>::transform_impl::<<moxcms::conversions::lut3x4::Lut3x4 as moxcms::transform::Stage>::transform::{closure#1}>
161
}
162
163
impl Stage for Lut3x4 {
164
0
    fn transform(&self, src: &[f32], dst: &mut [f32]) -> Result<(), CmsError> {
165
0
        let l_tbl = Cube::new_checked(&self.clut, self.grid_size as usize, 4)?;
166
167
        // If PCS is LAB then linear interpolation should be used
168
0
        if self.pcs == DataColorSpace::Lab || self.pcs == DataColorSpace::Xyz {
169
0
            return self.transform_impl(src, dst, |x, y, z| l_tbl.trilinear_vec4(x, y, z));
170
0
        }
171
172
0
        match self.interpolation_method {
173
            #[cfg(feature = "options")]
174
            InterpolationMethod::Tetrahedral => {
175
                self.transform_impl(src, dst, |x, y, z| l_tbl.tetra_vec4(x, y, z))?;
176
            }
177
            #[cfg(feature = "options")]
178
            InterpolationMethod::Pyramid => {
179
                self.transform_impl(src, dst, |x, y, z| l_tbl.pyramid_vec4(x, y, z))?;
180
            }
181
            #[cfg(feature = "options")]
182
            InterpolationMethod::Prism => {
183
                self.transform_impl(src, dst, |x, y, z| l_tbl.prism_vec4(x, y, z))?;
184
            }
185
            InterpolationMethod::Linear => {
186
0
                self.transform_impl(src, dst, |x, y, z| l_tbl.trilinear_vec4(x, y, z))?;
187
            }
188
        }
189
0
        Ok(())
190
0
    }
191
}
192
193
0
pub(crate) fn create_lut3_samples<T: Copy + 'static, const SAMPLES: usize>() -> Vec<T>
194
0
where
195
0
    u32: AsPrimitive<T>,
196
{
197
0
    let lut_size: u32 = (3 * SAMPLES * SAMPLES * SAMPLES) as u32;
198
199
0
    assert!(SAMPLES >= 1);
200
201
0
    let mut src = Vec::with_capacity(lut_size as usize);
202
0
    for x in 0..SAMPLES as u32 {
203
0
        for y in 0..SAMPLES as u32 {
204
0
            for z in 0..SAMPLES as u32 {
205
0
                src.push(x.as_());
206
0
                src.push(y.as_());
207
0
                src.push(z.as_());
208
0
            }
209
        }
210
    }
211
0
    src
212
0
}
Unexecuted instantiation: moxcms::conversions::lut3x4::create_lut3_samples::<f64, 33>
Unexecuted instantiation: moxcms::conversions::lut3x4::create_lut3_samples::<f32, 33>
Unexecuted instantiation: moxcms::conversions::lut3x4::create_lut3_samples::<u8, 33>
Unexecuted instantiation: moxcms::conversions::lut3x4::create_lut3_samples::<u16, 33>
213
214
0
pub(crate) fn create_lut3_samples_norm<const SAMPLES: usize>() -> Vec<f32> {
215
0
    let lut_size: u32 = (3 * SAMPLES * SAMPLES * SAMPLES) as u32;
216
217
0
    assert!(SAMPLES >= 1);
218
219
0
    let scale = 1. / (SAMPLES as f32 - 1.0);
220
221
0
    let mut src = Vec::with_capacity(lut_size as usize);
222
0
    for x in 0..SAMPLES as u32 {
223
0
        for y in 0..SAMPLES as u32 {
224
0
            for z in 0..SAMPLES as u32 {
225
0
                src.push(x as f32 * scale);
226
0
                src.push(y as f32 * scale);
227
0
                src.push(z as f32 * scale);
228
0
            }
229
        }
230
    }
231
0
    src
232
0
}
233
234
0
pub(crate) fn create_lut3x4(
235
0
    lut: &LutDataType,
236
0
    src: &[f32],
237
0
    options: TransformOptions,
238
0
    pcs: DataColorSpace,
239
0
) -> Result<Vec<f32>, CmsError> {
240
0
    if lut.num_input_channels != 3 || lut.num_output_channels != 4 {
241
0
        return Err(CmsError::UnsupportedProfileConnection);
242
0
    }
243
244
0
    let mut dest = try_vec![0.; (src.len() / 3) * 4];
245
246
0
    let lut_stage = stage_lut_3x4(lut, options, pcs)?;
247
0
    lut_stage.transform(src, &mut dest)?;
248
0
    Ok(dest)
249
0
}