Skip to main content

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