1use colorutils_rs::{TransferFunction, oklab_to_rgb, oklab_to_rgba, rgb_to_oklab, rgba_to_oklab};
30
31use crate::pic_scale_error::PicScaleError;
32use crate::scaler::ScalingF32;
33use crate::support::check_image_size_overflow;
34use crate::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, Scaling, ThreadingPolicy};
35
36#[derive(Debug, Copy, Clone)]
37pub struct OklabScaler {
39 pub(crate) scaler: Scaler,
40 pub(crate) transfer_function: TransferFunction,
41}
42
43impl OklabScaler {
44 pub fn new(filter: ResamplingFunction, transfer_function: TransferFunction) -> Self {
47 OklabScaler {
48 scaler: Scaler::new(filter),
49 transfer_function,
50 }
51 }
52}
53
54impl Scaling for OklabScaler {
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 &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 = 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 rgb_to_oklab(
119 store.buffer.as_ref(),
120 store.width as u32 * CN as u32,
121 lab_store.buffer.borrow_mut(),
122 lab_stride,
123 lab_store.width as u32,
124 lab_store.height as u32,
125 self.transfer_function,
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>::alloc(into.width, into.height);
138 self.scaler
139 .resize_rgb_f32(&new_immutable_store, &mut new_store)?;
140
141 let new_lab_stride = new_store.width as u32 * CN as u32 * size_of::<f32>() as u32;
142
143 oklab_to_rgb(
144 new_store.buffer.borrow(),
145 new_lab_stride,
146 into.buffer.borrow_mut(),
147 into.width as u32 * CN as u32,
148 new_store.width as u32,
149 new_store.height as u32,
150 self.transfer_function,
151 );
152 Ok(())
153 }
154
155 fn resize_rgba<'a>(
156 &'a self,
157 store: &ImageStore<'a, u8, 4>,
158 into: &mut ImageStoreMut<'a, u8, 4>,
159 premultiply_alpha: bool,
160 ) -> Result<(), PicScaleError> {
161 let new_size = into.get_size();
162 into.validate()?;
163 store.validate()?;
164 if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
165 return Err(PicScaleError::ZeroImageDimensions);
166 }
167
168 if check_image_size_overflow(store.width, store.height, store.channels) {
169 return Err(PicScaleError::SourceImageIsTooLarge);
170 }
171
172 if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
173 return Err(PicScaleError::DestinationImageIsTooLarge);
174 }
175
176 if store.width == new_size.width && store.height == new_size.height {
177 store.copied_to_mut(into);
178 return Ok(());
179 }
180
181 const CN: usize = 4;
182
183 let mut target_vertical = vec![f32::default(); store.width * store.height * CN];
184
185 let mut lab_store =
186 ImageStoreMut::<f32, CN>::from_slice(&mut target_vertical, store.width, store.height)?;
187 lab_store.bit_depth = into.bit_depth;
188
189 let lab_stride = lab_store.width as u32 * CN as u32 * size_of::<f32>() as u32;
190 rgba_to_oklab(
191 store.buffer.as_ref(),
192 store.width as u32 * CN as u32,
193 lab_store.buffer.borrow_mut(),
194 lab_stride,
195 lab_store.width as u32,
196 lab_store.height as u32,
197 self.transfer_function,
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>::alloc(into.width, into.height);
210 self.scaler
211 .resize_rgba_f32(&new_immutable_store, &mut new_store, premultiply_alpha)?;
212
213 let new_lab_stride = new_store.width as u32 * CN as u32 * size_of::<f32>() as u32;
214
215 oklab_to_rgba(
216 new_store.buffer.borrow(),
217 new_lab_stride,
218 into.buffer.borrow_mut(),
219 into.width as u32 * CN as u32,
220 new_store.width as u32,
221 new_store.height as u32,
222 self.transfer_function,
223 );
224 Ok(())
225 }
226}