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