Skip to content

Commit c808f52

Browse files
author
BuildTools
committed
Add ProPhoto RGB standard
1 parent 068eccc commit c808f52

File tree

3 files changed

+188
-0
lines changed

3 files changed

+188
-0
lines changed

palette/src/encoding.rs

+2
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ pub use self::adobe::AdobeRgb;
99
pub use self::gamma::{F2p2, Gamma};
1010
pub use self::linear::Linear;
1111
pub use self::p3::{DciP3, DciP3Plus, DisplayP3};
12+
pub use self::prophoto::ProPhotoRgb;
1213
pub use self::rec_standards::{Rec2020, Rec709};
1314
pub use self::srgb::Srgb;
1415

1516
pub mod adobe;
1617
pub mod gamma;
1718
pub mod linear;
1819
pub mod p3;
20+
pub mod prophoto;
1921
pub mod rec_standards;
2022
pub mod srgb;
2123

palette/src/encoding/prophoto.rs

+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
//! The ProPhoto RGB standard.
2+
3+
use crate::{
4+
bool_mask::LazySelect,
5+
encoding::{FromLinear, IntoLinear},
6+
luma::LumaStandard,
7+
num::{Arithmetics, PartialCmp, Powf, Real},
8+
rgb::{Primaries, RgbSpace, RgbStandard},
9+
white_point::{Any, D50},
10+
Mat3, Yxy,
11+
};
12+
13+
/// The ProPhoto RGB standard and color space with gamma 2.2 transfer function.
14+
///
15+
/// About 13% of the colors in this space are "[impossible colors](https://en.wikipedia.org/wiki/Impossible_color)"
16+
/// meaning that they model cone responses that are, in practice, impossible to
17+
/// achieve.
18+
///
19+
/// # As transfer function
20+
///
21+
/// `ProPhotoRgb` will not use any kind of approximation when converting from `T` to
22+
/// `T`. This involves a call to `powf`, which may make it too slow for certain
23+
/// applications.
24+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
25+
pub struct ProPhotoRgb;
26+
27+
impl<T: Real> Primaries<T> for ProPhotoRgb {
28+
fn red() -> Yxy<Any, T> {
29+
Yxy::new(
30+
T::from_f64(0.7347),
31+
T::from_f64(0.2653),
32+
T::from_f64(0.28804),
33+
)
34+
}
35+
fn green() -> Yxy<Any, T> {
36+
Yxy::new(
37+
T::from_f64(0.1596),
38+
T::from_f64(0.8404),
39+
T::from_f64(0.71187),
40+
)
41+
}
42+
fn blue() -> Yxy<Any, T> {
43+
Yxy::new(
44+
T::from_f64(0.0366),
45+
T::from_f64(0.0001),
46+
T::from_f64(0.000085663),
47+
)
48+
}
49+
}
50+
51+
impl RgbSpace for ProPhotoRgb {
52+
type Primaries = ProPhotoRgb;
53+
type WhitePoint = D50;
54+
55+
#[rustfmt::skip]
56+
#[inline(always)]
57+
fn rgb_to_xyz_matrix() -> Option<Mat3<f64>> {
58+
// Matrix from http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
59+
Some([
60+
0.7976749, 0.1351917, 0.0313534,
61+
0.2880402, 0.7118741, 0.0000857,
62+
0.0000000, 0.0000000, 0.8252100,
63+
])
64+
}
65+
66+
#[rustfmt::skip]
67+
#[inline(always)]
68+
fn xyz_to_rgb_matrix() -> Option<Mat3<f64>> {
69+
// Matrix from http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
70+
Some([
71+
1.3459433, -0.2556075, -0.0511118,
72+
-0.5445989, 1.5081673, 0.0205351,
73+
0.0000000, 0.0000000, 1.2118128,
74+
])
75+
}
76+
}
77+
78+
impl RgbStandard for ProPhotoRgb {
79+
type Space = ProPhotoRgb;
80+
type TransferFn = ProPhotoRgb;
81+
}
82+
83+
impl LumaStandard for ProPhotoRgb {
84+
type WhitePoint = D50;
85+
type TransferFn = ProPhotoRgb;
86+
}
87+
88+
impl<T> IntoLinear<T, T> for ProPhotoRgb
89+
where
90+
T: Real + Powf + Arithmetics + PartialCmp + Clone,
91+
T::Mask: LazySelect<T>,
92+
{
93+
fn into_linear(encoded: T) -> T {
94+
lazy_select! {
95+
if encoded.lt(&T::from_f64(0.03125)) => T::from_f64(1.0 / 16.0) * &encoded,
96+
else => encoded.clone().powf(T::from_f64(1.8)),
97+
}
98+
}
99+
}
100+
101+
impl<T> FromLinear<T, T> for ProPhotoRgb
102+
where
103+
T: Real + Powf + Arithmetics + PartialCmp + Clone,
104+
T::Mask: LazySelect<T>,
105+
{
106+
fn from_linear(linear: T) -> T {
107+
lazy_select! {
108+
if linear.lt(&T::from_f64(0.001953125)) => T::from_f64(16.0) * &linear,
109+
else => linear.clone().powf(T::from_f64(1.0 / 1.8)),
110+
}
111+
}
112+
}
113+
114+
#[cfg(test)]
115+
mod test {
116+
#[cfg(feature = "approx")]
117+
mod conversion {
118+
use crate::{
119+
convert::IntoColorUnclamped,
120+
encoding::prophoto::ProPhotoRgb,
121+
matrix::{matrix_inverse, rgb_to_xyz_matrix},
122+
rgb::{Primaries, RgbSpace},
123+
white_point::{Any, WhitePoint, D50},
124+
Xyz,
125+
};
126+
127+
#[test]
128+
fn rgb_to_xyz() {
129+
let dynamic = rgb_to_xyz_matrix::<ProPhotoRgb, f64>();
130+
let constant = ProPhotoRgb::rgb_to_xyz_matrix().unwrap();
131+
assert_relative_eq!(dynamic[..], constant[..], epsilon = 0.0000001);
132+
}
133+
134+
#[test]
135+
fn xyz_to_rgb() {
136+
let dynamic = matrix_inverse(rgb_to_xyz_matrix::<ProPhotoRgb, f64>());
137+
let constant = ProPhotoRgb::xyz_to_rgb_matrix().unwrap();
138+
assert_relative_eq!(dynamic[..], constant[..], epsilon = 0.0000001);
139+
}
140+
141+
#[test]
142+
fn primaries_prophoto() {
143+
let red: Xyz<Any, f64> = ProPhotoRgb::red().into_color_unclamped();
144+
let green: Xyz<Any, f64> = ProPhotoRgb::green().into_color_unclamped();
145+
let blue: Xyz<Any, f64> = ProPhotoRgb::blue().into_color_unclamped();
146+
// Compare sum of primaries to white point.
147+
assert_relative_eq!(red + green + blue, D50::get_xyz(), epsilon = 0.0001);
148+
}
149+
}
150+
}

