1#![forbid(unsafe_code)]
30use crate::convolution::{
31 ConvolutionOptions, HorizontalFilterPass, TrampolineFilter, VerticalConvolutionPass,
32};
33use crate::factory::{Ar30ByteOrder, Rgb30};
34use crate::image_size::ImageSize;
35use crate::image_store::{
36 AssociateAlpha, CheckStoreDensity, ImageStore, ImageStoreMut, UnassociateAlpha,
37};
38use crate::math::WeightsGenerator;
39use crate::plan::{
40 Ar30Destructuring, Ar30DestructuringImpl, Ar30Plan, BothAxesConvolvePlan,
41 HorizontalConvolvePlan, MultiStepResamplePlan, NoopPlan, ResampleNearestPlan, Resampling,
42 TrampolineFiltering, VerticalConvolvePlan, make_alpha_plan,
43};
44use crate::threading_policy::ThreadingPolicy;
45use crate::validation::PicScaleError;
46use crate::{
47 CbCr8ImageStore, CbCr16ImageStore, CbCrF32ImageStore, Planar8ImageStore, Planar16ImageStore,
48 PlanarF32ImageStore, ResamplingFunction, ResamplingPlan, Rgb8ImageStore, Rgb16ImageStore,
49 RgbF32ImageStore, Rgba8ImageStore, Rgba16ImageStore, RgbaF32ImageStore,
50};
51use std::fmt::Debug;
52use std::marker::PhantomData;
53use std::sync::Arc;
54
55#[derive(Debug, Copy, Clone)]
56pub struct Scaler {
58 pub(crate) function: ResamplingFunction,
59 pub(crate) threading_policy: ThreadingPolicy,
60 pub workload_strategy: WorkloadStrategy,
61 pub(crate) multi_step_upscaling: bool,
62 pub(crate) supersampling: bool,
63}
64
65#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Default)]
67pub enum WorkloadStrategy {
68 PreferQuality,
70 #[default]
72 PreferSpeed,
73}
74
75fn supersampling_prefilter(ratio_w: f64, ratio_h: f64) -> Option<ResamplingFunction> {
81 let ratio = ratio_w.max(ratio_h);
82 if ratio >= 4.0 {
83 Some(ResamplingFunction::Nearest)
84 } else if ratio >= 3.0 {
85 Some(ResamplingFunction::Box)
86 } else {
87 None
88 }
89}
90
91fn supersampling_intermediate_size(src: ImageSize, dst: ImageSize) -> ImageSize {
97 let w = (dst.width * 2).min(src.width).max(dst.width);
99 let h = (dst.height * 2).min(src.height).max(dst.height);
100 ImageSize::new(w, h)
101}
102
103impl Scaler {
104 pub fn new(filter: ResamplingFunction) -> Self {
109 Scaler {
110 function: filter,
111 threading_policy: ThreadingPolicy::Single,
112 workload_strategy: WorkloadStrategy::default(),
113 multi_step_upscaling: false,
114 supersampling: false,
115 }
116 }
117
118 pub fn set_workload_strategy(&mut self, workload_strategy: WorkloadStrategy) -> Self {
122 self.workload_strategy = workload_strategy;
123 *self
124 }
125
126 pub fn set_multi_step_upsampling(&mut self, value: bool) -> Self {
143 self.multi_step_upscaling = value;
144 *self
145 }
146
147 pub fn set_supersampling(&mut self, value: bool) -> Self {
166 self.supersampling = value;
167 *self
168 }
169}
170
171impl Scaler {
172 pub(crate) fn plan_intermediate_sizes(
176 src: ImageSize,
177 dst: ImageSize,
178 function: ResamplingFunction,
179 ) -> Vec<ImageSize> {
180 let max_ratio = function
181 .get_resampling_filter::<f32>()
182 .min_kernel_size
183 .max(1.5)
184 .min(4.0) as f64;
185
186 if max_ratio == f64::MAX {
188 return Vec::new();
189 }
190
191 let steps_w = if dst.width > src.width {
193 let ratio = dst.width as f64 / src.width as f64;
194 (ratio.log2() / max_ratio.log2()).ceil() as usize
195 } else {
196 0
197 };
198 let steps_h = if dst.height > src.height {
199 let ratio = dst.height as f64 / src.height as f64;
200 (ratio.log2() / max_ratio.log2()).ceil() as usize
201 } else {
202 0
203 };
204 let steps = steps_w.max(steps_h);
205
206 if steps <= 1 {
207 return Vec::new();
208 }
209
210 let mut sizes = Vec::with_capacity(steps - 1);
212 for i in 1..steps {
213 let t = i as f64 / steps as f64;
214 let w = if dst.width > src.width {
215 (src.width as f64 * (dst.width as f64 / src.width as f64).powf(t)).round() as usize
216 } else {
217 dst.width
218 };
219 let h = if dst.height > src.height {
220 (src.height as f64 * (dst.height as f64 / src.height as f64).powf(t)).round()
221 as usize
222 } else {
223 dst.height
224 };
225 let w = w.max(src.width).min(dst.width);
226 let h = h.max(src.height).min(dst.height);
227 sizes.push(ImageSize::new(w, h));
228 }
229
230 sizes.dedup_by(|a, b| a.width == b.width && a.height == b.height);
231
232 sizes
233 }
234
235 pub(crate) fn plan_generic_resize<
236 T: Clone + Copy + Debug + Send + Sync + Default + WeightsGenerator<W> + 'static,
237 W,
238 const N: usize,
239 >(
240 &self,
241 source_size: ImageSize,
242 destination_size: ImageSize,
243 bit_depth: usize,
244 ) -> Result<Arc<Resampling<T, N>>, PicScaleError>
245 where
246 for<'a> ImageStore<'a, T, N>:
247 VerticalConvolutionPass<T, W, N> + HorizontalFilterPass<T, W, N>,
248 for<'a> ImageStoreMut<'a, T, N>: CheckStoreDensity,
249 {
250 if self.function == ResamplingFunction::Nearest {
251 return Ok(Arc::new(ResampleNearestPlan {
252 source_size,
253 target_size: destination_size,
254 threading_policy: self.threading_policy,
255 _phantom_data: PhantomData,
256 }));
257 }
258
259 let is_upscale = destination_size.width >= source_size.width
261 && destination_size.height >= source_size.height;
262
263 if self.multi_step_upscaling && is_upscale {
264 let intermediates =
265 Scaler::plan_intermediate_sizes(source_size, destination_size, self.function);
266
267 if !intermediates.is_empty() {
268 let mut steps: Vec<Arc<Resampling<T, N>>> = Vec::new();
270 let mut prev_size = source_size;
271
272 for &next_size in intermediates
273 .iter()
274 .chain(std::iter::once(&destination_size))
275 {
276 let sub_plan =
277 self.build_single_step_plan::<T, W, N>(prev_size, next_size, bit_depth)?;
278 steps.push(sub_plan);
279 prev_size = next_size;
280 }
281
282 return Ok(Arc::new(MultiStepResamplePlan::new(
283 steps,
284 source_size,
285 destination_size,
286 )));
287 }
288 }
289
290 let is_downscale = destination_size.width <= source_size.width
292 && destination_size.height <= source_size.height;
293
294 if self.supersampling && is_downscale {
295 let ratio_w = source_size.width as f64 / destination_size.width as f64;
296 let ratio_h = source_size.height as f64 / destination_size.height as f64;
297
298 if let Some(prefilter) = supersampling_prefilter(ratio_w, ratio_h) {
299 let intermediate = supersampling_intermediate_size(source_size, destination_size);
300
301 if intermediate.width < source_size.width
304 || intermediate.height < source_size.height
305 {
306 let pre_scaler = Scaler {
308 function: prefilter,
309 threading_policy: self.threading_policy,
310 workload_strategy: self.workload_strategy,
311 multi_step_upscaling: false,
312 supersampling: false, };
314 let pre_plan = pre_scaler.build_single_step_plan::<T, W, N>(
315 source_size,
316 intermediate,
317 bit_depth,
318 )?;
319
320 let quality_plan = self.build_single_step_plan::<T, W, N>(
321 intermediate,
322 destination_size,
323 bit_depth,
324 )?;
325
326 return Ok(Arc::new(MultiStepResamplePlan::new(
327 vec![pre_plan, quality_plan],
328 source_size,
329 destination_size,
330 )));
331 }
332 }
333 }
334
335 self.build_single_step_plan::<T, W, N>(source_size, destination_size, bit_depth)
336 }
337
338 pub(crate) fn build_single_step_plan<
339 T: Clone + Copy + Debug + Send + Sync + Default + WeightsGenerator<W> + 'static,
340 W,
341 const N: usize,
342 >(
343 &self,
344 source_size: ImageSize,
345 destination_size: ImageSize,
346 bit_depth: usize,
347 ) -> Result<Arc<Resampling<T, N>>, PicScaleError>
348 where
349 for<'a> ImageStore<'a, T, N>:
350 VerticalConvolutionPass<T, W, N> + HorizontalFilterPass<T, W, N>,
351 for<'a> ImageStoreMut<'a, T, N>: CheckStoreDensity,
352 {
353 if self.function == ResamplingFunction::Nearest {
354 return Ok(Arc::new(ResampleNearestPlan {
355 source_size,
356 target_size: destination_size,
357 threading_policy: self.threading_policy,
358 _phantom_data: PhantomData,
359 }));
360 }
361
362 let should_do_horizontal = source_size.width != destination_size.width;
363 let should_do_vertical = source_size.height != destination_size.height;
364
365 let options = ConvolutionOptions {
366 workload_strategy: self.workload_strategy,
367 bit_depth,
368 src_size: source_size,
369 dst_size: destination_size,
370 };
371
372 let vertical_plan = if should_do_vertical {
373 let vertical_filters =
374 T::make_weights(self.function, source_size.height, destination_size.height)?;
375 Some(ImageStore::<T, N>::vertical_plan(
376 vertical_filters,
377 self.threading_policy,
378 options,
379 ))
380 } else {
381 None
382 };
383
384 let horizontal_plan = if should_do_horizontal {
385 let horizontal_filters =
386 T::make_weights(self.function, source_size.width, destination_size.width)?;
387 Some(ImageStore::<T, N>::horizontal_plan(
388 horizontal_filters,
389 self.threading_policy,
390 options,
391 ))
392 } else {
393 None
394 };
395
396 match (should_do_vertical, should_do_horizontal) {
397 (true, true) => {
398 let v = vertical_plan.expect("Should have a vertical filter");
399 let h = horizontal_plan.expect("Should have a horizontal filter");
400 let trampoline = Arc::new(TrampolineFiltering {
401 horizontal_filter: h.clone(),
402 vertical_filter: v.clone(),
403 source_size,
404 target_size: destination_size,
405 });
406 Ok(Arc::new(BothAxesConvolvePlan {
407 source_size,
408 target_size: destination_size,
409 horizontal_filter: h,
410 vertical_filter: v,
411 trampoline_filter: trampoline,
412 threading_policy: self.threading_policy,
413 }))
414 }
415 (true, false) => Ok(Arc::new(VerticalConvolvePlan {
416 source_size,
417 target_size: destination_size,
418 vertical_filter: vertical_plan.expect("Should have a vertical filter"),
419 })),
420 (false, true) => Ok(Arc::new(HorizontalConvolvePlan {
421 source_size,
422 target_size: destination_size,
423 horizontal_filter: horizontal_plan.expect("Should have a horizontal filter"),
424 })),
425 (false, false) => Ok(Arc::new(NoopPlan {
426 source_size,
427 target_size: destination_size,
428 _phantom: PhantomData,
429 })),
430 }
431 }
432
433 pub(crate) fn plan_generic_resize_with_alpha<
434 T: Clone + Copy + Debug + Send + Sync + Default + WeightsGenerator<W> + 'static,
435 W,
436 const N: usize,
437 >(
438 &self,
439 source_size: ImageSize,
440 destination_size: ImageSize,
441 bit_depth: usize,
442 needs_alpha_premultiplication: bool,
443 ) -> Result<Arc<Resampling<T, N>>, PicScaleError>
444 where
445 for<'a> ImageStore<'a, T, N>:
446 VerticalConvolutionPass<T, W, N> + HorizontalFilterPass<T, W, N> + AssociateAlpha<T, N>,
447 for<'a> ImageStoreMut<'a, T, N>: CheckStoreDensity + UnassociateAlpha<T, N>,
448 {
449 if self.function == ResamplingFunction::Nearest {
450 return Ok(Arc::new(ResampleNearestPlan {
451 source_size,
452 target_size: destination_size,
453 threading_policy: self.threading_policy,
454 _phantom_data: PhantomData,
455 }));
456 }
457
458 let is_upscale = destination_size.width >= source_size.width
459 && destination_size.height >= source_size.height;
460
461 if self.multi_step_upscaling && is_upscale {
462 let intermediates =
463 Scaler::plan_intermediate_sizes(source_size, destination_size, self.function);
464
465 if !intermediates.is_empty() {
466 let mut steps: Vec<Arc<Resampling<T, N>>> = Vec::new();
467 let mut prev_size = source_size;
468
469 let all_sizes: Vec<ImageSize> = intermediates
470 .iter()
471 .copied()
472 .chain(std::iter::once(destination_size))
473 .collect();
474 let last_idx = all_sizes.len() - 1;
475
476 for (idx, &next_size) in all_sizes.iter().enumerate() {
477 let is_first = idx == 0;
478 let is_last = idx == last_idx;
479
480 let sub_plan = self.build_single_step_plan_with_alpha::<T, W, N>(
489 prev_size, next_size, bit_depth, is_first, is_last,
490 )?;
491 steps.push(sub_plan);
492 prev_size = next_size;
493 }
494
495 return Ok(Arc::new(MultiStepResamplePlan::new(
496 steps,
497 source_size,
498 destination_size,
499 )));
500 }
501 }
502
503 let is_downscale = destination_size.width <= source_size.width
505 && destination_size.height <= source_size.height;
506
507 if self.supersampling && is_downscale {
508 let ratio_w = source_size.width as f64 / destination_size.width as f64;
509 let ratio_h = source_size.height as f64 / destination_size.height as f64;
510
511 if let Some(prefilter) = supersampling_prefilter(ratio_w, ratio_h) {
512 let intermediate = supersampling_intermediate_size(source_size, destination_size);
513
514 if intermediate.width < source_size.width
515 || intermediate.height < source_size.height
516 {
517 let prefilter_needs_alpha =
528 needs_alpha_premultiplication && prefilter != ResamplingFunction::Nearest;
529
530 let pre_scaler = Scaler {
531 function: prefilter,
532 threading_policy: self.threading_policy,
533 workload_strategy: self.workload_strategy,
534 multi_step_upscaling: false,
535 supersampling: false, };
537
538 let pre_plan = if prefilter_needs_alpha {
539 pre_scaler.build_single_step_plan_with_alpha::<T, W, N>(
543 source_size,
544 intermediate,
545 bit_depth,
546 true, false, )?
549 } else {
550 pre_scaler.build_single_step_plan::<T, W, N>(
552 source_size,
553 intermediate,
554 bit_depth,
555 )?
556 };
557
558 let quality_plan = if prefilter_needs_alpha {
559 self.build_single_step_plan_with_alpha::<T, W, N>(
562 intermediate,
563 destination_size,
564 bit_depth,
565 false, true, )?
568 } else {
569 self.build_single_step_plan_with_alpha::<T, W, N>(
572 intermediate,
573 destination_size,
574 bit_depth,
575 needs_alpha_premultiplication,
576 needs_alpha_premultiplication,
577 )?
578 };
579
580 return Ok(Arc::new(MultiStepResamplePlan::new(
581 vec![pre_plan, quality_plan],
582 source_size,
583 destination_size,
584 )));
585 }
586 }
587 }
588
589 self.build_single_step_plan_with_alpha::<T, W, N>(
590 source_size,
591 destination_size,
592 bit_depth,
593 needs_alpha_premultiplication,
594 needs_alpha_premultiplication,
595 )
596 }
597
598 pub(crate) fn build_single_step_plan_with_alpha<
599 T: Clone + Copy + Debug + Send + Sync + Default + WeightsGenerator<W> + 'static,
600 W,
601 const N: usize,
602 >(
603 &self,
604 source_size: ImageSize,
605 destination_size: ImageSize,
606 bit_depth: usize,
607 needs_alpha_premultiplication_forward: bool,
608 needs_alpha_premultiplication_backward: bool,
609 ) -> Result<Arc<Resampling<T, N>>, PicScaleError>
610 where
611 for<'a> ImageStore<'a, T, N>:
612 VerticalConvolutionPass<T, W, N> + HorizontalFilterPass<T, W, N> + AssociateAlpha<T, N>,
613 for<'a> ImageStoreMut<'a, T, N>: CheckStoreDensity + UnassociateAlpha<T, N>,
614 {
615 if self.function == ResamplingFunction::Nearest {
616 return Ok(Arc::new(ResampleNearestPlan {
617 source_size,
618 target_size: destination_size,
619 threading_policy: self.threading_policy,
620 _phantom_data: PhantomData,
621 }));
622 }
623 if !needs_alpha_premultiplication_backward && !needs_alpha_premultiplication_forward {
624 return self.plan_generic_resize(source_size, destination_size, bit_depth);
625 }
626
627 let should_do_horizontal = source_size.width != destination_size.width;
628 let should_do_vertical = source_size.height != destination_size.height;
629
630 if !should_do_vertical && !should_do_horizontal {
631 return Ok(Arc::new(NoopPlan {
632 source_size,
633 target_size: destination_size,
634 _phantom: PhantomData,
635 }));
636 }
637
638 let options = ConvolutionOptions {
639 workload_strategy: self.workload_strategy,
640 bit_depth,
641 src_size: source_size,
642 dst_size: destination_size,
643 };
644
645 let vertical_plan = if should_do_vertical {
646 let vertical_filters =
647 T::make_weights(self.function, source_size.height, destination_size.height)?;
648 Some(ImageStore::<T, N>::vertical_plan(
649 vertical_filters,
650 self.threading_policy,
651 options,
652 ))
653 } else {
654 None
655 };
656
657 let horizontal_plan = if should_do_horizontal {
658 let horizontal_filters =
659 T::make_weights(self.function, source_size.width, destination_size.width)?;
660 Some(ImageStore::<T, N>::horizontal_plan(
661 horizontal_filters,
662 self.threading_policy,
663 options,
664 ))
665 } else {
666 None
667 };
668
669 let trampoline = if should_do_vertical && should_do_horizontal {
670 Some(Arc::new(TrampolineFiltering {
671 horizontal_filter: horizontal_plan.as_ref().unwrap().clone(),
672 vertical_filter: vertical_plan.as_ref().unwrap().clone(),
673 source_size,
674 target_size: destination_size,
675 })
676 as Arc<dyn TrampolineFilter<T, N> + Send + Sync>)
677 } else {
678 None
679 };
680
681 Ok(make_alpha_plan(
682 source_size,
683 destination_size,
684 horizontal_plan,
685 vertical_plan,
686 trampoline,
687 self.threading_policy,
688 self.workload_strategy,
689 needs_alpha_premultiplication_forward,
690 needs_alpha_premultiplication_backward,
691 ))
692 }
693
694 pub fn plan_planar_resampling(
710 &self,
711 source_size: ImageSize,
712 target_size: ImageSize,
713 ) -> Result<Arc<Resampling<u8, 1>>, PicScaleError> {
714 self.plan_generic_resize(source_size, target_size, 8)
715 }
716
717 pub fn plan_gray_alpha_resampling(
736 &self,
737 source_size: ImageSize,
738 target_size: ImageSize,
739 premultiply_alpha: bool,
740 ) -> Result<Arc<Resampling<u8, 2>>, PicScaleError> {
741 if premultiply_alpha {
742 self.plan_generic_resize_with_alpha(source_size, target_size, 8, premultiply_alpha)
743 } else {
744 self.plan_generic_resize(source_size, target_size, 8)
745 }
746 }
747
748 pub fn plan_cbcr_resampling(
766 &self,
767 source_size: ImageSize,
768 target_size: ImageSize,
769 ) -> Result<Arc<Resampling<u8, 2>>, PicScaleError> {
770 self.plan_generic_resize(source_size, target_size, 8)
771 }
772
773 pub fn plan_rgb_resampling(
791 &self,
792 source_size: ImageSize,
793 target_size: ImageSize,
794 ) -> Result<Arc<Resampling<u8, 3>>, PicScaleError> {
795 self.plan_generic_resize(source_size, target_size, 8)
796 }
797
798 pub fn plan_rgba_resampling(
817 &self,
818 source_size: ImageSize,
819 target_size: ImageSize,
820 premultiply_alpha: bool,
821 ) -> Result<Arc<Resampling<u8, 4>>, PicScaleError> {
822 if premultiply_alpha {
823 self.plan_generic_resize_with_alpha(source_size, target_size, 8, premultiply_alpha)
824 } else {
825 self.plan_generic_resize(source_size, target_size, 8)
826 }
827 }
828
829 pub fn plan_planar_resampling16(
848 &self,
849 source_size: ImageSize,
850 target_size: ImageSize,
851 bit_depth: usize,
852 ) -> Result<Arc<Resampling<u16, 1>>, PicScaleError> {
853 self.plan_generic_resize(source_size, target_size, bit_depth)
854 }
855
856 pub fn plan_planar_resampling_s16(
875 &self,
876 source_size: ImageSize,
877 target_size: ImageSize,
878 bit_depth: usize,
879 ) -> Result<Arc<Resampling<i16, 1>>, PicScaleError> {
880 self.plan_generic_resize(source_size, target_size, bit_depth)
881 }
882
883 pub fn plan_cbcr_resampling16(
903 &self,
904 source_size: ImageSize,
905 target_size: ImageSize,
906 bit_depth: usize,
907 ) -> Result<Arc<Resampling<u16, 2>>, PicScaleError> {
908 self.plan_generic_resize(source_size, target_size, bit_depth)
909 }
910
911 pub fn plan_gray_alpha_resampling16(
932 &self,
933 source_size: ImageSize,
934 target_size: ImageSize,
935 premultiply_alpha: bool,
936 bit_depth: usize,
937 ) -> Result<Arc<Resampling<u16, 2>>, PicScaleError> {
938 if premultiply_alpha {
939 self.plan_generic_resize_with_alpha(
940 source_size,
941 target_size,
942 bit_depth,
943 premultiply_alpha,
944 )
945 } else {
946 self.plan_cbcr_resampling16(source_size, target_size, bit_depth)
947 }
948 }
949
950 pub fn plan_rgb_resampling16(
970 &self,
971 source_size: ImageSize,
972 target_size: ImageSize,
973 bit_depth: usize,
974 ) -> Result<Arc<Resampling<u16, 3>>, PicScaleError> {
975 self.plan_generic_resize(source_size, target_size, bit_depth)
976 }
977
978 pub fn plan_rgba_resampling16(
999 &self,
1000 source_size: ImageSize,
1001 target_size: ImageSize,
1002 premultiply_alpha: bool,
1003 bit_depth: usize,
1004 ) -> Result<Arc<Resampling<u16, 4>>, PicScaleError> {
1005 if premultiply_alpha {
1006 self.plan_generic_resize_with_alpha(
1007 source_size,
1008 target_size,
1009 bit_depth,
1010 premultiply_alpha,
1011 )
1012 } else {
1013 self.plan_generic_resize(source_size, target_size, bit_depth)
1014 }
1015 }
1016
1017 pub fn plan_planar_resampling_f32(
1041 &self,
1042 source_size: ImageSize,
1043 target_size: ImageSize,
1044 ) -> Result<Arc<Resampling<f32, 1>>, PicScaleError> {
1045 match self.workload_strategy {
1046 WorkloadStrategy::PreferQuality => {
1047 self.plan_generic_resize::<f32, f64, 1>(source_size, target_size, 8)
1048 }
1049 WorkloadStrategy::PreferSpeed => {
1050 self.plan_generic_resize::<f32, f32, 1>(source_size, target_size, 8)
1051 }
1052 }
1053 }
1054
1055 pub fn plan_cbcr_resampling_f32(
1080 &self,
1081 source_size: ImageSize,
1082 target_size: ImageSize,
1083 ) -> Result<Arc<dyn ResamplingPlan<f32, 2> + Send + Sync>, PicScaleError> {
1084 match self.workload_strategy {
1085 WorkloadStrategy::PreferQuality => {
1086 self.plan_generic_resize::<f32, f64, 2>(source_size, target_size, 8)
1087 }
1088 WorkloadStrategy::PreferSpeed => {
1089 self.plan_generic_resize::<f32, f32, 2>(source_size, target_size, 8)
1090 }
1091 }
1092 }
1093
1094 pub fn plan_gray_alpha_resampling_f32(
1122 &self,
1123 source_size: ImageSize,
1124 target_size: ImageSize,
1125 premultiply_alpha: bool,
1126 ) -> Result<Arc<Resampling<f32, 2>>, PicScaleError> {
1127 if premultiply_alpha {
1128 match self.workload_strategy {
1129 WorkloadStrategy::PreferQuality => self
1130 .plan_generic_resize_with_alpha::<f32, f64, 2>(
1131 source_size,
1132 target_size,
1133 8,
1134 premultiply_alpha,
1135 ),
1136 WorkloadStrategy::PreferSpeed => self
1137 .plan_generic_resize_with_alpha::<f32, f32, 2>(
1138 source_size,
1139 target_size,
1140 8,
1141 premultiply_alpha,
1142 ),
1143 }
1144 } else {
1145 self.plan_cbcr_resampling_f32(source_size, target_size)
1146 }
1147 }
1148
1149 pub fn plan_rgb_resampling_f32(
1174 &self,
1175 source_size: ImageSize,
1176 target_size: ImageSize,
1177 ) -> Result<Arc<Resampling<f32, 3>>, PicScaleError> {
1178 match self.workload_strategy {
1179 WorkloadStrategy::PreferQuality => {
1180 self.plan_generic_resize::<f32, f64, 3>(source_size, target_size, 8)
1181 }
1182 WorkloadStrategy::PreferSpeed => {
1183 self.plan_generic_resize::<f32, f32, 3>(source_size, target_size, 8)
1184 }
1185 }
1186 }
1187
1188 pub fn plan_rgba_resampling_f32(
1215 &self,
1216 source_size: ImageSize,
1217 target_size: ImageSize,
1218 premultiply_alpha: bool,
1219 ) -> Result<Arc<Resampling<f32, 4>>, PicScaleError> {
1220 if premultiply_alpha {
1221 match self.workload_strategy {
1222 WorkloadStrategy::PreferQuality => self
1223 .plan_generic_resize_with_alpha::<f32, f64, 4>(
1224 source_size,
1225 target_size,
1226 8,
1227 premultiply_alpha,
1228 ),
1229 WorkloadStrategy::PreferSpeed => self
1230 .plan_generic_resize_with_alpha::<f32, f32, 4>(
1231 source_size,
1232 target_size,
1233 8,
1234 premultiply_alpha,
1235 ),
1236 }
1237 } else {
1238 match self.workload_strategy {
1239 WorkloadStrategy::PreferQuality => {
1240 self.plan_generic_resize::<f32, f64, 4>(source_size, target_size, 8)
1241 }
1242 WorkloadStrategy::PreferSpeed => {
1243 self.plan_generic_resize::<f32, f32, 4>(source_size, target_size, 8)
1244 }
1245 }
1246 }
1247 }
1248
1249 pub fn set_threading_policy(&mut self, threading_policy: ThreadingPolicy) -> Self {
1250 self.threading_policy = threading_policy;
1251 *self
1252 }
1253}
1254
1255impl Scaler {
1256 pub(crate) fn plan_resize_ar30<const AR30_ORDER: usize>(
1257 &self,
1258 ar30_type: Rgb30,
1259 source_size: ImageSize,
1260 destination_size: ImageSize,
1261 ) -> Result<Arc<Resampling<u8, 4>>, PicScaleError> {
1262 if self.function == ResamplingFunction::Nearest {
1263 return Ok(Arc::new(ResampleNearestPlan {
1264 source_size,
1265 target_size: destination_size,
1266 threading_policy: self.threading_policy,
1267 _phantom_data: PhantomData,
1268 }));
1269 }
1270 let inner_plan = self.plan_rgb_resampling16(source_size, destination_size, 10)?;
1271 let mut _decomposer: Arc<dyn Ar30Destructuring + Send + Sync> =
1272 Arc::new(Ar30DestructuringImpl::<AR30_ORDER> { rgb30: ar30_type });
1273 #[cfg(all(target_arch = "x86_64", feature = "avx"))]
1274 {
1275 if std::arch::is_x86_feature_detected!("avx2") {
1276 use crate::avx2::{
1277 avx_column_handler_fixed_point_ar30, avx_convolve_horizontal_rgba_rows_4_ar30,
1278 avx_convolve_horizontal_rgba_rows_ar30,
1279 };
1280 use crate::plan::{HorizontalFiltering, VerticalFiltering};
1281 let should_do_horizontal = source_size.width != destination_size.width;
1282 let should_do_vertical = source_size.height != destination_size.height;
1283
1284 let vertical_plan = if should_do_vertical {
1285 let vertical_filters = u8::make_weights(
1286 self.function,
1287 source_size.height,
1288 destination_size.height,
1289 )?;
1290 Some(Arc::new(VerticalFiltering {
1291 filter_row: match ar30_type {
1292 Rgb30::Ar30 => {
1293 avx_column_handler_fixed_point_ar30::<
1294 { Rgb30::Ar30 as usize },
1295 AR30_ORDER,
1296 >
1297 }
1298 Rgb30::Ra30 => {
1299 avx_column_handler_fixed_point_ar30::<
1300 { Rgb30::Ra30 as usize },
1301 AR30_ORDER,
1302 >
1303 }
1304 },
1305 filter_weights: vertical_filters
1306 .numerical_approximation_i16::<{ crate::support::PRECISION }>(0),
1307 threading_policy: self.threading_policy,
1308 }))
1309 } else {
1310 None
1311 };
1312
1313 let horizontal_plan = if should_do_horizontal {
1314 let horizontal_filters =
1315 u8::make_weights(self.function, source_size.width, destination_size.width)?;
1316 Some(Arc::new(HorizontalFiltering {
1317 filter_row: match ar30_type {
1318 Rgb30::Ar30 => {
1319 avx_convolve_horizontal_rgba_rows_ar30::<
1320 { Rgb30::Ar30 as usize },
1321 AR30_ORDER,
1322 >
1323 }
1324 Rgb30::Ra30 => {
1325 avx_convolve_horizontal_rgba_rows_ar30::<
1326 { Rgb30::Ra30 as usize },
1327 AR30_ORDER,
1328 >
1329 }
1330 },
1331 filter_4_rows: Some(match ar30_type {
1332 Rgb30::Ar30 => {
1333 avx_convolve_horizontal_rgba_rows_4_ar30::<
1334 { Rgb30::Ar30 as usize },
1335 AR30_ORDER,
1336 >
1337 }
1338 Rgb30::Ra30 => {
1339 avx_convolve_horizontal_rgba_rows_4_ar30::<
1340 { Rgb30::Ra30 as usize },
1341 AR30_ORDER,
1342 >
1343 }
1344 }),
1345 threading_policy: self.threading_policy,
1346 filter_weights: horizontal_filters
1347 .numerical_approximation_i16::<{ crate::support::PRECISION }>(0),
1348 }))
1349 } else {
1350 None
1351 };
1352
1353 return Ok(match (should_do_vertical, should_do_horizontal) {
1354 (true, true) => {
1355 let v = vertical_plan.expect("Should have vertical plan");
1356 let h = horizontal_plan.expect("Should have horizontal plan");
1357 let trampoline = Arc::new(TrampolineFiltering {
1358 horizontal_filter: h.clone(),
1359 vertical_filter: v.clone(),
1360 source_size,
1361 target_size: destination_size,
1362 });
1363 Arc::new(BothAxesConvolvePlan {
1364 source_size,
1365 target_size: destination_size,
1366 horizontal_filter: h,
1367 vertical_filter: v,
1368 trampoline_filter: trampoline,
1369 threading_policy: self.threading_policy,
1370 })
1371 }
1372 (true, false) => Arc::new(VerticalConvolvePlan {
1373 source_size,
1374 target_size: destination_size,
1375 vertical_filter: vertical_plan.expect("Should have vertical plan"),
1376 }),
1377 (false, true) => Arc::new(HorizontalConvolvePlan {
1378 source_size,
1379 target_size: destination_size,
1380 horizontal_filter: horizontal_plan.expect("Should have horizontal plan"),
1381 }),
1382 (false, false) => Arc::new(NoopPlan {
1383 source_size,
1384 target_size: destination_size,
1385 _phantom: PhantomData,
1386 }),
1387 });
1388 }
1389 }
1390 Ok(Arc::new(Ar30Plan {
1391 source_size,
1392 target_size: destination_size,
1393 inner_filter: inner_plan,
1394 decomposer: _decomposer,
1395 }))
1396 }
1397
1398 pub fn plan_ar30_resampling(
1420 &self,
1421 source_size: ImageSize,
1422 target_size: ImageSize,
1423 order: Ar30ByteOrder,
1424 ) -> Result<Arc<Resampling<u8, 4>>, PicScaleError> {
1425 match order {
1426 Ar30ByteOrder::Host => self.plan_resize_ar30::<{ Ar30ByteOrder::Host as usize }>(
1427 Rgb30::Ar30,
1428 source_size,
1429 target_size,
1430 ),
1431 Ar30ByteOrder::Network => self.plan_resize_ar30::<{ Ar30ByteOrder::Network as usize }>(
1432 Rgb30::Ar30,
1433 source_size,
1434 target_size,
1435 ),
1436 }
1437 }
1438
1439 pub fn plan_ra30_resampling(
1461 &self,
1462 source_size: ImageSize,
1463 target_size: ImageSize,
1464 order: Ar30ByteOrder,
1465 ) -> Result<Arc<Resampling<u8, 4>>, PicScaleError> {
1466 match order {
1467 Ar30ByteOrder::Host => self.plan_resize_ar30::<{ Ar30ByteOrder::Host as usize }>(
1468 Rgb30::Ra30,
1469 source_size,
1470 target_size,
1471 ),
1472 Ar30ByteOrder::Network => self.plan_resize_ar30::<{ Ar30ByteOrder::Network as usize }>(
1473 Rgb30::Ra30,
1474 source_size,
1475 target_size,
1476 ),
1477 }
1478 }
1479}
1480
1481#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)]
1483pub struct ScalingOptions {
1484 pub resampling_function: ResamplingFunction,
1485 pub premultiply_alpha: bool,
1486 pub threading_policy: ThreadingPolicy,
1487}
1488
1489pub trait ImageStoreScaling<'b, T, const N: usize>
1491where
1492 T: Clone + Copy + Debug,
1493{
1494 fn scale(
1495 &self,
1496 store: &mut ImageStoreMut<'b, T, N>,
1497 options: ScalingOptions,
1498 ) -> Result<(), PicScaleError>;
1499}
1500
1501macro_rules! def_image_scaling_alpha {
1502 ($clazz: ident, $fx_type: ident, $cn: expr) => {
1503 impl<'b> ImageStoreScaling<'b, $fx_type, $cn> for $clazz<'b> {
1504 fn scale(
1505 &self,
1506 store: &mut ImageStoreMut<'b, $fx_type, $cn>,
1507 options: ScalingOptions,
1508 ) -> Result<(), PicScaleError> {
1509 let scaler = Scaler::new(options.resampling_function)
1510 .set_threading_policy(options.threading_policy);
1511 let plan = scaler.plan_generic_resize_with_alpha::<$fx_type, f32, $cn>(
1512 self.size(),
1513 store.size(),
1514 store.bit_depth,
1515 options.premultiply_alpha,
1516 )?;
1517 plan.resample(self, store)
1518 }
1519 }
1520 };
1521}
1522
1523macro_rules! def_image_scaling {
1524 ($clazz: ident, $fx_type: ident, $cn: expr) => {
1525 impl<'b> ImageStoreScaling<'b, $fx_type, $cn> for $clazz<'b> {
1526 fn scale(
1527 &self,
1528 store: &mut ImageStoreMut<'b, $fx_type, $cn>,
1529 options: ScalingOptions,
1530 ) -> Result<(), PicScaleError> {
1531 let scaler = Scaler::new(options.resampling_function)
1532 .set_threading_policy(options.threading_policy);
1533 let plan = scaler.plan_generic_resize::<$fx_type, f32, $cn>(
1534 self.size(),
1535 store.size(),
1536 store.bit_depth,
1537 )?;
1538 plan.resample(self, store)
1539 }
1540 }
1541 };
1542}
1543
1544def_image_scaling_alpha!(Rgba8ImageStore, u8, 4);
1545def_image_scaling!(Rgb8ImageStore, u8, 3);
1546def_image_scaling!(CbCr8ImageStore, u8, 2);
1547def_image_scaling!(Planar8ImageStore, u8, 1);
1548def_image_scaling!(Planar16ImageStore, u16, 1);
1549def_image_scaling!(CbCr16ImageStore, u16, 2);
1550def_image_scaling!(Rgb16ImageStore, u16, 3);
1551def_image_scaling_alpha!(Rgba16ImageStore, u16, 4);
1552def_image_scaling!(PlanarF32ImageStore, f32, 1);
1553def_image_scaling!(CbCrF32ImageStore, f32, 2);
1554def_image_scaling!(RgbF32ImageStore, f32, 3);
1555def_image_scaling_alpha!(RgbaF32ImageStore, f32, 4);
1556
1557#[cfg(test)]
1558mod tests {
1559 use super::*;
1560
1561 macro_rules! check_rgba8 {
1562 ($dst: expr, $image_width: expr, $max: expr) => {
1563 {
1564 for (y, row) in $dst.chunks_exact($image_width * 4).enumerate() {
1565 for (i, dst) in row.chunks_exact(4).enumerate() {
1566 let diff0 = (dst[0] as i32 - 124).abs();
1567 let diff1 = (dst[1] as i32 - 41).abs();
1568 let diff2 = (dst[2] as i32 - 99).abs();
1569 let diff3 = (dst[3] as i32 - 77).abs();
1570 assert!(
1571 diff0 < $max,
1572 "Diff for channel 0 is expected < {}, but it was {diff0}, at (y: {y}, x: {i})",
1573 $max
1574 );
1575 assert!(
1576 diff1 < $max,
1577 "Diff for channel 1 is expected < {}, but it was {diff1}, at (y: {y}, x: {i})",
1578 $max
1579 );
1580 assert!(
1581 diff2 < $max,
1582 "Diff for channel 2 is expected < {}, but it was {diff2}, at (y: {y}, x: {i})",
1583 $max
1584 );
1585 assert!(
1586 diff3 < $max,
1587 "Diff for channel 3 is expected < {}, but it was {diff3}, at (y: {y}, x: {i})",
1588 $max
1589 );
1590 }
1591 }
1592 }
1593 };
1594 }
1595
1596 macro_rules! check_rgb16 {
1597 ($dst: expr, $image_width: expr, $max: expr) => {
1598 {
1599 for (y, row) in $dst.chunks_exact($image_width * 3).enumerate() {
1600 for (i, dst) in row.chunks_exact(3).enumerate() {
1601 let diff0 = (dst[0] as i32 - 124).abs();
1602 let diff1 = (dst[1] as i32 - 41).abs();
1603 let diff2 = (dst[2] as i32 - 99).abs();
1604 assert!(
1605 diff0 < $max,
1606 "Diff for channel 0 is expected < {}, but it was {diff0}, at (y: {y}, x: {i})",
1607 $max
1608 );
1609 assert!(
1610 diff1 < $max,
1611 "Diff for channel 1 is expected < {}, but it was {diff1}, at (y: {y}, x: {i})",
1612 $max
1613 );
1614 assert!(
1615 diff2 < $max,
1616 "Diff for channel 2 is expected < {}, but it was {diff2}, at (y: {y}, x: {i})",
1617 $max
1618 );
1619 }
1620 }
1621 }
1622 };
1623 }
1624
1625 #[test]
1626 fn check_rgba8_resizing_vertical() {
1627 let image_width = 255;
1628 let image_height = 512;
1629 const CN: usize = 4;
1630 let mut image = vec![0u8; image_height * image_width * CN];
1631 for dst in image.chunks_exact_mut(4) {
1632 dst[0] = 124;
1633 dst[1] = 41;
1634 dst[2] = 99;
1635 dst[3] = 77;
1636 }
1637 let scaler =
1638 Scaler::new(ResamplingFunction::Bilinear).set_threading_policy(ThreadingPolicy::Single);
1639 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1640 let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1641 let planned = scaler
1642 .plan_rgba_resampling(src_store.size(), target_store.size(), false)
1643 .unwrap();
1644 planned.resample(&src_store, &mut target_store).unwrap();
1645 let target_data = target_store.buffer.borrow();
1646 check_rgba8!(target_data, image_width, 34);
1647 }
1648
1649 #[test]
1650 fn check_rgba8_resizing_both() {
1651 let image_width = 255;
1652 let image_height = 512;
1653 const CN: usize = 4;
1654 let mut image = vec![0u8; image_height * image_width * CN];
1655 for dst in image.chunks_exact_mut(4) {
1656 dst[0] = 124;
1657 dst[1] = 41;
1658 dst[2] = 99;
1659 dst[3] = 77;
1660 }
1661 image[3] = 78;
1662 let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
1663 scaler.set_threading_policy(ThreadingPolicy::Single);
1664 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1665 let mut target_store = ImageStoreMut::alloc(image_width / 2, image_height / 2);
1666 let planned = scaler
1667 .plan_rgba_resampling(src_store.size(), target_store.size(), false)
1668 .unwrap();
1669 planned.resample(&src_store, &mut target_store).unwrap();
1670 let target_data = target_store.buffer.borrow();
1671 check_rgba8!(target_data, image_width, 34);
1672 }
1673
1674 #[test]
1675 fn check_rgba8_resizing_alpha() {
1676 let image_width = 255;
1677 let image_height = 512;
1678 const CN: usize = 4;
1679 let mut image = vec![0u8; image_height * image_width * CN];
1680 for dst in image.chunks_exact_mut(4) {
1681 dst[0] = 124;
1682 dst[1] = 41;
1683 dst[2] = 99;
1684 dst[3] = 77;
1685 }
1686 image[3] = 78;
1687 let scaler =
1688 Scaler::new(ResamplingFunction::Lanczos3).set_threading_policy(ThreadingPolicy::Single);
1689 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1690 let mut target_store = ImageStoreMut::alloc(image_width / 2, image_height / 2);
1691 let planned = scaler
1692 .plan_rgba_resampling(src_store.size(), target_store.size(), true)
1693 .unwrap();
1694 planned.resample(&src_store, &mut target_store).unwrap();
1695 let target_data = target_store.buffer.borrow();
1696 check_rgba8!(target_data, image_width, 160);
1697 }
1698
1699 #[test]
1700 fn check_rgb8_resizing_vertical() {
1701 let image_width = 255;
1702 let image_height = 512;
1703 const CN: usize = 3;
1704 let mut image = vec![0u8; image_height * image_width * CN];
1705 for dst in image.chunks_exact_mut(3) {
1706 dst[0] = 124;
1707 dst[1] = 41;
1708 dst[2] = 99;
1709 }
1710 let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
1711 scaler.set_threading_policy(ThreadingPolicy::Single);
1712 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1713 let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1714 let planned = scaler
1715 .plan_rgb_resampling(src_store.size(), target_store.size())
1716 .unwrap();
1717 planned.resample(&src_store, &mut target_store).unwrap();
1718 let target_data = target_store.buffer.borrow();
1719
1720 check_rgb16!(target_data, image_width, 85);
1721 }
1722
1723 #[test]
1724 fn check_rgb8_resizing_vertical_threading() {
1725 let image_width = 255;
1726 let image_height = 512;
1727 const CN: usize = 3;
1728 let mut image = vec![0u8; image_height * image_width * CN];
1729 for dst in image.chunks_exact_mut(3) {
1730 dst[0] = 124;
1731 dst[1] = 41;
1732 dst[2] = 99;
1733 }
1734 let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
1735 scaler.set_threading_policy(ThreadingPolicy::Adaptive);
1736 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1737 let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1738 let planned = scaler
1739 .plan_rgb_resampling(src_store.size(), target_store.size())
1740 .unwrap();
1741 planned.resample(&src_store, &mut target_store).unwrap();
1742 let target_data = target_store.buffer.borrow();
1743
1744 check_rgb16!(target_data, image_width, 85);
1745 }
1746
1747 #[test]
1748 fn check_rgba10_resizing_vertical() {
1749 let image_width = 8;
1750 let image_height = 8;
1751 const CN: usize = 4;
1752 let mut image = vec![0u16; image_height * image_width * CN];
1753 for dst in image.chunks_exact_mut(4) {
1754 dst[0] = 124;
1755 dst[1] = 41;
1756 dst[2] = 99;
1757 dst[3] = 77;
1758 }
1759 image[3] = 78;
1760 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1761 scaler.set_threading_policy(ThreadingPolicy::Single);
1762 let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1763 src_store.bit_depth = 10;
1764 let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 10);
1765 let planned = scaler
1766 .plan_rgba_resampling16(src_store.size(), target_store.size(), true, 10)
1767 .unwrap();
1768 planned.resample(&src_store, &mut target_store).unwrap();
1769 let target_data = target_store.buffer.borrow();
1770
1771 check_rgba8!(target_data, image_width, 60);
1772 }
1773
1774 #[test]
1775 fn check_rgb10_resizing_vertical() {
1776 let image_width = 8;
1777 let image_height = 4;
1778 const CN: usize = 3;
1779 let mut image = vec![0; image_height * image_width * CN];
1780 for dst in image.chunks_exact_mut(3) {
1781 dst[0] = 124;
1782 dst[1] = 41;
1783 dst[2] = 99;
1784 }
1785 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1786 scaler.set_threading_policy(ThreadingPolicy::Single);
1787 let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1788 src_store.bit_depth = 10;
1789 let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 10);
1790 let planned = scaler
1791 .plan_rgb_resampling16(src_store.size(), target_store.size(), 10)
1792 .unwrap();
1793 planned.resample(&src_store, &mut target_store).unwrap();
1794 let target_data = target_store.buffer.borrow();
1795
1796 check_rgb16!(target_data, image_width, 85);
1797 }
1798
1799 #[test]
1800 fn check_rgb10_resizing_vertical_adaptive() {
1801 let image_width = 8;
1802 let image_height = 4;
1803 const CN: usize = 3;
1804 let mut image = vec![0; image_height * image_width * CN];
1805 for dst in image.chunks_exact_mut(3) {
1806 dst[0] = 124;
1807 dst[1] = 41;
1808 dst[2] = 99;
1809 }
1810 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1811 scaler.set_threading_policy(ThreadingPolicy::Adaptive);
1812 let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1813 src_store.bit_depth = 10;
1814 let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 10);
1815 let planned = scaler
1816 .plan_rgb_resampling16(src_store.size(), target_store.size(), 10)
1817 .unwrap();
1818 planned.resample(&src_store, &mut target_store).unwrap();
1819 let target_data = target_store.buffer.borrow();
1820
1821 check_rgb16!(target_data, image_width, 85);
1822 }
1823
1824 #[test]
1825 fn check_rgb16_resizing_vertical() {
1826 let image_width = 8;
1827 let image_height = 8;
1828 const CN: usize = 3;
1829 let mut image = vec![164; image_height * image_width * CN];
1830 for dst in image.chunks_exact_mut(3) {
1831 dst[0] = 124;
1832 dst[1] = 41;
1833 dst[2] = 99;
1834 }
1835 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1836 scaler.set_threading_policy(ThreadingPolicy::Single);
1837 let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1838 src_store.bit_depth = 10;
1839 let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 16);
1840 let planned = scaler
1841 .plan_rgb_resampling16(src_store.size(), target_store.size(), 16)
1842 .unwrap();
1843 planned.resample(&src_store, &mut target_store).unwrap();
1844 let target_data = target_store.buffer.borrow();
1845
1846 check_rgb16!(target_data, image_width, 100);
1847 }
1848
1849 #[test]
1850 fn check_rgba16_resizing_vertical() {
1851 let image_width = 8;
1852 let image_height = 8;
1853 const CN: usize = 4;
1854 let mut image = vec![0u16; image_height * image_width * CN];
1855 for dst in image.chunks_exact_mut(4) {
1856 dst[0] = 124;
1857 dst[1] = 41;
1858 dst[2] = 99;
1859 dst[3] = 255;
1860 }
1861 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1862 scaler.set_threading_policy(ThreadingPolicy::Single);
1863 let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1864 src_store.bit_depth = 10;
1865 let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 16);
1866 let planned = scaler
1867 .plan_rgba_resampling16(src_store.size(), target_store.size(), false, 16)
1868 .unwrap();
1869 planned.resample(&src_store, &mut target_store).unwrap();
1870 let target_data = target_store.buffer.borrow();
1871
1872 check_rgba8!(target_data, image_width, 180);
1873 }
1874
1875 #[test]
1876 fn check_rgba16_resizing_vertical_threading() {
1877 let image_width = 8;
1878 let image_height = 8;
1879 const CN: usize = 4;
1880 let mut image = vec![0u16; image_height * image_width * CN];
1881 for dst in image.chunks_exact_mut(4) {
1882 dst[0] = 124;
1883 dst[1] = 41;
1884 dst[2] = 99;
1885 dst[3] = 255;
1886 }
1887 let scaler = Scaler::new(ResamplingFunction::Lanczos3)
1888 .set_threading_policy(ThreadingPolicy::Adaptive);
1889 let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1890 src_store.bit_depth = 10;
1891 let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 16);
1892 let planned = scaler
1893 .plan_rgba_resampling16(src_store.size(), target_store.size(), false, 16)
1894 .unwrap();
1895 planned.resample(&src_store, &mut target_store).unwrap();
1896 let target_data = target_store.buffer.borrow();
1897
1898 check_rgba8!(target_data, image_width, 180);
1899 }
1900
1901 #[test]
1902 fn check_rgba8_nearest_vertical() {
1903 let image_width = 255;
1904 let image_height = 512;
1905 const CN: usize = 4;
1906 let mut image = vec![0u8; image_height * image_width * CN];
1907 for dst in image.chunks_exact_mut(4) {
1908 dst[0] = 124;
1909 dst[1] = 41;
1910 dst[2] = 99;
1911 dst[3] = 77;
1912 }
1913 let mut scaler = Scaler::new(ResamplingFunction::Nearest);
1914 scaler.set_threading_policy(ThreadingPolicy::Single);
1915 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1916 let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1917 let planned = scaler
1918 .plan_rgba_resampling(src_store.size(), target_store.size(), false)
1919 .unwrap();
1920 planned.resample(&src_store, &mut target_store).unwrap();
1921 let target_data = target_store.buffer.borrow();
1922
1923 check_rgba8!(target_data, image_width, 80);
1924 }
1925
1926 #[test]
1927 fn check_rgba8_nearest_vertical_threading() {
1928 let image_width = 255;
1929 let image_height = 512;
1930 const CN: usize = 4;
1931 let mut image = vec![0u8; image_height * image_width * CN];
1932 for dst in image.chunks_exact_mut(4) {
1933 dst[0] = 124;
1934 dst[1] = 41;
1935 dst[2] = 99;
1936 dst[3] = 77;
1937 }
1938 let mut scaler = Scaler::new(ResamplingFunction::Nearest);
1939 scaler.set_threading_policy(ThreadingPolicy::Adaptive);
1940 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1941 let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1942 let planned = scaler
1943 .plan_rgba_resampling(src_store.size(), target_store.size(), false)
1944 .unwrap();
1945 planned.resample(&src_store, &mut target_store).unwrap();
1946 let target_data = target_store.buffer.borrow();
1947
1948 check_rgba8!(target_data, image_width, 80);
1949 }
1950}