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>; }