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