/* * // Copyright (c) Radzivon Bartoshyk 2/2025. All rights reserved. * // * // Redistribution and use in source and binary forms, with or without modification, * // are permitted provided that the following conditions are met: * // * // 1. Redistributions of source code must retain the above copyright notice, this * // list of conditions and the following disclaimer. * // * // 2. Redistributions in binary form must reproduce the above copyright notice, * // this list of conditions and the following disclaimer in the documentation * // and/or other materials provided with the distribution. * // * // 3. Neither the name of the copyright holder nor the names of its * // contributors may be used to endorse or promote products derived from * // this software without specific prior written permission. * // * // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ use crate::gamma::{ bt1361_to_linear, hlg_to_linear, iec61966_to_linear, log100_sqrt10_to_linear, log100_to_linear, pq_to_linear, smpte240_to_linear, smpte428_to_linear, }; use crate::{ Chromaticity, ColorProfile, Matrix3d, Matrix3f, XyYRepresentable, err::CmsError, trc::{ToneReprCurve, build_trc_table, curve_from_gamma}, }; use std::convert::TryFrom; /// See [Rec. ITU-T H.273 (12/2016)](https://www.itu.int/rec/T-REC-H.273-201612-I/en) Table 2 /// Values 0, 3, 13–21, 23–255 are all reserved so all map to the same variant #[derive(Clone, Copy, Debug, PartialEq)] pub enum CicpColorPrimaries { /// For future use by ITU-T | ISO/IEC Reserved, /// Rec. ITU-R BT.709-6
/// Rec. ITU-R BT.1361-0 conventional colour gamut system and extended colour gamut system (historical)
/// IEC 61966-2-1 sRGB or sYCC IEC 61966-2-4
/// Society of Motion Picture and Television Engineers (MPTE) RP 177 (1993) Annex B
Bt709 = 1, /// Unspecified
/// Image characteristics are unknown or are determined by the application. Unspecified = 2, /// Rec. ITU-R BT.470-6 System M (historical)
/// United States National Television System Committee 1953 Recommendation for transmission standards for color television
/// United States Federal Communications Commission (2003) Title 47 Code of Federal Regulations 73.682 (a) (20)
Bt470M = 4, /// Rec. ITU-R BT.470-6 System B, G (historical) Rec. ITU-R BT.601-7 625
/// Rec. ITU-R BT.1358-0 625 (historical)
/// Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM
Bt470Bg = 5, /// Rec. ITU-R BT.601-7 525
/// Rec. ITU-R BT.1358-1 525 or 625 (historical) Rec. ITU-R BT.1700-0 NTSC
/// SMPTE 170M (2004)
/// (functionally the same as the value 7)
Bt601 = 6, /// SMPTE 240M (1999) (historical) (functionally the same as the value 6)
Smpte240 = 7, /// Generic film (colour filters using Illuminant C)
GenericFilm = 8, /// Rec. ITU-R BT.2020-2
/// Rec. ITU-R BT.2100-0
Bt2020 = 9, /// SMPTE ST 428-1
/// (CIE 1931 XYZ as in ISO 11664-1)
Xyz = 10, /// SMPTE RP 431-2 (2011)
Smpte431 = 11, /// SMPTE EG 432-1 (2010)
Smpte432 = 12, /// EBU Tech. 3213-E (1975)
Ebu3213 = 22, } impl TryFrom for CicpColorPrimaries { type Error = CmsError; #[allow(unreachable_patterns)] fn try_from(value: u8) -> Result { match value { // Values 0, 3, 13–21, 23–255 are all reserved so all map to the // same variant. 0 | 3 | 13..=21 | 23..=255 => Ok(Self::Reserved), 1 => Ok(Self::Bt709), 2 => Ok(Self::Unspecified), 4 => Ok(Self::Bt470M), 5 => Ok(Self::Bt470Bg), 6 => Ok(Self::Bt601), 7 => Ok(Self::Smpte240), 8 => Ok(Self::GenericFilm), 9 => Ok(Self::Bt2020), 10 => Ok(Self::Xyz), 11 => Ok(Self::Smpte431), 12 => Ok(Self::Smpte432), 22 => Ok(Self::Ebu3213), _ => Err(CmsError::InvalidCicp), } } } #[derive(Clone, Copy, Debug)] #[repr(C)] pub struct ColorPrimaries { pub red: Chromaticity, pub green: Chromaticity, pub blue: Chromaticity, } /// See [Rec. ITU-T H.273 (12/2016)](https://www.itu.int/rec/T-REC-H.273-201612-I/en) Table 2. impl ColorPrimaries { /// [ACEScg](https://en.wikipedia.org/wiki/Academy_Color_Encoding_System#ACEScg). pub const ACES_CG: ColorPrimaries = ColorPrimaries { red: Chromaticity { x: 0.713, y: 0.293 }, green: Chromaticity { x: 0.165, y: 0.830 }, blue: Chromaticity { x: 0.128, y: 0.044 }, }; /// [ACES2065-1](https://en.wikipedia.org/wiki/Academy_Color_Encoding_System#ACES2065-1). pub const ACES_2065_1: ColorPrimaries = ColorPrimaries { red: Chromaticity { x: 0.7347, y: 0.2653, }, green: Chromaticity { x: 0.0000, y: 1.0000, }, blue: Chromaticity { x: 0.0001, y: -0.0770, }, }; /// [Adobe RGB](https://en.wikipedia.org/wiki/Adobe_RGB_color_space) (1998). pub const ADOBE_RGB: ColorPrimaries = ColorPrimaries { red: Chromaticity { x: 0.64, y: 0.33 }, green: Chromaticity { x: 0.21, y: 0.71 }, blue: Chromaticity { x: 0.15, y: 0.06 }, }; /// [DCI P3](https://en.wikipedia.org/wiki/DCI-P3#DCI_P3). /// /// This is the same as [`DISPLAY_P3`](Self::DISPLAY_P3), /// [`SMPTE_431`](Self::SMPTE_431) and [`SMPTE_432`](Self::SMPTE_432). pub const DCI_P3: ColorPrimaries = ColorPrimaries { red: Chromaticity { x: 0.680, y: 0.320 }, green: Chromaticity { x: 0.265, y: 0.690 }, blue: Chromaticity { x: 0.150, y: 0.060 }, }; /// [Diplay P3](https://en.wikipedia.org/wiki/DCI-P3#Display_P3). /// /// This is the same as [`DCI_P3`](Self::DCI_P3), /// [`SMPTE_431`](Self::SMPTE_431) and [`SMPTE_432`](Self::SMPTE_432). pub const DISPLAY_P3: ColorPrimaries = Self::DCI_P3; /// SMPTE RP 431-2 (2011). /// /// This is the same as [`DCI_P3`](Self::DCI_P3), /// [`DISPLAY_P3`](Self::DISPLAY_P3) and [`SMPTE_432`](Self::SMPTE_432). pub const SMPTE_431: ColorPrimaries = Self::DCI_P3; /// SMPTE EG 432-1 (2010). /// /// This is the same as [`DCI_P3`](Self::DCI_P3), /// [`DISPLAY_P3`](Self::DISPLAY_P3) and [`SMPTE_431`](Self::SMPTE_431). pub const SMPTE_432: ColorPrimaries = Self::DCI_P3; /// [ProPhoto RGB](https://en.wikipedia.org/wiki/ProPhoto_RGB_color_space). pub const PRO_PHOTO_RGB: ColorPrimaries = ColorPrimaries { red: Chromaticity { x: 0.734699, y: 0.265301, }, green: Chromaticity { x: 0.159597, y: 0.840403, }, blue: Chromaticity { x: 0.036598, y: 0.000105, }, }; /// Rec. ITU-R BT.709-6 /// /// Rec. ITU-R BT.1361-0 conventional colour gamut system and extended /// colour gamut system (historical). /// /// IEC 61966-2-1 sRGB or sYCC IEC 61966-2-4). /// /// Society of Motion Picture and Television Engineers (MPTE) RP 177 (1993) Annex B. pub const BT_709: ColorPrimaries = ColorPrimaries { red: Chromaticity { x: 0.64, y: 0.33 }, green: Chromaticity { x: 0.30, y: 0.60 }, blue: Chromaticity { x: 0.15, y: 0.06 }, }; /// Rec. ITU-R BT.470-6 System M (historical). /// /// United States National Television System Committee 1953 Recommendation /// for transmission standards for color television. /// /// United States Federal Communications Commission (2003) Title 47 Code of /// Federal Regulations 73.682 (a) (20). pub const BT_470M: ColorPrimaries = ColorPrimaries { red: Chromaticity { x: 0.67, y: 0.33 }, green: Chromaticity { x: 0.21, y: 0.71 }, blue: Chromaticity { x: 0.14, y: 0.08 }, }; /// Rec. ITU-R BT.470-6 System B, G (historical) Rec. ITU-R BT.601-7 625. /// /// Rec. ITU-R BT.1358-0 625 (historical). /// Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM. pub const BT_470BG: ColorPrimaries = ColorPrimaries { red: Chromaticity { x: 0.64, y: 0.33 }, green: Chromaticity { x: 0.29, y: 0.60 }, blue: Chromaticity { x: 0.15, y: 0.06 }, }; /// Rec. ITU-R BT.601-7 525. /// /// Rec. ITU-R BT.1358-1 525 or 625 (historical) Rec. ITU-R BT.1700-0 NTSC. /// /// SMPTE 170M (2004) (functionally the same as the [`SMPTE_240`](Self::SMPTE_240)). pub const BT_601: ColorPrimaries = ColorPrimaries { red: Chromaticity { x: 0.630, y: 0.340 }, green: Chromaticity { x: 0.310, y: 0.595 }, blue: Chromaticity { x: 0.155, y: 0.070 }, }; /// SMPTE 240M (1999) (historical) (functionally the same as [`BT_601`](Self::BT_601)). pub const SMPTE_240: ColorPrimaries = Self::BT_601; /// Generic film (colour filters using Illuminant C). pub const GENERIC_FILM: ColorPrimaries = ColorPrimaries { red: Chromaticity { x: 0.681, y: 0.319 }, green: Chromaticity { x: 0.243, y: 0.692 }, blue: Chromaticity { x: 0.145, y: 0.049 }, }; /// Rec. ITU-R BT.2020-2. /// /// Rec. ITU-R BT.2100-0. pub const BT_2020: ColorPrimaries = ColorPrimaries { red: Chromaticity { x: 0.708, y: 0.292 }, green: Chromaticity { x: 0.170, y: 0.797 }, blue: Chromaticity { x: 0.131, y: 0.046 }, }; /// SMPTE ST 428-1 (CIE 1931 XYZ as in ISO 11664-1). pub const XYZ: ColorPrimaries = ColorPrimaries { red: Chromaticity { x: 1.0, y: 0.0 }, green: Chromaticity { x: 0.0, y: 1.0 }, blue: Chromaticity { x: 0.0, y: 0.0 }, }; /// EBU Tech. 3213-E (1975). pub const EBU_3213: ColorPrimaries = ColorPrimaries { red: Chromaticity { x: 0.630, y: 0.340 }, green: Chromaticity { x: 0.295, y: 0.605 }, blue: Chromaticity { x: 0.155, y: 0.077 }, }; } impl ColorPrimaries { /// Returns RGB -> XYZ conversion matrix /// /// # Arguments /// /// * `white_point`: [Chromaticity] or [crate::XyY] or any item conforming [XyYRepresentable] /// /// returns: [Matrix3d] pub fn transform_to_xyz_d(self, white_point: impl XyYRepresentable) -> Matrix3d { let red_xyz = self.red.to_scaled_xyzd(); let green_xyz = self.green.to_scaled_xyzd(); let blue_xyz = self.blue.to_scaled_xyzd(); let xyz_matrix = Matrix3d { v: [ [red_xyz.x, green_xyz.x, blue_xyz.x], [red_xyz.y, green_xyz.y, blue_xyz.y], [red_xyz.z, green_xyz.z, blue_xyz.z], ], }; ColorProfile::rgb_to_xyz_d(xyz_matrix, white_point.to_xyy().to_xyzd()) } /// Returns RGB -> XYZ conversion matrix /// /// # Arguments /// /// * `white_point`: [Chromaticity] or [crate::XyY] or any item conforming [XyYRepresentable] /// /// returns: [Matrix3f] pub fn transform_to_xyz(self, white_point: impl XyYRepresentable) -> Matrix3f { let red_xyz = self.red.to_scaled_xyz(); let green_xyz = self.green.to_scaled_xyz(); let blue_xyz = self.blue.to_scaled_xyz(); let xyz_matrix = Matrix3f { v: [ [red_xyz.x, green_xyz.x, blue_xyz.x], [red_xyz.y, green_xyz.y, blue_xyz.y], [red_xyz.z, green_xyz.z, blue_xyz.z], ], }; ColorProfile::rgb_to_xyz_static(xyz_matrix, white_point.to_xyy().to_xyz()) } } /// See [Rec. ITU-T H.273 (12/2016)](https://www.itu.int/rec/T-REC-H.273-201612-I/en) Table 3 /// Values 0, 3, 19–255 are all reserved so all map to the same variant #[derive(Clone, Copy, Debug, PartialEq)] pub enum TransferCharacteristics { /// For future use by ITU-T | ISO/IEC Reserved, /// Rec. ITU-R BT.709-6
/// Rec. ITU-R BT.1361-0 conventional colour gamut system (historical)
/// (functionally the same as the values 6, 14 and 15)
Bt709 = 1, /// Image characteristics are unknown or are determined by the application.
Unspecified = 2, /// Rec. ITU-R BT.470-6 System M (historical)
/// United States National Television System Committee 1953 Recommendation for transmission standards for color television
/// United States Federal Communications Commission (2003) Title 47 Code of Federal Regulations 73.682 (a) (20)
/// Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM
Bt470M = 4, /// Rec. ITU-R BT.470-6 System B, G (historical)
Bt470Bg = 5, /// Rec. ITU-R BT.601-7 525 or 625
/// Rec. ITU-R BT.1358-1 525 or 625 (historical)
/// Rec. ITU-R BT.1700-0 NTSC SMPTE 170M (2004)
/// (functionally the same as the values 1, 14 and 15)
Bt601 = 6, /// SMPTE 240M (1999) (historical)
Smpte240 = 7, /// Linear transfer characteristics
Linear = 8, /// Logarithmic transfer characteristic (100:1 range)
Log100 = 9, /// Logarithmic transfer characteristic (100 * Sqrt( 10 ) : 1 range)
Log100sqrt10 = 10, /// IEC 61966-2-4
Iec61966 = 11, /// Rec. ITU-R BT.1361-0 extended colour gamut system (historical)
Bt1361 = 12, /// IEC 61966-2-1 sRGB or sYCC
Srgb = 13, /// Rec. ITU-R BT.2020-2 (10-bit system)
/// (functionally the same as the values 1, 6 and 15)
Bt202010bit = 14, /// Rec. ITU-R BT.2020-2 (12-bit system)
/// (functionally the same as the values 1, 6 and 14)
Bt202012bit = 15, /// SMPTE ST 2084 for 10-, 12-, 14- and 16-bitsystems
/// Rec. ITU-R BT.2100-0 perceptual quantization (PQ) system
Smpte2084 = 16, /// SMPTE ST 428-1
Smpte428 = 17, /// ARIB STD-B67
/// Rec. ITU-R BT.2100-0 hybrid log- gamma (HLG) system
Hlg = 18, } impl TryFrom for TransferCharacteristics { type Error = CmsError; #[allow(unreachable_patterns)] fn try_from(value: u8) -> Result { match value { 0 | 3 | 19..=255 => Ok(Self::Reserved), 1 => Ok(Self::Bt709), 2 => Ok(Self::Unspecified), 4 => Ok(Self::Bt470M), 5 => Ok(Self::Bt470Bg), 6 => Ok(Self::Bt601), 7 => Ok(Self::Smpte240), // unimplemented 8 => Ok(Self::Linear), 9 => Ok(Self::Log100), 10 => Ok(Self::Log100sqrt10), 11 => Ok(Self::Iec61966), // unimplemented 12 => Ok(Self::Bt1361), // unimplemented 13 => Ok(Self::Srgb), 14 => Ok(Self::Bt202010bit), 15 => Ok(Self::Bt202012bit), 16 => Ok(Self::Smpte2084), 17 => Ok(Self::Smpte428), // unimplemented 18 => Ok(Self::Hlg), _ => Err(CmsError::InvalidCicp), } } } impl CicpColorPrimaries { pub(crate) const fn has_chromaticity(self) -> bool { self as u8 != Self::Reserved as u8 && self as u8 != Self::Unspecified as u8 } pub(crate) const fn white_point(self) -> Result { Ok(match self { Self::Reserved => return Err(CmsError::UnsupportedColorPrimaries(self as u8)), Self::Bt709 | Self::Bt470Bg | Self::Bt601 | Self::Smpte240 | Self::Bt2020 | Self::Smpte432 | Self::Ebu3213 => Chromaticity::D65, Self::Unspecified => return Err(CmsError::UnsupportedColorPrimaries(self as u8)), Self::Bt470M => Chromaticity { x: 0.310, y: 0.316 }, Self::GenericFilm => Chromaticity { x: 0.310, y: 0.316 }, Self::Xyz => Chromaticity { x: 1. / 3., y: 1. / 3., }, Self::Smpte431 => Chromaticity { x: 0.314, y: 0.351 }, }) } } impl TryFrom for ColorPrimaries { type Error = CmsError; fn try_from(value: CicpColorPrimaries) -> Result { match value { CicpColorPrimaries::Reserved => Err(CmsError::UnsupportedColorPrimaries(value as u8)), CicpColorPrimaries::Bt709 => Ok(ColorPrimaries::BT_709), CicpColorPrimaries::Unspecified => { Err(CmsError::UnsupportedColorPrimaries(value as u8)) } CicpColorPrimaries::Bt470M => Ok(ColorPrimaries::BT_470M), CicpColorPrimaries::Bt470Bg => Ok(ColorPrimaries::BT_470BG), CicpColorPrimaries::Bt601 | CicpColorPrimaries::Smpte240 => Ok(ColorPrimaries::BT_601), CicpColorPrimaries::GenericFilm => Ok(ColorPrimaries::GENERIC_FILM), CicpColorPrimaries::Bt2020 => Ok(ColorPrimaries::BT_2020), CicpColorPrimaries::Xyz => Ok(ColorPrimaries::XYZ), // These two share primaries, but have distinct white points CicpColorPrimaries::Smpte431 | CicpColorPrimaries::Smpte432 => { Ok(ColorPrimaries::SMPTE_431) } CicpColorPrimaries::Ebu3213 => Ok(ColorPrimaries::EBU_3213), } } } impl TransferCharacteristics { pub(crate) fn has_transfer_curve(self) -> bool { self != Self::Reserved && self != Self::Unspecified } } pub(crate) fn create_rec709_parametric() -> [f32; 5] { const POW_EXP: f32 = 0.45; const G: f32 = 1. / POW_EXP; const B: f32 = (0.09929682680944f64 / 1.09929682680944f64) as f32; const C: f32 = 1f32 / 4.5f32; const D: f32 = (4.5f64 * 0.018053968510807f64) as f32; const A: f32 = (1. / 1.09929682680944f64) as f32; [G, A, B, C, D] } impl TryFrom for ToneReprCurve { type Error = CmsError; /// See [ICC.1:2010](https://www.color.org/specification/ICC1v43_2010-12.pdf) /// 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) fn try_from(value: TransferCharacteristics) -> Result { const NUM_TRC_TABLE_ENTRIES: i32 = 1024; Ok(match value { TransferCharacteristics::Reserved => { return Err(CmsError::UnsupportedTrc(value as u8)); } TransferCharacteristics::Bt709 | TransferCharacteristics::Bt601 | TransferCharacteristics::Bt202010bit | TransferCharacteristics::Bt202012bit => { // The opto-electronic transfer characteristic function (OETF) // as defined in ITU-T H.273 table 3, row 1: // // V = (α * Lc^0.45) − (α − 1) for 1 >= Lc >= β // V = 4.500 * Lc for β > Lc >= 0 // // Inverting gives the electro-optical transfer characteristic // function (EOTF) which can be represented as ICC // parametricCurveType with 4 parameters (ICC.1:2010 Table 5). // Converting between the two (Lc ↔︎ Y, V ↔︎ X): // // Y = (a * X + b)^g for (X >= d) // Y = c * X for (X < d) // // g, a, b, c, d can then be defined in terms of α and β: // // g = 1 / 0.45 // a = 1 / α // b = 1 - α // c = 1 / 4.500 // d = 4.500 * β // // α and β are determined by solving the piecewise equations to // ensure continuity of both value and slope at the value β. // We use the values specified for 10-bit systems in // https://www.itu.int/rec/R-REC-BT.2020-2-201510-I Table 4 // since this results in the similar values as available ICC // profiles after converting to s15Fixed16Number, providing us // good test coverage. ToneReprCurve::Parametric(create_rec709_parametric().to_vec()) } TransferCharacteristics::Unspecified => { return Err(CmsError::UnsupportedTrc(value as u8)); } TransferCharacteristics::Bt470M => curve_from_gamma(2.2), TransferCharacteristics::Bt470Bg => curve_from_gamma(2.8), TransferCharacteristics::Smpte240 => { let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, smpte240_to_linear); ToneReprCurve::Lut(table) } TransferCharacteristics::Linear => curve_from_gamma(1.), TransferCharacteristics::Log100 => { let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, log100_to_linear); ToneReprCurve::Lut(table) } TransferCharacteristics::Log100sqrt10 => { let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, log100_sqrt10_to_linear); ToneReprCurve::Lut(table) } TransferCharacteristics::Iec61966 => { let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, iec61966_to_linear); ToneReprCurve::Lut(table) } TransferCharacteristics::Bt1361 => { let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, bt1361_to_linear); ToneReprCurve::Lut(table) } TransferCharacteristics::Srgb => { ToneReprCurve::Parametric(vec![2.4, 1. / 1.055, 0.055 / 1.055, 1. / 12.92, 0.04045]) } TransferCharacteristics::Smpte2084 => { let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, pq_to_linear); ToneReprCurve::Lut(table) } TransferCharacteristics::Smpte428 => { let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, smpte428_to_linear); ToneReprCurve::Lut(table) } TransferCharacteristics::Hlg => { let table = build_trc_table(NUM_TRC_TABLE_ENTRIES, hlg_to_linear); ToneReprCurve::Lut(table) } }) } } /// Matrix Coefficients Enum (from ISO/IEC 23091-4 / MPEG CICP) #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[repr(C)] pub enum MatrixCoefficients { Identity = 0, // RGB (Identity matrix) Bt709 = 1, // Rec. 709 Unspecified = 2, // Unspecified Reserved = 3, // Reserved Fcc = 4, // FCC Bt470Bg = 5, // BT.470BG / BT.601-625 Smpte170m = 6, // SMPTE 170M / BT.601-525 Smpte240m = 7, // SMPTE 240M YCgCo = 8, // YCgCo Bt2020Ncl = 9, // BT.2020 (non-constant luminance) Bt2020Cl = 10, // BT.2020 (constant luminance) Smpte2085 = 11, // SMPTE ST 2085 ChromaticityDerivedNCL = 12, // Chromaticity-derived non-constant luminance ChromaticityDerivedCL = 13, // Chromaticity-derived constant luminance ICtCp = 14, // ICtCp } impl TryFrom for MatrixCoefficients { type Error = CmsError; fn try_from(value: u8) -> Result { match value { 0 => Ok(MatrixCoefficients::Identity), 1 => Ok(MatrixCoefficients::Bt709), 2 => Ok(MatrixCoefficients::Unspecified), 3 => Ok(MatrixCoefficients::Reserved), 4 => Ok(MatrixCoefficients::Fcc), 5 => Ok(MatrixCoefficients::Bt470Bg), 6 => Ok(MatrixCoefficients::Smpte170m), 7 => Ok(MatrixCoefficients::Smpte240m), 8 => Ok(MatrixCoefficients::YCgCo), 9 => Ok(MatrixCoefficients::Bt2020Ncl), 10 => Ok(MatrixCoefficients::Bt2020Cl), 11 => Ok(MatrixCoefficients::Smpte2085), 12 => Ok(MatrixCoefficients::ChromaticityDerivedNCL), 13 => Ok(MatrixCoefficients::ChromaticityDerivedCL), 14 => Ok(MatrixCoefficients::ICtCp), _ => Err(CmsError::InvalidCicp), } } } #[cfg(test)] mod tests { use super::*; use crate::WHITE_POINT_D65; #[test] fn test_to_xyz_using_absolute_coordinates() { let conversion_matrix = ColorPrimaries::BT_709.transform_to_xyz_d(WHITE_POINT_D65); assert!((conversion_matrix.v[0][0] - 0.4121524015214193).abs() < 1e-14); assert!((conversion_matrix.v[1][1] - 0.7153537403945436).abs() < 1e-14); assert!((conversion_matrix.v[2][2] - 0.9497138466283235).abs() < 1e-14); } #[test] fn test_to_xyz_using_absolute_coordinates_xyz() { let conversion_matrix = ColorPrimaries::XYZ.transform_to_xyz_d(WHITE_POINT_D65); assert!((conversion_matrix.v[0][0] - 0.95015469385536477).abs() < 1e-14); assert!((conversion_matrix.v[1][1] - 1.0).abs() < 1e-14); assert!((conversion_matrix.v[2][2] - 1.0882590676722474).abs() < 1e-14); } #[test] fn test_to_xyz_using_absolute_coordinates_f() { let conversion_matrix = ColorPrimaries::BT_709.transform_to_xyz(WHITE_POINT_D65); assert!((conversion_matrix.v[0][0] - 0.4121524015214193).abs() < 1e-5); assert!((conversion_matrix.v[1][1] - 0.7153537403945436).abs() < 1e-5); assert!((conversion_matrix.v[2][2] - 0.9497138466283235).abs() < 1e-5); } }