moxcms/cicp.rs
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 */
29use 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};
33use crate::{
34 Chromaticity, ColorProfile, Matrix3d, Matrix3f, XyYRepresentable,
35 err::CmsError,
36 trc::{ToneReprCurve, build_trc_table, curve_from_gamma},
37};
38use 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)]
43pub 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
85impl TryFrom<u8> for CicpColorPrimaries {
86 type Error = CmsError;
87
88 #[allow(unreachable_patterns)]
89 fn try_from(value: u8) -> Result<Self, Self::Error> {
90 match value {
91 // Values 0, 3, 13–21, 23–255 are all reserved so all map to the
92 // same variant.
93 0 | 3 | 13..=21 | 23..=255 => Ok(Self::Reserved),
94 1 => Ok(Self::Bt709),
95 2 => Ok(Self::Unspecified),
96 4 => Ok(Self::Bt470M),
97 5 => Ok(Self::Bt470Bg),
98 6 => Ok(Self::Bt601),
99 7 => Ok(Self::Smpte240),
100 8 => Ok(Self::GenericFilm),
101 9 => Ok(Self::Bt2020),
102 10 => Ok(Self::Xyz),
103 11 => Ok(Self::Smpte431),
104 12 => Ok(Self::Smpte432),
105 22 => Ok(Self::Ebu3213),
106 _ => Err(CmsError::InvalidCicp),
107 }
108 }
109}
110
111#[derive(Clone, Copy, Debug)]
112#[repr(C)]
113pub 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.
120impl 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
277impl 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 pub fn transform_to_xyz_d(self, white_point: impl XyYRepresentable) -> Matrix3d {
286 let red_xyz = self.red.to_scaled_xyzd();
287 let green_xyz = self.green.to_scaled_xyzd();
288 let blue_xyz = self.blue.to_scaled_xyzd();
289
290 let xyz_matrix = Matrix3d {
291 v: [
292 [red_xyz.x, green_xyz.x, blue_xyz.x],
293 [red_xyz.y, green_xyz.y, blue_xyz.y],
294 [red_xyz.z, green_xyz.z, blue_xyz.z],
295 ],
296 };
297 ColorProfile::rgb_to_xyz_d(xyz_matrix, white_point.to_xyy().to_xyzd())
298 }
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 pub fn transform_to_xyz(self, white_point: impl XyYRepresentable) -> Matrix3f {
308 let red_xyz = self.red.to_scaled_xyz();
309 let green_xyz = self.green.to_scaled_xyz();
310 let blue_xyz = self.blue.to_scaled_xyz();
311
312 let xyz_matrix = Matrix3f {
313 v: [
314 [red_xyz.x, green_xyz.x, blue_xyz.x],
315 [red_xyz.y, green_xyz.y, blue_xyz.y],
316 [red_xyz.z, green_xyz.z, blue_xyz.z],
317 ],
318 };
319 ColorProfile::rgb_to_xyz_static(xyz_matrix, white_point.to_xyy().to_xyz())
320 }
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)]
326pub 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
377impl TryFrom<u8> for TransferCharacteristics {
378 type Error = CmsError;
379
380 #[allow(unreachable_patterns)]
381 fn try_from(value: u8) -> Result<Self, Self::Error> {
382 match value {
383 0 | 3 | 19..=255 => Ok(Self::Reserved),
384 1 => Ok(Self::Bt709),
385 2 => Ok(Self::Unspecified),
386 4 => Ok(Self::Bt470M),
387 5 => Ok(Self::Bt470Bg),
388 6 => Ok(Self::Bt601),
389 7 => Ok(Self::Smpte240), // unimplemented
390 8 => Ok(Self::Linear),
391 9 => Ok(Self::Log100),
392 10 => Ok(Self::Log100sqrt10),
393 11 => Ok(Self::Iec61966), // unimplemented
394 12 => Ok(Self::Bt1361), // unimplemented
395 13 => Ok(Self::Srgb),
396 14 => Ok(Self::Bt202010bit),
397 15 => Ok(Self::Bt202012bit),
398 16 => Ok(Self::Smpte2084),
399 17 => Ok(Self::Smpte428), // unimplemented
400 18 => Ok(Self::Hlg),
401 _ => Err(CmsError::InvalidCicp),
402 }
403 }
404}
405
406impl CicpColorPrimaries {
407 pub(crate) const fn has_chromaticity(self) -> bool {
408 self as u8 != Self::Reserved as u8 && self as u8 != Self::Unspecified as u8
409 }
410
411 pub(crate) const fn white_point(self) -> Result<Chromaticity, CmsError> {
412 Ok(match self {
413 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 | Self::Ebu3213 => Chromaticity::D65,
421 Self::Unspecified => return Err(CmsError::UnsupportedColorPrimaries(self as u8)),
422 Self::Bt470M => Chromaticity { x: 0.310, y: 0.316 },
423 Self::GenericFilm => Chromaticity { x: 0.310, y: 0.316 },
424 Self::Xyz => Chromaticity {
425 x: 1. / 3.,
426 y: 1. / 3.,
427 },
428 Self::Smpte431 => Chromaticity { x: 0.314, y: 0.351 },
429 })
430 }
431}
432
433impl TryFrom<CicpColorPrimaries> for ColorPrimaries {
434 type Error = CmsError;
435
436 fn try_from(value: CicpColorPrimaries) -> Result<Self, Self::Error> {
437 match value {
438 CicpColorPrimaries::Reserved => Err(CmsError::UnsupportedColorPrimaries(value as u8)),
439 CicpColorPrimaries::Bt709 => Ok(ColorPrimaries::BT_709),
440 CicpColorPrimaries::Unspecified => {
441 Err(CmsError::UnsupportedColorPrimaries(value as u8))
442 }
443 CicpColorPrimaries::Bt470M => Ok(ColorPrimaries::BT_470M),
444 CicpColorPrimaries::Bt470Bg => Ok(ColorPrimaries::BT_470BG),
445 CicpColorPrimaries::Bt601 | CicpColorPrimaries::Smpte240 => Ok(ColorPrimaries::BT_601),
446 CicpColorPrimaries::GenericFilm => Ok(ColorPrimaries::GENERIC_FILM),
447 CicpColorPrimaries::Bt2020 => Ok(ColorPrimaries::BT_2020),
448 CicpColorPrimaries::Xyz => Ok(ColorPrimaries::XYZ),
449 // These two share primaries, but have distinct white points
450 CicpColorPrimaries::Smpte431 | CicpColorPrimaries::Smpte432 => {
451 Ok(ColorPrimaries::SMPTE_431)
452 }
453 CicpColorPrimaries::Ebu3213 => Ok(ColorPrimaries::EBU_3213),
454 }
455 }
456}
457
458impl TransferCharacteristics {
459 pub(crate) fn has_transfer_curve(self) -> bool {
460 self != Self::Reserved && self != Self::Unspecified
461 }
462}
463
464pub(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 [G, A, B, C, D]
474}
475
476impl 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 fn try_from(value: TransferCharacteristics) -> Result<Self, Self::Error> {
481 const NUM_TRC_TABLE_ENTRIES: i32 = 1024;
482
483 Ok(match value {
484 TransferCharacteristics::Reserved => {
485 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 ToneReprCurve::Parametric(create_rec709_parametric().to_vec())
522 }
523 TransferCharacteristics::Unspecified => {
524 return Err(CmsError::UnsupportedTrc(value as u8));
525 }
526 TransferCharacteristics::Bt470M => curve_from_gamma(2.2),
527 TransferCharacteristics::Bt470Bg => curve_from_gamma(2.8),
528 TransferCharacteristics::Smpte240 => {
529 let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, smpte240_to_linear);
530 ToneReprCurve::Lut(table)
531 }
532 TransferCharacteristics::Linear => curve_from_gamma(1.),
533 TransferCharacteristics::Log100 => {
534 let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, log100_to_linear);
535 ToneReprCurve::Lut(table)
536 }
537 TransferCharacteristics::Log100sqrt10 => {
538 let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, log100_sqrt10_to_linear);
539 ToneReprCurve::Lut(table)
540 }
541 TransferCharacteristics::Iec61966 => {
542 let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, iec61966_to_linear);
543 ToneReprCurve::Lut(table)
544 }
545 TransferCharacteristics::Bt1361 => {
546 let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, bt1361_to_linear);
547 ToneReprCurve::Lut(table)
548 }
549 TransferCharacteristics::Srgb => {
550 ToneReprCurve::Parametric(vec![2.4, 1. / 1.055, 0.055 / 1.055, 1. / 12.92, 0.04045])
551 }
552 TransferCharacteristics::Smpte2084 => {
553 let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, pq_to_linear);
554 ToneReprCurve::Lut(table)
555 }
556 TransferCharacteristics::Smpte428 => {
557 let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, smpte428_to_linear);
558 ToneReprCurve::Lut(table)
559 }
560 TransferCharacteristics::Hlg => {
561 let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, hlg_to_linear);
562 ToneReprCurve::Lut(table)
563 }
564 })
565 }
566}
567
568/// Matrix Coefficients Enum (from ISO/IEC 23091-4 / MPEG CICP)
569#[derive(Debug, Copy, Clone, PartialEq, Eq)]
570#[repr(C)]
571pub 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
589impl TryFrom<u8> for MatrixCoefficients {
590 type Error = CmsError;
591
592 fn try_from(value: u8) -> Result<Self, CmsError> {
593 match value {
594 0 => Ok(MatrixCoefficients::Identity),
595 1 => Ok(MatrixCoefficients::Bt709),
596 2 => Ok(MatrixCoefficients::Unspecified),
597 3 => Ok(MatrixCoefficients::Reserved),
598 4 => Ok(MatrixCoefficients::Fcc),
599 5 => Ok(MatrixCoefficients::Bt470Bg),
600 6 => Ok(MatrixCoefficients::Smpte170m),
601 7 => Ok(MatrixCoefficients::Smpte240m),
602 8 => Ok(MatrixCoefficients::YCgCo),
603 9 => Ok(MatrixCoefficients::Bt2020Ncl),
604 10 => Ok(MatrixCoefficients::Bt2020Cl),
605 11 => Ok(MatrixCoefficients::Smpte2085),
606 12 => Ok(MatrixCoefficients::ChromaticityDerivedNCL),
607 13 => Ok(MatrixCoefficients::ChromaticityDerivedCL),
608 14 => Ok(MatrixCoefficients::ICtCp),
609 _ => Err(CmsError::InvalidCicp),
610 }
611 }
612}
613
614#[cfg(test)]
615mod 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}