1#![forbid(unsafe_code)]
30use crate::convolution::{ConvolutionOptions, HorizontalFilterPass, VerticalConvolutionPass};
31use crate::factory::{Ar30ByteOrder, Rgb30};
32use crate::image_size::ImageSize;
33use crate::image_store::{
34 AssociateAlpha, CheckStoreDensity, ImageStore, ImageStoreMut, UnassociateAlpha,
35};
36use crate::math::WeightsGenerator;
37use crate::plan::{
38 AlphaConvolvePlan, Ar30Destructuring, Ar30DestructuringImpl, Ar30Plan, NonAlphaConvolvePlan,
39 ResampleNearestPlan, Resampling, TrampolineFiltering,
40};
41use crate::threading_policy::ThreadingPolicy;
42use crate::validation::PicScaleError;
43use crate::{
44 CbCr8ImageStore, CbCr16ImageStore, CbCrF32ImageStore, Planar8ImageStore, Planar16ImageStore,
45 PlanarF32ImageStore, ResamplingFunction, ResamplingPlan, Rgb8ImageStore, Rgb16ImageStore,
46 RgbF32ImageStore, Rgba8ImageStore, Rgba16ImageStore, RgbaF32ImageStore,
47};
48use std::fmt::Debug;
49use std::marker::PhantomData;
50use std::sync::Arc;
51
52#[derive(Debug, Copy, Clone)]
53pub struct Scaler {
55 pub(crate) function: ResamplingFunction,
56 pub(crate) threading_policy: ThreadingPolicy,
57 pub workload_strategy: WorkloadStrategy,
58}
59
60#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Default)]
62pub enum WorkloadStrategy {
63 PreferQuality,
65 #[default]
67 PreferSpeed,
68}
69
70impl Scaler {
71 pub fn new(filter: ResamplingFunction) -> Self {
76 Scaler {
77 function: filter,
78 threading_policy: ThreadingPolicy::Single,
79 workload_strategy: WorkloadStrategy::default(),
80 }
81 }
82
83 pub fn set_workload_strategy(&mut self, workload_strategy: WorkloadStrategy) -> Self {
87 self.workload_strategy = workload_strategy;
88 *self
89 }
90}
91
92impl Scaler {
93 pub(crate) fn plan_generic_resize<
94 T: Clone + Copy + Debug + Send + Sync + Default + WeightsGenerator<W> + 'static,
95 W,
96 const N: usize,
97 >(
98 &self,
99 source_size: ImageSize,
100 destination_size: ImageSize,
101 bit_depth: usize,
102 ) -> Result<Arc<Resampling<T, N>>, PicScaleError>
103 where
104 for<'a> ImageStore<'a, T, N>:
105 VerticalConvolutionPass<T, W, N> + HorizontalFilterPass<T, W, N>,
106 for<'a> ImageStoreMut<'a, T, N>: CheckStoreDensity,
107 {
108 if self.function == ResamplingFunction::Nearest {
109 return Ok(Arc::new(ResampleNearestPlan {
110 source_size,
111 target_size: destination_size,
112 threading_policy: self.threading_policy,
113 _phantom_data: PhantomData,
114 }));
115 }
116 let vertical_filters =
117 T::make_weights(self.function, source_size.height, destination_size.height)?;
118 let horizontal_filters =
119 T::make_weights(self.function, source_size.width, destination_size.width)?;
120 let options = ConvolutionOptions {
121 workload_strategy: self.workload_strategy,
122 bit_depth,
123 src_size: source_size,
124 dst_size: destination_size,
125 };
126 let vertical_plan =
127 ImageStore::<T, N>::vertical_plan(vertical_filters, self.threading_policy, options);
128 let horizontal_plan =
129 ImageStore::<T, N>::horizontal_plan(horizontal_filters, self.threading_policy, options);
130
131 let should_do_horizontal = source_size.width != destination_size.width;
132 let should_do_vertical = source_size.height != destination_size.height;
133
134 let trampoline_filter = Arc::new(TrampolineFiltering {
135 horizontal_filter: horizontal_plan.clone(),
136 vertical_filter: vertical_plan.clone(),
137 source_size,
138 target_size: destination_size,
139 });
140
141 Ok(Arc::new(NonAlphaConvolvePlan {
142 source_size,
143 target_size: destination_size,
144 horizontal_filter: horizontal_plan,
145 vertical_filter: vertical_plan,
146 trampoline_filter,
147 should_do_vertical,
148 should_do_horizontal,
149 threading_policy: self.threading_policy,
150 }))
151 }
152
153 pub(crate) fn plan_generic_resize_with_alpha<
154 T: Clone + Copy + Debug + Send + Sync + Default + WeightsGenerator<W> + 'static,
155 W,
156 const N: usize,
157 >(
158 &self,
159 source_size: ImageSize,
160 destination_size: ImageSize,
161 bit_depth: usize,
162 needs_alpha_premultiplication: bool,
163 ) -> Result<Arc<Resampling<T, N>>, PicScaleError>
164 where
165 for<'a> ImageStore<'a, T, N>:
166 VerticalConvolutionPass<T, W, N> + HorizontalFilterPass<T, W, N> + AssociateAlpha<T, N>,
167 for<'a> ImageStoreMut<'a, T, N>: CheckStoreDensity + UnassociateAlpha<T, N>,
168 {
169 if self.function == ResamplingFunction::Nearest {
170 return Ok(Arc::new(ResampleNearestPlan {
171 source_size,
172 target_size: destination_size,
173 threading_policy: self.threading_policy,
174 _phantom_data: PhantomData,
175 }));
176 }
177 if !needs_alpha_premultiplication {
178 return self.plan_generic_resize(source_size, destination_size, bit_depth);
179 }
180 let vertical_filters =
181 T::make_weights(self.function, source_size.height, destination_size.height)?;
182 let horizontal_filters =
183 T::make_weights(self.function, source_size.width, destination_size.width)?;
184 let options = ConvolutionOptions {
185 workload_strategy: self.workload_strategy,
186 bit_depth,
187 src_size: source_size,
188 dst_size: destination_size,
189 };
190 let vertical_plan =
191 ImageStore::<T, N>::vertical_plan(vertical_filters, self.threading_policy, options);
192 let horizontal_plan =
193 ImageStore::<T, N>::horizontal_plan(horizontal_filters, self.threading_policy, options);
194
195 let should_do_horizontal = source_size.width != destination_size.width;
196 let should_do_vertical = source_size.height != destination_size.height;
197
198 let trampoline_filter = Arc::new(TrampolineFiltering {
199 horizontal_filter: horizontal_plan.clone(),
200 vertical_filter: vertical_plan.clone(),
201 source_size,
202 target_size: destination_size,
203 });
204
205 Ok(Arc::new(AlphaConvolvePlan {
206 source_size,
207 target_size: destination_size,
208 threading_policy: self.threading_policy,
209 horizontal_filter: horizontal_plan,
210 vertical_filter: vertical_plan,
211 trampoline_filter,
212 should_do_vertical,
213 should_do_horizontal,
214 workload_strategy: self.workload_strategy,
215 }))
216 }
217
218 pub fn plan_planar_resampling(
234 &self,
235 source_size: ImageSize,
236 target_size: ImageSize,
237 ) -> Result<Arc<Resampling<u8, 1>>, PicScaleError> {
238 self.plan_generic_resize(source_size, target_size, 8)
239 }
240
241 pub fn plan_gray_alpha_resampling(
260 &self,
261 source_size: ImageSize,
262 target_size: ImageSize,
263 premultiply_alpha: bool,
264 ) -> Result<Arc<Resampling<u8, 2>>, PicScaleError> {
265 if premultiply_alpha {
266 self.plan_generic_resize_with_alpha(source_size, target_size, 8, premultiply_alpha)
267 } else {
268 self.plan_generic_resize(source_size, target_size, 8)
269 }
270 }
271
272 pub fn plan_cbcr_resampling(
290 &self,
291 source_size: ImageSize,
292 target_size: ImageSize,
293 ) -> Result<Arc<Resampling<u8, 2>>, PicScaleError> {
294 self.plan_generic_resize(source_size, target_size, 8)
295 }
296
297 pub fn plan_rgb_resampling(
315 &self,
316 source_size: ImageSize,
317 target_size: ImageSize,
318 ) -> Result<Arc<Resampling<u8, 3>>, PicScaleError> {
319 self.plan_generic_resize(source_size, target_size, 8)
320 }
321
322 pub fn plan_rgba_resampling(
341 &self,
342 source_size: ImageSize,
343 target_size: ImageSize,
344 premultiply_alpha: bool,
345 ) -> Result<Arc<Resampling<u8, 4>>, PicScaleError> {
346 if premultiply_alpha {
347 self.plan_generic_resize_with_alpha(source_size, target_size, 8, premultiply_alpha)
348 } else {
349 self.plan_generic_resize(source_size, target_size, 8)
350 }
351 }
352
353 pub fn plan_planar_resampling16(
372 &self,
373 source_size: ImageSize,
374 target_size: ImageSize,
375 bit_depth: usize,
376 ) -> Result<Arc<Resampling<u16, 1>>, PicScaleError> {
377 self.plan_generic_resize(source_size, target_size, bit_depth)
378 }
379
380 pub fn plan_planar_resampling_s16(
399 &self,
400 source_size: ImageSize,
401 target_size: ImageSize,
402 bit_depth: usize,
403 ) -> Result<Arc<Resampling<i16, 1>>, PicScaleError> {
404 self.plan_generic_resize(source_size, target_size, bit_depth)
405 }
406
407 pub fn plan_cbcr_resampling16(
427 &self,
428 source_size: ImageSize,
429 target_size: ImageSize,
430 bit_depth: usize,
431 ) -> Result<Arc<Resampling<u16, 2>>, PicScaleError> {
432 self.plan_generic_resize(source_size, target_size, bit_depth)
433 }
434
435 pub fn plan_gray_alpha_resampling16(
456 &self,
457 source_size: ImageSize,
458 target_size: ImageSize,
459 premultiply_alpha: bool,
460 bit_depth: usize,
461 ) -> Result<Arc<Resampling<u16, 2>>, PicScaleError> {
462 if premultiply_alpha {
463 self.plan_generic_resize_with_alpha(
464 source_size,
465 target_size,
466 bit_depth,
467 premultiply_alpha,
468 )
469 } else {
470 self.plan_cbcr_resampling16(source_size, target_size, bit_depth)
471 }
472 }
473
474 pub fn plan_rgb_resampling16(
494 &self,
495 source_size: ImageSize,
496 target_size: ImageSize,
497 bit_depth: usize,
498 ) -> Result<Arc<Resampling<u16, 3>>, PicScaleError> {
499 self.plan_generic_resize(source_size, target_size, bit_depth)
500 }
501
502 pub fn plan_rgba_resampling16(
523 &self,
524 source_size: ImageSize,
525 target_size: ImageSize,
526 premultiply_alpha: bool,
527 bit_depth: usize,
528 ) -> Result<Arc<Resampling<u16, 4>>, PicScaleError> {
529 if premultiply_alpha {
530 self.plan_generic_resize_with_alpha(
531 source_size,
532 target_size,
533 bit_depth,
534 premultiply_alpha,
535 )
536 } else {
537 self.plan_generic_resize(source_size, target_size, bit_depth)
538 }
539 }
540
541 pub fn plan_planar_resampling_f32(
565 &self,
566 source_size: ImageSize,
567 target_size: ImageSize,
568 ) -> Result<Arc<Resampling<f32, 1>>, PicScaleError> {
569 match self.workload_strategy {
570 WorkloadStrategy::PreferQuality => {
571 self.plan_generic_resize::<f32, f64, 1>(source_size, target_size, 8)
572 }
573 WorkloadStrategy::PreferSpeed => {
574 self.plan_generic_resize::<f32, f32, 1>(source_size, target_size, 8)
575 }
576 }
577 }
578
579 pub fn plan_cbcr_resampling_f32(
604 &self,
605 source_size: ImageSize,
606 target_size: ImageSize,
607 ) -> Result<Arc<dyn ResamplingPlan<f32, 2> + Send + Sync>, PicScaleError> {
608 match self.workload_strategy {
609 WorkloadStrategy::PreferQuality => {
610 self.plan_generic_resize::<f32, f64, 2>(source_size, target_size, 8)
611 }
612 WorkloadStrategy::PreferSpeed => {
613 self.plan_generic_resize::<f32, f32, 2>(source_size, target_size, 8)
614 }
615 }
616 }
617
618 pub fn plan_gray_alpha_resampling_f32(
646 &self,
647 source_size: ImageSize,
648 target_size: ImageSize,
649 premultiply_alpha: bool,
650 ) -> Result<Arc<Resampling<f32, 2>>, PicScaleError> {
651 if premultiply_alpha {
652 match self.workload_strategy {
653 WorkloadStrategy::PreferQuality => self
654 .plan_generic_resize_with_alpha::<f32, f64, 2>(
655 source_size,
656 target_size,
657 8,
658 premultiply_alpha,
659 ),
660 WorkloadStrategy::PreferSpeed => self
661 .plan_generic_resize_with_alpha::<f32, f32, 2>(
662 source_size,
663 target_size,
664 8,
665 premultiply_alpha,
666 ),
667 }
668 } else {
669 self.plan_cbcr_resampling_f32(source_size, target_size)
670 }
671 }
672
673 pub fn plan_rgb_resampling_f32(
698 &self,
699 source_size: ImageSize,
700 target_size: ImageSize,
701 ) -> Result<Arc<Resampling<f32, 3>>, PicScaleError> {
702 match self.workload_strategy {
703 WorkloadStrategy::PreferQuality => {
704 self.plan_generic_resize::<f32, f64, 3>(source_size, target_size, 8)
705 }
706 WorkloadStrategy::PreferSpeed => {
707 self.plan_generic_resize::<f32, f32, 3>(source_size, target_size, 8)
708 }
709 }
710 }
711
712 pub fn plan_rgba_resampling_f32(
739 &self,
740 source_size: ImageSize,
741 target_size: ImageSize,
742 premultiply_alpha: bool,
743 ) -> Result<Arc<Resampling<f32, 4>>, PicScaleError> {
744 if premultiply_alpha {
745 match self.workload_strategy {
746 WorkloadStrategy::PreferQuality => self
747 .plan_generic_resize_with_alpha::<f32, f64, 4>(
748 source_size,
749 target_size,
750 8,
751 premultiply_alpha,
752 ),
753 WorkloadStrategy::PreferSpeed => self
754 .plan_generic_resize_with_alpha::<f32, f32, 4>(
755 source_size,
756 target_size,
757 8,
758 premultiply_alpha,
759 ),
760 }
761 } else {
762 match self.workload_strategy {
763 WorkloadStrategy::PreferQuality => {
764 self.plan_generic_resize::<f32, f64, 4>(source_size, target_size, 8)
765 }
766 WorkloadStrategy::PreferSpeed => {
767 self.plan_generic_resize::<f32, f32, 4>(source_size, target_size, 8)
768 }
769 }
770 }
771 }
772
773 pub fn set_threading_policy(&mut self, threading_policy: ThreadingPolicy) -> Self {
774 self.threading_policy = threading_policy;
775 *self
776 }
777}
778
779impl Scaler {
780 pub(crate) fn plan_resize_ar30<const AR30_ORDER: usize>(
781 &self,
782 ar30_type: Rgb30,
783 source_size: ImageSize,
784 destination_size: ImageSize,
785 ) -> Result<Arc<Resampling<u8, 4>>, PicScaleError> {
786 if self.function == ResamplingFunction::Nearest {
787 return Ok(Arc::new(ResampleNearestPlan {
788 source_size,
789 target_size: destination_size,
790 threading_policy: self.threading_policy,
791 _phantom_data: PhantomData,
792 }));
793 }
794 let inner_plan = self.plan_rgb_resampling16(source_size, destination_size, 10)?;
795 let mut _decomposer: Arc<dyn Ar30Destructuring + Send + Sync> =
796 Arc::new(Ar30DestructuringImpl::<AR30_ORDER> { rgb30: ar30_type });
797 #[cfg(all(target_arch = "x86_64", feature = "avx"))]
798 {
799 if std::arch::is_x86_feature_detected!("avx2") {
800 let vertical_filters =
801 u8::make_weights(self.function, source_size.height, destination_size.height)?;
802 let horizontal_filters =
803 u8::make_weights(self.function, source_size.width, destination_size.width)?;
804 use crate::avx2::{
805 avx_column_handler_fixed_point_ar30, avx_convolve_horizontal_rgba_rows_4_ar30,
806 avx_convolve_horizontal_rgba_rows_ar30,
807 };
808 use crate::plan::{HorizontalFiltering, VerticalFiltering};
809 let vertical_plan = Arc::new(VerticalFiltering {
810 filter_row: match ar30_type {
811 Rgb30::Ar30 => {
812 avx_column_handler_fixed_point_ar30::<
813 { Rgb30::Ar30 as usize },
814 AR30_ORDER,
815 >
816 }
817 Rgb30::Ra30 => {
818 avx_column_handler_fixed_point_ar30::<
819 { Rgb30::Ra30 as usize },
820 AR30_ORDER,
821 >
822 }
823 },
824 filter_weights: vertical_filters
825 .numerical_approximation_i16::<{ crate::support::PRECISION }>(0),
826 threading_policy: self.threading_policy,
827 });
828 let horizontal_plan = Arc::new(HorizontalFiltering {
829 filter_row: match ar30_type {
830 Rgb30::Ar30 => {
831 avx_convolve_horizontal_rgba_rows_ar30::<
832 { Rgb30::Ar30 as usize },
833 AR30_ORDER,
834 >
835 }
836 Rgb30::Ra30 => {
837 avx_convolve_horizontal_rgba_rows_ar30::<
838 { Rgb30::Ra30 as usize },
839 AR30_ORDER,
840 >
841 }
842 },
843 filter_4_rows: Some(match ar30_type {
844 Rgb30::Ar30 => {
845 avx_convolve_horizontal_rgba_rows_4_ar30::<
846 { Rgb30::Ar30 as usize },
847 AR30_ORDER,
848 >
849 }
850 Rgb30::Ra30 => {
851 avx_convolve_horizontal_rgba_rows_4_ar30::<
852 { Rgb30::Ra30 as usize },
853 AR30_ORDER,
854 >
855 }
856 }),
857 threading_policy: self.threading_policy,
858 filter_weights: horizontal_filters
859 .numerical_approximation_i16::<{ crate::support::PRECISION }>(0),
860 });
861
862 let should_do_horizontal = source_size.width != destination_size.width;
863 let should_do_vertical = source_size.height != destination_size.height;
864
865 let trampoline_filter = Arc::new(TrampolineFiltering {
866 horizontal_filter: horizontal_plan.clone(),
867 vertical_filter: vertical_plan.clone(),
868 source_size,
869 target_size: destination_size,
870 });
871
872 return Ok(Arc::new(NonAlphaConvolvePlan {
873 source_size,
874 target_size: destination_size,
875 horizontal_filter: horizontal_plan,
876 vertical_filter: vertical_plan,
877 trampoline_filter,
878 should_do_vertical,
879 should_do_horizontal,
880 threading_policy: self.threading_policy,
881 }));
882 }
883 }
884 Ok(Arc::new(Ar30Plan {
885 source_size,
886 target_size: destination_size,
887 inner_filter: inner_plan,
888 decomposer: _decomposer,
889 }))
890 }
891
892 pub fn plan_ar30_resampling(
914 &self,
915 source_size: ImageSize,
916 target_size: ImageSize,
917 order: Ar30ByteOrder,
918 ) -> Result<Arc<Resampling<u8, 4>>, PicScaleError> {
919 match order {
920 Ar30ByteOrder::Host => self.plan_resize_ar30::<{ Ar30ByteOrder::Host as usize }>(
921 Rgb30::Ar30,
922 source_size,
923 target_size,
924 ),
925 Ar30ByteOrder::Network => self.plan_resize_ar30::<{ Ar30ByteOrder::Network as usize }>(
926 Rgb30::Ar30,
927 source_size,
928 target_size,
929 ),
930 }
931 }
932
933 pub fn plan_ra30_resampling(
955 &self,
956 source_size: ImageSize,
957 target_size: ImageSize,
958 order: Ar30ByteOrder,
959 ) -> Result<Arc<Resampling<u8, 4>>, PicScaleError> {
960 match order {
961 Ar30ByteOrder::Host => self.plan_resize_ar30::<{ Ar30ByteOrder::Host as usize }>(
962 Rgb30::Ra30,
963 source_size,
964 target_size,
965 ),
966 Ar30ByteOrder::Network => self.plan_resize_ar30::<{ Ar30ByteOrder::Network as usize }>(
967 Rgb30::Ra30,
968 source_size,
969 target_size,
970 ),
971 }
972 }
973}
974
975#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)]
977pub struct ScalingOptions {
978 pub resampling_function: ResamplingFunction,
979 pub premultiply_alpha: bool,
980 pub threading_policy: ThreadingPolicy,
981}
982
983pub trait ImageStoreScaling<'b, T, const N: usize>
985where
986 T: Clone + Copy + Debug,
987{
988 fn scale(
989 &self,
990 store: &mut ImageStoreMut<'b, T, N>,
991 options: ScalingOptions,
992 ) -> Result<(), PicScaleError>;
993}
994
995macro_rules! def_image_scaling_alpha {
996 ($clazz: ident, $fx_type: ident, $cn: expr) => {
997 impl<'b> ImageStoreScaling<'b, $fx_type, $cn> for $clazz<'b> {
998 fn scale(
999 &self,
1000 store: &mut ImageStoreMut<'b, $fx_type, $cn>,
1001 options: ScalingOptions,
1002 ) -> Result<(), PicScaleError> {
1003 let scaler = Scaler::new(options.resampling_function)
1004 .set_threading_policy(options.threading_policy);
1005 let plan = scaler.plan_generic_resize_with_alpha::<$fx_type, f32, $cn>(
1006 self.size(),
1007 store.size(),
1008 store.bit_depth,
1009 options.premultiply_alpha,
1010 )?;
1011 plan.resample(self, store)
1012 }
1013 }
1014 };
1015}
1016
1017macro_rules! def_image_scaling {
1018 ($clazz: ident, $fx_type: ident, $cn: expr) => {
1019 impl<'b> ImageStoreScaling<'b, $fx_type, $cn> for $clazz<'b> {
1020 fn scale(
1021 &self,
1022 store: &mut ImageStoreMut<'b, $fx_type, $cn>,
1023 options: ScalingOptions,
1024 ) -> Result<(), PicScaleError> {
1025 let scaler = Scaler::new(options.resampling_function)
1026 .set_threading_policy(options.threading_policy);
1027 let plan = scaler.plan_generic_resize::<$fx_type, f32, $cn>(
1028 self.size(),
1029 store.size(),
1030 store.bit_depth,
1031 )?;
1032 plan.resample(self, store)
1033 }
1034 }
1035 };
1036}
1037
1038def_image_scaling_alpha!(Rgba8ImageStore, u8, 4);
1039def_image_scaling!(Rgb8ImageStore, u8, 3);
1040def_image_scaling!(CbCr8ImageStore, u8, 2);
1041def_image_scaling!(Planar8ImageStore, u8, 1);
1042def_image_scaling!(Planar16ImageStore, u16, 1);
1043def_image_scaling!(CbCr16ImageStore, u16, 2);
1044def_image_scaling!(Rgb16ImageStore, u16, 3);
1045def_image_scaling_alpha!(Rgba16ImageStore, u16, 4);
1046def_image_scaling!(PlanarF32ImageStore, f32, 1);
1047def_image_scaling!(CbCrF32ImageStore, f32, 2);
1048def_image_scaling!(RgbF32ImageStore, f32, 3);
1049def_image_scaling_alpha!(RgbaF32ImageStore, f32, 4);
1050
1051#[cfg(test)]
1052mod tests {
1053 use super::*;
1054
1055 macro_rules! check_rgba8 {
1056 ($dst: expr, $image_width: expr, $max: expr) => {
1057 {
1058 for (y, row) in $dst.chunks_exact($image_width * 4).enumerate() {
1059 for (i, dst) in row.chunks_exact(4).enumerate() {
1060 let diff0 = (dst[0] as i32 - 124).abs();
1061 let diff1 = (dst[1] as i32 - 41).abs();
1062 let diff2 = (dst[2] as i32 - 99).abs();
1063 let diff3 = (dst[3] as i32 - 77).abs();
1064 assert!(
1065 diff0 < $max,
1066 "Diff for channel 0 is expected < {}, but it was {diff0}, at (y: {y}, x: {i})",
1067 $max
1068 );
1069 assert!(
1070 diff1 < $max,
1071 "Diff for channel 1 is expected < {}, but it was {diff1}, at (y: {y}, x: {i})",
1072 $max
1073 );
1074 assert!(
1075 diff2 < $max,
1076 "Diff for channel 2 is expected < {}, but it was {diff2}, at (y: {y}, x: {i})",
1077 $max
1078 );
1079 assert!(
1080 diff3 < $max,
1081 "Diff for channel 3 is expected < {}, but it was {diff3}, at (y: {y}, x: {i})",
1082 $max
1083 );
1084 }
1085 }
1086 }
1087 };
1088 }
1089
1090 macro_rules! check_rgb16 {
1091 ($dst: expr, $image_width: expr, $max: expr) => {
1092 {
1093 for (y, row) in $dst.chunks_exact($image_width * 3).enumerate() {
1094 for (i, dst) in row.chunks_exact(3).enumerate() {
1095 let diff0 = (dst[0] as i32 - 124).abs();
1096 let diff1 = (dst[1] as i32 - 41).abs();
1097 let diff2 = (dst[2] as i32 - 99).abs();
1098 assert!(
1099 diff0 < $max,
1100 "Diff for channel 0 is expected < {}, but it was {diff0}, at (y: {y}, x: {i})",
1101 $max
1102 );
1103 assert!(
1104 diff1 < $max,
1105 "Diff for channel 1 is expected < {}, but it was {diff1}, at (y: {y}, x: {i})",
1106 $max
1107 );
1108 assert!(
1109 diff2 < $max,
1110 "Diff for channel 2 is expected < {}, but it was {diff2}, at (y: {y}, x: {i})",
1111 $max
1112 );
1113 }
1114 }
1115 }
1116 };
1117 }
1118
1119 #[test]
1120 fn check_rgba8_resizing_vertical() {
1121 let image_width = 255;
1122 let image_height = 512;
1123 const CN: usize = 4;
1124 let mut image = vec![0u8; image_height * image_width * CN];
1125 for dst in image.chunks_exact_mut(4) {
1126 dst[0] = 124;
1127 dst[1] = 41;
1128 dst[2] = 99;
1129 dst[3] = 77;
1130 }
1131 let scaler =
1132 Scaler::new(ResamplingFunction::Bilinear).set_threading_policy(ThreadingPolicy::Single);
1133 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1134 let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1135 let planned = scaler
1136 .plan_rgba_resampling(src_store.size(), target_store.size(), false)
1137 .unwrap();
1138 planned.resample(&src_store, &mut target_store).unwrap();
1139 let target_data = target_store.buffer.borrow();
1140 check_rgba8!(target_data, image_width, 34);
1141 }
1142
1143 #[test]
1144 fn check_rgba8_resizing_both() {
1145 let image_width = 255;
1146 let image_height = 512;
1147 const CN: usize = 4;
1148 let mut image = vec![0u8; image_height * image_width * CN];
1149 for dst in image.chunks_exact_mut(4) {
1150 dst[0] = 124;
1151 dst[1] = 41;
1152 dst[2] = 99;
1153 dst[3] = 77;
1154 }
1155 image[3] = 78;
1156 let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
1157 scaler.set_threading_policy(ThreadingPolicy::Single);
1158 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1159 let mut target_store = ImageStoreMut::alloc(image_width / 2, image_height / 2);
1160 let planned = scaler
1161 .plan_rgba_resampling(src_store.size(), target_store.size(), false)
1162 .unwrap();
1163 planned.resample(&src_store, &mut target_store).unwrap();
1164 let target_data = target_store.buffer.borrow();
1165 check_rgba8!(target_data, image_width, 34);
1166 }
1167
1168 #[test]
1169 fn check_rgba8_resizing_alpha() {
1170 let image_width = 255;
1171 let image_height = 512;
1172 const CN: usize = 4;
1173 let mut image = vec![0u8; image_height * image_width * CN];
1174 for dst in image.chunks_exact_mut(4) {
1175 dst[0] = 124;
1176 dst[1] = 41;
1177 dst[2] = 99;
1178 dst[3] = 77;
1179 }
1180 image[3] = 78;
1181 let scaler =
1182 Scaler::new(ResamplingFunction::Lanczos3).set_threading_policy(ThreadingPolicy::Single);
1183 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1184 let mut target_store = ImageStoreMut::alloc(image_width / 2, image_height / 2);
1185 let planned = scaler
1186 .plan_rgba_resampling(src_store.size(), target_store.size(), true)
1187 .unwrap();
1188 planned.resample(&src_store, &mut target_store).unwrap();
1189 let target_data = target_store.buffer.borrow();
1190 check_rgba8!(target_data, image_width, 160);
1191 }
1192
1193 #[test]
1194 fn check_rgb8_resizing_vertical() {
1195 let image_width = 255;
1196 let image_height = 512;
1197 const CN: usize = 3;
1198 let mut image = vec![0u8; image_height * image_width * CN];
1199 for dst in image.chunks_exact_mut(3) {
1200 dst[0] = 124;
1201 dst[1] = 41;
1202 dst[2] = 99;
1203 }
1204 let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
1205 scaler.set_threading_policy(ThreadingPolicy::Single);
1206 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1207 let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1208 let planned = scaler
1209 .plan_rgb_resampling(src_store.size(), target_store.size())
1210 .unwrap();
1211 planned.resample(&src_store, &mut target_store).unwrap();
1212 let target_data = target_store.buffer.borrow();
1213
1214 check_rgb16!(target_data, image_width, 85);
1215 }
1216
1217 #[test]
1218 fn check_rgb8_resizing_vertical_threading() {
1219 let image_width = 255;
1220 let image_height = 512;
1221 const CN: usize = 3;
1222 let mut image = vec![0u8; image_height * image_width * CN];
1223 for dst in image.chunks_exact_mut(3) {
1224 dst[0] = 124;
1225 dst[1] = 41;
1226 dst[2] = 99;
1227 }
1228 let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
1229 scaler.set_threading_policy(ThreadingPolicy::Adaptive);
1230 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1231 let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1232 let planned = scaler
1233 .plan_rgb_resampling(src_store.size(), target_store.size())
1234 .unwrap();
1235 planned.resample(&src_store, &mut target_store).unwrap();
1236 let target_data = target_store.buffer.borrow();
1237
1238 check_rgb16!(target_data, image_width, 85);
1239 }
1240
1241 #[test]
1242 fn check_rgba10_resizing_vertical() {
1243 let image_width = 8;
1244 let image_height = 8;
1245 const CN: usize = 4;
1246 let mut image = vec![0u16; image_height * image_width * CN];
1247 for dst in image.chunks_exact_mut(4) {
1248 dst[0] = 124;
1249 dst[1] = 41;
1250 dst[2] = 99;
1251 dst[3] = 77;
1252 }
1253 image[3] = 78;
1254 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1255 scaler.set_threading_policy(ThreadingPolicy::Single);
1256 let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1257 src_store.bit_depth = 10;
1258 let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 10);
1259 let planned = scaler
1260 .plan_rgba_resampling16(src_store.size(), target_store.size(), true, 10)
1261 .unwrap();
1262 planned.resample(&src_store, &mut target_store).unwrap();
1263 let target_data = target_store.buffer.borrow();
1264
1265 check_rgba8!(target_data, image_width, 60);
1266 }
1267
1268 #[test]
1269 fn check_rgb10_resizing_vertical() {
1270 let image_width = 8;
1271 let image_height = 4;
1272 const CN: usize = 3;
1273 let mut image = vec![0; image_height * image_width * CN];
1274 for dst in image.chunks_exact_mut(3) {
1275 dst[0] = 124;
1276 dst[1] = 41;
1277 dst[2] = 99;
1278 }
1279 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1280 scaler.set_threading_policy(ThreadingPolicy::Single);
1281 let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1282 src_store.bit_depth = 10;
1283 let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 10);
1284 let planned = scaler
1285 .plan_rgb_resampling16(src_store.size(), target_store.size(), 10)
1286 .unwrap();
1287 planned.resample(&src_store, &mut target_store).unwrap();
1288 let target_data = target_store.buffer.borrow();
1289
1290 check_rgb16!(target_data, image_width, 85);
1291 }
1292
1293 #[test]
1294 fn check_rgb10_resizing_vertical_adaptive() {
1295 let image_width = 8;
1296 let image_height = 4;
1297 const CN: usize = 3;
1298 let mut image = vec![0; image_height * image_width * CN];
1299 for dst in image.chunks_exact_mut(3) {
1300 dst[0] = 124;
1301 dst[1] = 41;
1302 dst[2] = 99;
1303 }
1304 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1305 scaler.set_threading_policy(ThreadingPolicy::Adaptive);
1306 let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1307 src_store.bit_depth = 10;
1308 let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 10);
1309 let planned = scaler
1310 .plan_rgb_resampling16(src_store.size(), target_store.size(), 10)
1311 .unwrap();
1312 planned.resample(&src_store, &mut target_store).unwrap();
1313 let target_data = target_store.buffer.borrow();
1314
1315 check_rgb16!(target_data, image_width, 85);
1316 }
1317
1318 #[test]
1319 fn check_rgb16_resizing_vertical() {
1320 let image_width = 8;
1321 let image_height = 8;
1322 const CN: usize = 3;
1323 let mut image = vec![164; image_height * image_width * CN];
1324 for dst in image.chunks_exact_mut(3) {
1325 dst[0] = 124;
1326 dst[1] = 41;
1327 dst[2] = 99;
1328 }
1329 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1330 scaler.set_threading_policy(ThreadingPolicy::Single);
1331 let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1332 src_store.bit_depth = 10;
1333 let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 16);
1334 let planned = scaler
1335 .plan_rgb_resampling16(src_store.size(), target_store.size(), 16)
1336 .unwrap();
1337 planned.resample(&src_store, &mut target_store).unwrap();
1338 let target_data = target_store.buffer.borrow();
1339
1340 check_rgb16!(target_data, image_width, 100);
1341 }
1342
1343 #[test]
1344 fn check_rgba16_resizing_vertical() {
1345 let image_width = 8;
1346 let image_height = 8;
1347 const CN: usize = 4;
1348 let mut image = vec![0u16; image_height * image_width * CN];
1349 for dst in image.chunks_exact_mut(4) {
1350 dst[0] = 124;
1351 dst[1] = 41;
1352 dst[2] = 99;
1353 dst[3] = 255;
1354 }
1355 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1356 scaler.set_threading_policy(ThreadingPolicy::Single);
1357 let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1358 src_store.bit_depth = 10;
1359 let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 16);
1360 let planned = scaler
1361 .plan_rgba_resampling16(src_store.size(), target_store.size(), false, 16)
1362 .unwrap();
1363 planned.resample(&src_store, &mut target_store).unwrap();
1364 let target_data = target_store.buffer.borrow();
1365
1366 check_rgba8!(target_data, image_width, 180);
1367 }
1368
1369 #[test]
1370 fn check_rgba16_resizing_vertical_threading() {
1371 let image_width = 8;
1372 let image_height = 8;
1373 const CN: usize = 4;
1374 let mut image = vec![0u16; image_height * image_width * CN];
1375 for dst in image.chunks_exact_mut(4) {
1376 dst[0] = 124;
1377 dst[1] = 41;
1378 dst[2] = 99;
1379 dst[3] = 255;
1380 }
1381 let scaler = Scaler::new(ResamplingFunction::Lanczos3)
1382 .set_threading_policy(ThreadingPolicy::Adaptive);
1383 let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1384 src_store.bit_depth = 10;
1385 let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 16);
1386 let planned = scaler
1387 .plan_rgba_resampling16(src_store.size(), target_store.size(), false, 16)
1388 .unwrap();
1389 planned.resample(&src_store, &mut target_store).unwrap();
1390 let target_data = target_store.buffer.borrow();
1391
1392 check_rgba8!(target_data, image_width, 180);
1393 }
1394
1395 #[test]
1396 fn check_rgba8_nearest_vertical() {
1397 let image_width = 255;
1398 let image_height = 512;
1399 const CN: usize = 4;
1400 let mut image = vec![0u8; image_height * image_width * CN];
1401 for dst in image.chunks_exact_mut(4) {
1402 dst[0] = 124;
1403 dst[1] = 41;
1404 dst[2] = 99;
1405 dst[3] = 77;
1406 }
1407 let mut scaler = Scaler::new(ResamplingFunction::Nearest);
1408 scaler.set_threading_policy(ThreadingPolicy::Single);
1409 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1410 let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1411 let planned = scaler
1412 .plan_rgba_resampling(src_store.size(), target_store.size(), false)
1413 .unwrap();
1414 planned.resample(&src_store, &mut target_store).unwrap();
1415 let target_data = target_store.buffer.borrow();
1416
1417 check_rgba8!(target_data, image_width, 80);
1418 }
1419
1420 #[test]
1421 fn check_rgba8_nearest_vertical_threading() {
1422 let image_width = 255;
1423 let image_height = 512;
1424 const CN: usize = 4;
1425 let mut image = vec![0u8; image_height * image_width * CN];
1426 for dst in image.chunks_exact_mut(4) {
1427 dst[0] = 124;
1428 dst[1] = 41;
1429 dst[2] = 99;
1430 dst[3] = 77;
1431 }
1432 let mut scaler = Scaler::new(ResamplingFunction::Nearest);
1433 scaler.set_threading_policy(ThreadingPolicy::Adaptive);
1434 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1435 let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1436 let planned = scaler
1437 .plan_rgba_resampling(src_store.size(), target_store.size(), false)
1438 .unwrap();
1439 planned.resample(&src_store, &mut target_store).unwrap();
1440 let target_data = target_store.buffer.borrow();
1441
1442 check_rgba8!(target_data, image_width, 80);
1443 }
1444}