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