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 lab_to_srgb, lab_with_alpha_to_rgba, rgb_to_lab, rgba_to_lab_with_alpha, TransferFunction,
36 SRGB_TO_XYZ_D65, XYZ_TO_SRGB_D65,
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 * std::mem::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 let new_store = 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 new_store
74 }
75
76 fn laba_to_srgba<'a>(store: &ImageStoreMut<'a, f32, 4>, into: &mut ImageStoreMut<'a, u8, 4>) {
77 lab_with_alpha_to_rgba(
78 store.buffer.borrow(),
79 store.width as u32 * 4u32 * std::mem::size_of::<f32>() as u32,
80 into.buffer.borrow_mut(),
81 store.width as u32 * 4u32,
82 store.width as u32,
83 store.height as u32,
84 &XYZ_TO_SRGB_D65,
85 TransferFunction::Srgb,
86 );
87 }
88}
89
90impl Scaling for LabScaler {
91 fn set_threading_policy(&mut self, threading_policy: ThreadingPolicy) {
92 self.scaler.threading_policy = threading_policy;
93 }
94
95 fn resize_cbcr8<'a>(
96 &'a self,
97 _: &ImageStore<'a, u8, 2>,
98 _: &mut ImageStoreMut<'a, u8, 2>,
99 ) -> Result<(), PicScaleError> {
100 unimplemented!()
101 }
102
103 fn resize_rgb<'a>(
104 &self,
105 store: &ImageStore<'a, u8, 3>,
106 into: &mut ImageStoreMut<'a, u8, 3>,
107 ) -> Result<(), PicScaleError> {
108 let new_size = into.get_size();
109 into.validate()?;
110 store.validate()?;
111 if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
112 return Err(PicScaleError::ZeroImageDimensions);
113 }
114
115 if check_image_size_overflow(store.width, store.height, store.channels) {
116 return Err(PicScaleError::SourceImageIsTooLarge);
117 }
118
119 if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
120 return Err(PicScaleError::DestinationImageIsTooLarge);
121 }
122
123 if store.width == new_size.width && store.height == new_size.height {
124 store.copied_to_mut(into);
125 return Ok(());
126 }
127
128 const COMPONENTS: usize = 3;
129
130 let mut target = vec![f32::default(); store.width * store.height * COMPONENTS];
131
132 let mut lab_store =
133 ImageStoreMut::<f32, COMPONENTS>::from_slice(&mut target, store.width, store.height)?;
134 lab_store.bit_depth = into.bit_depth;
135
136 let lab_stride =
137 lab_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
138
139 rgb_to_lab(
140 store.buffer.as_ref(),
141 store.width as u32 * COMPONENTS as u32,
142 lab_store.buffer.borrow_mut(),
143 lab_stride,
144 lab_store.width as u32,
145 lab_store.height as u32,
146 &SRGB_TO_XYZ_D65,
147 TransferFunction::Srgb,
148 );
149
150 let new_immutable_store = ImageStore::<f32, COMPONENTS> {
151 buffer: std::borrow::Cow::Owned(target),
152 channels: COMPONENTS,
153 width: store.width,
154 height: store.height,
155 stride: store.width * COMPONENTS,
156 bit_depth: into.bit_depth,
157 };
158
159 let mut new_store = ImageStoreMut::<f32, COMPONENTS>::alloc(into.width, into.height);
160 self.scaler
161 .resize_rgb_f32(&new_immutable_store, &mut new_store)?;
162
163 let new_lab_stride =
164 new_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
165 lab_to_srgb(
166 new_store.buffer.borrow(),
167 new_lab_stride,
168 into.buffer.borrow_mut(),
169 into.width as u32 * COMPONENTS as u32,
170 new_store.width as u32,
171 new_store.height as u32,
172 );
173 Ok(())
174 }
175
176 fn resize_rgba<'a>(
177 &'a self,
178 store: &ImageStore<'a, u8, 4>,
179 into: &mut ImageStoreMut<'a, u8, 4>,
180 premultiply_alpha: bool,
181 ) -> Result<(), PicScaleError> {
182 let new_size = into.get_size();
183 into.validate()?;
184 store.validate()?;
185 if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
186 return Err(PicScaleError::ZeroImageDimensions);
187 }
188
189 if check_image_size_overflow(store.width, store.height, store.channels) {
190 return Err(PicScaleError::SourceImageIsTooLarge);
191 }
192
193 if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
194 return Err(PicScaleError::DestinationImageIsTooLarge);
195 }
196
197 if store.width == new_size.width && store.height == new_size.height {
198 store.copied_to_mut(into);
199 return Ok(());
200 }
201
202 let lab_store = Self::rgba_to_laba(store);
203 let mut new_target_store = ImageStoreMut::alloc(new_size.width, new_size.height);
204
205 self.scaler
206 .resize_rgba_f32(&lab_store, &mut new_target_store, premultiply_alpha)?;
207 Self::laba_to_srgba(&new_target_store, into);
208 Ok(())
209 }
210}