Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Adobe RGB #411

Merged
merged 3 commits into from
Aug 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading