1use colorutils_rs::{
31 SRGB_TO_XYZ_D65, TransferFunction, XYZ_TO_SRGB_D65, lch_to_rgb, lch_with_alpha_to_rgba,
32 rgb_to_lch, rgba_to_lch_with_alpha,
33};
34
35use crate::pic_scale_error::PicScaleError;
36use crate::scaler::ScalingF32;
37use crate::support::check_image_size_overflow;
38use crate::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, Scaling, ThreadingPolicy};
39
40#[derive(Debug, Copy, Clone)]
41pub struct LChScaler {
43 pub(crate) scaler: Scaler,
44}
45
46impl LChScaler {
47 pub fn new(filter: ResamplingFunction) -> Self {
48 LChScaler {
49 scaler: Scaler::new(filter),
50 }
51 }
52
53 fn rgba_to_lcha<'a>(store: &ImageStore<'a, u8, 4>) -> ImageStore<'a, f32, 4> {
54 let mut source_slice = vec![f32::default(); 4 * store.width * store.height];
55 let lab_stride = store.width as u32 * 4u32 * size_of::<f32>() as u32;
56 rgba_to_lch_with_alpha(
57 store.buffer.as_ref(),
58 store.width as u32 * 4u32,
59 &mut source_slice,
60 lab_stride,
61 store.width as u32,
62 store.height as u32,
63 &SRGB_TO_XYZ_D65,
64 TransferFunction::Srgb,
65 );
66
67 ImageStore::<f32, 4> {
68 buffer: std::borrow::Cow::Owned(source_slice),
69 channels: 4,
70 width: store.width,
71 height: store.height,
72 stride: store.width * 4,
73 bit_depth: store.bit_depth,
74 }
75 }
76
77 fn lcha_to_srgba<'a>(store: &ImageStoreMut<'a, f32, 4>, into: &mut ImageStoreMut<'a, u8, 4>) {
78 lch_with_alpha_to_rgba(
79 store.buffer.borrow(),
80 store.width as u32 * 4u32 * size_of::<f32>() as u32,
81 into.buffer.borrow_mut(),
82 store.width as u32 * 4u32,
83 store.width as u32,
84 store.height as u32,
85 &XYZ_TO_SRGB_D65,
86 TransferFunction::Srgb,
87 );
88 }
89}
90
91impl Scaling for LChScaler {
92 fn set_threading_policy(&mut self, threading_policy: ThreadingPolicy) {
93 self.scaler.set_threading_policy(threading_policy)
94 }
95
96 fn resize_plane<'a>(
97 &'a self,
98 _: &ImageStore<'a, u8, 1>,
99 _: &mut ImageStoreMut<'a, u8, 1>,
100 ) -> Result<(), PicScaleError> {
101 unimplemented!()
102 }
103
104 fn resize_cbcr8<'a>(
105 &'a self,
106 _: &ImageStore<'a, u8, 2>,
107 _: &mut ImageStoreMut<'a, u8, 2>,
108 ) -> Result<(), PicScaleError> {
109 unimplemented!()
110 }
111
112 fn resize_gray_alpha<'a>(
113 &'a self,
114 _: &ImageStore<'a, u8, 2>,
115 _: &mut ImageStoreMut<'a, u8, 2>,
116 _: bool,
117 ) -> Result<(), PicScaleError> {
118 unimplemented!()
119 }
120
121 fn resize_rgb<'a>(
122 &'a self,
123 store: &ImageStore<'a, u8, 3>,
124 into: &mut ImageStoreMut<'a, u8, 3>,
125 ) -> Result<(), PicScaleError> {
126 let new_size = into.get_size();
127 into.validate()?;
128 store.validate()?;
129 if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
130 return Err(PicScaleError::ZeroImageDimensions);
131 }
132
133 if check_image_size_overflow(store.width, store.height, store.channels) {
134 return Err(PicScaleError::SourceImageIsTooLarge);
135 }
136
137 if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
138 return Err(PicScaleError::DestinationImageIsTooLarge);
139 }
140
141 if store.width == new_size.width && store.height == new_size.height {
142 store.copied_to_mut(into);
143 return Ok(());
144 }
145
146 const COMPONENTS: usize = 3;
147
148 let mut target_vertical = vec![f32::default(); store.width * store.height * COMPONENTS];
149
150 let mut lab_store = ImageStoreMut::<f32, COMPONENTS>::from_slice(
151 &mut target_vertical,
152 store.width,
153 store.height,
154 )?;
155 lab_store.bit_depth = into.bit_depth;
156
157 let lab_stride =
158 lab_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
159 rgb_to_lch(
160 store.buffer.as_ref(),
161 store.width as u32 * COMPONENTS as u32,
162 lab_store.buffer.borrow_mut(),
163 lab_stride,
164 lab_store.width as u32,
165 lab_store.height as u32,
166 &SRGB_TO_XYZ_D65,
167 TransferFunction::Srgb,
168 );
169
170 let new_immutable_store = ImageStore::<f32, COMPONENTS> {
171 buffer: std::borrow::Cow::Owned(target_vertical),
172 channels: COMPONENTS,
173 width: store.width,
174 height: store.height,
175 stride: store.width * COMPONENTS,
176 bit_depth: into.bit_depth,
177 };
178
179 let mut new_store = ImageStoreMut::<f32, COMPONENTS>::alloc(into.width, into.height);
180 self.scaler
181 .resize_rgb_f32(&new_immutable_store, &mut new_store)?;
182
183 let new_lab_stride =
184 new_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
185
186 lch_to_rgb(
187 new_store.buffer.borrow(),
188 new_lab_stride,
189 into.buffer.borrow_mut(),
190 into.width as u32 * COMPONENTS as u32,
191 new_store.width as u32,
192 new_store.height as u32,
193 &XYZ_TO_SRGB_D65,
194 TransferFunction::Srgb,
195 );
196 Ok(())
197 }
198
199 fn resize_rgba<'a>(
200 &'a self,
201 store: &ImageStore<'a, u8, 4>,
202 into: &mut ImageStoreMut<'a, u8, 4>,
203 premultiply_alpha: bool,
204 ) -> Result<(), PicScaleError> {
205 let new_size = into.get_size();
206 into.validate()?;
207 store.validate()?;
208 if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
209 return Err(PicScaleError::ZeroImageDimensions);
210 }
211
212 if check_image_size_overflow(store.width, store.height, store.channels) {
213 return Err(PicScaleError::SourceImageIsTooLarge);
214 }
215
216 if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
217 return Err(PicScaleError::DestinationImageIsTooLarge);
218 }
219
220 if store.width == new_size.width && store.height == new_size.height {
221 store.copied_to_mut(into);
222 return Ok(());
223 }
224
225 let lab_store = Self::rgba_to_lcha(store);
226 let mut new_target_store = ImageStoreMut::alloc(new_size.width, new_size.height);
227
228 self.scaler
229 .resize_rgba_f32(&lab_store, &mut new_target_store, premultiply_alpha)?;
230 Self::lcha_to_srgba(&new_target_store, into);
231 Ok(())
232 }
233}