palette/src/rgb.rs

+36
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,42 @@ pub type LinAdobeRgb<T = f32> = Rgb<Linear<encoding::AdobeRgb>, T>;
222222
/// create a value and use it.
223223
pub type LinAdobeRgba<T = f32> = Rgba<Linear<encoding::AdobeRgb>, T>;
224224

225+
/// Non-linear ProPhoto RGB, a wide color gamut RGB format.
226+
///
227+
/// This is an RGB standard with a color gamut designed to include 100% of likely
228+
/// occurring real-world colors.
229+
///
230+
/// See [`Rgb`] for more details on how to create a value and use it.
231+
pub type ProPhotoRgb<T = f32> = Rgb<encoding::ProPhotoRgb, T>;
232+
233+
/// Non-linear ProPhoto RGB with an alpha component.
234+
///
235+
/// This is a transparent version of [`ProPhotoRgb`], which is commonly used as the
236+
/// input or output format.
237+
///
238+
/// See [`Rgb`], [`Rgba`] and [`Alpha`](crate::Alpha) for more details on how to
239+
/// create a value and use it.
240+
pub type ProPhotoRgba<T = f32> = Rgba<encoding::ProPhotoRgb, T>;
241+
242+
/// Linear ProPhoto RGB.
243+
///
244+
/// You probably want [`ProPhotoRgb`] if you are looking for an input or output format.
245+
/// This is the linear version of ProPhoto RGB, which is what you would usually convert
246+
/// to before working with the color.
247+
///
248+
/// See [`Rgb`] for more details on how to create a value and use it.
249+
pub type LinProPhotoRgb<T = f32> = Rgb<Linear<encoding::ProPhotoRgb>, T>;
250+
251+
/// Linear ProPhoto RGB with an alpha component.
252+
///
253+
/// You probably want [`ProPhotoRgba`] if you are looking for an input or output format.
254+
/// This is the linear version of ProPhoto RGBA, which is what you would usually convert
255+
/// to before working with the color.
256+
///
257+
/// See [`Rgb`], [`Rgba`] and [`Alpha`](crate::Alpha) for more details on how to
258+
/// create a value and use it.
259+
pub type LinProPhotoRgba<T = f32> = Rgba<Linear<encoding::ProPhotoRgb>, T>;
260+
225261
/// Rec. 709.
226262
///
227263
/// This standard has the same primaries as [`Srgb`], but uses the transfer

0 commit comments

Comments
 (0)