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