chromashift/
display_p3.rs1use crate::{LinearRgb, ToAlpha, XyzD65, round_dp};
2use core::fmt;
3
4#[derive(Debug, Clone, Copy, PartialEq)]
11pub struct DisplayP3 {
12 pub red: f64,
13 pub green: f64,
14 pub blue: f64,
15 pub alpha: f32,
16}
17
18impl DisplayP3 {
19 pub fn new(red: f64, green: f64, blue: f64, alpha: f32) -> Self {
20 Self { red, green, blue, alpha: alpha.clamp(0.0, 100.0) }
21 }
22}
23
24impl ToAlpha for DisplayP3 {
25 fn to_alpha(&self) -> f32 {
26 self.alpha
27 }
28}
29
30impl fmt::Display for DisplayP3 {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 let Self { red, green, blue, alpha } = self;
33 write!(f, "color(display-p3 {} {} {}", round_dp(*red, 2), round_dp(*green, 2), round_dp(*blue, 2))?;
34 if *alpha < 100.0 {
35 write!(f, " / {}", round_dp(*alpha as f64, 2))?;
36 }
37 write!(f, ")")
38 }
39}
40
41fn gamma(u: f64) -> f64 {
43 let abs = u.abs();
44 if abs <= 0.0031308 { u * 12.92 } else { u.signum() * (1.055 * abs.powf(1.0 / 2.4) - 0.055) }
45}
46
47fn linear(c: f64) -> f64 {
49 let abs = c.abs();
50 if abs > 0.04045 { c.signum() * ((abs + 0.055) / 1.055).powf(2.4) } else { c / 12.92 }
51}
52
53impl From<XyzD65> for DisplayP3 {
54 fn from(value: XyzD65) -> Self {
55 let XyzD65 { x, y, z, alpha } = value;
56 let x = x / 100.0;
57 let y = y / 100.0;
58 let z = z / 100.0;
59 let lr = x * (446124.0 / 178915.0) + y * (-333277.0 / 357830.0) + z * (-72051.0 / 178915.0);
61 let lg = x * (-14852.0 / 17905.0) + y * (63121.0 / 35810.0) + z * (423.0 / 17905.0);
62 let lb = x * (11844.0 / 330415.0) + y * (-50337.0 / 660830.0) + z * (316169.0 / 330415.0);
63 DisplayP3::new(gamma(lr), gamma(lg), gamma(lb), alpha)
65 }
66}
67
68impl From<DisplayP3> for XyzD65 {
69 fn from(value: DisplayP3) -> Self {
70 let DisplayP3 { red, green, blue, alpha } = value;
71 let lr = linear(red);
73 let lg = linear(green);
74 let lb = linear(blue);
75 let x = lr * (608311.0 / 1250200.0) + lg * (189793.0 / 714400.0) + lb * (198249.0 / 1000160.0);
77 let y = lr * (35783.0 / 156275.0) + lg * (247089.0 / 357200.0) + lb * (198249.0 / 2500400.0);
78 let z = lg * (32229.0 / 714400.0) + lb * (5220557.0 / 5000800.0);
79 XyzD65::new(x * 100.0, y * 100.0, z * 100.0, alpha)
80 }
81}
82
83impl From<DisplayP3> for LinearRgb {
84 fn from(value: DisplayP3) -> Self {
85 let DisplayP3 { red, green, blue, alpha } = value;
86 let lr = linear(red);
88 let lg = linear(green);
89 let lb = linear(blue);
90 let red = lr * (3685649.0 / 3008840.0) + lg * (-676809.0 / 3008840.0);
92 let green = lr * (-5617931.0 / 133579120.0) + lg * (139197051.0 / 133579120.0);
93 let blue = lr * (-1323971.0 / 67420360.0) + lg * (-1514763.0 / 19262960.0) + lb * (148092003.0 / 134840720.0);
94 LinearRgb::new(red, green, blue, alpha)
95 }
96}
97
98impl From<LinearRgb> for DisplayP3 {
99 fn from(value: LinearRgb) -> Self {
100 let LinearRgb { red, green, blue, alpha } = value;
101 let lr = red * (2442703.0 / 2969989.0) + green * (527286.0 / 2969989.0);
103 let lg = red * (621563.0 / 18725049.0) + green * (18103486.0 / 18725049.0);
104 let lb =
105 red * (281089.0 / 16454667.0) + green * (10721482.0 / 148092003.0) + blue * (134840720.0 / 148092003.0);
106 DisplayP3::new(gamma(lr), gamma(lg), gamma(lb), alpha)
108 }
109}