1#![allow(dead_code)]
30use crate::conversions::LutBarycentricReduction;
31use crate::conversions::interpolator::{BarycentricWeight, MultidimensionalInterpolation};
32use crate::conversions::lut_transforms::Lut3x3Factory;
33use crate::transform::PointeeSizeExpressible;
34use crate::{
35 BarycentricWeightScale, CmsError, DataColorSpace, InterpolationMethod, Layout,
36 TransformExecutor, TransformOptions,
37};
38use num_traits::AsPrimitive;
39use std::marker::PhantomData;
40
41pub(crate) struct TransformLut3x3<
42 T,
43 U,
44 const SRC_LAYOUT: u8,
45 const DST_LAYOUT: u8,
46 const GRID_SIZE: usize,
47 const BIT_DEPTH: usize,
48 const BINS: usize,
49 const BARYCENTRIC_BINS: usize,
50> {
51 pub(crate) lut: Vec<f32>,
52 pub(crate) _phantom: PhantomData<T>,
53 pub(crate) _phantom1: PhantomData<U>,
54 pub(crate) interpolation_method: InterpolationMethod,
55 pub(crate) weights: Box<[BarycentricWeight<f32>; BINS]>,
56 pub(crate) color_space: DataColorSpace,
57 pub(crate) is_linear: bool,
58}
59
60impl<
61 T: Copy + AsPrimitive<f32> + Default + PointeeSizeExpressible,
62 U: AsPrimitive<usize>,
63 const SRC_LAYOUT: u8,
64 const DST_LAYOUT: u8,
65 const GRID_SIZE: usize,
66 const BIT_DEPTH: usize,
67 const BINS: usize,
68 const BARYCENTRIC_BINS: usize,
69> TransformLut3x3<T, U, SRC_LAYOUT, DST_LAYOUT, GRID_SIZE, BIT_DEPTH, BINS, BARYCENTRIC_BINS>
70where
71 f32: AsPrimitive<T>,
72 u32: AsPrimitive<T>,
73 (): LutBarycentricReduction<T, U>,
74{
75 #[inline(never)]
76 fn transform_chunk(
77 &self,
78 src: &[T],
79 dst: &mut [T],
80 interpolator: Box<dyn MultidimensionalInterpolation + Send + Sync>,
81 ) {
82 let src_cn = Layout::from(SRC_LAYOUT);
83 let src_channels = src_cn.channels();
84
85 let dst_cn = Layout::from(DST_LAYOUT);
86 let dst_channels = dst_cn.channels();
87
88 let value_scale = ((1 << BIT_DEPTH) - 1) as f32;
89 let max_value = ((1u32 << BIT_DEPTH) - 1).as_();
90
91 for (src, dst) in src
92 .chunks_exact(src_channels)
93 .zip(dst.chunks_exact_mut(dst_channels))
94 {
95 let x = <() as LutBarycentricReduction<T, U>>::reduce::<BIT_DEPTH, BARYCENTRIC_BINS>(
96 src[src_cn.r_i()],
97 );
98 let y = <() as LutBarycentricReduction<T, U>>::reduce::<BIT_DEPTH, BARYCENTRIC_BINS>(
99 src[src_cn.g_i()],
100 );
101 let z = <() as LutBarycentricReduction<T, U>>::reduce::<BIT_DEPTH, BARYCENTRIC_BINS>(
102 src[src_cn.b_i()],
103 );
104
105 let a = if src_channels == 4 {
106 src[src_cn.a_i()]
107 } else {
108 max_value
109 };
110
111 let v = interpolator.inter3(
112 &self.lut,
113 &self.weights[x.as_()],
114 &self.weights[y.as_()],
115 &self.weights[z.as_()],
116 );
117 if T::FINITE {
118 let r = v * value_scale + 0.5;
119 dst[dst_cn.r_i()] = r.v[0].min(value_scale).max(0.).as_();
120 dst[dst_cn.g_i()] = r.v[1].min(value_scale).max(0.).as_();
121 dst[dst_cn.b_i()] = r.v[2].min(value_scale).max(0.).as_();
122 if dst_channels == 4 {
123 dst[dst_cn.a_i()] = a;
124 }
125 } else {
126 dst[dst_cn.r_i()] = v.v[0].as_();
127 dst[dst_cn.g_i()] = v.v[1].as_();
128 dst[dst_cn.b_i()] = v.v[2].as_();
129 if dst_channels == 4 {
130 dst[dst_cn.a_i()] = a;
131 }
132 }
133 }
134 }
135}
136
137impl<
138 T: Copy + AsPrimitive<f32> + Default + PointeeSizeExpressible,
139 U: AsPrimitive<usize>,
140 const SRC_LAYOUT: u8,
141 const DST_LAYOUT: u8,
142 const GRID_SIZE: usize,
143 const BIT_DEPTH: usize,
144 const BINS: usize,
145 const BARYCENTRIC_BINS: usize,
146> TransformExecutor<T>
147 for TransformLut3x3<T, U, SRC_LAYOUT, DST_LAYOUT, GRID_SIZE, BIT_DEPTH, BINS, BARYCENTRIC_BINS>
148where
149 f32: AsPrimitive<T>,
150 u32: AsPrimitive<T>,
151 (): LutBarycentricReduction<T, U>,
152{
153 fn transform(&self, src: &[T], dst: &mut [T]) -> Result<(), CmsError> {
154 let src_cn = Layout::from(SRC_LAYOUT);
155 let src_channels = src_cn.channels();
156
157 let dst_cn = Layout::from(DST_LAYOUT);
158 let dst_channels = dst_cn.channels();
159 if src.len() % src_channels != 0 {
160 return Err(CmsError::LaneMultipleOfChannels);
161 }
162 if dst.len() % dst_channels != 0 {
163 return Err(CmsError::LaneMultipleOfChannels);
164 }
165 let src_chunks = src.len() / src_channels;
166 let dst_chunks = dst.len() / dst_channels;
167 if src_chunks != dst_chunks {
168 return Err(CmsError::LaneSizeMismatch);
169 }
170
171 if self.color_space == DataColorSpace::Lab
172 || (self.is_linear && self.color_space == DataColorSpace::Rgb)
173 || self.color_space == DataColorSpace::Xyz
174 {
175 use crate::conversions::interpolator::Trilinear;
176 self.transform_chunk(src, dst, Box::new(Trilinear::<GRID_SIZE> {}));
177 } else {
178 match self.interpolation_method {
179 #[cfg(feature = "options")]
180 InterpolationMethod::Tetrahedral => {
181 use crate::conversions::interpolator::Tetrahedral;
182 self.transform_chunk(src, dst, Box::new(Tetrahedral::<GRID_SIZE> {}));
183 }
184 #[cfg(feature = "options")]
185 InterpolationMethod::Pyramid => {
186 use crate::conversions::interpolator::Pyramidal;
187 self.transform_chunk(src, dst, Box::new(Pyramidal::<GRID_SIZE> {}));
188 }
189 #[cfg(feature = "options")]
190 InterpolationMethod::Prism => {
191 use crate::conversions::interpolator::Prismatic;
192 self.transform_chunk(src, dst, Box::new(Prismatic::<GRID_SIZE> {}));
193 }
194 InterpolationMethod::Linear => {
195 use crate::conversions::interpolator::Trilinear;
196 self.transform_chunk(src, dst, Box::new(Trilinear::<GRID_SIZE> {}));
197 }
198 }
199 }
200
201 Ok(())
202 }
203}
204
205pub(crate) struct DefaultLut3x3Factory {}
206
207impl Lut3x3Factory for DefaultLut3x3Factory {
208 fn make_transform_3x3<
209 T: Copy + AsPrimitive<f32> + Default + PointeeSizeExpressible + 'static + Send + Sync,
210 const SRC_LAYOUT: u8,
211 const DST_LAYOUT: u8,
212 const GRID_SIZE: usize,
213 const BIT_DEPTH: usize,
214 >(
215 lut: Vec<f32>,
216 options: TransformOptions,
217 color_space: DataColorSpace,
218 is_linear: bool,
219 ) -> Box<dyn TransformExecutor<T> + Send + Sync>
220 where
221 f32: AsPrimitive<T>,
222 u32: AsPrimitive<T>,
223 (): LutBarycentricReduction<T, u8>,
224 (): LutBarycentricReduction<T, u16>,
225 {
226 match options.barycentric_weight_scale {
227 BarycentricWeightScale::Low => Box::new(TransformLut3x3::<
228 T,
229 u8,
230 SRC_LAYOUT,
231 DST_LAYOUT,
232 GRID_SIZE,
233 BIT_DEPTH,
234 256,
235 256,
236 > {
237 lut,
238 _phantom: PhantomData,
239 _phantom1: PhantomData,
240 interpolation_method: options.interpolation_method,
241 weights: BarycentricWeight::<f32>::create_ranged_256::<GRID_SIZE>(),
242 color_space,
243 is_linear,
244 }),
245 #[cfg(feature = "options")]
246 BarycentricWeightScale::High => Box::new(TransformLut3x3::<
247 T,
248 u16,
249 SRC_LAYOUT,
250 DST_LAYOUT,
251 GRID_SIZE,
252 BIT_DEPTH,
253 65536,
254 65536,
255 > {
256 lut,
257 _phantom: PhantomData,
258 _phantom1: PhantomData,
259 interpolation_method: options.interpolation_method,
260 weights: BarycentricWeight::<f32>::create_binned::<GRID_SIZE, 65536>(),
261 color_space,
262 is_linear,
263 }),
264 }
265 }
266}