1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
use crate::{
    Color, ColorAlpha, ColorResult, DynamicAlphaState, DynamicColor, DynamicColorAlpha,
    DynamicColorSpace, DynamicState, Premultiplied, Separate,
};

use glam::{Vec3, Vec4};

use core::fmt;

/// A type that implements ColorSpace represents a specific color space. See the documentation
/// of [`DynamicColorSpace`] for more information about what a color space is.
pub trait ColorSpace: Default + fmt::Display {
    /// The [`DynamicColorSpace`] that this type represents.
    const SPACE: DynamicColorSpace;

    /// The closest linear color space to this space.
    type LinearSpace: LinearColorSpace + ConvertFromRaw<Self>;

    /// The 'bag of components' that this color space uses.
    type ComponentStruct: Clone + Copy + fmt::Display;
}

/// Marks a type as representing a linear color space.
pub trait LinearColorSpace: ColorSpace {}

/// Marks a type as representing a nonlinear color space.
pub trait NonlinearColorSpace: ColorSpace {}

/// Marks a type as representing an encoded color space.
///
/// A color in an [`EncodedColorSpace`] has few operations
/// that can be performed on it other than just converting it
/// to a [`WorkingColorSpace`]
pub trait EncodedColorSpace: ColorSpace {
    type DecodedSpace: WorkingColorSpace + ConvertFromRaw<Self>;
}

/// Marks a type as representing a color space that is not [encoded][EncodedColorSpace] and is therefore
/// able to have many more operations performed on it.
pub trait WorkingColorSpace: ColorSpace {}

/// Performs the raw conversion from the [`ColorSpace`] represented by `SrcSpc` to
/// the [`ColorSpace`] represented by `Self` in three concrete steps, each of which
/// may do some work or be a no-op.
pub trait ConvertFromRaw<SrcSpace: ColorSpace>: ColorSpace {
    fn src_transform_raw(color: Vec3) -> Vec3;
    fn linear_part_raw(color: Vec3) -> Vec3;
    fn dst_transform_raw(color: Vec3) -> Vec3;
}

/// The complement of [`ConvertFromRaw`].
///
/// Performs the raw conversion from the [`ColorSpace`] represented by `Self` to
/// the [`ColorSpace`] represented by `DstSpace` in three concrete steps, each of which
/// may do some work or be a no-op.
///
/// This is automatically implemented for all types that implement [`ConvertFromRaw`],
/// much like how the [From] and [Into] traits work, where [From] gets you [Into] for free.
pub trait ConvertToRaw<DstSpace: ColorSpace>: ColorSpace {
    fn src_transform_raw(color: Vec3) -> Vec3;
    fn linear_part_raw(color: Vec3) -> Vec3;
    fn dst_transform_raw(color: Vec3) -> Vec3;
}

impl<SrcSpace: ColorSpace, DstSpace: ConvertFromRaw<SrcSpace>> ConvertToRaw<DstSpace> for SrcSpace {
    #[inline]
    fn src_transform_raw(color: Vec3) -> Vec3 {
        <DstSpace as ConvertFromRaw<SrcSpace>>::src_transform_raw(color)
    }

    #[inline]
    fn linear_part_raw(color: Vec3) -> Vec3 {
        <DstSpace as ConvertFromRaw<SrcSpace>>::linear_part_raw(color)
    }

    #[inline]
    fn dst_transform_raw(color: Vec3) -> Vec3 {
        <DstSpace as ConvertFromRaw<SrcSpace>>::dst_transform_raw(color)
    }
}

/// A trait meant to be used as a replacement for [`Into`] in situations where you want
/// to bound a type as being able to be converted into a specific type of color.
/// Because of how `colstodian` works and how [`From`]/[`Into`] are implemented, we can't use them directly
/// for this purpose.
///
/// # Example
///
/// ```rust
/// use colstodian::*;
///
/// fn tint_color(input_color: impl ColorInto<Color<AcesCg, Display>>) -> Color<AcesCg, Display> {
///     let color = input_color.into();
///     let tint: Color<AcesCg, Display> = Color::new(0.5, 0.8, 0.4);
///     color * tint
/// }
///
/// let color = color::srgb_u8(225, 200, 86);
/// let tinted: Color<EncodedSrgb, Display> = tint_color(color).convert();
///
/// println!("Pre-tint: {}, Post-tint: {}", color, tinted);
/// ```
pub trait ColorInto<T> {
    fn into(self) -> T;
}

