use core::marker::PhantomData;
use crate::adaptation::Bradford;
use crate::illuminant::Illuminant;
use crate::math::{DefaultMath, Mat3};
use crate::primaries::{
AcesAp0Primaries, AcesAp1Primaries, DciP3Primaries, P3Primaries, Primaries, PrimariesToXyz, ProPhotoPrimaries,
Rec2020Primaries, SrgbPrimaries,
};
use crate::transfer::{
AcesCcTf, AcesCctTf, DciP3Tf, HlgTf, IsDisplayReferred, IsLinearEncoding, IsSceneReferred, LinearTf, PqTf,
ProPhotoTf, Rec709Tf, SrgbTf, TransferFunction,
};
use crate::xyz::Xyz;
use crate::{
Asserts, Color, ColorSpace, DisplayReferred, LinearLight, OutOfGamut, SceneReferred, Transform, TryTransform,
};
pub trait ChannelMap<const N: usize>: 'static {
const INDICES: [usize; N];
const ALPHA: Option<usize>;
}
macro_rules! define_layout {
($name:ident, [$r:expr, $g:expr, $b:expr, $a:expr]) => {
#[doc = concat!("Four-channel layout. R=`", stringify!($r), "` G=`", stringify!($g), "` B=`", stringify!($b), "` A=`", stringify!($a), "`.")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct $name;
impl Asserts<[f32; 4]> for $name {}
#[cfg(feature = "glam")]
impl Asserts<glam::Vec4> for $name {}
impl ChannelMap<4> for $name {
const INDICES: [usize; 4] = [$r, $g, $b, $a];
const ALPHA: Option<usize> = Some(3);
}
};
($name:ident, [$r:expr, $g:expr, $b:expr]) => {
#[doc = concat!("Three-channel layout. R=`", stringify!($r), "` G=`", stringify!($g), "` B=`", stringify!($b), "`. No alpha.")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct $name;
impl Asserts<[f32; 3]> for $name {}
#[cfg(feature = "glam")]
impl Asserts<glam::Vec3> for $name {}
#[cfg(feature = "glam")]
impl Asserts<glam::Vec3A> for $name {}
impl ChannelMap<3> for $name {
const INDICES: [usize; 3] = [$r, $g, $b];
const ALPHA: Option<usize> = None;
}
};
($name:ident, [$a:expr, $b:expr]) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct $name;
impl Asserts<[f32; 2]> for $name {}
#[cfg(feature = "glam")]
impl Asserts<glam::Vec2> for $name {}
impl ChannelMap<2> for $name {
const INDICES: [usize; 2] = [$a, $b];
const ALPHA: Option<usize> = None;
}
};
($name:ident, [$a:expr], alpha) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct $name;
impl Asserts<[f32; 1]> for $name {}
impl Asserts<f32> for $name {}
impl ChannelMap<1> for $name {
const INDICES: [usize; 1] = [$a];
const ALPHA: Option<usize> = Some(0);
}
};
($name:ident, [$a:expr]) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct $name;
impl Asserts<[f32; 1]> for $name {}
impl Asserts<f32> for $name {}
impl ChannelMap<1> for $name {
const INDICES: [usize; 1] = [$a];
const ALPHA: Option<usize> = None;
}
};
}
define_layout!(Rgba, [0, 1, 2, 3]);
define_layout!(Bgra, [2, 1, 0, 3]);
define_layout!(Argb, [1, 2, 3, 0]);
define_layout!(Abgr, [3, 2, 1, 0]);
define_layout!(Rgb, [0, 1, 2]);
define_layout!(Bgr, [2, 1, 0]);
define_layout!(Rg, [0, 1]);
define_layout!(Ra, [0, 1]);
define_layout!(R, [0]);
define_layout!(G, [0]);
define_layout!(B, [0]);
define_layout!(A, [0], alpha);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct RgbSpace<P: Primaries, TF: TransferFunction, L = Rgba>(PhantomData<(P, TF, L)>);
pub trait RgbColorSpace: ColorSpace {}
impl<P, TF, L> RgbColorSpace for RgbSpace<P, TF, L>
where
P: Primaries,
TF: TransferFunction,
L: 'static,
{
}
impl<P, TF, L> ColorSpace for RgbSpace<P, TF, L>
where
P: Primaries,
TF: TransferFunction,
L: 'static,
{
const CHANNELS: usize = 3;
const LUMINANCE_WEIGHTS: Option<[f32; 3]> = Some(P::TO_XYZ_NATIVE.luminance_weights());
}
impl<P, TF, L, S> Asserts<S> for RgbSpace<P, TF, L>
where
P: Primaries,
TF: TransferFunction,
L: Asserts<S> + 'static,
S: 'static,
{
}
impl<P, TF, L> LinearLight for RgbSpace<P, TF, L>
where
P: Primaries,
TF: TransferFunction + IsLinearEncoding,
L: 'static,
{
}
impl<P, TF, L> SceneReferred for RgbSpace<P, TF, L>
where
P: Primaries,
TF: TransferFunction + IsSceneReferred,
L: 'static,
{
}
impl<P, TF, L> DisplayReferred for RgbSpace<P, TF, L>
where
P: Primaries,
TF: TransferFunction + IsDisplayReferred,
L: 'static,
{
}
const fn direct_matrix<P1: Primaries, P2: Primaries>() -> Mat3 {
use crate::adaptation::adapt;
let a = adapt::<Bradford>(P1::Native::WHITE_POINT_XYZ, P2::Native::WHITE_POINT_XYZ);
let adapted = Mat3::mul(&a, &P1::TO_XYZ_NATIVE);
Mat3::mul(&P2::FROM_XYZ_NATIVE, &adapted)
}
impl<P, TF, L1, L2> Transform<Color<[f32; 4], RgbSpace<P, TF, L1>>> for Color<[f32; 4], RgbSpace<P, TF, L2>>
where
P: Primaries,
TF: TransferFunction,
L1: Asserts<[f32; 4]> + ChannelMap<4> + 'static,
L2: Asserts<[f32; 4]> + ChannelMap<4> + 'static,
{
fn transform_from(src: Color<[f32; 4], RgbSpace<P, TF, L1>>, _: &()) -> Self {
let s = src.inner();
let [r1, g1, b1, a1] = L1::INDICES;
let [r2, g2, b2, a2] = L2::INDICES;
let mut out = [0.0f32; 4];
out[r2] = s[r1];
out[g2] = s[g1];
out[b2] = s[b1];
out[a2] = s[a1];
Color::new_unchecked(out)
}
}
impl<P, TF, L1, L2> Transform<Color<[f32; 3], RgbSpace<P, TF, L1>>> for Color<[f32; 3], RgbSpace<P, TF, L2>>
where
P: Primaries,
TF: TransferFunction,
L1: Asserts<[f32; 3]> + ChannelMap<3> + 'static,
L2: Asserts<[f32; 3]> + ChannelMap<3> + 'static,
{
fn transform_from(src: Color<[f32; 3], RgbSpace<P, TF, L1>>, _: &()) -> Self {
let s = src.inner();
let [r1, g1, b1] = L1::INDICES;
let [r2, g2, b2] = L2::INDICES;
let mut out = [0.0f32; 3];
out[r2] = s[r1];
out[g2] = s[g1];
out[b2] = s[b1];
Color::new_unchecked(out)
}
}
impl<P, TF, W, L> Transform<Color<[f32; 4], RgbSpace<P, TF, L>>> for Color<[f32; 4], Xyz<W>>
where
P: Primaries + PrimariesToXyz<W, Bradford>,
TF: TransferFunction,
W: Illuminant,
L: Asserts<[f32; 4]> + ChannelMap<4> + 'static,
Xyz<W>: Asserts<[f32; 4]>,
{
fn transform_from(src: Color<[f32; 4], RgbSpace<P, TF, L>>, _: &()) -> Self {
let s = src.inner();
let [ri, gi, bi, ai] = L::INDICES;
let rgb = TF::decode::<DefaultMath>([s[ri], s[gi], s[bi]]);
let xyz = <P as PrimariesToXyz<W, Bradford>>::TO_XYZ.apply(rgb);
Color::new_unchecked([xyz[0], xyz[1], xyz[2], s[ai]])
}
}
impl<P, TF, W, L> TryTransform<Color<[f32; 4], Xyz<W>>> for Color<[f32; 4], RgbSpace<P, TF, L>>
where
P: Primaries + PrimariesToXyz<W, Bradford>,
TF: TransferFunction,
W: Illuminant,
L: Asserts<[f32; 4]> + ChannelMap<4> + 'static,
Xyz<W>: Asserts<[f32; 4]>,
{
fn try_transform_from(src: Color<[f32; 4], Xyz<W>>, _: &()) -> Result<Self, OutOfGamut<Self>> {
let x = src.inner();
let rgb = <P as PrimariesToXyz<W, Bradford>>::FROM_XYZ.apply([x[0], x[1], x[2]]);
let enc = TF::encode::<DefaultMath>(rgb);
let [ri, gi, bi, ai] = L::INDICES;
let mut s = [0.0f32; 4];
s[ri] = enc[0];
s[gi] = enc[1];
s[bi] = enc[2];
s[ai] = x[3];
let min = TF::ENCODED_MIN;
let max = TF::ENCODED_MAX;
let clipped = enc[0] < min[0]
|| enc[0] > max[0]
|| enc[1] < min[1]
|| enc[1] > max[1]
|| enc[2] < min[2]
|| enc[2] > max[2]
|| enc[0].is_nan()
|| enc[1].is_nan()
|| enc[2].is_nan();
if clipped {
let c = |v: f32, lo: f32| if v.is_nan() { lo } else { v };
s[ri] = c(enc[0], min[0]).clamp(min[0], max[0]);
s[gi] = c(enc[1], min[1]).clamp(min[1], max[1]);
s[bi] = c(enc[2], min[2]).clamp(min[2], max[2]);
Err(OutOfGamut {
clamped: Color::new_unchecked(s),
})
} else {
Ok(Color::new_unchecked(s))
}
}
}
impl<P, TF, W, L> Transform<Color<[f32; 3], RgbSpace<P, TF, L>>> for Color<[f32; 3], Xyz<W>>
where
P: Primaries + PrimariesToXyz<W, Bradford>,
TF: TransferFunction,
W: Illuminant,
L: Asserts<[f32; 3]> + ChannelMap<3> + 'static,
Xyz<W>: Asserts<[f32; 3]>,
{
fn transform_from(src: Color<[f32; 3], RgbSpace<P, TF, L>>, _: &()) -> Self {
let s = src.inner();
let [ri, gi, bi] = L::INDICES;
let rgb = TF::decode::<DefaultMath>([s[ri], s[gi], s[bi]]);
let xyz = <P as PrimariesToXyz<W, Bradford>>::TO_XYZ.apply(rgb);
Color::new_unchecked([xyz[0], xyz[1], xyz[2]])
}
}
impl<P, TF, W, L> TryTransform<Color<[f32; 3], Xyz<W>>> for Color<[f32; 3], RgbSpace<P, TF, L>>
where
P: Primaries + PrimariesToXyz<W, Bradford>,
TF: TransferFunction,
W: Illuminant,
L: Asserts<[f32; 3]> + ChannelMap<3> + 'static,
Xyz<W>: Asserts<[f32; 3]>,
{
fn try_transform_from(src: Color<[f32; 3], Xyz<W>>, _: &()) -> Result<Self, OutOfGamut<Self>> {
let x = src.inner();
let rgb = <P as PrimariesToXyz<W, Bradford>>::FROM_XYZ.apply([x[0], x[1], x[2]]);
let enc = TF::encode::<DefaultMath>(rgb);
let [ri, gi, bi] = L::INDICES;
let mut s = [0.0f32; 3];
s[ri] = enc[0];
s[gi] = enc[1];
s[bi] = enc[2];
let min = TF::ENCODED_MIN;
let max = TF::ENCODED_MAX;
let clipped = enc[0] < min[0]
|| enc[0] > max[0]
|| enc[1] < min[1]
|| enc[1] > max[1]
|| enc[2] < min[2]
|| enc[2] > max[2]
|| enc[0].is_nan()
|| enc[1].is_nan()
|| enc[2].is_nan();
if clipped {
let c = |v: f32, lo: f32| if v.is_nan() { lo } else { v };
s[ri] = c(enc[0], min[0]).clamp(min[0], max[0]);
s[gi] = c(enc[1], min[1]).clamp(min[1], max[1]);
s[bi] = c(enc[2], min[2]).clamp(min[2], max[2]);
Err(OutOfGamut {
clamped: Color::new_unchecked(s),
})
} else {
Ok(Color::new_unchecked(s))
}
}
}
#[cfg(feature = "glam")]
impl<P, TF, W, L> Transform<Color<glam::Vec4, RgbSpace<P, TF, L>>> for Color<glam::Vec4, Xyz<W>>
where
P: Primaries + PrimariesToXyz<W, Bradford>,
TF: TransferFunction,
W: Illuminant,
L: Asserts<glam::Vec4> + ChannelMap<4> + 'static,
Xyz<W>: Asserts<glam::Vec4>,
{
fn transform_from(src: Color<glam::Vec4, RgbSpace<P, TF, L>>, _: &()) -> Self {
let s = src.inner().to_array();
let [ri, gi, bi, ai] = L::INDICES;
let rgb = TF::decode::<DefaultMath>([s[ri], s[gi], s[bi]]);
let xyz = <P as PrimariesToXyz<W, Bradford>>::TO_XYZ.apply(rgb);
Color::new_unchecked(glam::Vec4::new(xyz[0], xyz[1], xyz[2], s[ai]))
}
}
#[cfg(feature = "glam")]
impl<P, TF, W, L> TryTransform<Color<glam::Vec4, Xyz<W>>> for Color<glam::Vec4, RgbSpace<P, TF, L>>
where
P: Primaries + PrimariesToXyz<W, Bradford>,
TF: TransferFunction,
W: Illuminant,
L: Asserts<glam::Vec4> + ChannelMap<4> + 'static,
Xyz<W>: Asserts<glam::Vec4>,
{
fn try_transform_from(src: Color<glam::Vec4, Xyz<W>>, _: &()) -> Result<Self, OutOfGamut<Self>> {
let x = src.inner().to_array();
let rgb = <P as PrimariesToXyz<W, Bradford>>::FROM_XYZ.apply([x[0], x[1], x[2]]);
let enc = TF::encode::<DefaultMath>(rgb);
let [ri, gi, bi, ai] = L::INDICES;
let mut s = [0.0f32; 4];
s[ri] = enc[0];
s[gi] = enc[1];
s[bi] = enc[2];
s[ai] = x[3];
let min = TF::ENCODED_MIN;
let max = TF::ENCODED_MAX;
let clipped = enc[0] < min[0]
|| enc[0] > max[0]
|| enc[1] < min[1]
|| enc[1] > max[1]
|| enc[2] < min[2]
|| enc[2] > max[2]
|| enc[0].is_nan()
|| enc[1].is_nan()
|| enc[2].is_nan();
if clipped {
let c = |v: f32, lo: f32| if v.is_nan() { lo } else { v };
s[ri] = c(enc[0], min[0]).clamp(min[0], max[0]);
s[gi] = c(enc[1], min[1]).clamp(min[1], max[1]);
s[bi] = c(enc[2], min[2]).clamp(min[2], max[2]);
Err(OutOfGamut {
clamped: Color::new_unchecked(glam::Vec4::from_array(s)),
})
} else {
Ok(Color::new_unchecked(glam::Vec4::from_array(s)))
}
}
}
#[cfg(feature = "glam")]
impl<P, TF, W, L> Transform<Color<glam::Vec3A, RgbSpace<P, TF, L>>> for Color<glam::Vec3A, Xyz<W>>
where
P: Primaries + PrimariesToXyz<W, Bradford>,
TF: TransferFunction,
W: Illuminant,
L: Asserts<glam::Vec3A> + ChannelMap<3> + 'static,
Xyz<W>: Asserts<glam::Vec3A>,
{
fn transform_from(src: Color<glam::Vec3A, RgbSpace<P, TF, L>>, _: &()) -> Self {
let s = src.inner().to_array();
let [ri, gi, bi] = L::INDICES;
let rgb = TF::decode::<DefaultMath>([s[ri], s[gi], s[bi]]);
let xyz = <P as PrimariesToXyz<W, Bradford>>::TO_XYZ.apply(rgb);
Color::new_unchecked(glam::Vec3A::new(xyz[0], xyz[1], xyz[2]))
}
}
#[cfg(feature = "glam")]
impl<P, TF, W, L> TryTransform<Color<glam::Vec3A, Xyz<W>>> for Color<glam::Vec3A, RgbSpace<P, TF, L>>
where
P: Primaries + PrimariesToXyz<W, Bradford>,
TF: TransferFunction,
W: Illuminant,
L: Asserts<glam::Vec3A> + ChannelMap<3> + 'static,
Xyz<W>: Asserts<glam::Vec3A>,
{
fn try_transform_from(src: Color<glam::Vec3A, Xyz<W>>, _: &()) -> Result<Self, OutOfGamut<Self>> {
let x = src.inner().to_array();
let rgb = <P as PrimariesToXyz<W, Bradford>>::FROM_XYZ.apply([x[0], x[1], x[2]]);
let enc = TF::encode::<DefaultMath>(rgb);
let [ri, gi, bi] = L::INDICES;
let mut s = [0.0f32; 3];
s[ri] = enc[0];
s[gi] = enc[1];
s[bi] = enc[2];
let min = TF::ENCODED_MIN;
let max = TF::ENCODED_MAX;
let clipped = enc[0] < min[0]
|| enc[0] > max[0]
|| enc[1] < min[1]
|| enc[1] > max[1]
|| enc[2] < min[2]
|| enc[2] > max[2]
|| enc[0].is_nan()
|| enc[1].is_nan()
|| enc[2].is_nan();
if clipped {
let c = |v: f32, lo: f32| if v.is_nan() { lo } else { v };
s[ri] = c(enc[0], min[0]).clamp(min[0], max[0]);
s[gi] = c(enc[1], min[1]).clamp(min[1], max[1]);
s[bi] = c(enc[2], min[2]).clamp(min[2], max[2]);
Err(OutOfGamut {
clamped: Color::new_unchecked(glam::Vec3A::from_array(s)),
})
} else {
Ok(Color::new_unchecked(glam::Vec3A::from_array(s)))
}
}
}
impl<L> Transform<Color<[f32; 4], Srgb<L>>> for Color<[f32; 4], DisplayP3<L>>
where
L: Asserts<[f32; 4]> + ChannelMap<4> + 'static,
{
fn transform_from(src: Color<[f32; 4], Srgb<L>>, _: &()) -> Self {
let s = src.inner();
let [ri, gi, bi, _ai] = L::INDICES;
let lin = SrgbTf::decode::<DefaultMath>([s[ri], s[gi], s[bi]]);
let out = const { direct_matrix::<SrgbPrimaries, P3Primaries>() }.apply(lin);
let enc = SrgbTf::encode::<DefaultMath>(out);
let mut r = s;
r[ri] = enc[0];
r[gi] = enc[1];
r[bi] = enc[2];
Color::new_unchecked(r)
}
}
impl<L> Transform<Color<[f32; 3], Srgb<L>>> for Color<[f32; 3], DisplayP3<L>>
where
L: Asserts<[f32; 3]> + ChannelMap<3> + 'static,
{
fn transform_from(src: Color<[f32; 3], Srgb<L>>, _: &()) -> Self {
let s = src.inner();
let [ri, gi, bi] = L::INDICES;
let lin = SrgbTf::decode::<DefaultMath>([s[ri], s[gi], s[bi]]);
let out = const { direct_matrix::<SrgbPrimaries, P3Primaries>() }.apply(lin);
let enc = SrgbTf::encode::<DefaultMath>(out);
let mut r = s;
r[ri] = enc[0];
r[gi] = enc[1];
r[bi] = enc[2];
Color::new_unchecked(r)
}
}
impl<L> Transform<Color<[f32; 4], DisplayP3<L>>> for Color<[f32; 4], Srgb<L>>
where
L: Asserts<[f32; 4]> + ChannelMap<4> + 'static,
{
fn transform_from(src: Color<[f32; 4], DisplayP3<L>>, _: &()) -> Self {
let s = src.inner();
let [ri, gi, bi, _ai] = L::INDICES;
let lin = SrgbTf::decode::<DefaultMath>([s[ri], s[gi], s[bi]]);
let out = const { direct_matrix::<P3Primaries, SrgbPrimaries>() }.apply(lin);
let enc = SrgbTf::encode::<DefaultMath>(out);
let mut r = s;
r[ri] = enc[0];
r[gi] = enc[1];
r[bi] = enc[2];
Color::new_unchecked(r)
}
}
impl<L> Transform<Color<[f32; 3], DisplayP3<L>>> for Color<[f32; 3], Srgb<L>>
where
L: Asserts<[f32; 3]> + ChannelMap<3> + 'static,
{
fn transform_from(src: Color<[f32; 3], DisplayP3<L>>, _: &()) -> Self {
let s = src.inner();
let [ri, gi, bi] = L::INDICES;
let lin = SrgbTf::decode::<DefaultMath>([s[ri], s[gi], s[bi]]);
let out = const { direct_matrix::<P3Primaries, SrgbPrimaries>() }.apply(lin);
let enc = SrgbTf::encode::<DefaultMath>(out);
let mut r = s;
r[ri] = enc[0];
r[gi] = enc[1];
r[bi] = enc[2];
Color::new_unchecked(r)
}
}
impl<L> Transform<Color<[f32; 4], Srgb<L>>> for Color<[f32; 4], Rec709<L>>
where
L: Asserts<[f32; 4]> + ChannelMap<4> + 'static,
{
fn transform_from(src: Color<[f32; 4], Srgb<L>>, _: &()) -> Self {
let s = src.inner();
let [ri, gi, bi, _ai] = L::INDICES;
let lin = SrgbTf::decode::<DefaultMath>([s[ri], s[gi], s[bi]]);
let enc = Rec709Tf::encode::<DefaultMath>(lin);
let mut r = s;
r[ri] = enc[0];
r[gi] = enc[1];
r[bi] = enc[2];
Color::new_unchecked(r)
}
}
impl<L> Transform<Color<[f32; 3], Srgb<L>>> for Color<[f32; 3], Rec709<L>>
where
L: Asserts<[f32; 3]> + ChannelMap<3> + 'static,
{
fn transform_from(src: Color<[f32; 3], Srgb<L>>, _: &()) -> Self {
let s = src.inner();
let [ri, gi, bi] = L::INDICES;
let lin = SrgbTf::decode::<DefaultMath>([s[ri], s[gi], s[bi]]);
let enc = Rec709Tf::encode::<DefaultMath>(lin);
let mut r = s;
r[ri] = enc[0];
r[gi] = enc[1];
r[bi] = enc[2];
Color::new_unchecked(r)
}
}
impl<L> Transform<Color<[f32; 4], Rec709<L>>> for Color<[f32; 4], Srgb<L>>
where
L: Asserts<[f32; 4]> + ChannelMap<4> + 'static,
{
fn transform_from(src: Color<[f32; 4], Rec709<L>>, _: &()) -> Self {
let s = src.inner();
let [ri, gi, bi, _ai] = L::INDICES;
let lin = Rec709Tf::decode::<DefaultMath>([s[ri], s[gi], s[bi]]);
let enc = SrgbTf::encode::<DefaultMath>(lin);
let mut r = s;
r[ri] = enc[0];
r[gi] = enc[1];
r[bi] = enc[2];
Color::new_unchecked(r)
}
}
impl<L> Transform<Color<[f32; 3], Rec709<L>>> for Color<[f32; 3], Srgb<L>>
where
L: Asserts<[f32; 3]> + ChannelMap<3> + 'static,
{
fn transform_from(src: Color<[f32; 3], Rec709<L>>, _: &()) -> Self {
let s = src.inner();
let [ri, gi, bi] = L::INDICES;
let lin = Rec709Tf::decode::<DefaultMath>([s[ri], s[gi], s[bi]]);
let enc = SrgbTf::encode::<DefaultMath>(lin);
let mut r = s;
r[ri] = enc[0];
r[gi] = enc[1];
r[bi] = enc[2];
Color::new_unchecked(r)
}
}
pub type Srgb<L = Rgba> = RgbSpace<SrgbPrimaries, SrgbTf, L>;
pub type LinearSrgb<L = Rgba> = RgbSpace<SrgbPrimaries, LinearTf, L>;
pub type Rec709<L = Rgba> = RgbSpace<SrgbPrimaries, Rec709Tf, L>;
pub type DisplayP3<L = Rgba> = RgbSpace<P3Primaries, SrgbTf, L>;
pub type LinearP3<L = Rgba> = RgbSpace<P3Primaries, LinearTf, L>;
pub type Hdr10<L = Rgba> = RgbSpace<Rec2020Primaries, PqTf, L>;
pub type Hlg<L = Rgba> = RgbSpace<Rec2020Primaries, HlgTf, L>;
pub type LinearRec2020<L = Rgba> = RgbSpace<Rec2020Primaries, LinearTf, L>;
pub type AcesCg<L = Rgba> = RgbSpace<AcesAp1Primaries, LinearTf, L>;
pub type Aces2065<L = Rgba> = RgbSpace<AcesAp0Primaries, LinearTf, L>;
pub type AcesCc<L = Rgba> = RgbSpace<AcesAp1Primaries, AcesCcTf, L>;
pub type AcesCct<L = Rgba> = RgbSpace<AcesAp1Primaries, AcesCctTf, L>;
pub type ProPhoto<L = Rgba> = RgbSpace<ProPhotoPrimaries, ProPhotoTf, L>;
pub type LinearProPhoto<L = Rgba> = RgbSpace<ProPhotoPrimaries, LinearTf, L>;
pub type DciP3<L = Rgba> = RgbSpace<DciP3Primaries, DciP3Tf, L>;
pub type P3D65Gamma26<L = Rgba> = RgbSpace<P3Primaries, DciP3Tf, L>;