1use colorutils_rs::{
31 rgba_to_xyz_with_alpha, srgb_to_xyz, xyz_to_srgb, xyz_with_alpha_to_rgba, TransferFunction,
32 SRGB_TO_XYZ_D65, XYZ_TO_SRGB_D65,
33};
34
35use crate::pic_scale_error::PicScaleError;
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_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 &'a 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
106 srgb_to_xyz(
107 store.buffer.as_ref(),
108 store.width as u32 * COMPONENTS as u32,
109 lab_store.buffer.borrow_mut(),
110 lab_stride,
111 lab_store.width as u32,
112 lab_store.height as u32,
113 );
114
115 let new_immutable_store = ImageStore::<f32, COMPONENTS> {
116 buffer: std::borrow::Cow::Owned(target_vertical),
117 channels: COMPONENTS,
118 width: store.width,
119 height: store.height,
120 stride: store.width * COMPONENTS,
121 bit_depth: into.bit_depth,
122 };
123
124 let mut new_store = ImageStoreMut::<f32, COMPONENTS>::alloc(into.width, into.height);
125
126 self.scaler
127 .resize_rgb_f32(&new_immutable_store, &mut new_store)?;
128 let new_lab_stride =
129 new_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
130 xyz_to_srgb(
131 new_store.buffer.borrow(),
132 new_lab_stride,
133 into.buffer.borrow_mut(),
134 into.width as u32 * COMPONENTS as u32,
135 new_store.width as u32,
136 new_store.height as u32,
137 );
138 Ok(())
139 }
140
141 fn resize_rgba<'a>(
142 &'a self,
143 store: &ImageStore<'a, u8, 4>,
144 into: &mut ImageStoreMut<'a, u8, 4>,
145 premultiply_alpha: bool,
146 ) -> Result<(), PicScaleError> {
147 let new_size = into.get_size();
148 into.validate()?;
149 store.validate()?;
150 if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
151 return Err(PicScaleError::ZeroImageDimensions);
152 }
153
154 if check_image_size_overflow(store.width, store.height, store.channels) {
155 return Err(PicScaleError::SourceImageIsTooLarge);
156 }
157
158 if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
159 return Err(PicScaleError::DestinationImageIsTooLarge);
160 }
161
162 if store.width == new_size.width && store.height == new_size.height {
163 store.copied_to_mut(into);
164 return Ok(());
165 }
166
167 const COMPONENTS: usize = 4;
168
169 let mut target_vertical = vec![f32::default(); store.width * store.height * COMPONENTS];
170
171 let mut lab_store = ImageStoreMut::<f32, COMPONENTS>::from_slice(
172 &mut target_vertical,
173 store.width,
174 store.height,
175 )?;
176 lab_store.bit_depth = into.bit_depth;
177
178 let lab_stride =
179 lab_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
180
181 rgba_to_xyz_with_alpha(
182 store.buffer.as_ref(),
183 store.width as u32 * COMPONENTS as u32,
184 lab_store.buffer.borrow_mut(),
185 lab_stride,
186 lab_store.width as u32,
187 lab_store.height as u32,
188 &SRGB_TO_XYZ_D65,
189 TransferFunction::Srgb,
190 );
191
192 let new_immutable_store = ImageStore::<f32, COMPONENTS> {
193 buffer: std::borrow::Cow::Owned(target_vertical),
194 channels: COMPONENTS,
195 width: store.width,
196 height: store.height,
197 stride: store.width * COMPONENTS,
198 bit_depth: into.bit_depth,
199 };
200
201 let mut new_store = ImageStoreMut::<f32, COMPONENTS>::alloc(into.width, into.height);
202
203 self.scaler
204 .resize_rgba_f32(&new_immutable_store, &mut new_store, premultiply_alpha)?;
205 let new_lab_stride =
206 new_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
207 xyz_with_alpha_to_rgba(
208 new_store.buffer.borrow(),
209 new_lab_stride,
210 into.buffer.borrow_mut(),
211 into.width as u32 * COMPONENTS as u32,
212 new_store.width as u32,
213 new_store.height as u32,
214 &XYZ_TO_SRGB_D65,
215 TransferFunction::Srgb,
216 );
217 Ok(())
218 }
219}