/// A type that implements this trait can be converted directly to and from
/// an appropriately sized array of `u8`s.
pub trait AsU8Array {}

/// A type that implements this trait represents a color's State.
///
/// All colors have units. Sometimes a color's units are explicit, such as measuring the emitted
/// light from a display using a spectroradiometer and being able to reference pixel values in CIE XYZ cd/m2.
/// Other times, the units are only indirectly related to the real world, and then providing a
/// mathematical conversion to measurable quantities. For example, in the case of display technology, common color encodings
/// (relations of code value to measurable XYZ performance) include sRGB, DCI-P3, and BT.2020.
///
/// Howver, considering color as a displayed quantity only provides part of the color encoding story. In addition to relating RGB
/// values to display measurements, one can also relate RGB values to the performance characteristics of an
/// *input device* (i.e., a camera, or a virtual camera in a 3d renderer). Input colorimetry can be measured in real world units as well.
/// In the case of a 3d renderer, these units are often (or at least should be) defined in the renderer as a radiometric quantity like
/// radiance, with the relation to XYZ values dictated by a linear transformation to the rendering color space.
/// Even in the case of a real world camera, it is not difficult to measure an input spectra with the spectrophotometer
/// in XYZ, and then compare this to the RGB values output from the camera.
///
/// It is a meaningful abstraction to categorize color spaces by the “direction” of this relationship to real world
/// quantities, which we refer to as State. Colors which are defined in relation to display
/// characteristic are called [`Display`][crate::Display]-referred, while color spaces which are defined in relation to input
/// devices (scenes) are [`Scene`][crate::Scene]-referred.
pub trait State: Default + fmt::Display {
    const STATE: DynamicState;
}

/// A type that implements this trait represents a color's alpha state.
///
/// A color can either have a [`Separate`] alpha channel or have been pre-multiplied
/// with its alpha channel and so have [`Premultiplied`] alpha.
pub trait AlphaState
where
    Self: Default
        + fmt::Display
        + ConvertToAlphaRaw<Separate>
        + ConvertToAlphaRaw<Premultiplied>
        + ConvertFromAlphaRaw<Separate>
        + ConvertFromAlphaRaw<Premultiplied>,
{
    const STATE: DynamicAlphaState;
}

/// Performs the conversion from [alpha state][AlphaState] `SrcAlphaState` into `Self`
/// on a raw color.
pub trait ConvertFromAlphaRaw<SrcAlphaState> {
    fn convert_raw(raw: Vec3, alpha: f32) -> Vec3;
}

impl<T> ConvertFromAlphaRaw<T> for T {
    #[inline]
    fn convert_raw(raw: Vec3, _alpha: f32) -> Vec3 {
        raw
    }
}

/// The complement of [`ConvertFromAlphaRaw`]. Performs the conversion from [alpha state][AlphaState] `Self` into `DstAlphaState`
/// on a raw color.
///
/// This is automatically implemented for all types that implement [`ConvertFromAlphaRaw`],
/// much like how the [From] and [Into] traits work, where [From] gets you [Into] for free.
pub trait ConvertToAlphaRaw<DstAlphaState> {
    fn convert_raw(raw: Vec3, alpha: f32) -> Vec3;
}

impl<SrcAlpha, DstAlpha: ConvertFromAlphaRaw<SrcAlpha>> ConvertToAlphaRaw<DstAlpha> for SrcAlpha {
    fn convert_raw(raw: Vec3, alpha: f32) -> Vec3 {
        <DstAlpha as ConvertFromAlphaRaw<SrcAlpha>>::convert_raw(raw, alpha)
    }
}

/// A "conversion query" for a [`Color`][crate::Color].
///
/// A type that implements this
/// trait is able to be used as the type parameter for [`Color::convert`][crate::Color::convert].
///
/// The types that implement this trait are:
/// * [`ColorSpace`] types
/// * [`Color`][crate::Color] types (in which case it will be converted to that color's space)
pub trait ColorConversionQuery<SrcSpace: ColorSpace, St: State> {
    type DstSpace: ConvertFromRaw<SrcSpace>;
}

