#![no_std]
#![warn(missing_docs)]
#![doc = include_str!("../README.md")]
#[cfg(feature = "std")]
extern crate std;
pub mod adaptation;
pub mod illuminant;
pub mod math;
pub mod perceptual;
pub mod primaries;
pub mod rgb;
pub mod spectral;
pub mod transfer;
pub mod xyz;
use core::marker::PhantomData;
pub use adaptation::{Bradford, Cat02, Cat16, ChromaticAdaptation, VonKries};
pub use illuminant::{AcesWhitePoint, D50, D60, D65, DciWhite, Illuminant};
pub use perceptual::{LCh, LChD65, Lab, LabD65, Oklab, Oklch};
pub use rgb::{
Abgr, Aces2065, AcesCc, AcesCct, AcesCg, Argb, Bgr, Bgra, ChannelMap, DciP3, DisplayP3, Hdr10, Hlg, LinearP3,
LinearProPhoto, LinearRec2020, LinearSrgb, P3D65Gamma26, ProPhoto, Rec709, Rgb, RgbColorSpace, RgbSpace, Rgba,
Srgb,
};
pub use spectral::{
Bispectral, IsBispectral, IsRadiance, IsReflectance, IsTransmittance, Radiance, Reflectance, SpectralKind,
SpectralSpace, Transmittance, WavelengthGrid,
};
pub use transfer::{
AcesCcTf, AcesCctTf, DciP3Tf, HlgTf, IsDisplayReferred, IsLinearEncoding, IsSceneReferred, LinearTf, PqTf,
ProPhotoTf, Rec709Tf, SrgbTf, TransferFunction,
};
pub use xyz::{Xyz, XyzAces, XyzD50, XyzD65};
pub trait Asserts<T: 'static> {}
pub trait ColorSpace: 'static {
const CHANNELS: usize;
const LUMINANCE_WEIGHTS: Option<[f32; 3]>;
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Color<S, Sp>
where
Sp: Asserts<S>,
S: Copy + 'static,
Sp: 'static,
{
storage: S,
_space: PhantomData<Sp>,
}
impl<S, Sp> Color<S, Sp>
where
S: Copy + 'static,
Sp: Asserts<S> + 'static,
{
#[inline(always)]
pub fn new_unchecked(storage: S) -> Self {
Self {
storage,
_space: PhantomData,
}
}
#[inline(always)]
pub fn inner(&self) -> S {
self.storage
}
#[inline(always)]
pub fn transform<Dst>(self) -> Dst
where
Dst: Transform<Self>,
{
Dst::transform_from(self, &())
}
#[inline(always)]
pub fn transform_to<Dst, Ctx>(self, ctx: &Ctx) -> Dst
where
Ctx: ?Sized,
Dst: Transform<Self, Ctx>,
{
Dst::transform_from(self, ctx)
}
#[inline(always)]
pub fn try_transform<Dst>(self) -> Result<Dst, OutOfGamut<Dst>>
where
Dst: TryTransform<Self>,
{
Dst::try_transform_from(self, &())
}
#[inline(always)]
pub fn try_transform_to<Dst, Ctx>(self, ctx: &Ctx) -> Result<Dst, OutOfGamut<Dst>>
where
Ctx: ?Sized,
Dst: TryTransform<Self, Ctx>,
{
Dst::try_transform_from(self, ctx)
}
#[inline(always)]
pub fn transform_via<Via, Dst>(self) -> Dst
where
Via: Transform<Self>,
Dst: Transform<Via>,
{
Dst::transform_from(Via::transform_from(self, &()), &())
}
#[inline(always)]
pub fn try_transform_via<Via, Dst>(self) -> Result<Dst, OutOfGamut<Dst>>
where
Via: TryTransform<Self>,
Dst: TryTransform<Via>,
{
let (via, first_clipped) = match Via::try_transform_from(self, &()) {
Ok(v) => (v, false),
Err(e) => (e.clamped, true),
};
match Dst::try_transform_from(via, &()) {
Ok(dst) if !first_clipped => Ok(dst),
Ok(dst) => Err(OutOfGamut { clamped: dst }),
Err(e) => Err(e),
}
}
}
pub trait Transform<Src, Ctx: ?Sized = ()>: Sized + 'static {
fn transform_from(src: Src, ctx: &Ctx) -> Self;
}
pub trait TryTransform<Src, Ctx: ?Sized = ()>: Sized + 'static {
fn try_transform_from(src: Src, ctx: &Ctx) -> Result<Self, OutOfGamut<Self>>;
}
impl<Src, Ctx, Dst> TryTransform<Src, Ctx> for Dst
where
Ctx: ?Sized,
Dst: Transform<Src, Ctx>,
{
fn try_transform_from(src: Src, ctx: &Ctx) -> Result<Self, OutOfGamut<Self>> {
Ok(Self::transform_from(src, ctx))
}
}
pub trait LinearLight: ColorSpace {}
pub trait SceneReferred: ColorSpace {}
pub trait DisplayReferred: ColorSpace {}
pub trait Absolute: ColorSpace {}
pub trait Delta<Sp: ColorSpace> {
type Output;
fn delta<S>(a: Color<S, Sp>, b: Color<S, Sp>) -> Self::Output
where
S: Copy + 'static,
Sp: Asserts<S>;
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct OutOfGamut<T> {
pub clamped: T,
}
impl<T> core::fmt::Display for OutOfGamut<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "color out of gamut")
}
}
impl<T: core::fmt::Debug> core::error::Error for OutOfGamut<T> {}