1#![forbid(unsafe_code)]
30use crate::ar30::{Ar30ByteOrder, Rgb30};
31use crate::convolution::{ConvolutionOptions, HorizontalFilterPass, VerticalConvolutionPass};
32use crate::filter_weights::{DefaultWeightsConverter, WeightsConverter};
33use crate::image_size::ImageSize;
34use crate::image_store::{
35 AssociateAlpha, CheckStoreDensity, ImageStore, ImageStoreMut, UnassociateAlpha,
36};
37use crate::math::WeightsGenerator;
38use crate::plan::{
39 AlphaConvolvePlan, HorizontalFiltering, NonAlphaConvolvePlan, ResampleNearestPlan, Resampling,
40 TrampolineFiltering, VerticalFiltering,
41};
42use crate::threading_policy::ThreadingPolicy;
43use crate::validation::PicScaleError;
44use crate::{
45 CbCr8ImageStore, CbCr16ImageStore, CbCrF32ImageStore, Planar8ImageStore, Planar16ImageStore,
46 PlanarF32ImageStore, ResamplingFunction, ResamplingPlan, Rgb8ImageStore, Rgb16ImageStore,
47 RgbF32ImageStore, Rgba8ImageStore, Rgba16ImageStore, RgbaF32ImageStore,
48};
49use std::fmt::Debug;
50use std::marker::PhantomData;
51use std::sync::Arc;
52
53#[derive(Debug, Copy, Clone)]
54pub struct Scaler {
56 pub(crate) function: ResamplingFunction,
57 pub(crate) threading_policy: ThreadingPolicy,
58 pub workload_strategy: WorkloadStrategy,
59}
60
61#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Default)]
63pub enum WorkloadStrategy {
64 PreferQuality,
66 #[default]
68 PreferSpeed,
69}
70
71impl Scaler {
72 pub fn new(filter: ResamplingFunction) -> Self {
77 Scaler {
78 function: filter,
79 threading_policy: ThreadingPolicy::Single,
80 workload_strategy: WorkloadStrategy::default(),
81 }
82 }
83
84 pub fn set_workload_strategy(&mut self, workload_strategy: WorkloadStrategy) -> Self {
88 self.workload_strategy = workload_strategy;
89 *self
90 }
91}
92
93impl Scaler {
94 pub(crate) fn plan_generic_resize<
95 T: Clone + Copy + Debug + Send + Sync + Default + WeightsGenerator<W> + 'static,
96 W,
97 const N: usize,
98 >(
99 &self,
100 source_size: ImageSize,
101 destination_size: ImageSize,
102 bit_depth: usize,
103 ) -> Result<Arc<Resampling<T, N>>, PicScaleError>
104 where
105 for<'a> ImageStore<'a, T, N>:
106 VerticalConvolutionPass<T, W, N> + HorizontalFilterPass<T, W, N>,
107 for<'a> ImageStoreMut<'a, T, N>: CheckStoreDensity,
108 {
109 if self.function == ResamplingFunction::Nearest {
110 return Ok(Arc::new(ResampleNearestPlan {
111 source_size,
112 target_size: destination_size,
113 threading_policy: self.threading_policy,
114 _phantom_data: PhantomData,
115 }));
116 }
117 let vertical_filters =
118 T::make_weights(self.function, source_size.height, destination_size.height)?;
119 let horizontal_filters =
120 T::make_weights(self.function, source_size.width, destination_size.width)?;
121 let options = ConvolutionOptions {
122 workload_strategy: self.workload_strategy,
123 bit_depth,
124 src_size: source_size,
125 dst_size: destination_size,
126 };
127 let vertical_plan =
128 ImageStore::<T, N>::vertical_plan(vertical_filters, self.threading_policy, options);
129 let horizontal_plan =
130 ImageStore::<T, N>::horizontal_plan(horizontal_filters, self.threading_policy, options);
131
132 let should_do_horizontal = source_size.width != destination_size.width;
133 let should_do_vertical = source_size.height != destination_size.height;
134
135 let trampoline_filter = Arc::new(TrampolineFiltering {
136 horizontal_filter: horizontal_plan.clone(),
137 vertical_filter: vertical_plan.clone(),
138 source_size,
139 target_size: destination_size,
140 });
141
142 Ok(Arc::new(NonAlphaConvolvePlan {
143 source_size,
144 target_size: destination_size,
145 horizontal_filter: horizontal_plan,
146 vertical_filter: vertical_plan,
147 trampoline_filter,
148 should_do_vertical,
149 should_do_horizontal,
150 threading_policy: self.threading_policy,
151 }))
152 }
153
154 pub(crate) fn plan_generic_resize_with_alpha<
155 T: Clone + Copy + Debug + Send + Sync + Default + WeightsGenerator<W> + 'static,
156 W,
157 const N: usize,
158 >(
159 &self,
160 source_size: ImageSize,
161 destination_size: ImageSize,
162 bit_depth: usize,
163 needs_alpha_premultiplication: bool,
164 ) -> Result<Arc<Resampling<T, N>>, PicScaleError>
165 where
166 for<'a> ImageStore<'a, T, N>:
167 VerticalConvolutionPass<T, W, N> + HorizontalFilterPass<T, W, N> + AssociateAlpha<T, N>,
168 for<'a> ImageStoreMut<'a, T, N>: CheckStoreDensity + UnassociateAlpha<T, N>,
169 {
170 if self.function == ResamplingFunction::Nearest {
171 return Ok(Arc::new(ResampleNearestPlan {
172 source_size,
173 target_size: destination_size,
174 threading_policy: self.threading_policy,
175 _phantom_data: PhantomData,
176 }));
177 }
178 if !needs_alpha_premultiplication {
179 return self.plan_generic_resize(source_size, destination_size, bit_depth);
180 }
181 let vertical_filters =
182 T::make_weights(self.function, source_size.height, destination_size.height)?;
183 let horizontal_filters =
184 T::make_weights(self.function, source_size.width, destination_size.width)?;
185 let options = ConvolutionOptions {
186 workload_strategy: self.workload_strategy,
187 bit_depth,
188 src_size: source_size,
189 dst_size: destination_size,
190 };
191 let vertical_plan =
192 ImageStore::<T, N>::vertical_plan(vertical_filters, self.threading_policy, options);
193 let horizontal_plan =
194 ImageStore::<T, N>::horizontal_plan(horizontal_filters, self.threading_policy, options);
195
196 let should_do_horizontal = source_size.width != destination_size.width;
197 let should_do_vertical = source_size.height != destination_size.height;
198
199 let trampoline_filter = Arc::new(TrampolineFiltering {
200 horizontal_filter: horizontal_plan.clone(),
201 vertical_filter: vertical_plan.clone(),
202 source_size,
203 target_size: destination_size,
204 });
205
206 Ok(Arc::new(AlphaConvolvePlan {
207 source_size,
208 target_size: destination_size,
209 threading_policy: self.threading_policy,
210 horizontal_filter: horizontal_plan,
211 vertical_filter: vertical_plan,
212 trampoline_filter,
213 should_do_vertical,
214 should_do_horizontal,
215 workload_strategy: self.workload_strategy,
216 }))
217 }
218
219 pub fn plan_planar_resampling(
235 &self,
236 source_size: ImageSize,
237 target_size: ImageSize,
238 ) -> Result<Arc<Resampling<u8, 1>>, PicScaleError> {
239 self.plan_generic_resize(source_size, target_size, 8)
240 }
241
242 pub fn plan_gray_alpha_resampling(
261 &self,
262 source_size: ImageSize,
263 target_size: ImageSize,
264 premultiply_alpha: bool,
265 ) -> Result<Arc<Resampling<u8, 2>>, PicScaleError> {
266 if premultiply_alpha {
267 self.plan_generic_resize_with_alpha(source_size, target_size, 8, premultiply_alpha)
268 } else {
269 self.plan_generic_resize(source_size, target_size, 8)
270 }
271 }
272
273 pub fn plan_cbcr_resampling(
291 &self,
292 source_size: ImageSize,
293 target_size: ImageSize,
294 ) -> Result<Arc<Resampling<u8, 2>>, PicScaleError> {
295 self.plan_generic_resize(source_size, target_size, 8)
296 }
297
298 pub fn plan_rgb_resampling(
316 &self,
317 source_size: ImageSize,
318 target_size: ImageSize,
319 ) -> Result<Arc<Resampling<u8, 3>>, PicScaleError> {
320 self.plan_generic_resize(source_size, target_size, 8)
321 }
322
323 pub fn plan_rgba_resampling(
342 &self,
343 source_size: ImageSize,
344 target_size: ImageSize,
345 premultiply_alpha: bool,
346 ) -> Result<Arc<Resampling<u8, 4>>, PicScaleError> {
347 if premultiply_alpha {
348 self.plan_generic_resize_with_alpha(source_size, target_size, 8, premultiply_alpha)
349 } else {
350 self.plan_generic_resize(source_size, target_size, 8)
351 }
352 }
353
354 pub fn plan_planar_resampling16(
373 &self,
374 source_size: ImageSize,
375 target_size: ImageSize,
376 bit_depth: usize,
377 ) -> Result<Arc<Resampling<u16, 1>>, PicScaleError> {
378 self.plan_generic_resize(source_size, target_size, bit_depth)
379 }
380
381 pub fn plan_cbcr_resampling16(
401 &self,
402 source_size: ImageSize,
403 target_size: ImageSize,
404 bit_depth: usize,
405 ) -> Result<Arc<Resampling<u16, 2>>, PicScaleError> {
406 self.plan_generic_resize(source_size, target_size, bit_depth)
407 }
408
409 pub fn plan_gray_alpha_resampling16(
430 &self,
431 source_size: ImageSize,
432 target_size: ImageSize,
433 premultiply_alpha: bool,
434 bit_depth: usize,
435 ) -> Result<Arc<Resampling<u16, 2>>, PicScaleError> {
436 if premultiply_alpha {
437 self.plan_generic_resize_with_alpha(
438 source_size,
439 target_size,
440 bit_depth,
441 premultiply_alpha,
442 )
443 } else {
444 self.plan_cbcr_resampling16(source_size, target_size, bit_depth)
445 }
446 }
447
448 pub fn plan_rgb_resampling16(
468 &self,
469 source_size: ImageSize,
470 target_size: ImageSize,
471 bit_depth: usize,
472 ) -> Result<Arc<Resampling<u16, 3>>, PicScaleError> {
473 self.plan_generic_resize(source_size, target_size, bit_depth)
474 }
475
476 pub fn plan_rgba_resampling16(
497 &self,
498 source_size: ImageSize,
499 target_size: ImageSize,
500 premultiply_alpha: bool,
501 bit_depth: usize,
502 ) -> Result<Arc<Resampling<u16, 4>>, PicScaleError> {
503 if premultiply_alpha {
504 self.plan_generic_resize_with_alpha(
505 source_size,
506 target_size,
507 bit_depth,
508 premultiply_alpha,
509 )
510 } else {
511 self.plan_generic_resize(source_size, target_size, bit_depth)
512 }
513 }
514
515 pub fn plan_planar_resampling_f32(
539 &self,
540 source_size: ImageSize,
541 target_size: ImageSize,
542 ) -> Result<Arc<Resampling<f32, 1>>, PicScaleError> {
543 match self.workload_strategy {
544 WorkloadStrategy::PreferQuality => {
545 self.plan_generic_resize::<f32, f64, 1>(source_size, target_size, 8)
546 }
547 WorkloadStrategy::PreferSpeed => {
548 self.plan_generic_resize::<f32, f32, 1>(source_size, target_size, 8)
549 }
550 }
551 }
552
553 pub fn plan_cbcr_resampling_f32(
578 &self,
579 source_size: ImageSize,
580 target_size: ImageSize,
581 ) -> Result<Arc<dyn ResamplingPlan<f32, 2> + Send + Sync>, PicScaleError> {
582 match self.workload_strategy {
583 WorkloadStrategy::PreferQuality => {
584 self.plan_generic_resize::<f32, f64, 2>(source_size, target_size, 8)
585 }
586 WorkloadStrategy::PreferSpeed => {
587 self.plan_generic_resize::<f32, f32, 2>(source_size, target_size, 8)
588 }
589 }
590 }
591
592 pub fn plan_gray_alpha_resampling_f32(
620 &self,
621 source_size: ImageSize,
622 target_size: ImageSize,
623 premultiply_alpha: bool,
624 ) -> Result<Arc<Resampling<f32, 2>>, PicScaleError> {
625 if premultiply_alpha {
626 match self.workload_strategy {
627 WorkloadStrategy::PreferQuality => self
628 .plan_generic_resize_with_alpha::<f32, f64, 2>(
629 source_size,
630 target_size,
631 8,
632 premultiply_alpha,
633 ),
634 WorkloadStrategy::PreferSpeed => self
635 .plan_generic_resize_with_alpha::<f32, f32, 2>(
636 source_size,
637 target_size,
638 8,
639 premultiply_alpha,
640 ),
641 }
642 } else {
643 self.plan_cbcr_resampling_f32(source_size, target_size)
644 }
645 }
646
647 pub fn plan_rgb_resampling_f32(
672 &self,
673 source_size: ImageSize,
674 target_size: ImageSize,
675 ) -> Result<Arc<Resampling<f32, 3>>, PicScaleError> {
676 match self.workload_strategy {
677 WorkloadStrategy::PreferQuality => {
678 self.plan_generic_resize::<f32, f64, 3>(source_size, target_size, 8)
679 }
680 WorkloadStrategy::PreferSpeed => {
681 self.plan_generic_resize::<f32, f32, 3>(source_size, target_size, 8)
682 }
683 }
684 }
685
686 pub fn plan_rgba_resampling_f32(
713 &self,
714 source_size: ImageSize,
715 target_size: ImageSize,
716 premultiply_alpha: bool,
717 ) -> Result<Arc<Resampling<f32, 4>>, PicScaleError> {
718 if premultiply_alpha {
719 match self.workload_strategy {
720 WorkloadStrategy::PreferQuality => self
721 .plan_generic_resize_with_alpha::<f32, f64, 4>(
722 source_size,
723 target_size,
724 8,
725 premultiply_alpha,
726 ),
727 WorkloadStrategy::PreferSpeed => self
728 .plan_generic_resize_with_alpha::<f32, f32, 4>(
729 source_size,
730 target_size,
731 8,
732 premultiply_alpha,
733 ),
734 }
735 } else {
736 match self.workload_strategy {
737 WorkloadStrategy::PreferQuality => {
738 self.plan_generic_resize::<f32, f64, 4>(source_size, target_size, 8)
739 }
740 WorkloadStrategy::PreferSpeed => {
741 self.plan_generic_resize::<f32, f32, 4>(source_size, target_size, 8)
742 }
743 }
744 }
745 }
746
747 pub fn set_threading_policy(&mut self, threading_policy: ThreadingPolicy) -> Self {
748 self.threading_policy = threading_policy;
749 *self
750 }
751}
752
753impl Scaler {
754 pub(crate) fn plan_resize_ar30<const AR30_TYPE: usize, const AR30_ORDER: usize>(
755 &self,
756 source_size: ImageSize,
757 destination_size: ImageSize,
758 ) -> Result<Arc<Resampling<u8, 4>>, PicScaleError> {
759 if self.function == ResamplingFunction::Nearest {
760 return Ok(Arc::new(ResampleNearestPlan {
761 source_size,
762 target_size: destination_size,
763 threading_policy: self.threading_policy,
764 _phantom_data: PhantomData,
765 }));
766 }
767 let vertical_filters =
768 u8::make_weights(self.function, source_size.height, destination_size.height)?;
769 let horizontal_filters =
770 u8::make_weights(self.function, source_size.width, destination_size.width)?;
771 let options = ConvolutionOptions {
772 workload_strategy: self.workload_strategy,
773 bit_depth: 10,
774 src_size: source_size,
775 dst_size: destination_size,
776 };
777 let q_vert_filters = DefaultWeightsConverter::default().prepare_weights(&vertical_filters);
778 use crate::dispatch_group_ar30::{
779 get_horizontal_dispatch_ar30, get_horizontal_dispatch4_ar30,
780 get_vertical_dispatcher_ar30,
781 };
782 let vertical_plan = Arc::new(VerticalFiltering {
783 filter_weights: q_vert_filters,
784 threading_policy: self.threading_policy,
785 filter_row: get_vertical_dispatcher_ar30::<AR30_TYPE, AR30_ORDER>(options),
786 });
787
788 let hor_dispatch4 = get_horizontal_dispatch4_ar30::<AR30_TYPE, AR30_ORDER>(options);
789 let hor_dispatch = get_horizontal_dispatch_ar30::<AR30_TYPE, AR30_ORDER>();
790 let q_hor_filters = DefaultWeightsConverter::default().prepare_weights(&horizontal_filters);
791
792 let horizontal_plan = Arc::new(HorizontalFiltering {
793 filter_weights: q_hor_filters,
794 threading_policy: self.threading_policy,
795 filter_4_rows: Some(hor_dispatch4),
796 filter_row: hor_dispatch,
797 });
798
799 let should_do_horizontal = source_size.width != destination_size.width;
800 let should_do_vertical = source_size.height != destination_size.height;
801
802 let trampoline_filter = Arc::new(TrampolineFiltering {
803 horizontal_filter: horizontal_plan.clone(),
804 vertical_filter: vertical_plan.clone(),
805 source_size,
806 target_size: destination_size,
807 });
808
809 Ok(Arc::new(NonAlphaConvolvePlan {
810 source_size,
811 target_size: destination_size,
812 horizontal_filter: horizontal_plan,
813 vertical_filter: vertical_plan,
814 trampoline_filter,
815 should_do_vertical,
816 should_do_horizontal,
817 threading_policy: self.threading_policy,
818 }))
819 }
820
821 pub fn plan_ar30_resampling(
843 &self,
844 source_size: ImageSize,
845 target_size: ImageSize,
846 order: Ar30ByteOrder,
847 ) -> Result<Arc<Resampling<u8, 4>>, PicScaleError> {
848 match order {
849 Ar30ByteOrder::Host => self
850 .plan_resize_ar30::<{ Rgb30::Ar30 as usize }, { Ar30ByteOrder::Host as usize }>(
851 source_size,
852 target_size,
853 ),
854 Ar30ByteOrder::Network => self
855 .plan_resize_ar30::<{ Rgb30::Ar30 as usize }, { Ar30ByteOrder::Network as usize }>(
856 source_size,
857 target_size,
858 ),
859 }
860 }
861
862 pub fn resize_ra30(
884 &self,
885 source_size: ImageSize,
886 target_size: ImageSize,
887 order: Ar30ByteOrder,
888 ) -> Result<Arc<Resampling<u8, 4>>, PicScaleError> {
889 match order {
890 Ar30ByteOrder::Host => self
891 .plan_resize_ar30::<{ Rgb30::Ra30 as usize }, { Ar30ByteOrder::Host as usize }>(
892 source_size,
893 target_size,
894 ),
895 Ar30ByteOrder::Network => self
896 .plan_resize_ar30::<{ Rgb30::Ra30 as usize }, { Ar30ByteOrder::Network as usize }>(
897 source_size,
898 target_size,
899 ),
900 }
901 }
902}
903
904#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)]
906pub struct ScalingOptions {
907 pub resampling_function: ResamplingFunction,
908 pub premultiply_alpha: bool,
909 pub threading_policy: ThreadingPolicy,
910}
911
912pub trait ImageStoreScaling<'b, T, const N: usize>
914where
915 T: Clone + Copy + Debug,
916{
917 fn scale(
918 &self,
919 store: &mut ImageStoreMut<'b, T, N>,
920 options: ScalingOptions,
921 ) -> Result<(), PicScaleError>;
922}
923
924macro_rules! def_image_scaling_alpha {
925 ($clazz: ident, $fx_type: ident, $cn: expr) => {
926 impl<'b> ImageStoreScaling<'b, $fx_type, $cn> for $clazz<'b> {
927 fn scale(
928 &self,
929 store: &mut ImageStoreMut<'b, $fx_type, $cn>,
930 options: ScalingOptions,
931 ) -> Result<(), PicScaleError> {
932 let scaler = Scaler::new(options.resampling_function)
933 .set_threading_policy(options.threading_policy);
934 let plan = scaler.plan_generic_resize_with_alpha::<$fx_type, f32, $cn>(
935 self.size(),
936 store.size(),
937 store.bit_depth,
938 options.premultiply_alpha,
939 )?;
940 plan.resample(self, store)
941 }
942 }
943 };
944}
945
946macro_rules! def_image_scaling {
947 ($clazz: ident, $fx_type: ident, $cn: expr) => {
948 impl<'b> ImageStoreScaling<'b, $fx_type, $cn> for $clazz<'b> {
949 fn scale(
950 &self,
951 store: &mut ImageStoreMut<'b, $fx_type, $cn>,
952 options: ScalingOptions,
953 ) -> Result<(), PicScaleError> {
954 let scaler = Scaler::new(options.resampling_function)
955 .set_threading_policy(options.threading_policy);
956 let plan = scaler.plan_generic_resize::<$fx_type, f32, $cn>(
957 self.size(),
958 store.size(),
959 store.bit_depth,
960 )?;
961 plan.resample(self, store)
962 }
963 }
964 };
965}
966
967def_image_scaling_alpha!(Rgba8ImageStore, u8, 4);
968def_image_scaling!(Rgb8ImageStore, u8, 3);
969def_image_scaling!(CbCr8ImageStore, u8, 2);
970def_image_scaling!(Planar8ImageStore, u8, 1);
971def_image_scaling!(Planar16ImageStore, u16, 1);
972def_image_scaling!(CbCr16ImageStore, u16, 2);
973def_image_scaling!(Rgb16ImageStore, u16, 3);
974def_image_scaling_alpha!(Rgba16ImageStore, u16, 4);
975def_image_scaling!(PlanarF32ImageStore, f32, 1);
976def_image_scaling!(CbCrF32ImageStore, f32, 2);
977def_image_scaling!(RgbF32ImageStore, f32, 3);
978def_image_scaling_alpha!(RgbaF32ImageStore, f32, 4);
979
980#[cfg(test)]
981mod tests {
982 use super::*;
983
984 macro_rules! check_rgba8 {
985 ($dst: expr, $image_width: expr, $max: expr) => {
986 {
987 for (y, row) in $dst.chunks_exact($image_width * 4).enumerate() {
988 for (i, dst) in row.chunks_exact(4).enumerate() {
989 let diff0 = (dst[0] as i32 - 124).abs();
990 let diff1 = (dst[1] as i32 - 41).abs();
991 let diff2 = (dst[2] as i32 - 99).abs();
992 let diff3 = (dst[3] as i32 - 77).abs();
993 assert!(
994 diff0 < $max,
995 "Diff for channel 0 is expected < {}, but it was {diff0}, at (y: {y}, x: {i})",
996 $max
997 );
998 assert!(
999 diff1 < $max,
1000 "Diff for channel 1 is expected < {}, but it was {diff1}, at (y: {y}, x: {i})",
1001 $max
1002 );
1003 assert!(
1004 diff2 < $max,
1005 "Diff for channel 2 is expected < {}, but it was {diff2}, at (y: {y}, x: {i})",
1006 $max
1007 );
1008 assert!(
1009 diff3 < $max,
1010 "Diff for channel 3 is expected < {}, but it was {diff3}, at (y: {y}, x: {i})",
1011 $max
1012 );
1013 }
1014 }
1015 }
1016 };
1017 }
1018
1019 macro_rules! check_rgb16 {
1020 ($dst: expr, $image_width: expr, $max: expr) => {
1021 {
1022 for (y, row) in $dst.chunks_exact($image_width * 3).enumerate() {
1023 for (i, dst) in row.chunks_exact(3).enumerate() {
1024 let diff0 = (dst[0] as i32 - 124).abs();
1025 let diff1 = (dst[1] as i32 - 41).abs();
1026 let diff2 = (dst[2] as i32 - 99).abs();
1027 assert!(
1028 diff0 < $max,
1029 "Diff for channel 0 is expected < {}, but it was {diff0}, at (y: {y}, x: {i})",
1030 $max
1031 );
1032 assert!(
1033 diff1 < $max,
1034 "Diff for channel 1 is expected < {}, but it was {diff1}, at (y: {y}, x: {i})",
1035 $max
1036 );
1037 assert!(
1038 diff2 < $max,
1039 "Diff for channel 2 is expected < {}, but it was {diff2}, at (y: {y}, x: {i})",
1040 $max
1041 );
1042 }
1043 }
1044 }
1045 };
1046 }
1047
1048 #[test]
1049 fn check_rgba8_resizing_vertical() {
1050 let image_width = 255;
1051 let image_height = 512;
1052 const CN: usize = 4;
1053 let mut image = vec![0u8; image_height * image_width * CN];
1054 for dst in image.chunks_exact_mut(4) {
1055 dst[0] = 124;
1056 dst[1] = 41;
1057 dst[2] = 99;
1058 dst[3] = 77;
1059 }
1060 let scaler =
1061 Scaler::new(ResamplingFunction::Bilinear).set_threading_policy(ThreadingPolicy::Single);
1062 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1063 let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1064 let planned = scaler
1065 .plan_rgba_resampling(src_store.size(), target_store.size(), false)
1066 .unwrap();
1067 planned.resample(&src_store, &mut target_store).unwrap();
1068 let target_data = target_store.buffer.borrow();
1069 check_rgba8!(target_data, image_width, 34);
1070 }
1071
1072 #[test]
1073 fn check_rgba8_resizing_both() {
1074 let image_width = 255;
1075 let image_height = 512;
1076 const CN: usize = 4;
1077 let mut image = vec![0u8; image_height * image_width * CN];
1078 for dst in image.chunks_exact_mut(4) {
1079 dst[0] = 124;
1080 dst[1] = 41;
1081 dst[2] = 99;
1082 dst[3] = 77;
1083 }
1084 image[3] = 78;
1085 let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
1086 scaler.set_threading_policy(ThreadingPolicy::Single);
1087 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1088 let mut target_store = ImageStoreMut::alloc(image_width / 2, image_height / 2);
1089 let planned = scaler
1090 .plan_rgba_resampling(src_store.size(), target_store.size(), false)
1091 .unwrap();
1092 planned.resample(&src_store, &mut target_store).unwrap();
1093 let target_data = target_store.buffer.borrow();
1094 check_rgba8!(target_data, image_width, 34);
1095 }
1096
1097 #[test]
1098 fn check_rgba8_resizing_alpha() {
1099 let image_width = 255;
1100 let image_height = 512;
1101 const CN: usize = 4;
1102 let mut image = vec![0u8; image_height * image_width * CN];
1103 for dst in image.chunks_exact_mut(4) {
1104 dst[0] = 124;
1105 dst[1] = 41;
1106 dst[2] = 99;
1107 dst[3] = 77;
1108 }
1109 image[3] = 78;
1110 let scaler =
1111 Scaler::new(ResamplingFunction::Lanczos3).set_threading_policy(ThreadingPolicy::Single);
1112 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1113 let mut target_store = ImageStoreMut::alloc(image_width / 2, image_height / 2);
1114 let planned = scaler
1115 .plan_rgba_resampling(src_store.size(), target_store.size(), true)
1116 .unwrap();
1117 planned.resample(&src_store, &mut target_store).unwrap();
1118 let target_data = target_store.buffer.borrow();
1119 check_rgba8!(target_data, image_width, 160);
1120 }
1121
1122 #[test]
1123 fn check_rgb8_resizing_vertical() {
1124 let image_width = 255;
1125 let image_height = 512;
1126 const CN: usize = 3;
1127 let mut image = vec![0u8; image_height * image_width * CN];
1128 for dst in image.chunks_exact_mut(3) {
1129 dst[0] = 124;
1130 dst[1] = 41;
1131 dst[2] = 99;
1132 }
1133 let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
1134 scaler.set_threading_policy(ThreadingPolicy::Single);
1135 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1136 let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1137 let planned = scaler
1138 .plan_rgb_resampling(src_store.size(), target_store.size())
1139 .unwrap();
1140 planned.resample(&src_store, &mut target_store).unwrap();
1141 let target_data = target_store.buffer.borrow();
1142
1143 check_rgb16!(target_data, image_width, 85);
1144 }
1145
1146 #[test]
1147 fn check_rgb8_resizing_vertical_threading() {
1148 let image_width = 255;
1149 let image_height = 512;
1150 const CN: usize = 3;
1151 let mut image = vec![0u8; image_height * image_width * CN];
1152 for dst in image.chunks_exact_mut(3) {
1153 dst[0] = 124;
1154 dst[1] = 41;
1155 dst[2] = 99;
1156 }
1157 let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
1158 scaler.set_threading_policy(ThreadingPolicy::Adaptive);
1159 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1160 let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1161 let planned = scaler
1162 .plan_rgb_resampling(src_store.size(), target_store.size())
1163 .unwrap();
1164 planned.resample(&src_store, &mut target_store).unwrap();
1165 let target_data = target_store.buffer.borrow();
1166
1167 check_rgb16!(target_data, image_width, 85);
1168 }
1169
1170 #[test]
1171 fn check_rgba10_resizing_vertical() {
1172 let image_width = 8;
1173 let image_height = 8;
1174 const CN: usize = 4;
1175 let mut image = vec![0u16; image_height * image_width * CN];
1176 for dst in image.chunks_exact_mut(4) {
1177 dst[0] = 124;
1178 dst[1] = 41;
1179 dst[2] = 99;
1180 dst[3] = 77;
1181 }
1182 image[3] = 78;
1183 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1184 scaler.set_threading_policy(ThreadingPolicy::Single);
1185 let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1186 src_store.bit_depth = 10;
1187 let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 10);
1188 let planned = scaler
1189 .plan_rgba_resampling16(src_store.size(), target_store.size(), true, 10)
1190 .unwrap();
1191 planned.resample(&src_store, &mut target_store).unwrap();
1192 let target_data = target_store.buffer.borrow();
1193
1194 check_rgba8!(target_data, image_width, 60);
1195 }
1196
1197 #[test]
1198 fn check_rgb10_resizing_vertical() {
1199 let image_width = 8;
1200 let image_height = 4;
1201 const CN: usize = 3;
1202 let mut image = vec![0; image_height * image_width * CN];
1203 for dst in image.chunks_exact_mut(3) {
1204 dst[0] = 124;
1205 dst[1] = 41;
1206 dst[2] = 99;
1207 }
1208 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1209 scaler.set_threading_policy(ThreadingPolicy::Single);
1210 let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1211 src_store.bit_depth = 10;
1212 let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 10);
1213 let planned = scaler
1214 .plan_rgb_resampling16(src_store.size(), target_store.size(), 10)
1215 .unwrap();
1216 planned.resample(&src_store, &mut target_store).unwrap();
1217 let target_data = target_store.buffer.borrow();
1218
1219 check_rgb16!(target_data, image_width, 85);
1220 }
1221
1222 #[test]
1223 fn check_rgb10_resizing_vertical_adaptive() {
1224 let image_width = 8;
1225 let image_height = 4;
1226 const CN: usize = 3;
1227 let mut image = vec![0; image_height * image_width * CN];
1228 for dst in image.chunks_exact_mut(3) {
1229 dst[0] = 124;
1230 dst[1] = 41;
1231 dst[2] = 99;
1232 }
1233 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1234 scaler.set_threading_policy(ThreadingPolicy::Adaptive);
1235 let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1236 src_store.bit_depth = 10;
1237 let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 10);
1238 let planned = scaler
1239 .plan_rgb_resampling16(src_store.size(), target_store.size(), 10)
1240 .unwrap();
1241 planned.resample(&src_store, &mut target_store).unwrap();
1242 let target_data = target_store.buffer.borrow();
1243
1244 check_rgb16!(target_data, image_width, 85);
1245 }
1246
1247 #[test]
1248 fn check_rgb16_resizing_vertical() {
1249 let image_width = 8;
1250 let image_height = 8;
1251 const CN: usize = 3;
1252 let mut image = vec![164; image_height * image_width * CN];
1253 for dst in image.chunks_exact_mut(3) {
1254 dst[0] = 124;
1255 dst[1] = 41;
1256 dst[2] = 99;
1257 }
1258 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1259 scaler.set_threading_policy(ThreadingPolicy::Single);
1260 let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1261 src_store.bit_depth = 10;
1262 let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 16);
1263 let planned = scaler
1264 .plan_rgb_resampling16(src_store.size(), target_store.size(), 16)
1265 .unwrap();
1266 planned.resample(&src_store, &mut target_store).unwrap();
1267 let target_data = target_store.buffer.borrow();
1268
1269 check_rgb16!(target_data, image_width, 100);
1270 }
1271
1272 #[test]
1273 fn check_rgba16_resizing_vertical() {
1274 let image_width = 8;
1275 let image_height = 8;
1276 const CN: usize = 4;
1277 let mut image = vec![0u16; image_height * image_width * CN];
1278 for dst in image.chunks_exact_mut(4) {
1279 dst[0] = 124;
1280 dst[1] = 41;
1281 dst[2] = 99;
1282 dst[3] = 255;
1283 }
1284 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1285 scaler.set_threading_policy(ThreadingPolicy::Single);
1286 let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1287 src_store.bit_depth = 10;
1288 let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 16);
1289 let planned = scaler
1290 .plan_rgba_resampling16(src_store.size(), target_store.size(), false, 16)
1291 .unwrap();
1292 planned.resample(&src_store, &mut target_store).unwrap();
1293 let target_data = target_store.buffer.borrow();
1294
1295 check_rgba8!(target_data, image_width, 180);
1296 }
1297
1298 #[test]
1299 fn check_rgba16_resizing_vertical_threading() {
1300 let image_width = 8;
1301 let image_height = 8;
1302 const CN: usize = 4;
1303 let mut image = vec![0u16; image_height * image_width * CN];
1304 for dst in image.chunks_exact_mut(4) {
1305 dst[0] = 124;
1306 dst[1] = 41;
1307 dst[2] = 99;
1308 dst[3] = 255;
1309 }
1310 let scaler = Scaler::new(ResamplingFunction::Lanczos3)
1311 .set_threading_policy(ThreadingPolicy::Adaptive);
1312 let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1313 src_store.bit_depth = 10;
1314 let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 16);
1315 let planned = scaler
1316 .plan_rgba_resampling16(src_store.size(), target_store.size(), false, 16)
1317 .unwrap();
1318 planned.resample(&src_store, &mut target_store).unwrap();
1319 let target_data = target_store.buffer.borrow();
1320
1321 check_rgba8!(target_data, image_width, 180);
1322 }
1323
1324 #[test]
1325 fn check_rgba8_nearest_vertical() {
1326 let image_width = 255;
1327 let image_height = 512;
1328 const CN: usize = 4;
1329 let mut image = vec![0u8; image_height * image_width * CN];
1330 for dst in image.chunks_exact_mut(4) {
1331 dst[0] = 124;
1332 dst[1] = 41;
1333 dst[2] = 99;
1334 dst[3] = 77;
1335 }
1336 let mut scaler = Scaler::new(ResamplingFunction::Nearest);
1337 scaler.set_threading_policy(ThreadingPolicy::Single);
1338 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1339 let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1340 let planned = scaler
1341 .plan_rgba_resampling(src_store.size(), target_store.size(), false)
1342 .unwrap();
1343 planned.resample(&src_store, &mut target_store).unwrap();
1344 let target_data = target_store.buffer.borrow();
1345
1346 check_rgba8!(target_data, image_width, 80);
1347 }
1348
1349 #[test]
1350 fn check_rgba8_nearest_vertical_threading() {
1351 let image_width = 255;
1352 let image_height = 512;
1353 const CN: usize = 4;
1354 let mut image = vec![0u8; image_height * image_width * CN];
1355 for dst in image.chunks_exact_mut(4) {
1356 dst[0] = 124;
1357 dst[1] = 41;
1358 dst[2] = 99;
1359 dst[3] = 77;
1360 }
1361 let mut scaler = Scaler::new(ResamplingFunction::Nearest);
1362 scaler.set_threading_policy(ThreadingPolicy::Adaptive);
1363 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1364 let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1365 let planned = scaler
1366 .plan_rgba_resampling(src_store.size(), target_store.size(), false)
1367 .unwrap();
1368 planned.resample(&src_store, &mut target_store).unwrap();
1369 let target_data = target_store.buffer.borrow();
1370
1371 check_rgba8!(target_data, image_width, 80);
1372 }
1373}