moxcms/
helpers.rs

1/*
2 * // Copyright (c) Radzivon Bartoshyk 6/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::matan::{
30    does_curve_have_discontinuity, is_curve_ascending, is_curve_degenerated, is_curve_descending,
31    is_curve_linear8, is_curve_linear16, is_curve_monotonic,
32};
33use crate::reader::{
34    s15_fixed16_number_to_double, uint8_number_to_float_fast, uint16_number_to_float_fast,
35};
36use crate::{CmsError, LutStore, Matrix3d, ToneReprCurve, Vector3d};
37
38impl LutStore {
39    pub fn to_clut_f32(&self) -> Vec<f32> {
40        match self {
41            LutStore::Store8(store) => store
42                .iter()
43                .map(|x| uint8_number_to_float_fast(*x))
44                .collect(),
45            LutStore::Store16(store) => store
46                .iter()
47                .map(|x| uint16_number_to_float_fast(*x as u32))
48                .collect(),
49        }
50    }
51
52    pub(crate) fn is_degenerated(&self, entries: usize, channel: usize) -> bool {
53        let start = entries * channel;
54        let end = start + entries;
55
56        match &self {
57            LutStore::Store8(v) => is_curve_degenerated(&v[start..end]),
58            LutStore::Store16(v) => is_curve_degenerated(&v[start..end]),
59        }
60    }
61
62    pub(crate) fn is_monotonic(&self, entries: usize, channel: usize) -> bool {
63        let start = entries * channel;
64        let end = start + entries;
65
66        match &self {
67            LutStore::Store8(v) => is_curve_monotonic(&v[start..end]),
68            LutStore::Store16(v) => is_curve_monotonic(&v[start..end]),
69        }
70    }
71
72    pub(crate) fn have_discontinuities(&self, entries: usize, channel: usize) -> bool {
73        let start = entries * channel;
74        let end = start + entries;
75
76        match &self {
77            LutStore::Store8(v) => does_curve_have_discontinuity(&v[start..end]),
78            LutStore::Store16(v) => does_curve_have_discontinuity(&v[start..end]),
79        }
80    }
81
82    #[allow(dead_code)]
83    pub(crate) fn is_linear(&self, entries: usize, channel: usize) -> bool {
84        let start = entries * channel;
85        let end = start + entries;
86
87        match &self {
88            LutStore::Store8(v) => is_curve_linear8(&v[start..end]),
89            LutStore::Store16(v) => is_curve_linear16(&v[start..end]),
90        }
91    }
92
93    #[allow(dead_code)]
94    pub(crate) fn is_descending(&self, entries: usize, channel: usize) -> bool {
95        let start = entries * channel;
96        let end = start + entries;
97
98        match &self {
99            LutStore::Store8(v) => is_curve_descending(&v[start..end]),
100            LutStore::Store16(v) => is_curve_descending(&v[start..end]),
101        }
102    }
103
104    #[allow(dead_code)]
105    pub(crate) fn is_ascending(&self, entries: usize, channel: usize) -> bool {
106        let start = entries * channel;
107        let end = start + entries;
108
109        match &self {
110            LutStore::Store8(v) => is_curve_ascending(&v[start..end]),
111            LutStore::Store16(v) => is_curve_ascending(&v[start..end]),
112        }
113    }
114}
115
116impl ToneReprCurve {
117    pub(crate) fn is_linear(&self) -> bool {
118        match &self {
119            ToneReprCurve::Lut(lut) => {
120                if lut.is_empty() {
121                    return true;
122                }
123                if lut.len() == 1 {
124                    let gamma = 1. / crate::trc::u8_fixed_8number_to_float(lut[0]);
125                    if (gamma - 1.).abs() < 1e-4 {
126                        return true;
127                    }
128                }
129                is_curve_linear16(lut)
130            }
131            ToneReprCurve::Parametric(parametric) => {
132                if parametric.is_empty() {
133                    return true;
134                }
135                if parametric.len() == 1 && parametric[0] == 1. {
136                    return true;
137                }
138                false
139            }
140        }
141    }
142
143    pub(crate) fn is_monotonic(&self) -> bool {
144        match &self {
145            ToneReprCurve::Lut(lut) => is_curve_monotonic(lut),
146            ToneReprCurve::Parametric(_) => true,
147        }
148    }
149
150    pub(crate) fn is_degenerated(&self) -> bool {
151        match &self {
152            ToneReprCurve::Lut(lut) => is_curve_degenerated(lut),
153            ToneReprCurve::Parametric(_) => false,
154        }
155    }
156
157    pub(crate) fn have_discontinuities(&self) -> bool {
158        match &self {
159            ToneReprCurve::Lut(lut) => does_curve_have_discontinuity(lut),
160            ToneReprCurve::Parametric(_) => false,
161        }
162    }
163}
164
165pub(crate) fn read_matrix_3d(arr: &[u8]) -> Result<Matrix3d, CmsError> {
166    if arr.len() < 36 {
167        return Err(CmsError::InvalidProfile);
168    }
169
170    let m_tag = &arr[..36];
171
172    let e00 = i32::from_be_bytes([m_tag[0], m_tag[1], m_tag[2], m_tag[3]]);
173    let e01 = i32::from_be_bytes([m_tag[4], m_tag[5], m_tag[6], m_tag[7]]);
174    let e02 = i32::from_be_bytes([m_tag[8], m_tag[9], m_tag[10], m_tag[11]]);
175
176    let e10 = i32::from_be_bytes([m_tag[12], m_tag[13], m_tag[14], m_tag[15]]);
177    let e11 = i32::from_be_bytes([m_tag[16], m_tag[17], m_tag[18], m_tag[19]]);
178    let e12 = i32::from_be_bytes([m_tag[20], m_tag[21], m_tag[22], m_tag[23]]);
179
180    let e20 = i32::from_be_bytes([m_tag[24], m_tag[25], m_tag[26], m_tag[27]]);
181    let e21 = i32::from_be_bytes([m_tag[28], m_tag[29], m_tag[30], m_tag[31]]);
182    let e22 = i32::from_be_bytes([m_tag[32], m_tag[33], m_tag[34], m_tag[35]]);
183
184    Ok(Matrix3d {
185        v: [
186            [
187                s15_fixed16_number_to_double(e00),
188                s15_fixed16_number_to_double(e01),
189                s15_fixed16_number_to_double(e02),
190            ],
191            [
192                s15_fixed16_number_to_double(e10),
193                s15_fixed16_number_to_double(e11),
194                s15_fixed16_number_to_double(e12),
195            ],
196            [
197                s15_fixed16_number_to_double(e20),
198                s15_fixed16_number_to_double(e21),
199                s15_fixed16_number_to_double(e22),
200            ],
201        ],
202    })
203}
204
205pub(crate) fn read_vector_3d(arr: &[u8]) -> Result<Vector3d, CmsError> {
206    if arr.len() < 12 {
207        return Err(CmsError::InvalidProfile);
208    }
209
210    let m_tag = &arr[..12];
211
212    let b0 = i32::from_be_bytes([m_tag[0], m_tag[1], m_tag[2], m_tag[3]]);
213    let b1 = i32::from_be_bytes([m_tag[4], m_tag[5], m_tag[6], m_tag[7]]);
214    let b2 = i32::from_be_bytes([m_tag[8], m_tag[9], m_tag[10], m_tag[11]]);
215
216    Ok(Vector3d {
217        v: [
218            s15_fixed16_number_to_double(b0),
219            s15_fixed16_number_to_double(b1),
220            s15_fixed16_number_to_double(b2),
221        ],
222    })
223}