Skip to content

Commit

Permalink
Merge pull request #411 from Kirk-Fox/master
Browse files Browse the repository at this point in the history
Add Adobe RGB
  • Loading branch information
Ogeon authored Aug 11, 2024
2 parents 366046b + a06ce7a commit fab4412
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 1 deletion.
2 changes: 2 additions & 0 deletions palette/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
//! represented as type parameters in Palette, as a form of type branding, to
//! prevent accidental mixups.

pub use self::adobe::AdobeRgb;
pub use self::gamma::{F2p2, Gamma};
pub use self::linear::Linear;
pub use self::rec_standards::{Rec2020, Rec709};
pub use self::srgb::Srgb;

pub mod adobe;
pub mod gamma;
pub mod linear;
pub mod rec_standards;
Expand Down
157 changes: 157 additions & 0 deletions palette/src/encoding/adobe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
//! The Adobe RGB (1998) standard.

use crate::{
luma::LumaStandard,
num::{Powf, Real},
rgb::{Primaries, RgbSpace, RgbStandard},
white_point::{Any, D65},
Mat3, Yxy,
};

use super::{FromLinear, IntoLinear};

/// The Adobe RGB (1998) (a.k.a. opRGB) color space and standard.
///
/// This color space was designed to encompass most colors achievable by CMYK
/// printers using RGB primaries. It has a wider color gamut than sRGB, primarily
/// in cyan-green hues.
///
/// The Adobe RGB standard uses a gamma 2.2 transfer function.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct AdobeRgb;

impl<T: Real> Primaries<T> for AdobeRgb {
// Primary values from https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf with
// `luma` values taken from the conversion matrix in `RgbSpace` implementation.
fn red() -> Yxy<Any, T> {
Yxy::new(
T::from_f64(0.6400),
T::from_f64(0.3300),
T::from_f64(0.2974),
)
}
fn green() -> Yxy<Any, T> {
Yxy::new(
T::from_f64(0.2100),
T::from_f64(0.7100),
T::from_f64(0.6273),
)
}
fn blue() -> Yxy<Any, T> {
Yxy::new(
T::from_f64(0.1500),
T::from_f64(0.0600),
T::from_f64(0.0753),
)
}
}

impl RgbSpace for AdobeRgb {
type Primaries = AdobeRgb;
type WhitePoint = D65;

#[rustfmt::skip]
#[inline(always)]
fn rgb_to_xyz_matrix() -> Option<Mat3<f64>> {
// Matrix from http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
Some([
0.5767309, 0.1855540, 0.1881852,
0.2973769, 0.6273491, 0.0752741,
0.0270343, 0.0706872, 0.9911085,
])
}

#[rustfmt::skip]
#[inline(always)]
fn xyz_to_rgb_matrix() -> Option<Mat3<f64>> {
// Matrix from http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
Some([
2.0413690, -0.5649464, -0.3446944,
-0.9692660, 1.8760108, 0.0415560,
0.0134474, -0.1183897, 1.0154096,
])
}
}

impl RgbStandard for AdobeRgb {
type Space = AdobeRgb;
type TransferFn = AdobeRgb;
}

impl LumaStandard for AdobeRgb {
type WhitePoint = D65;
type TransferFn = AdobeRgb;
}

impl<T> IntoLinear<T, T> for AdobeRgb
where
T: Real + Powf,
{
fn into_linear(encoded: T) -> T {
encoded.powf(T::from_f64(563.0 / 256.0))
}
}

impl<T> FromLinear<T, T> for AdobeRgb
where
T: Real + Powf,
{
fn from_linear(linear: T) -> T {
linear.powf(T::from_f64(256.0 / 563.0))
}
}