impl<SrcSpace, DstSpace, St> ColorConversionQuery<SrcSpace, St> for Color<DstSpace, St>
where
    SrcSpace: ColorSpace,
    DstSpace: ConvertFromRaw<SrcSpace>,
    St: State,
{
    type DstSpace = DstSpace;
}

/// A "conversion query" for a [`ColorAlpha`][crate::ColorAlpha].
///
/// A type that implements this
/// trait is able to be used as the type parameter for [`ColorAlpha::convert_to`][crate::ColorAlpha::convert_to].
///
/// The types that implement this trait are:
/// * [`ColorSpace`] types
/// * [`AlphaState`] types
/// * [`ColorAlpha`][crate::ColorAlpha] types (in which case it will be converted to that color's space and alpha state)
pub trait ColorAlphaConversionQuery<SrcSpace: ColorSpace, SrcAlpha: AlphaState> {
    type DstSpace: ConvertFromRaw<SrcSpace>;
    type DstAlpha: ConvertFromAlphaRaw<SrcAlpha> + AlphaState;
}

impl<SrcSpace, DstSpc, SrcAlpha, DstAlpha> ColorAlphaConversionQuery<SrcSpace, SrcAlpha>
    for ColorAlpha<DstSpc, DstAlpha>
where
    SrcSpace: ColorSpace,
    DstSpc: ConvertFromRaw<SrcSpace>,
    SrcAlpha: AlphaState,
    DstAlpha: ConvertFromAlphaRaw<SrcAlpha> + AlphaState,
{
    type DstSpace = DstSpc;
    type DstAlpha = DstAlpha;
}

/// An object-safe trait implemented by both [`Color`] and [`DynamicColor`].
pub trait AnyColor {
    fn raw(&self) -> Vec3;
    fn space(&self) -> DynamicColorSpace;
    fn state(&self) -> DynamicState;

    /// Upcasts `self` into a [`DynamicColor`]
    fn dynamic(&self) -> DynamicColor {
        DynamicColor::new(self.raw(), self.space(), self.state())
    }
}

impl<'a> From<&'a dyn AnyColor> for DynamicColor {
    fn from(color: &'a dyn AnyColor) -> DynamicColor {
        color.dynamic()
    }
}

/// A type that implements this trait provides the ability to downcast from a dynamically-typed
/// color to a statically-typed [`Color`]. This is implemented for all types that implement [`AnyColor`]
pub trait DynColor {
    /// Attempt to convert to a typed `Color`. Returns an error if `self`'s color space and state do not match
    /// the given types.
    fn downcast<Spc: ColorSpace, St: State>(&self) -> ColorResult<Color<Spc, St>>;

    /// Convert to a typed `Color` without checking if the color space and state types
    /// match this color's space and state. Use only if you are sure that this color
    /// is in the correct format.
    fn downcast_unchecked<Spc: ColorSpace, St: State>(&self) -> Color<Spc, St>;
}

/// An object-safe trait implemented by both [`ColorAlpha`] and [`DynamicColorAlpha`]
pub trait AnyColorAlpha {
    fn raw(&self) -> Vec4;
    fn space(&self) -> DynamicColorSpace;
    fn alpha_state(&self) -> DynamicAlphaState;

    /// Upcasts `self` into a [`DynamicColorAlpha`]
    fn dynamic(&self) -> DynamicColorAlpha {
        DynamicColorAlpha::new(self.raw(), self.space(), self.alpha_state())
    }
}

/// A type that implements this trait provides the ability to downcast from a dynamically-typed
/// color to a statically-typed [`ColorAlpha`]. This is implemented for all types that implement [`AnyColorAlpha`]
pub trait DynColorAlpha {
    /// Attempt to downcast to a typed [`ColorAlpha`]. Returns an error if `self`'s color space and alpha state do not match
    /// the given types.
    fn downcast<Spc: ColorSpace, A: AlphaState>(&self) -> ColorResult<ColorAlpha<Spc, A>>;

    /// Downcast to a typed [`ColorAlpha`] without checking if the color space and state types
    /// match this color's space and state. Use only if you are sure that this color
    /// is in the correct format.
    fn downcast_unchecked<Spc: ColorSpace, A: AlphaState>(&self) -> ColorAlpha<Spc, A>;
}