/*
* // 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);
}
}