Coverage Report

Created: 2026-01-22 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/moxcms-0.7.11/src/chromaticity.rs
Line
Count
Source
1
/*
2
 * // Copyright (c) Radzivon Bartoshyk 8/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::{CmsError, XyY, XyYRepresentable, Xyz, Xyzd};
30
31
#[derive(Clone, Debug, Copy)]
32
#[repr(C)]
33
pub struct Chromaticity {
34
    pub x: f32,
35
    pub y: f32,
36
}
37
38
impl Chromaticity {
39
    #[inline]
40
0
    pub const fn new(x: f32, y: f32) -> Self {
41
0
        Self { x, y }
42
0
    }
Unexecuted instantiation: <moxcms::chromaticity::Chromaticity>::new
Unexecuted instantiation: <moxcms::chromaticity::Chromaticity>::new
43
44
    /// Converts this chromaticity (`x`, `y`) to a tristimulus [`Xyz`] value,
45
    /// normalized such that `y = 1.0`.
46
    #[inline]
47
0
    pub const fn to_xyz(&self) -> Xyz {
48
0
        let reciprocal = if self.y != 0. { 1. / self.y } else { 0. };
49
0
        Xyz {
50
0
            x: self.x * reciprocal,
51
0
            y: 1f32,
52
0
            z: (1f32 - self.x - self.y) * reciprocal,
53
0
        }
54
0
    }
55
56
    /// Get the color representation with component sum `1`.
57
    ///
58
    /// In contrast to the XYZ representation defined through setting `Y` to a known
59
    /// value (such as `1` in [`Self::to_xyz`]) this representation can be uniquely
60
    /// derived from the `xy` coordinates with no ambiguities. It is scaled from the
61
    /// original XYZ color by diving by `X + Y + Z`. Note that, in particular, this
62
    /// method is well-defined even if the original color had pure chromamatic
63
    /// information with no luminance (Y = `0`) and will preserve that information,
64
    /// whereas [`Self::to_xyz`] is ill-defined and returns an incorrect value.
65
    #[inline]
66
0
    pub const fn to_scaled_xyzd(&self) -> Xyzd {
67
0
        let z = 1.0 - self.x as f64 - self.y as f64;
68
0
        Xyzd::new(self.x as f64, self.y as f64, z)
69
0
    }
70
71
    /// Get the color representation with component sum `1`.
72
    ///
73
    /// In contrast to the XYZ representation defined through setting `Y` to a known
74
    /// value (such as `1` in [`Self::to_xyz`]) this representation can be uniquely
75
    /// derived from the `xy` coordinates with no ambiguities. It is scaled from the
76
    /// original XYZ color by diving by `X + Y + Z`. Note that, in particular, this
77
    /// method is well-defined even if the original color had pure chromamatic
78
    /// information with no luminance (Y = `0`) and will preserve that information,
79
    /// whereas [`Self::to_xyz`] is ill-defined and returns an incorrect value.
80
    #[inline]
81
0
    pub const fn to_scaled_xyz(&self) -> Xyz {
82
0
        let z = 1.0 - self.x - self.y;
83
0
        Xyz::new(self.x, self.y, z)
84
0
    }
Unexecuted instantiation: <moxcms::chromaticity::Chromaticity>::to_scaled_xyz
Unexecuted instantiation: <moxcms::chromaticity::Chromaticity>::to_scaled_xyz
85
86
    #[inline]
87
0
    pub const fn to_xyzd(&self) -> Xyzd {
88
0
        let reciprocal = if self.y != 0. { 1. / self.y } else { 0. };
89
0
        Xyzd {
90
0
            x: self.x as f64 * reciprocal as f64,
91
0
            y: 1f64,
92
0
            z: (1f64 - self.x as f64 - self.y as f64) * reciprocal as f64,
93
0
        }
94
0
    }
95
96
    #[inline]
97
0
    pub const fn to_xyyb(&self) -> XyY {
98
0
        XyY {
99
0
            x: self.x as f64,
100
0
            y: self.y as f64,
101
0
            yb: 1.,
102
0
        }
103
0
    }
104
105
    pub const D65: Chromaticity = Chromaticity {
106
        x: 0.31272,
107
        y: 0.32903,
108
    };
109
110
    pub const D50: Chromaticity = Chromaticity {
111
        x: 0.34567,
112
        y: 0.35850,
113
    };
114
}
115
116
impl XyYRepresentable for Chromaticity {
117
0
    fn to_xyy(self) -> XyY {
118
0
        self.to_xyyb()
119
0
    }
120
}
121
122
impl TryFrom<Xyz> for Chromaticity {
123
    type Error = CmsError;
124
125
    #[inline]
126
0
    fn try_from(xyz: Xyz) -> Result<Self, Self::Error> {
127
0
        let sum = xyz.x + xyz.y + xyz.z;
128
129
        // Avoid division by zero or invalid XYZ values
130
0
        if sum == 0.0 {
131
0
            return Err(CmsError::DivisionByZero);
132
0
        }
133
0
        let rec = 1f32 / sum;
134
135
0
        let chromaticity_x = xyz.x * rec;
136
0
        let chromaticity_y = xyz.y * rec;
137
138
0
        Ok(Chromaticity {
139
0
            x: chromaticity_x,
140
0
            y: chromaticity_y,
141
0
        })
142
0
    }
143
}