Coverage Report

Created: 2025-11-11 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/moxcms-0.7.9/src/cicp.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
use crate::gamma::{
30
    bt1361_to_linear, hlg_to_linear, iec61966_to_linear, log100_sqrt10_to_linear, log100_to_linear,
31
    pq_to_linear, smpte240_to_linear, smpte428_to_linear,
32
};
33
use crate::{
34
    Chromaticity, ColorProfile, Matrix3d, Matrix3f, XyYRepresentable,
35
    err::CmsError,
36
    trc::{ToneReprCurve, build_trc_table, curve_from_gamma},
37
};
38
use std::convert::TryFrom;
39
40
/// See [Rec. ITU-T H.273 (12/2016)](https://www.itu.int/rec/T-REC-H.273-201612-I/en) Table 2
41
/// Values 0, 3, 13–21, 23–255 are all reserved so all map to the same variant
42
#[derive(Clone, Copy, Debug, PartialEq)]
43
pub enum CicpColorPrimaries {
44
    /// For future use by ITU-T | ISO/IEC
45
    Reserved,
46
    /// Rec. ITU-R BT.709-6<br />
47
    /// Rec. ITU-R BT.1361-0 conventional colour gamut system and extended colour gamut system (historical)<br />
48
    /// IEC 61966-2-1 sRGB or sYCC IEC 61966-2-4<br />
49
    /// Society of Motion Picture and Television Engineers (MPTE) RP 177 (1993) Annex B<br />
50
    Bt709 = 1,
51
    /// Unspecified<br />
52
    /// Image characteristics are unknown or are determined by the application.
53
    Unspecified = 2,
54
    /// Rec. ITU-R BT.470-6 System M (historical)<br />
55
    /// United States National Television System Committee 1953 Recommendation for transmission standards for color television<br />
56
    /// United States Federal Communications Commission (2003) Title 47 Code of Federal Regulations 73.682 (a) (20)<br />
57
    Bt470M = 4,
58
    /// Rec. ITU-R BT.470-6 System B, G (historical) Rec. ITU-R BT.601-7 625<br />
59
    /// Rec. ITU-R BT.1358-0 625 (historical)<br />
60
    /// Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM<br />
61
    Bt470Bg = 5,
62
    /// Rec. ITU-R BT.601-7 525<br />
63
    /// Rec. ITU-R BT.1358-1 525 or 625 (historical) Rec. ITU-R BT.1700-0 NTSC<br />
64
    /// SMPTE 170M (2004)<br />
65
    /// (functionally the same as the value 7)<br />
66
    Bt601 = 6,
67
    /// SMPTE 240M (1999) (historical) (functionally the same as the value 6)<br />
68
    Smpte240 = 7,
69
    /// Generic film (colour filters using Illuminant C)<br />
70
    GenericFilm = 8,
71
    /// Rec. ITU-R BT.2020-2<br />
72
    /// Rec. ITU-R BT.2100-0<br />
73
    Bt2020 = 9,
74
    /// SMPTE ST 428-1<br />
75
    /// (CIE 1931 XYZ as in ISO 11664-1)<br />
76
    Xyz = 10,
77
    /// SMPTE RP 431-2 (2011)<br />
78
    Smpte431 = 11,
79
    /// SMPTE EG 432-1 (2010)<br />
80
    Smpte432 = 12,
81
    /// EBU Tech. 3213-E (1975)<br />
82
    Ebu3213 = 22,
83
}
84
85
impl TryFrom<u8> for CicpColorPrimaries {
86
    type Error = CmsError;
87
88
    #[allow(unreachable_patterns)]
89
0
    fn try_from(value: u8) -> Result<Self, Self::Error> {
90
0
        match value {
91
            // Values 0, 3, 13–21, 23–255 are all reserved so all map to the
92
            // same variant.
93
0
            0 | 3 | 13..=21 | 23..=255 => Ok(Self::Reserved),
94
0
            1 => Ok(Self::Bt709),
95
0
            2 => Ok(Self::Unspecified),
96
0
            4 => Ok(Self::Bt470M),
97
0
            5 => Ok(Self::Bt470Bg),
98
0
            6 => Ok(Self::Bt601),
99
0
            7 => Ok(Self::Smpte240),
100
0
            8 => Ok(Self::GenericFilm),
101
0
            9 => Ok(Self::Bt2020),
102
0
            10 => Ok(Self::Xyz),
103
0
            11 => Ok(Self::Smpte431),
104
0
            12 => Ok(Self::Smpte432),
105
0
            22 => Ok(Self::Ebu3213),
106
0
            _ => Err(CmsError::InvalidCicp),
107
        }
108
0
    }
109
}
110
111
#[derive(Clone, Copy, Debug)]
112
#[repr(C)]
113
pub struct ColorPrimaries {
114
    pub red: Chromaticity,
115
    pub green: Chromaticity,
116
    pub blue: Chromaticity,
117
}
118
119
/// See [Rec. ITU-T H.273 (12/2016)](https://www.itu.int/rec/T-REC-H.273-201612-I/en) Table 2.
120
impl ColorPrimaries {
121
    /// [ACEScg](https://en.wikipedia.org/wiki/Academy_Color_Encoding_System#ACEScg).
122
    pub const ACES_CG: ColorPrimaries = ColorPrimaries {
123
        red: Chromaticity { x: 0.713, y: 0.293 },
124
        green: Chromaticity { x: 0.165, y: 0.830 },
125
        blue: Chromaticity { x: 0.128, y: 0.044 },
126
    };
127
128
    /// [ACES2065-1](https://en.wikipedia.org/wiki/Academy_Color_Encoding_System#ACES2065-1).
129
    pub const ACES_2065_1: ColorPrimaries = ColorPrimaries {
130
        red: Chromaticity {
131
            x: 0.7347,
132
            y: 0.2653,
133
        },
134
        green: Chromaticity {
135
            x: 0.0000,
136
            y: 1.0000,
137
        },
138
        blue: Chromaticity {
139
            x: 0.0001,
140
            y: -0.0770,
141
        },
142
    };
143
144
    /// [Adobe RGB](https://en.wikipedia.org/wiki/Adobe_RGB_color_space) (1998).
145
    pub const ADOBE_RGB: ColorPrimaries = ColorPrimaries {
146
        red: Chromaticity { x: 0.64, y: 0.33 },
147
        green: Chromaticity { x: 0.21, y: 0.71 },
148
        blue: Chromaticity { x: 0.15, y: 0.06 },
149
    };
150
151
    /// [DCI P3](https://en.wikipedia.org/wiki/DCI-P3#DCI_P3).
152
    ///
153
    /// This is the same as [`DISPLAY_P3`](Self::DISPLAY_P3),
154
    /// [`SMPTE_431`](Self::SMPTE_431) and [`SMPTE_432`](Self::SMPTE_432).
155
    pub const DCI_P3: ColorPrimaries = ColorPrimaries {
156
        red: Chromaticity { x: 0.680, y: 0.320 },
157
        green: Chromaticity { x: 0.265, y: 0.690 },
158
        blue: Chromaticity { x: 0.150, y: 0.060 },
159
    };
160
161
    /// [Diplay P3](https://en.wikipedia.org/wiki/DCI-P3#Display_P3).
162
    ///
163
    /// This is the same as [`DCI_P3`](Self::DCI_P3),
164
    /// [`SMPTE_431`](Self::SMPTE_431) and [`SMPTE_432`](Self::SMPTE_432).
165
    pub const DISPLAY_P3: ColorPrimaries = Self::DCI_P3;
166
167
    /// SMPTE RP 431-2 (2011).
168
    ///
169
    /// This is the same as [`DCI_P3`](Self::DCI_P3),
170
    /// [`DISPLAY_P3`](Self::DISPLAY_P3) and [`SMPTE_432`](Self::SMPTE_432).
171
    pub const SMPTE_431: ColorPrimaries = Self::DCI_P3;
172
173
    /// SMPTE EG 432-1 (2010).
174
    ///
175
    /// This is the same as [`DCI_P3`](Self::DCI_P3),
176
    /// [`DISPLAY_P3`](Self::DISPLAY_P3) and [`SMPTE_431`](Self::SMPTE_431).
177
    pub const SMPTE_432: ColorPrimaries = Self::DCI_P3;
178
179
    /// [ProPhoto RGB](https://en.wikipedia.org/wiki/ProPhoto_RGB_color_space).
180
    pub const PRO_PHOTO_RGB: ColorPrimaries = ColorPrimaries {
181
        red: Chromaticity {
182
            x: 0.734699,
183
            y: 0.265301,
184
        },
185
        green: Chromaticity {
186
            x: 0.159597,
187
            y: 0.840403,
188
        },
189
        blue: Chromaticity {
190
            x: 0.036598,
191
            y: 0.000105,
192
        },
193
    };
194
195
    /// Rec. ITU-R BT.709-6
196
    ///
197
    /// Rec. ITU-R BT.1361-0 conventional colour gamut system and extended
198
    /// colour gamut system (historical).
199
    ///
200
    /// IEC 61966-2-1 sRGB or sYCC IEC 61966-2-4).
201
    ///
202
    /// Society of Motion Picture and Television Engineers (MPTE) RP 177 (1993) Annex B.
203
    pub const BT_709: ColorPrimaries = ColorPrimaries {
204
        red: Chromaticity { x: 0.64, y: 0.33 },
205
        green: Chromaticity { x: 0.30, y: 0.60 },
206
        blue: Chromaticity { x: 0.15, y: 0.06 },
207
    };
208
209
    /// Rec. ITU-R BT.470-6 System M (historical).
210
    ///
211
    /// United States National Television System Committee 1953 Recommendation
212
    /// for transmission standards for color television.
213
    ///
214
    /// United States Federal Communications Commission (2003) Title 47 Code of
215
    /// Federal Regulations 73.682 (a) (20).
216
    pub const BT_470M: ColorPrimaries = ColorPrimaries {
217
        red: Chromaticity { x: 0.67, y: 0.33 },
218
        green: Chromaticity { x: 0.21, y: 0.71 },
219
        blue: Chromaticity { x: 0.14, y: 0.08 },
220
    };
221
222
    /// Rec. ITU-R BT.470-6 System B, G (historical) Rec. ITU-R BT.601-7 625.
223
    ///
224
    /// Rec. ITU-R BT.1358-0 625 (historical).
225
    /// Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM.
226
    pub const BT_470BG: ColorPrimaries = ColorPrimaries {
227
        red: Chromaticity { x: 0.64, y: 0.33 },
228
        green: Chromaticity { x: 0.29, y: 0.60 },
229
        blue: Chromaticity { x: 0.15, y: 0.06 },
230
    };
231
232
    /// Rec. ITU-R BT.601-7 525.
233
    ///
234
    /// Rec. ITU-R BT.1358-1 525 or 625 (historical) Rec. ITU-R BT.1700-0 NTSC.
235
    ///
236
    /// SMPTE 170M (2004) (functionally the same as the [`SMPTE_240`](Self::SMPTE_240)).
237
    pub const BT_601: ColorPrimaries = ColorPrimaries {
238
        red: Chromaticity { x: 0.630, y: 0.340 },
239
        green: Chromaticity { x: 0.310, y: 0.595 },
240
        blue: Chromaticity { x: 0.155, y: 0.070 },
241
    };
242
243
    /// SMPTE 240M (1999) (historical) (functionally the same as [`BT_601`](Self::BT_601)).
244
    pub const SMPTE_240: ColorPrimaries = Self::BT_601;
245
246
    /// Generic film (colour filters using Illuminant C).
247
    pub const GENERIC_FILM: ColorPrimaries = ColorPrimaries {
248
        red: Chromaticity { x: 0.681, y: 0.319 },
249
        green: Chromaticity { x: 0.243, y: 0.692 },
250
        blue: Chromaticity { x: 0.145, y: 0.049 },
251
    };
252
253
    /// Rec. ITU-R BT.2020-2.
254
    ///
255
    /// Rec. ITU-R BT.2100-0.
256
    pub const BT_2020: ColorPrimaries = ColorPrimaries {
257
        red: Chromaticity { x: 0.708, y: 0.292 },
258
        green: Chromaticity { x: 0.170, y: 0.797 },
259
        blue: Chromaticity { x: 0.131, y: 0.046 },
260
    };
261
262
    /// SMPTE ST 428-1 (CIE 1931 XYZ as in ISO 11664-1).
263
    pub const XYZ: ColorPrimaries = ColorPrimaries {
264
        red: Chromaticity { x: 1.0, y: 0.0 },
265
        green: Chromaticity { x: 0.0, y: 1.0 },
266
        blue: Chromaticity { x: 0.0, y: 0.0 },
267
    };
268
269
    /// EBU Tech. 3213-E (1975).
270
    pub const EBU_3213: ColorPrimaries = ColorPrimaries {
271
        red: Chromaticity { x: 0.630, y: 0.340 },
272
        green: Chromaticity { x: 0.295, y: 0.605 },
273
        blue: Chromaticity { x: 0.155, y: 0.077 },
274
    };
275
}
276
277
impl ColorPrimaries {
278
    /// Returns RGB -> XYZ conversion matrix
279
    ///
280
    /// # Arguments
281
    ///
282
    /// * `white_point`: [Chromaticity] or [crate::XyY] or any item conforming [XyYRepresentable]
283
    ///
284
    /// returns: [Matrix3d]
285
0
    pub fn transform_to_xyz_d(self, white_point: impl XyYRepresentable) -> Matrix3d {
286
0
        let red_xyz = self.red.to_scaled_xyzd();
287
0
        let green_xyz = self.green.to_scaled_xyzd();
288
0
        let blue_xyz = self.blue.to_scaled_xyzd();
289
290
0
        let xyz_matrix = Matrix3d {
291
0
            v: [
292
0
                [red_xyz.x, green_xyz.x, blue_xyz.x],
293
0
                [red_xyz.y, green_xyz.y, blue_xyz.y],
294
0
                [red_xyz.z, green_xyz.z, blue_xyz.z],
295
0
            ],
296
0
        };
297
0
        ColorProfile::rgb_to_xyz_d(xyz_matrix, white_point.to_xyy().to_xyzd())
298
0
    }
299
300
    /// Returns RGB -> XYZ conversion matrix
301
    ///
302
    /// # Arguments
303
    ///
304
    /// * `white_point`: [Chromaticity] or [crate::XyY] or any item conforming [XyYRepresentable]
305
    ///
306
    /// returns: [Matrix3f]
307
0
    pub fn transform_to_xyz(self, white_point: impl XyYRepresentable) -> Matrix3f {
308
0
        let red_xyz = self.red.to_scaled_xyz();
309
0
        let green_xyz = self.green.to_scaled_xyz();
310
0
        let blue_xyz = self.blue.to_scaled_xyz();
311
312
0
        let xyz_matrix = Matrix3f {
313
0
            v: [
314
0
                [red_xyz.x, green_xyz.x, blue_xyz.x],
315
0
                [red_xyz.y, green_xyz.y, blue_xyz.y],
316
0
                [red_xyz.z, green_xyz.z, blue_xyz.z],
317
0
            ],
318
0
        };
319
0
        ColorProfile::rgb_to_xyz_static(xyz_matrix, white_point.to_xyy().to_xyz())
320
0
    }
Unexecuted instantiation: <moxcms::cicp::ColorPrimaries>::transform_to_xyz::<moxcms::chromaticity::Chromaticity>
Unexecuted instantiation: <moxcms::cicp::ColorPrimaries>::transform_to_xyz::<_>
321
}
322
323
/// See [Rec. ITU-T H.273 (12/2016)](https://www.itu.int/rec/T-REC-H.273-201612-I/en) Table 3
324
/// Values 0, 3, 19–255 are all reserved so all map to the same variant
325
#[derive(Clone, Copy, Debug, PartialEq)]
326
pub enum TransferCharacteristics {
327
    /// For future use by ITU-T | ISO/IEC
328
    Reserved,
329
    /// Rec. ITU-R BT.709-6<br />
330
    /// Rec. ITU-R BT.1361-0 conventional colour gamut system (historical)<br />
331
    /// (functionally the same as the values 6, 14 and 15)    <br />
332
    Bt709 = 1,
333
    /// Image characteristics are unknown or are determined by the application.<br />
334
    Unspecified = 2,
335
    /// Rec. ITU-R BT.470-6 System M (historical)<br />
336
    /// United States National Television System Committee 1953 Recommendation for transmission standards for color television<br />
337
    /// United States Federal Communications Commission (2003) Title 47 Code of Federal Regulations 73.682 (a) (20)<br />
338
    /// Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM<br />
339
    Bt470M = 4,
340
    /// Rec. ITU-R BT.470-6 System B, G (historical)<br />
341
    Bt470Bg = 5,
342
    /// Rec. ITU-R BT.601-7 525 or 625<br />
343
    /// Rec. ITU-R BT.1358-1 525 or 625 (historical)<br />
344
    /// Rec. ITU-R BT.1700-0 NTSC SMPTE 170M (2004)<br />
345
    /// (functionally the same as the values 1, 14 and 15)<br />
346
    Bt601 = 6,
347
    /// SMPTE 240M (1999) (historical)<br />
348
    Smpte240 = 7,
349
    /// Linear transfer characteristics<br />
350
    Linear = 8,
351
    /// Logarithmic transfer characteristic (100:1 range)<br />
352
    Log100 = 9,
353
    /// Logarithmic transfer characteristic (100 * Sqrt( 10 ) : 1 range)<br />
354
    Log100sqrt10 = 10,
355
    /// IEC 61966-2-4<br />
356
    Iec61966 = 11,
357
    /// Rec. ITU-R BT.1361-0 extended colour gamut system (historical)<br />
358
    Bt1361 = 12,
359
    /// IEC 61966-2-1 sRGB or sYCC<br />
360
    Srgb = 13,
361
    /// Rec. ITU-R BT.2020-2 (10-bit system)<br />
362
    /// (functionally the same as the values 1, 6 and 15)<br />
363
    Bt202010bit = 14,
364
    /// Rec. ITU-R BT.2020-2 (12-bit system)<br />
365
    /// (functionally the same as the values 1, 6 and 14)<br />
366
    Bt202012bit = 15,
367
    /// SMPTE ST 2084 for 10-, 12-, 14- and 16-bitsystems<br />
368
    /// Rec. ITU-R BT.2100-0 perceptual quantization (PQ) system<br />
369
    Smpte2084 = 16,
370
    /// SMPTE ST 428-1<br />
371
    Smpte428 = 17,
372
    /// ARIB STD-B67<br />
373
    /// Rec. ITU-R BT.2100-0 hybrid log- gamma (HLG) system<br />
374
    Hlg = 18,
375
}
376
377
impl TryFrom<u8> for TransferCharacteristics {
378
    type Error = CmsError;
379
380
    #[allow(unreachable_patterns)]
381
0
    fn try_from(value: u8) -> Result<Self, Self::Error> {
382
0
        match value {
383
0
            0 | 3 | 19..=255 => Ok(Self::Reserved),
384
0
            1 => Ok(Self::Bt709),
385
0
            2 => Ok(Self::Unspecified),
386
0
            4 => Ok(Self::Bt470M),
387
0
            5 => Ok(Self::Bt470Bg),
388
0
            6 => Ok(Self::Bt601),
389
0
            7 => Ok(Self::Smpte240), // unimplemented
390
0
            8 => Ok(Self::Linear),
391
0
            9 => Ok(Self::Log100),
392
0
            10 => Ok(Self::Log100sqrt10),
393
0
            11 => Ok(Self::Iec61966), // unimplemented
394
0
            12 => Ok(Self::Bt1361),   // unimplemented
395
0
            13 => Ok(Self::Srgb),
396
0
            14 => Ok(Self::Bt202010bit),
397
0
            15 => Ok(Self::Bt202012bit),
398
0
            16 => Ok(Self::Smpte2084),
399
0
            17 => Ok(Self::Smpte428), // unimplemented
400
0
            18 => Ok(Self::Hlg),
401
0
            _ => Err(CmsError::InvalidCicp),
402
        }
403
0
    }
404
}
405
406
impl CicpColorPrimaries {
407
0
    pub(crate) const fn has_chromaticity(self) -> bool {
408
0
        self as u8 != Self::Reserved as u8 && self as u8 != Self::Unspecified as u8
409
0
    }
410
411
0
    pub(crate) const fn white_point(self) -> Result<Chromaticity, CmsError> {
412
0
        Ok(match self {
413
0
            Self::Reserved => return Err(CmsError::UnsupportedColorPrimaries(self as u8)),
414
            Self::Bt709
415
            | Self::Bt470Bg
416
            | Self::Bt601
417
            | Self::Smpte240
418
            | Self::Bt2020
419
            | Self::Smpte432
420
0
            | Self::Ebu3213 => Chromaticity::D65,
421
0
            Self::Unspecified => return Err(CmsError::UnsupportedColorPrimaries(self as u8)),
422
0
            Self::Bt470M => Chromaticity { x: 0.310, y: 0.316 },
423
0
            Self::GenericFilm => Chromaticity { x: 0.310, y: 0.316 },
424
0
            Self::Xyz => Chromaticity {
425
0
                x: 1. / 3.,
426
0
                y: 1. / 3.,
427
0
            },
428
0
            Self::Smpte431 => Chromaticity { x: 0.314, y: 0.351 },
429
        })
430
0
    }
431
}
432
433
impl TryFrom<CicpColorPrimaries> for ColorPrimaries {
434
    type Error = CmsError;
435
436
0
    fn try_from(value: CicpColorPrimaries) -> Result<Self, Self::Error> {
437
0
        match value {
438
0
            CicpColorPrimaries::Reserved => Err(CmsError::UnsupportedColorPrimaries(value as u8)),
439
0
            CicpColorPrimaries::Bt709 => Ok(ColorPrimaries::BT_709),
440
            CicpColorPrimaries::Unspecified => {
441
0
                Err(CmsError::UnsupportedColorPrimaries(value as u8))
442
            }
443
0
            CicpColorPrimaries::Bt470M => Ok(ColorPrimaries::BT_470M),
444
0
            CicpColorPrimaries::Bt470Bg => Ok(ColorPrimaries::BT_470BG),
445
0
            CicpColorPrimaries::Bt601 | CicpColorPrimaries::Smpte240 => Ok(ColorPrimaries::BT_601),
446
0
            CicpColorPrimaries::GenericFilm => Ok(ColorPrimaries::GENERIC_FILM),
447
0
            CicpColorPrimaries::Bt2020 => Ok(ColorPrimaries::BT_2020),
448
0
            CicpColorPrimaries::Xyz => Ok(ColorPrimaries::XYZ),
449
            // These two share primaries, but have distinct white points
450
            CicpColorPrimaries::Smpte431 | CicpColorPrimaries::Smpte432 => {
451
0
                Ok(ColorPrimaries::SMPTE_431)
452
            }
453
0
            CicpColorPrimaries::Ebu3213 => Ok(ColorPrimaries::EBU_3213),
454
        }
455
0
    }
456
}
457
458
impl TransferCharacteristics {
459
0
    pub(crate) fn has_transfer_curve(self) -> bool {
460
0
        self != Self::Reserved && self != Self::Unspecified
461
0
    }
462
}
463
464
0
pub(crate) fn create_rec709_parametric() -> [f32; 5] {
465
    const POW_EXP: f32 = 0.45;
466
467
    const G: f32 = 1. / POW_EXP;
468
    const B: f32 = (0.09929682680944f64 / 1.09929682680944f64) as f32;
469
    const C: f32 = 1f32 / 4.5f32;
470
    const D: f32 = (4.5f64 * 0.018053968510807f64) as f32;
471
    const A: f32 = (1. / 1.09929682680944f64) as f32;
472
473
0
    [G, A, B, C, D]
474
0
}
475
476
impl TryFrom<TransferCharacteristics> for ToneReprCurve {
477
    type Error = CmsError;
478
    /// See [ICC.1:2010](https://www.color.org/specification/ICC1v43_2010-12.pdf)
479
    /// See [Rec. ITU-R BT.2100-2](https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-2-201807-I!!PDF-E.pdf)
480
0
    fn try_from(value: TransferCharacteristics) -> Result<Self, Self::Error> {
481
        const NUM_TRC_TABLE_ENTRIES: i32 = 1024;
482
483
0
        Ok(match value {
484
            TransferCharacteristics::Reserved => {
485
0
                return Err(CmsError::UnsupportedTrc(value as u8));
486
            }
487
            TransferCharacteristics::Bt709
488
            | TransferCharacteristics::Bt601
489
            | TransferCharacteristics::Bt202010bit
490
            | TransferCharacteristics::Bt202012bit => {
491
                // The opto-electronic transfer characteristic function (OETF)
492
                // as defined in ITU-T H.273 table 3, row 1:
493
                //
494
                // V = (α * Lc^0.45) − (α − 1)  for 1 >= Lc >= β
495
                // V = 4.500 * Lc               for β >  Lc >= 0
496
                //
497
                // Inverting gives the electro-optical transfer characteristic
498
                // function (EOTF) which can be represented as ICC
499
                // parametricCurveType with 4 parameters (ICC.1:2010 Table 5).
500
                // Converting between the two (Lc ↔︎ Y, V ↔︎ X):
501
                //
502
                // Y = (a * X + b)^g  for (X >= d)
503
                // Y = c * X          for (X < d)
504
                //
505
                // g, a, b, c, d can then be defined in terms of α and β:
506
                //
507
                // g = 1 / 0.45
508
                // a = 1 / α
509
                // b = 1 - α
510
                // c = 1 / 4.500
511
                // d = 4.500 * β
512
                //
513
                // α and β are determined by solving the piecewise equations to
514
                // ensure continuity of both value and slope at the value β.
515
                // We use the values specified for 10-bit systems in
516
                // https://www.itu.int/rec/R-REC-BT.2020-2-201510-I Table 4
517
                // since this results in the similar values as available ICC
518
                // profiles after converting to s15Fixed16Number, providing us
519
                // good test coverage.
520
521
0
                ToneReprCurve::Parametric(create_rec709_parametric().to_vec())
522
            }
523
            TransferCharacteristics::Unspecified => {
524
0
                return Err(CmsError::UnsupportedTrc(value as u8));
525
            }
526
0
            TransferCharacteristics::Bt470M => curve_from_gamma(2.2),
527
0
            TransferCharacteristics::Bt470Bg => curve_from_gamma(2.8),
528
            TransferCharacteristics::Smpte240 => {
529
0
                let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, smpte240_to_linear);
530
0
                ToneReprCurve::Lut(table)
531
            }
532
0
            TransferCharacteristics::Linear => curve_from_gamma(1.),
533
            TransferCharacteristics::Log100 => {
534
0
                let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, log100_to_linear);
535
0
                ToneReprCurve::Lut(table)
536
            }
537
            TransferCharacteristics::Log100sqrt10 => {
538
0
                let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, log100_sqrt10_to_linear);
539
0
                ToneReprCurve::Lut(table)
540
            }
541
            TransferCharacteristics::Iec61966 => {
542
0
                let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, iec61966_to_linear);
543
0
                ToneReprCurve::Lut(table)
544
            }
545
            TransferCharacteristics::Bt1361 => {
546
0
                let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, bt1361_to_linear);
547
0
                ToneReprCurve::Lut(table)
548
            }
549
            TransferCharacteristics::Srgb => {
550
0
                ToneReprCurve::Parametric(vec![2.4, 1. / 1.055, 0.055 / 1.055, 1. / 12.92, 0.04045])
551
            }
552
            TransferCharacteristics::Smpte2084 => {
553
0
                let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, pq_to_linear);
554
0
                ToneReprCurve::Lut(table)
555
            }
556
            TransferCharacteristics::Smpte428 => {
557
0
                let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, smpte428_to_linear);
558
0
                ToneReprCurve::Lut(table)
559
            }
560
            TransferCharacteristics::Hlg => {
561
0
                let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, hlg_to_linear);
562
0
                ToneReprCurve::Lut(table)
563
            }
564
        })
565
0
    }
566
}
567
568
/// Matrix Coefficients Enum (from ISO/IEC 23091-4 / MPEG CICP)
569
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
570
#[repr(C)]
571
pub enum MatrixCoefficients {
572
    Identity = 0,                // RGB (Identity matrix)
573
    Bt709 = 1,                   // Rec. 709
574
    Unspecified = 2,             // Unspecified
575
    Reserved = 3,                // Reserved
576
    Fcc = 4,                     // FCC
577
    Bt470Bg = 5,                 // BT.470BG / BT.601-625
578
    Smpte170m = 6,               // SMPTE 170M / BT.601-525
579
    Smpte240m = 7,               // SMPTE 240M
580
    YCgCo = 8,                   // YCgCo
581
    Bt2020Ncl = 9,               // BT.2020 (non-constant luminance)
582
    Bt2020Cl = 10,               // BT.2020 (constant luminance)
583
    Smpte2085 = 11,              // SMPTE ST 2085
584
    ChromaticityDerivedNCL = 12, // Chromaticity-derived non-constant luminance
585
    ChromaticityDerivedCL = 13,  // Chromaticity-derived constant luminance
586
    ICtCp = 14,                  // ICtCp
587
}
588
589
impl TryFrom<u8> for MatrixCoefficients {
590
    type Error = CmsError;
591
592
0
    fn try_from(value: u8) -> Result<Self, CmsError> {
593
0
        match value {
594
0
            0 => Ok(MatrixCoefficients::Identity),
595
0
            1 => Ok(MatrixCoefficients::Bt709),
596
0
            2 => Ok(MatrixCoefficients::Unspecified),
597
0
            3 => Ok(MatrixCoefficients::Reserved),
598
0
            4 => Ok(MatrixCoefficients::Fcc),
599
0
            5 => Ok(MatrixCoefficients::Bt470Bg),
600
0
            6 => Ok(MatrixCoefficients::Smpte170m),
601
0
            7 => Ok(MatrixCoefficients::Smpte240m),
602
0
            8 => Ok(MatrixCoefficients::YCgCo),
603
0
            9 => Ok(MatrixCoefficients::Bt2020Ncl),
604
0
            10 => Ok(MatrixCoefficients::Bt2020Cl),
605
0
            11 => Ok(MatrixCoefficients::Smpte2085),
606
0
            12 => Ok(MatrixCoefficients::ChromaticityDerivedNCL),
607
0
            13 => Ok(MatrixCoefficients::ChromaticityDerivedCL),
608
0
            14 => Ok(MatrixCoefficients::ICtCp),
609
0
            _ => Err(CmsError::InvalidCicp),
610
        }
611
0
    }
612
}
613
614
#[cfg(test)]
615
mod tests {
616
    use super::*;
617
    use crate::WHITE_POINT_D65;
618
619
    #[test]
620
    fn test_to_xyz_using_absolute_coordinates() {
621
        let conversion_matrix = ColorPrimaries::BT_709.transform_to_xyz_d(WHITE_POINT_D65);
622
        assert!((conversion_matrix.v[0][0] - 0.4121524015214193).abs() < 1e-14);
623
        assert!((conversion_matrix.v[1][1] - 0.7153537403945436).abs() < 1e-14);
624
        assert!((conversion_matrix.v[2][2] - 0.9497138466283235).abs() < 1e-14);
625
    }
626
627
    #[test]
628
    fn test_to_xyz_using_absolute_coordinates_xyz() {
629
        let conversion_matrix = ColorPrimaries::XYZ.transform_to_xyz_d(WHITE_POINT_D65);
630
        assert!((conversion_matrix.v[0][0] - 0.95015469385536477).abs() < 1e-14);
631
        assert!((conversion_matrix.v[1][1] - 1.0).abs() < 1e-14);
632
        assert!((conversion_matrix.v[2][2] - 1.0882590676722474).abs() < 1e-14);
633
    }
634
635
    #[test]
636
    fn test_to_xyz_using_absolute_coordinates_f() {
637
        let conversion_matrix = ColorPrimaries::BT_709.transform_to_xyz(WHITE_POINT_D65);
638
        assert!((conversion_matrix.v[0][0] - 0.4121524015214193).abs() < 1e-5);
639
        assert!((conversion_matrix.v[1][1] - 0.7153537403945436).abs() < 1e-5);
640
        assert!((conversion_matrix.v[2][2] - 0.9497138466283235).abs() < 1e-5);
641
    }
642
}