kolor_64/details/
conversion.rs1use super::{
2 color::{RGBPrimaries, TransformFn},
3 transform::ColorTransform,
4 xyz::{rgb_to_xyz, xyz_to_rgb},
5};
6use crate::{ColorSpace, FType, Mat3, Vec3};
7#[cfg(feature = "serde1")]
8use serde::{Deserialize, Serialize};
9
10#[derive(Copy, Clone, Debug, PartialEq)]
12#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
13pub struct LinearColorConversion {
14 mat: Mat3,
15 input_space: ColorSpace,
16 output_space: ColorSpace,
17}
18
19impl LinearColorConversion {
20 pub fn input_space(&self) -> ColorSpace {
21 self.input_space
22 }
23 pub fn output_space(&self) -> ColorSpace {
24 self.output_space
25 }
26 pub fn convert(&self, color: Vec3) -> Vec3 {
27 self.mat * color
28 }
29 pub fn matrix(&self) -> Mat3 {
30 self.mat
31 }
32
33 pub fn new(src: ColorSpace, dst: ColorSpace) -> Self {
34 if !src.is_linear() {
35 panic!("{:?} is not a linear color space", src);
36 }
37 if !dst.is_linear() {
38 panic!("{:?} is not a linear color space", dst);
39 }
40 #[cfg(feature = "color-matrices")]
41 let const_conversion = super::generated_matrices::const_conversion_matrix(
42 src.primaries(),
43 src.white_point(),
44 dst.primaries(),
45 dst.white_point(),
46 );
47 #[cfg(not(feature = "color-matrices"))]
48 let const_conversion: Option<Mat3> = None;
49
50 let mat = if let Some(const_mat) = const_conversion {
51 const_mat
52 } else {
53 let src_to_xyz = if src.primaries() == RGBPrimaries::CIE_XYZ {
54 Mat3::IDENTITY
55 } else {
56 rgb_to_xyz(src.primaries().values(), src.white_point().values())
57 };
58 let xyz_to_dst = if dst.primaries() == RGBPrimaries::CIE_XYZ {
59 Mat3::IDENTITY
60 } else {
61 xyz_to_rgb(dst.primaries().values(), dst.white_point().values())
62 };
63 if src.white_point() != dst.white_point() {
64 let white_point_transform = super::cat::chromatic_adaptation_transform(
65 Vec3::from_slice(src.white_point().values()),
66 Vec3::from_slice(dst.white_point().values()),
67 super::cat::LMSConeSpace::Sharp,
68 );
69 xyz_to_dst * white_point_transform * src_to_xyz
70 } else {
71 xyz_to_dst * src_to_xyz
72 }
73 };
74 Self {
75 mat,
76 input_space: src,
77 output_space: dst,
78 }
79 }
80}
81
82#[derive(Copy, Clone)]
85pub struct ColorConversion {
86 src_space: ColorSpace,
87 dst_space: ColorSpace,
88 src_transform: Option<ColorTransform>,
89 linear_transform: Option<LinearColorConversion>,
90 dst_transform: Option<ColorTransform>,
91}
92impl PartialEq for ColorConversion {
93 fn eq(&self, other: &Self) -> bool {
94 self.src_space == other.src_space && self.dst_space == other.dst_space
95 }
96}
97impl core::fmt::Debug for ColorConversion {
98 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
99 let src_transform = if !self.src_space.is_linear() {
100 self.src_space.transform_function()
101 } else {
102 TransformFn::NONE
103 };
104 let dst_transform = if !self.dst_space.is_linear() {
105 self.dst_space.transform_function()
106 } else {
107 TransformFn::NONE
108 };
109 f.debug_struct("ColorConversion")
110 .field("src_space", &self.src_space)
111 .field("dst_space", &self.dst_space)
112 .field("src_transform", &src_transform)
113 .field("linear_transform", &self.linear_transform)
114 .field("dst_transform", &dst_transform)
115 .finish()
116 }
117}
118
119impl ColorConversion {
120 pub fn new(src: ColorSpace, dst: ColorSpace) -> Self {
121 let src_transform = if !src.is_linear() {
122 ColorTransform::new(src.transform_function(), TransformFn::NONE)
123 } else {
124 None
125 };
126 let src_linear = ColorSpace::linear(src.primaries(), src.white_point());
127 let dst_linear = ColorSpace::linear(dst.primaries(), dst.white_point());
128 let linear_transform = LinearColorConversion::new(src_linear, dst_linear);
129 let linear_transform = if linear_transform.mat == Mat3::IDENTITY {
130 None
131 } else {
132 Some(linear_transform)
133 };
134 let dst_transform = if !dst.is_linear() {
135 ColorTransform::new(TransformFn::NONE, dst.transform_function())
136 } else {
137 None
138 };
139 Self {
140 src_space: src,
141 dst_space: dst,
142 src_transform,
143 dst_transform,
144 linear_transform,
145 }
146 }
147 pub fn invert(&self) -> Self {
148 ColorConversion::new(self.dst_space, self.src_space)
149 }
150 pub fn is_linear(&self) -> bool {
151 self.src_transform.is_none() && self.dst_transform.is_none()
152 }
153 pub fn linear_part(&self) -> LinearColorConversion {
154 if let Some(transform) = self.linear_transform.as_ref() {
155 *transform
156 } else {
157 LinearColorConversion {
158 input_space: self.src_space,
159 output_space: self.dst_space,
160 mat: Mat3::IDENTITY,
161 }
162 }
163 }
164 pub fn src_transform(&self) -> Option<ColorTransform> {
165 self.src_transform
166 }
167 pub fn dst_transform(&self) -> Option<ColorTransform> {
168 self.dst_transform
169 }
170 pub fn src_transform_fn(&self) -> TransformFn {
171 self.src_transform
172 .map(|_| self.src_space.transform_function())
173 .unwrap_or(TransformFn::NONE)
174 }
175 pub fn dst_transform_fn(&self) -> TransformFn {
176 self.dst_transform
177 .map(|_| self.dst_space.transform_function())
178 .unwrap_or(TransformFn::NONE)
179 }
180 pub fn src_space(&self) -> ColorSpace {
181 self.src_space
182 }
183 pub fn dst_space(&self) -> ColorSpace {
184 self.dst_space
185 }
186 pub fn convert_float(&self, color: &mut [FType; 3]) {
187 let vec3 = Vec3::from_slice(color);
188 *color = self.convert(vec3).into();
189 }
190 pub fn apply_src_transform(&self, color: Vec3) -> Vec3 {
191 if let Some(src_transform) = self.src_transform.as_ref() {
192 src_transform.apply(color, self.src_space.white_point())
193 } else {
194 color
195 }
196 }
197 pub fn apply_linear_part(&self, color: Vec3) -> Vec3 {
198 if let Some(transform) = self.linear_transform.as_ref() {
199 transform.convert(color)
200 } else {
201 color
202 }
203 }
204 pub fn apply_dst_transform(&self, color: Vec3) -> Vec3 {
205 if let Some(dst_transform) = self.dst_transform.as_ref() {
206 dst_transform.apply(color, self.dst_space.white_point())
207 } else {
208 color
209 }
210 }
211 pub fn convert(&self, mut color: Vec3) -> Vec3 {
212 color = self.apply_src_transform(color);
213 color = self.apply_linear_part(color);
214 color = self.apply_dst_transform(color);
215 color
216 }
217}