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