1#![forbid(unsafe_code)]
30use crate::image_store::ImageStoreMut;
31use crate::plan::Resampling;
32use crate::scaler::ScalingOptions;
33use crate::validation::PicScaleError;
34use crate::{
35 CbCrF16ImageStore, ImageSize, ImageStoreScaling, PlanarF16ImageStore, RgbF16ImageStore,
36 RgbaF16ImageStore, Scaler,
37};
38use core::f16;
39use std::sync::Arc;
40
41#[cfg_attr(docsrs, doc(cfg(feature = "nightly_f16")))]
43impl Scaler {
44 pub fn plan_planar_resampling_f16(
62 &self,
63 source_size: ImageSize,
64 target_size: ImageSize,
65 ) -> Result<Arc<Resampling<f16, 1>>, PicScaleError> {
66 self.plan_generic_resize::<f16, f32, 1>(source_size, target_size, 8)
67 }
68
69 pub fn plan_cbcr_resampling_f16(
88 &self,
89 source_size: ImageSize,
90 target_size: ImageSize,
91 ) -> Result<Arc<Resampling<f16, 2>>, PicScaleError> {
92 self.plan_generic_resize::<f16, f32, 2>(source_size, target_size, 8)
93 }
94
95 pub fn plan_rgb_resampling_f16(
114 &self,
115 source_size: ImageSize,
116 target_size: ImageSize,
117 ) -> Result<Arc<Resampling<f16, 3>>, PicScaleError> {
118 self.plan_generic_resize::<f16, f32, 3>(source_size, target_size, 8)
119 }
120
121 pub fn plan_rgba_resampling_f16(
140 &self,
141 source_size: ImageSize,
142 target_size: ImageSize,
143 premultiply_alpha: bool,
144 ) -> Result<Arc<Resampling<f16, 4>>, PicScaleError> {
145 self.plan_generic_resize_with_alpha::<f16, f32, 4>(
146 source_size,
147 target_size,
148 8,
149 premultiply_alpha,
150 )
151 }
152}
153
154#[cfg_attr(docsrs, doc(cfg(feature = "nightly_f16")))]
155impl<'b> ImageStoreScaling<'b, f16, 1> for PlanarF16ImageStore<'b> {
156 fn scale(
157 &self,
158 store: &mut ImageStoreMut<'b, f16, 1>,
159 options: ScalingOptions,
160 ) -> Result<(), PicScaleError> {
161 let mut scaler = Scaler::new(options.resampling_function);
162 scaler.set_threading_policy(options.threading_policy);
163 let plan = scaler.plan_generic_resize(self.size(), store.size(), store.bit_depth)?;
164 plan.resample(self, store)
165 }
166}
167
168#[cfg_attr(docsrs, doc(cfg(feature = "nightly_f16")))]
169impl<'b> ImageStoreScaling<'b, f16, 2> for CbCrF16ImageStore<'b> {
170 fn scale(
171 &self,
172 store: &mut ImageStoreMut<'b, f16, 2>,
173 options: ScalingOptions,
174 ) -> Result<(), PicScaleError> {
175 let mut scaler = Scaler::new(options.resampling_function);
176 scaler.set_threading_policy(options.threading_policy);
177 let plan = scaler.plan_generic_resize(self.size(), store.size(), store.bit_depth)?;
178 plan.resample(self, store)
179 }
180}
181
182#[cfg_attr(docsrs, doc(cfg(feature = "nightly_f16")))]
183impl<'b> ImageStoreScaling<'b, f16, 3> for RgbF16ImageStore<'b> {
184 fn scale(
185 &self,
186 store: &mut ImageStoreMut<'b, f16, 3>,
187 options: ScalingOptions,
188 ) -> Result<(), PicScaleError> {
189 let mut scaler = Scaler::new(options.resampling_function);
190 scaler.set_threading_policy(options.threading_policy);
191 let plan = scaler.plan_generic_resize(self.size(), store.size(), store.bit_depth)?;
192 plan.resample(self, store)
193 }
194}
195
196#[cfg_attr(docsrs, doc(cfg(feature = "nightly_f16")))]
197impl<'b> ImageStoreScaling<'b, f16, 4> for RgbaF16ImageStore<'b> {
198 fn scale(
199 &self,
200 store: &mut ImageStoreMut<'b, f16, 4>,
201 options: ScalingOptions,
202 ) -> Result<(), PicScaleError> {
203 let mut scaler = Scaler::new(options.resampling_function);
204 scaler.set_threading_policy(options.threading_policy);
205 let plan = scaler.plan_generic_resize_with_alpha(
206 self.size(),
207 store.size(),
208 store.bit_depth,
209 options.premultiply_alpha,
210 )?;
211 plan.resample(self, store)
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218 use crate::{ImageStore, ResamplingFunction, ThreadingPolicy};
219 use core::f16;
220
221 #[test]
222 fn check_rgba_f16_resizing_vertical() {
223 let image_width = 8;
224 let image_height = 8;
225 const CN: usize = 4;
226 let mut image = vec![0_f16; image_height * image_width * CN];
227 for dst in image.as_chunks_mut::<CN>().0.iter_mut() {
228 dst[0] = (124f32 / 255f32) as f16;
229 dst[1] = (41f32 / 255f32) as f16;
230 dst[2] = (99f32 / 255f32) as f16;
231 dst[3] = 1f32 as f16;
232 }
233 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
234 scaler.set_threading_policy(ThreadingPolicy::Single);
235 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
236 let mut target_store = ImageStoreMut::<f16, 4>::alloc(image_width, image_height / 2);
237 let planned = scaler
238 .plan_rgba_resampling_f16(src_store.size(), target_store.size(), false)
239 .unwrap();
240 planned.resample(&src_store, &mut target_store).unwrap();
241 let target_data = target_store.buffer.borrow();
242
243 for dst in target_data.chunks_exact(CN) {
244 assert!(
245 (dst[0] as f32 * 255f32 - 124f32).abs() < 3f32,
246 "R channel mismatch: {}",
247 dst[0] as f32 * 255f32
248 );
249 assert!(
250 (dst[1] as f32 * 255f32 - 41f32).abs() < 3f32,
251 "G channel mismatch: {}",
252 dst[1] as f32 * 255f32
253 );
254 assert!(
255 (dst[2] as f32 * 255f32 - 99f32).abs() < 3f32,
256 "B channel mismatch: {}",
257 dst[2] as f32 * 255f32
258 );
259 assert!(
260 (dst[3] as f32 - 1f32).abs() < 0.01f32,
261 "A channel mismatch: {}",
262 dst[3] as f32
263 );
264 }
265 }
266
267 #[test]
268 fn check_rgba_f16_resizing_vertical_threading() {
269 let image_width = 8;
270 let image_height = 8;
271 const CN: usize = 4;
272 let mut image = vec![0_f16; image_height * image_width * CN];
273 for dst in image.as_chunks_mut::<CN>().0.iter_mut() {
274 dst[0] = (124f32 / 255f32) as f16;
275 dst[1] = (41f32 / 255f32) as f16;
276 dst[2] = (99f32 / 255f32) as f16;
277 dst[3] = 1f32 as f16;
278 }
279 let scaler = Scaler::new(ResamplingFunction::Lanczos3)
280 .set_threading_policy(ThreadingPolicy::Adaptive);
281 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
282 let mut target_store = ImageStoreMut::<f16, 4>::alloc(image_width, image_height / 2);
283 let planned = scaler
284 .plan_rgba_resampling_f16(src_store.size(), target_store.size(), false)
285 .unwrap();
286 planned.resample(&src_store, &mut target_store).unwrap();
287 let target_data = target_store.buffer.borrow();
288
289 for dst in target_data.chunks_exact(CN) {
290 assert!(
291 (dst[0] as f32 * 255f32 - 124f32).abs() < 3f32,
292 "R channel mismatch: {}",
293 dst[0] as f32 * 255f32
294 );
295 assert!(
296 (dst[1] as f32 * 255f32 - 41f32).abs() < 3f32,
297 "G channel mismatch: {}",
298 dst[1] as f32 * 255f32
299 );
300 assert!(
301 (dst[2] as f32 * 255f32 - 99f32).abs() < 3f32,
302 "B channel mismatch: {}",
303 dst[2] as f32 * 255f32
304 );
305 assert!(
306 (dst[3] as f32 - 1f32).abs() < 0.01f32,
307 "A channel mismatch: {}",
308 dst[3] as f32
309 );
310 }
311 }
312
313 #[test]
314 fn check_rgba_f16_nearest_vertical() {
315 let image_width = 255;
316 let image_height = 512;
317 const CN: usize = 4;
318 let mut image = vec![0_f16; image_height * image_width * CN];
319 for dst in image.as_chunks_mut::<CN>().0.iter_mut() {
320 dst[0] = (124f32 / 255f32) as f16;
321 dst[1] = (41f32 / 255f32) as f16;
322 dst[2] = (99f32 / 255f32) as f16;
323 dst[3] = (77f32 / 255f32) as f16;
324 }
325 let mut scaler = Scaler::new(ResamplingFunction::Nearest);
326 scaler.set_threading_policy(ThreadingPolicy::Single);
327 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
328 let mut target_store = ImageStoreMut::<f16, 4>::alloc(image_width, image_height / 2);
329 let planned = scaler
330 .plan_rgba_resampling_f16(src_store.size(), target_store.size(), false)
331 .unwrap();
332 planned.resample(&src_store, &mut target_store).unwrap();
333 let target_data = target_store.buffer.borrow();
334
335 for dst in target_data.chunks_exact(CN) {
336 assert!(
337 (dst[0] as f32 * 255f32 - 124f32).abs() < 3f32,
338 "R channel mismatch: {}",
339 dst[0] as f32 * 255f32
340 );
341 assert!(
342 (dst[1] as f32 * 255f32 - 41f32).abs() < 3f32,
343 "G channel mismatch: {}",
344 dst[1] as f32 * 255f32
345 );
346 assert!(
347 (dst[2] as f32 * 255f32 - 99f32).abs() < 3f32,
348 "B channel mismatch: {}",
349 dst[2] as f32 * 255f32
350 );
351 assert!(
352 (dst[3] as f32 * 255f32 - 77f32).abs() < 3f32,
353 "A channel mismatch: {}",
354 dst[3] as f32 * 255f32
355 );
356 }
357 }
358
359 #[test]
360 fn check_rgba_f16_nearest_vertical_threading() {
361 let image_width = 255;
362 let image_height = 512;
363 const CN: usize = 4;
364 let mut image = vec![0_f16; image_height * image_width * CN];
365 for dst in image.as_chunks_mut::<CN>().0.iter_mut() {
366 dst[0] = (124f32 / 255f32) as f16;
367 dst[1] = (41f32 / 255f32) as f16;
368 dst[2] = (99f32 / 255f32) as f16;
369 dst[3] = (77f32 / 255f32) as f16;
370 }
371 let scaler = Scaler::new(ResamplingFunction::Nearest)
372 .set_threading_policy(ThreadingPolicy::Adaptive);
373 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
374 let mut target_store = ImageStoreMut::<f16, 4>::alloc(image_width, image_height / 2);
375 let planned = scaler
376 .plan_rgba_resampling_f16(src_store.size(), target_store.size(), false)
377 .unwrap();
378 planned.resample(&src_store, &mut target_store).unwrap();
379 let target_data = target_store.buffer.borrow();
380
381 for dst in target_data.chunks_exact(CN) {
382 assert!(
383 (dst[0] as f32 * 255f32 - 124f32).abs() < 3f32,
384 "R channel mismatch: {}",
385 dst[0] as f32 * 255f32
386 );
387 assert!(
388 (dst[1] as f32 * 255f32 - 41f32).abs() < 3f32,
389 "G channel mismatch: {}",
390 dst[1] as f32 * 255f32
391 );
392 assert!(
393 (dst[2] as f32 * 255f32 - 99f32).abs() < 3f32,
394 "B channel mismatch: {}",
395 dst[2] as f32 * 255f32
396 );
397 assert!(
398 (dst[3] as f32 * 255f32 - 77f32).abs() < 3f32,
399 "A channel mismatch: {}",
400 dst[3] as f32 * 255f32
401 );
402 }
403 }
404
405 #[test]
406 fn check_rgb_f16_resizing_vertical() {
407 let image_width = 8;
408 let image_height = 8;
409 const CN: usize = 3;
410 let mut image = vec![0_f16; image_height * image_width * CN];
411 for dst in image.as_chunks_mut::<CN>().0.iter_mut() {
412 dst[0] = (124f32 / 255f32) as f16;
413 dst[1] = (41f32 / 255f32) as f16;
414 dst[2] = (99f32 / 255f32) as f16;
415 }
416 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
417 scaler.set_threading_policy(ThreadingPolicy::Single);
418 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
419 let mut target_store = ImageStoreMut::<f16, 3>::alloc(image_width, image_height / 2);
420 let planned = scaler
421 .plan_rgb_resampling_f16(src_store.size(), target_store.size())
422 .unwrap();
423 planned.resample(&src_store, &mut target_store).unwrap();
424 let target_data = target_store.buffer.borrow();
425
426 for dst in target_data.as_chunks::<CN>().0.iter() {
427 assert!(
428 (dst[0] as f32 * 255f32 - 124f32).abs() < 3f32,
429 "R channel mismatch: {}",
430 dst[0] as f32 * 255f32
431 );
432 assert!(
433 (dst[1] as f32 * 255f32 - 41f32).abs() < 3f32,
434 "G channel mismatch: {}",
435 dst[1] as f32 * 255f32
436 );
437 assert!(
438 (dst[2] as f32 * 255f32 - 99f32).abs() < 3f32,
439 "B channel mismatch: {}",
440 dst[2] as f32 * 255f32
441 );
442 }
443 }
444
445 #[test]
446 fn check_cbcr_f16_resizing_vertical() {
447 let image_width = 8;
448 let image_height = 8;
449 const CN: usize = 2;
450 let mut image = vec![0_f16; image_height * image_width * CN];
451 for dst in image.as_chunks_mut::<CN>().0.iter_mut() {
452 dst[0] = (124f32 / 255f32) as f16;
453 dst[1] = (41f32 / 255f32) as f16;
454 }
455 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
456 scaler.set_threading_policy(ThreadingPolicy::Single);
457 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
458 let mut target_store = ImageStoreMut::<f16, 2>::alloc(image_width, image_height / 2);
459 let planned = scaler
460 .plan_cbcr_resampling_f16(src_store.size(), target_store.size())
461 .unwrap();
462 planned.resample(&src_store, &mut target_store).unwrap();
463 let target_data = target_store.buffer.borrow();
464
465 for dst in target_data.as_chunks::<CN>().0.iter() {
466 assert!(
467 (dst[0] as f32 * 255f32 - 124f32).abs() < 3f32,
468 "R channel mismatch: {}",
469 dst[0] as f32 * 255f32
470 );
471 assert!(
472 (dst[1] as f32 * 255f32 - 41f32).abs() < 3f32,
473 "G channel mismatch: {}",
474 dst[1] as f32 * 255f32
475 );
476 }
477 }
478
479 #[test]
480 fn check_planar_f16_resizing_vertical() {
481 let image_width = 8;
482 let image_height = 8;
483 const CN: usize = 1;
484 let mut image = vec![0_f16; image_height * image_width * CN];
485 for dst in image.as_chunks_mut::<CN>().0.iter_mut() {
486 dst[0] = (124f32 / 255f32) as f16;
487 }
488 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
489 scaler.set_threading_policy(ThreadingPolicy::Single);
490 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
491 let mut target_store = ImageStoreMut::<f16, 1>::alloc(image_width, image_height / 2);
492 let planned = scaler
493 .plan_planar_resampling_f16(src_store.size(), target_store.size())
494 .unwrap();
495 planned.resample(&src_store, &mut target_store).unwrap();
496 let target_data = target_store.buffer.borrow();
497
498 for dst in target_data.as_chunks::<CN>().0.iter() {
499 assert!(
500 (dst[0] as f32 * 255f32 - 124f32).abs() < 3f32,
501 "R channel mismatch: {}",
502 dst[0] as f32 * 255f32
503 );
504 }
505 }
506}