/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 | | } |