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