#[cfg(test)]
mod test {
#[cfg(feature = "approx")]
mod conversion {
use crate::{
encoding::adobe::AdobeRgb,
matrix::{matrix_inverse, rgb_to_xyz_matrix},
rgb::RgbSpace,
};

#[test]
fn rgb_to_xyz() {
let dynamic = rgb_to_xyz_matrix::<AdobeRgb, f64>();
let constant = AdobeRgb::rgb_to_xyz_matrix().unwrap();
assert_relative_eq!(dynamic[..], constant[..], epsilon = 0.0000001);
}

#[test]
fn xyz_to_rgb() {
let dynamic = matrix_inverse(rgb_to_xyz_matrix::<AdobeRgb, f64>());
let constant = AdobeRgb::xyz_to_rgb_matrix().unwrap();
assert_relative_eq!(dynamic[..], constant[..], epsilon = 0.0000001);
}
}

#[cfg(feature = "approx")]
mod transfer {
use crate::encoding::{AdobeRgb, FromLinear, IntoLinear};

#[test]
fn lin_to_enc_to_lin() {
for i in 0..=100 {
let linear = i as f64 / 100.0;
let encoded: f64 = AdobeRgb::from_linear(linear);
assert_relative_eq!(linear, AdobeRgb::into_linear(encoded), epsilon = 0.0000001);
}
}

#[test]
fn enc_to_lin_to_enc() {
for i in 0..=100 {
let encoded = i as f64 / 100.0;
let linear: f64 = AdobeRgb::into_linear(encoded);
assert_relative_eq!(encoded, AdobeRgb::from_linear(linear), epsilon = 0.0000001);
}
}

#[test]
fn correct_values() {
assert_relative_eq!(AdobeRgb::from_linear(0.5), 0.72965838, epsilon = 0.0000001);
assert_relative_eq!(AdobeRgb::into_linear(0.5), 0.21775552, epsilon = 0.0000001);
}
}
}
5 changes: 4 additions & 1 deletion palette/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,10 @@ pub use oklab::{Oklab, Oklaba};
#[doc(inline)]
pub use oklch::{Oklch, Oklcha};
#[doc(inline)]
pub use rgb::{GammaSrgb, GammaSrgba, LinRec2020, LinSrgb, LinSrgba, Rec2020, Rec709, Srgb, Srgba};
pub use rgb::{
AdobeRgb, AdobeRgba, GammaSrgb, GammaSrgba, LinAdobeRgb, LinAdobeRgba, LinRec2020, LinSrgb,
LinSrgba, Rec2020, Rec709, Srgb, Srgba,
};
#[doc(inline)]
pub use xyz::{Xyz, Xyza};
#[doc(inline)]
Expand Down
36 changes: 36 additions & 0 deletions palette/src/rgb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,42 @@ pub type GammaSrgb<T = f32> = Rgb<Gamma<encoding::Srgb>, T>;
/// create a value and use it.
pub type GammaSrgba<T = f32> = Rgba<Gamma<encoding::Srgb>, T>;

/// Non-linear Adobe RGB.
///
/// This is a gamma 2.2 encoded RGB color space designed to include most colors
/// producable by CMYK printers.
///
/// See [`Rgb`] for more details on how to create a value and use it.
pub type AdobeRgb<T = f32> = Rgb<encoding::AdobeRgb, T>;

/// Non-linear Adobe RGB with an alpha component.
///
/// This is a transparent version of [`AdobeRgb`], which is commonly used as the
/// input or output format.
///
/// See [`Rgb`], [`Rgba`] and [`Alpha`](crate::Alpha) for more details on how to
/// create a value and use it.
pub type AdobeRgba<T = f32> = Rgba<encoding::AdobeRgb, T>;

/// Linear Adobe RGB.
///
/// You probably want [`AdobeRgb`] if you are looking for an input or output format.
/// This is the linear version of Adobe RGB, which is what you would usually convert
/// to before working with the color.
///
/// See [`Rgb`] for more details on how to create a value and use it.
pub type LinAdobeRgb<T = f32> = Rgb<Linear<encoding::AdobeRgb>, T>;

/// Linear Adobe RGB with an alpha component.
///
/// You probably want [`AdobeRgba`] if you are looking for an input or output format.
/// This is the linear version of Adobe RGBA, which is what you would usually convert
/// to before working with the color.
///
/// See [`Rgb`], [`Rgba`] and [`Alpha`](crate::Alpha) for more details on how to
/// create a value and use it.
pub type LinAdobeRgba<T = f32> = Rgba<Linear<encoding::AdobeRgb>, T>;

/// Rec. 709.
///
/// This standard has the same primaries as [`Srgb`], but uses the transfer
Expand Down

0 comments on commit fab4412

Please sign in to comment.