1use colorutils_rs::{oklab_to_rgb, oklab_to_rgba, rgb_to_oklab, rgba_to_oklab, TransferFunction};
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_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_oklab(
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 self.transfer_function,
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 self.scaler
126 .resize_rgb_f32(&new_immutable_store, &mut new_store)?;
127
128 let new_lab_stride =
129 new_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
130
131 oklab_to_rgb(
132 new_store.buffer.borrow(),
133 new_lab_stride,
134 into.buffer.borrow_mut(),
135 into.width as u32 * COMPONENTS as u32,
136 new_store.width as u32,
137 new_store.height as u32,
138 self.transfer_function,
139 );
140 Ok(())
141 }
142
143 fn resize_rgba<'a>(
144 &'a self,
145 store: &ImageStore<'a, u8, 4>,
146 into: &mut ImageStoreMut<'a, u8, 4>,
147 premultiply_alpha: bool,
148 ) -> Result<(), PicScaleError> {
149 let new_size = into.get_size();
150 into.validate()?;
151 store.validate()?;
152 if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
153 return Err(PicScaleError::ZeroImageDimensions);
154 }
155
156 if check_image_size_overflow(store.width, store.height, store.channels) {
157 return Err(PicScaleError::SourceImageIsTooLarge);
158 }
159
160 if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
161 return Err(PicScaleError::DestinationImageIsTooLarge);
162 }
163
164 if store.width == new_size.width && store.height == new_size.height {
165 store.copied_to_mut(into);
166 return Ok(());
167 }
168
169 const COMPONENTS: usize = 4;
170
171 let mut target_vertical = vec![f32::default(); store.width * store.height * COMPONENTS];
172
173 let mut lab_store = ImageStoreMut::<f32, COMPONENTS>::from_slice(
174 &mut target_vertical,
175 store.width,
176 store.height,
177 )?;
178 lab_store.bit_depth = into.bit_depth;
179
180 let lab_stride =
181 lab_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
182 rgba_to_oklab(
183 store.buffer.as_ref(),
184 store.width as u32 * COMPONENTS as u32,
185 lab_store.buffer.borrow_mut(),
186 lab_stride,
187 lab_store.width as u32,
188 lab_store.height as u32,
189 self.transfer_function,
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 self.scaler
203 .resize_rgba_f32(&new_immutable_store, &mut new_store, premultiply_alpha)?;
204
205 let new_lab_stride =
206 new_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
207
208 oklab_to_rgba(
209 new_store.buffer.borrow(),
210 new_lab_stride,
211 into.buffer.borrow_mut(),
212 into.width as u32 * COMPONENTS as u32,
213 new_store.width as u32,
214 new_store.height as u32,
215 self.transfer_function,
216 );
217 Ok(())
218 }
219}