pic_scale/
scaler.rs

1/*
2 * Copyright (c) Radzivon Bartoshyk. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 *
7 * 1.  Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * 2.  Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * 3.  Neither the name of the copyright holder nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29#![forbid(unsafe_code)]
30use crate::ar30::{Ar30ByteOrder, Rgb30};
31use crate::convolution::{ConvolutionOptions, HorizontalConvolutionPass, VerticalConvolutionPass};
32use crate::image_size::ImageSize;
33use crate::image_store::{
34    AssociateAlpha, CheckStoreDensity, ImageStore, ImageStoreMut, UnassociateAlpha,
35};
36use crate::math::WeightsGenerator;
37use crate::nearest_sampler::resize_nearest;
38use crate::pic_scale_error::{PicScaleError, try_vec};
39use crate::resize_ar30::resize_ar30_impl;
40use crate::support::check_image_size_overflow;
41use crate::threading_policy::ThreadingPolicy;
42use crate::{
43    CbCr8ImageStore, CbCr16ImageStore, CbCrF32ImageStore, Planar8ImageStore, Planar16ImageStore,
44    PlanarF32ImageStore, ResamplingFunction, Rgb8ImageStore, Rgb16ImageStore, RgbF32ImageStore,
45    Rgba8ImageStore, Rgba16ImageStore, RgbaF32ImageStore,
46};
47use std::fmt::Debug;
48
49#[derive(Debug, Copy, Clone)]
50/// Represents base scaling structure
51pub struct Scaler {
52    pub(crate) function: ResamplingFunction,
53    pub(crate) threading_policy: ThreadingPolicy,
54    pub workload_strategy: WorkloadStrategy,
55}
56
57/// 8 bit-depth images scaling trait
58pub trait Scaling {
59    /// Sets threading policy
60    ///
61    /// Setting up threading policy, refer to [crate::ThreadingPolicy] for more info
62    ///
63    /// # Example
64    ///
65    /// #[no_build]
66    /// ```rust
67    /// use pic_scale::{ResamplingFunction, Scaler, Scaling, ThreadingPolicy};
68    /// let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
69    /// scaler.set_threading_policy(ThreadingPolicy::Adaptive);
70    /// ```
71    fn set_threading_policy(&mut self, threading_policy: ThreadingPolicy);
72
73    /// Performs rescaling for planar image
74    ///
75    /// # Example
76    ///
77    /// #[no_build]
78    /// ```rust
79    ///  use pic_scale::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, Scaling};
80    ///  let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
81    ///  let src_store = ImageStore::alloc(100, 100);
82    ///  let mut dst_store = ImageStoreMut::<u8, 1>::alloc(50, 50);
83    ///  scaler.resize_plane(&src_store, &mut dst_store).unwrap();
84    /// ```
85    fn resize_plane<'a>(
86        &'a self,
87        store: &ImageStore<'a, u8, 1>,
88        into: &mut ImageStoreMut<'a, u8, 1>,
89    ) -> Result<(), PicScaleError>;
90
91    /// Performs rescaling for CbCr8 ( or 2 interleaved channels )
92    ///
93    /// Scales 2 interleaved channels as CbCr8, optionally it could handle LumaAlpha images also
94    ///
95    /// # Example
96    ///
97    /// #[no_build]
98    /// ```rust
99    ///  use pic_scale::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, Scaling};
100    ///  let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
101    ///  let src_store = ImageStore::alloc(100, 100);
102    ///  let mut dst_store = ImageStoreMut::<u8, 2>::alloc(50, 50);
103    ///  scaler.resize_cbcr8(&src_store, &mut dst_store).unwrap();
104    /// ```
105    fn resize_cbcr8<'a>(
106        &'a self,
107        store: &ImageStore<'a, u8, 2>,
108        into: &mut ImageStoreMut<'a, u8, 2>,
109    ) -> Result<(), PicScaleError>;
110
111    /// Performs rescaling for Gray Alpha ( or 2 interleaved channels with aloha )
112    ///
113    /// Scales 2 interleaved channels as Gray Alpha
114    ///
115    /// # Example
116    ///
117    /// #[no_build]
118    /// ```rust
119    ///  use pic_scale::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, Scaling};
120    ///  let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
121    ///  let src_store = ImageStore::alloc(100, 100);
122    ///  let mut dst_store = ImageStoreMut::<u8, 2>::alloc(50, 50);
123    ///  scaler.resize_gray_alpha(&src_store, &mut dst_store, true).unwrap();
124    /// ```
125    fn resize_gray_alpha<'a>(
126        &'a self,
127        store: &ImageStore<'a, u8, 2>,
128        into: &mut ImageStoreMut<'a, u8, 2>,
129        premultiply_alpha: bool,
130    ) -> Result<(), PicScaleError>;
131
132    /// Performs rescaling for RGB, channel order does not matter
133    ///
134    /// # Example
135    ///
136    /// #[no_build]
137    /// ```rust
138    ///  use pic_scale::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, Scaling};
139    ///  let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
140    ///  let src_store = ImageStore::alloc(100, 100);
141    ///  let mut dst_store = ImageStoreMut::<u8, 3>::alloc(50, 50);
142    ///  scaler.resize_rgb(&src_store, &mut dst_store).unwrap();
143    /// ```
144    fn resize_rgb<'a>(
145        &'a self,
146        store: &ImageStore<'a, u8, 3>,
147        into: &mut ImageStoreMut<'a, u8, 3>,
148    ) -> Result<(), PicScaleError>;
149
150    /// Performs rescaling for RGBA
151    ///
152    /// This method may premultiply and un associate alpha if required.
153    /// Alpha position is always considered as last
154    ///
155    /// # Example
156    ///
157    /// #[no_build]
158    /// ```rust
159    ///  use pic_scale::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, Scaling};
160    ///  let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
161    ///  let src_store = ImageStore::alloc(100, 100);
162    ///  let mut dst_store = ImageStoreMut::<u8, 4>::alloc(50, 50);
163    ///  scaler.resize_rgba(&src_store, &mut dst_store, false).unwrap();
164    /// ```
165    fn resize_rgba<'a>(
166        &'a self,
167        store: &ImageStore<'a, u8, 4>,
168        into: &mut ImageStoreMut<'a, u8, 4>,
169        premultiply_alpha: bool,
170    ) -> Result<(), PicScaleError>;
171}
172
173/// f32 images scaling trait
174pub trait ScalingF32 {
175    /// Performs rescaling planar f32 image
176    ///
177    /// # Example
178    ///
179    /// #[no_build]
180    /// ```rust
181    ///  use pic_scale::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, Scaling, ScalingF32};
182    ///  let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
183    ///  let src_store = ImageStore::alloc(100, 100);
184    ///  let mut dst_store = ImageStoreMut::<f32, 1>::alloc(50, 50);
185    ///  scaler.resize_plane_f32(&src_store, &mut dst_store).unwrap();
186    /// ```
187    fn resize_plane_f32<'a>(
188        &'a self,
189        store: &ImageStore<'a, f32, 1>,
190        into: &mut ImageStoreMut<'a, f32, 1>,
191    ) -> Result<(), PicScaleError>;
192
193    /// Performs rescaling for CbCr f32 image
194    ///
195    /// Scales an interleaved CbCr f32. Also, could handle LumaAlpha images.
196    ///
197    /// # Example
198    ///
199    /// #[no_build]
200    /// ```rust
201    ///  use pic_scale::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, Scaling, ScalingF32};
202    ///  let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
203    ///  let src_store = ImageStore::alloc(100, 100);
204    ///  let mut dst_store = ImageStoreMut::<f32, 2>::alloc(50, 50);
205    ///  scaler.resize_cbcr_f32(&src_store, &mut dst_store).unwrap();
206    /// ```
207    fn resize_cbcr_f32<'a>(
208        &'a self,
209        store: &ImageStore<'a, f32, 2>,
210        into: &mut ImageStoreMut<'a, f32, 2>,
211    ) -> Result<(), PicScaleError>;
212
213    /// Performs rescaling for Gray Alpha f32 image
214    ///
215    /// Scales an interleaved Gray and Alpha in f32.
216    ///
217    /// # Example
218    ///
219    /// #[no_build]
220    /// ```rust
221    ///  use pic_scale::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, Scaling, ScalingF32};
222    ///  let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
223    ///  let src_store = ImageStore::alloc(100, 100);
224    ///  let mut dst_store = ImageStoreMut::<f32, 2>::alloc(50, 50);
225    ///  scaler.resize_gray_alpha_f32(&src_store, &mut dst_store, true).unwrap();
226    /// ```
227    fn resize_gray_alpha_f32<'a>(
228        &'a self,
229        store: &ImageStore<'a, f32, 2>,
230        into: &mut ImageStoreMut<'a, f32, 2>,
231        premultiply_alpha: bool,
232    ) -> Result<(), PicScaleError>;
233
234    /// Performs rescaling for RGB f32
235    ///
236    /// Scales an image RGB f32, channel order does not matter
237    ///
238    /// # Example
239    ///
240    /// #[no_build]
241    /// ```rust
242    ///  use pic_scale::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, Scaling, ScalingF32};
243    ///  let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
244    ///  let src_store = ImageStore::alloc(100, 100);
245    ///  let mut dst_store = ImageStoreMut::<f32, 3>::alloc(50, 50);
246    ///  scaler.resize_rgb_f32(&src_store, &mut dst_store).unwrap();
247    /// ```
248    fn resize_rgb_f32<'a>(
249        &'a self,
250        store: &ImageStore<'a, f32, 3>,
251        into: &mut ImageStoreMut<'a, f32, 3>,
252    ) -> Result<(), PicScaleError>;
253
254    /// Performs rescaling for RGBA f32
255    ///
256    /// Scales an image RGBA f32, alpha expected to be at last position if
257    /// alpha pre-multiplication is requested
258    ///
259    /// # Example
260    ///
261    /// #[no_build]
262    /// ```rust
263    ///  use pic_scale::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, Scaling, ScalingF32};
264    ///  let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
265    ///  let src_store = ImageStore::alloc(100, 100);
266    ///  let mut dst_store = ImageStoreMut::<f32, 4>::alloc(50, 50);
267    ///  scaler.resize_rgba_f32(&src_store, &mut dst_store, false).unwrap();
268    /// ```
269    fn resize_rgba_f32<'a>(
270        &'a self,
271        store: &ImageStore<'a, f32, 4>,
272        into: &mut ImageStoreMut<'a, f32, 4>,
273        premultiply_alpha: bool,
274    ) -> Result<(), PicScaleError>;
275}
276
277/// Defines execution hint about preferred strategy
278#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Default)]
279pub enum WorkloadStrategy {
280    /// Prefers quality to speed
281    PreferQuality,
282    /// Prefers speed to quality
283    #[default]
284    PreferSpeed,
285}
286
287/// 8+ bit-depth images scaling trait
288pub trait ScalingU16 {
289    /// Performs rescaling for Planar u16
290    ///
291    /// Scales planar high bit-depth image stored in `u16` type.
292    /// To perform scaling image bit-depth should be set in target image,
293    /// source image expects to have the same one.
294    ///
295    /// # Arguments
296    /// `store` - original image store
297    /// `into` - target image store
298    ///
299    /// # Panic
300    /// Method panics if bit-depth < 1 or bit-depth > 16
301    ///
302    /// # Example
303    ///
304    /// #[no_build]
305    /// ```rust
306    ///  use pic_scale::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, ScalingU16};
307    ///  let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
308    ///  let src_store = ImageStore::alloc(100, 100);
309    ///  let mut dst_store = ImageStoreMut::<u16, 1>::alloc_with_depth(50, 50, 10);
310    ///  scaler.resize_plane_u16(&src_store, &mut dst_store).unwrap();
311    /// ```
312    fn resize_plane_u16<'a>(
313        &'a self,
314        store: &ImageStore<'a, u16, 1>,
315        into: &mut ImageStoreMut<'a, u16, 1>,
316    ) -> Result<(), PicScaleError>;
317
318    /// Performs rescaling for CbCr16
319    ///
320    /// Scales CbCr high bit-depth interleaved image in `u16` type, optionally it could handle LumaAlpha images also
321    /// To perform scaling image bit-depth should be set in target image,
322    /// source image expects to have the same one.
323    /// Channel order does not matter.
324    ///
325    /// # Arguments
326    /// `store` - original image store
327    /// `into` - target image store
328    ///
329    /// # Panics
330    /// Method panics if bit-depth < 1 or bit-depth > 16
331    ///
332    /// # Example
333    ///
334    /// #[no_build]
335    /// ```rust
336    ///  use pic_scale::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, ScalingU16};
337    ///  let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
338    ///  let src_store = ImageStore::alloc(100, 100);
339    ///  let mut dst_store = ImageStoreMut::<u16, 2>::alloc_with_depth(50, 50, 10);
340    ///  scaler.resize_cbcr_u16(&src_store, &mut dst_store).unwrap();
341    /// ```
342    ///
343    fn resize_cbcr_u16<'a>(
344        &'a self,
345        store: &ImageStore<'a, u16, 2>,
346        into: &mut ImageStoreMut<'a, u16, 2>,
347    ) -> Result<(), PicScaleError>;
348
349    /// Performs rescaling for Gray Alpha high bit-depth ( or 2 interleaved channels with aloha )
350    ///
351    /// Scales 2 interleaved channels as Gray Alpha
352    ///
353    /// # Example
354    ///
355    /// #[no_build]
356    /// ```rust
357    ///  use pic_scale::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, Scaling, ScalingU16};
358    ///  let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
359    ///  let src_store = ImageStore::alloc(100, 100);
360    ///  let mut dst_store = ImageStoreMut::<u16, 2>::alloc_with_depth(50, 50, 16);
361    ///  scaler.resize_gray_alpha16(&src_store, &mut dst_store, true).unwrap();
362    /// ```
363    fn resize_gray_alpha16<'a>(
364        &'a self,
365        store: &ImageStore<'a, u16, 2>,
366        into: &mut ImageStoreMut<'a, u16, 2>,
367        premultiply_alpha: bool,
368    ) -> Result<(), PicScaleError>;
369
370    /// Performs rescaling for RGB
371    ///
372    /// Scales RGB high bit-depth image stored in `u16` type.
373    /// To perform scaling image bit-depth should be set in target image,
374    /// source image expects to have the same one.
375    /// Channel order does not matter.
376    ///
377    /// # Arguments
378    /// `store` - original image store
379    /// `into` - target image store
380    ///
381    /// # Panics
382    /// Method panics if bit-depth < 1 or bit-depth > 16
383    ///
384    /// # Example
385    ///
386    /// #[no_build]
387    /// ```rust
388    ///  use pic_scale::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, ScalingU16};
389    ///  let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
390    ///  let src_store = ImageStore::alloc(100, 100);
391    ///  let mut dst_store = ImageStoreMut::<u16, 3>::alloc_with_depth(50, 50, 10);
392    ///  scaler.resize_rgb_u16(&src_store, &mut dst_store).unwrap();
393    /// ```
394    ///
395    fn resize_rgb_u16<'a>(
396        &'a self,
397        store: &ImageStore<'a, u16, 3>,
398        into: &mut ImageStoreMut<'a, u16, 3>,
399    ) -> Result<(), PicScaleError>;
400
401    /// Performs rescaling for RGBA high bit-depth
402    ///
403    /// Scales RGB high bit-depth image stored in `u16` type.
404    /// To perform scaling image bit-depth should be set in target image,
405    /// source image expects to have the same one.
406    /// If pre-multiplication is requested alpha should be at last, otherwise
407    /// channel order does not matter.
408    ///
409    /// # Arguments
410    /// `store` - original image store
411    /// `into` - target image store
412    /// `premultiply_alpha` - flags is alpha is premultiplied
413    ///
414    /// # Panics
415    /// Method panics if bit-depth < 1 or bit-depth > 16
416    ///
417    /// # Example
418    ///
419    /// #[no_build]
420    /// ```rust
421    ///  use pic_scale::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, ScalingU16};
422    ///  let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
423    ///  let src_store = ImageStore::alloc(100, 100);
424    ///  let mut dst_store = ImageStoreMut::<u16, 4>::alloc_with_depth(50, 50, 10);
425    ///  scaler.resize_rgba_u16(&src_store, &mut dst_store, true).unwrap();
426    /// ```
427    ///
428    fn resize_rgba_u16<'a>(
429        &'a self,
430        store: &ImageStore<'a, u16, 4>,
431        into: &mut ImageStoreMut<'a, u16, 4>,
432        premultiply_alpha: bool,
433    ) -> Result<(), PicScaleError>;
434}
435
436impl Scaler {
437    /// Creates new [Scaler] instance with corresponding filter
438    ///
439    /// Creates default [crate::Scaler] with corresponding filter and default [ThreadingPolicy::Single]
440    ///
441    pub fn new(filter: ResamplingFunction) -> Self {
442        Scaler {
443            function: filter,
444            threading_policy: ThreadingPolicy::Single,
445            workload_strategy: WorkloadStrategy::default(),
446        }
447    }
448
449    /// Sets preferred workload strategy
450    ///
451    /// This is hint only, it may change something, or may not.
452    pub fn set_workload_strategy(&mut self, workload_strategy: WorkloadStrategy) {
453        self.workload_strategy = workload_strategy;
454    }
455}
456
457impl Scaler {
458    pub(crate) fn generic_resize<
459        'a,
460        T: Clone + Copy + Debug + Send + Sync + Default + WeightsGenerator<W> + 'static,
461        W,
462        const N: usize,
463    >(
464        &self,
465        store: &ImageStore<'a, T, N>,
466        into: &mut ImageStoreMut<'a, T, N>,
467    ) -> Result<(), PicScaleError>
468    where
469        ImageStore<'a, T, N>: VerticalConvolutionPass<T, W, N> + HorizontalConvolutionPass<T, W, N>,
470        ImageStoreMut<'a, T, N>: CheckStoreDensity,
471    {
472        let new_size = into.get_size();
473        into.validate()?;
474        store.validate()?;
475        if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
476            return Err(PicScaleError::ZeroImageDimensions);
477        }
478
479        if check_image_size_overflow(store.width, store.height, store.channels) {
480            return Err(PicScaleError::SourceImageIsTooLarge);
481        }
482
483        if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
484            return Err(PicScaleError::DestinationImageIsTooLarge);
485        }
486
487        if into.should_have_bit_depth() && !(1..=16).contains(&into.bit_depth) {
488            return Err(PicScaleError::UnsupportedBitDepth(into.bit_depth));
489        }
490
491        if store.width == new_size.width && store.height == new_size.height {
492            store.copied_to_mut(into);
493            return Ok(());
494        }
495
496        let nova_thread_pool = self
497            .threading_policy
498            .get_nova_pool(ImageSize::new(new_size.width, new_size.height));
499
500        if self.function == ResamplingFunction::Nearest {
501            resize_nearest::<T, N>(
502                store.buffer.as_ref(),
503                store.width,
504                store.height,
505                into.buffer.borrow_mut(),
506                new_size.width,
507                new_size.height,
508                &nova_thread_pool,
509            );
510            return Ok(());
511        }
512
513        let should_do_horizontal = store.width != new_size.width;
514        let should_do_vertical = store.height != new_size.height;
515        assert!(should_do_horizontal || should_do_vertical);
516
517        if should_do_vertical && should_do_horizontal {
518            let mut target_vertical = try_vec![T::default(); store.width * new_size.height * N];
519
520            let mut new_image_vertical = ImageStoreMut::<T, N>::from_slice(
521                &mut target_vertical,
522                store.width,
523                new_size.height,
524            )?;
525            new_image_vertical.bit_depth = into.bit_depth;
526            let vertical_filters = T::make_weights(self.function, store.height, new_size.height)?;
527            let options = ConvolutionOptions::new(self.workload_strategy);
528            store.convolve_vertical(
529                vertical_filters,
530                &mut new_image_vertical,
531                &nova_thread_pool,
532                options,
533            );
534
535            let new_immutable_store = ImageStore::<T, N> {
536                buffer: std::borrow::Cow::Owned(target_vertical),
537                channels: N,
538                width: store.width,
539                height: new_size.height,
540                stride: store.width * N,
541                bit_depth: into.bit_depth,
542            };
543            let horizontal_filters = T::make_weights(self.function, store.width, new_size.width)?;
544            let options = ConvolutionOptions::new(self.workload_strategy);
545            new_immutable_store.convolve_horizontal(
546                horizontal_filters,
547                into,
548                &nova_thread_pool,
549                options,
550            );
551            Ok(())
552        } else if should_do_vertical {
553            let vertical_filters = T::make_weights(self.function, store.height, new_size.height)?;
554            let options = ConvolutionOptions::new(self.workload_strategy);
555            store.convolve_vertical(vertical_filters, into, &nova_thread_pool, options);
556            Ok(())
557        } else {
558            assert!(should_do_horizontal);
559            let horizontal_filters = T::make_weights(self.function, store.width, new_size.width)?;
560            let options = ConvolutionOptions::new(self.workload_strategy);
561            store.convolve_horizontal(horizontal_filters, into, &nova_thread_pool, options);
562            Ok(())
563        }
564    }
565
566    fn forward_resize_with_alpha<
567        'a,
568        T: Clone + Copy + Debug + Send + Sync + Default + WeightsGenerator<W> + 'static,
569        W,
570        const N: usize,
571    >(
572        &self,
573        store: &ImageStore<'a, T, N>,
574        into: &mut ImageStoreMut<'a, T, N>,
575        premultiply_alpha_requested: bool,
576        nova_thread_pool: &novtb::ThreadPool,
577    ) -> Result<(), PicScaleError>
578    where
579        ImageStore<'a, T, N>: VerticalConvolutionPass<T, W, N>
580            + HorizontalConvolutionPass<T, W, N>
581            + AssociateAlpha<T, N>,
582        ImageStoreMut<'a, T, N>: CheckStoreDensity + UnassociateAlpha<T, N>,
583    {
584        let new_size = into.get_size();
585        let mut src_store: std::borrow::Cow<'_, ImageStore<'_, T, N>> =
586            std::borrow::Cow::Borrowed(store);
587
588        let mut has_alpha_premultiplied = true;
589
590        if premultiply_alpha_requested {
591            let is_alpha_premultiplication_reasonable =
592                src_store.is_alpha_premultiplication_needed();
593            if is_alpha_premultiplication_reasonable {
594                let mut target_premultiplied =
595                    try_vec![T::default(); src_store.width * src_store.height * N];
596                let mut new_store = ImageStoreMut::<T, N>::from_slice(
597                    &mut target_premultiplied,
598                    src_store.width,
599                    src_store.height,
600                )?;
601                new_store.bit_depth = into.bit_depth;
602                src_store.premultiply_alpha(&mut new_store, nova_thread_pool);
603                src_store = std::borrow::Cow::Owned(ImageStore::<T, N> {
604                    buffer: std::borrow::Cow::Owned(target_premultiplied),
605                    channels: N,
606                    width: src_store.width,
607                    height: src_store.height,
608                    stride: src_store.width * N,
609                    bit_depth: into.bit_depth,
610                });
611                has_alpha_premultiplied = true;
612            }
613        }
614
615        let mut target_vertical = try_vec![T::default(); src_store.width * new_size.height * N];
616
617        let mut new_image_vertical = ImageStoreMut::<T, N>::from_slice(
618            &mut target_vertical,
619            src_store.width,
620            new_size.height,
621        )?;
622        new_image_vertical.bit_depth = into.bit_depth;
623        let vertical_filters = T::make_weights(self.function, src_store.height, new_size.height)?;
624        let options = ConvolutionOptions::new(self.workload_strategy);
625        src_store.convolve_vertical(
626            vertical_filters,
627            &mut new_image_vertical,
628            nova_thread_pool,
629            options,
630        );
631
632        let new_immutable_store = ImageStore::<T, N> {
633            buffer: std::borrow::Cow::Owned(target_vertical),
634            channels: N,
635            width: src_store.width,
636            height: new_size.height,
637            stride: src_store.width * N,
638            bit_depth: into.bit_depth,
639        };
640        let horizontal_filters = T::make_weights(self.function, src_store.width, new_size.width)?;
641        let options = ConvolutionOptions::new(self.workload_strategy);
642        new_immutable_store.convolve_horizontal(
643            horizontal_filters,
644            into,
645            nova_thread_pool,
646            options,
647        );
648
649        if premultiply_alpha_requested && has_alpha_premultiplied {
650            into.unpremultiply_alpha(nova_thread_pool, self.workload_strategy);
651        }
652
653        Ok(())
654    }
655
656    fn forward_resize_vertical_with_alpha<
657        'a,
658        T: Clone + Copy + Debug + Send + Sync + Default + WeightsGenerator<W> + 'static,
659        W,
660        const N: usize,
661    >(
662        &self,
663        store: &ImageStore<'a, T, N>,
664        into: &mut ImageStoreMut<'a, T, N>,
665        premultiply_alpha_requested: bool,
666        nova_thread_pool: &novtb::ThreadPool,
667    ) -> Result<(), PicScaleError>
668    where
669        ImageStore<'a, T, N>: VerticalConvolutionPass<T, W, N>
670            + HorizontalConvolutionPass<T, W, N>
671            + AssociateAlpha<T, N>,
672        ImageStoreMut<'a, T, N>: CheckStoreDensity + UnassociateAlpha<T, N>,
673    {
674        let new_size = into.get_size();
675        let mut src_store = std::borrow::Cow::Borrowed(store);
676
677        let mut has_alpha_premultiplied = true;
678
679        if premultiply_alpha_requested {
680            let is_alpha_premultiplication_reasonable =
681                src_store.is_alpha_premultiplication_needed();
682            if is_alpha_premultiplication_reasonable {
683                let mut target_premultiplied =
684                    try_vec![T::default(); src_store.width * src_store.height * N];
685                let mut new_store = ImageStoreMut::<T, N>::from_slice(
686                    &mut target_premultiplied,
687                    src_store.width,
688                    src_store.height,
689                )?;
690                new_store.bit_depth = into.bit_depth;
691                src_store.premultiply_alpha(&mut new_store, nova_thread_pool);
692                src_store = std::borrow::Cow::Owned(ImageStore::<T, N> {
693                    buffer: std::borrow::Cow::Owned(target_premultiplied),
694                    channels: N,
695                    width: src_store.width,
696                    height: src_store.height,
697                    stride: src_store.width * N,
698                    bit_depth: into.bit_depth,
699                });
700                has_alpha_premultiplied = true;
701            }
702        }
703
704        let vertical_filters = T::make_weights(self.function, src_store.height, new_size.height)?;
705        let options = ConvolutionOptions::new(self.workload_strategy);
706        src_store.convolve_vertical(vertical_filters, into, nova_thread_pool, options);
707
708        if premultiply_alpha_requested && has_alpha_premultiplied {
709            into.unpremultiply_alpha(nova_thread_pool, self.workload_strategy);
710        }
711
712        Ok(())
713    }
714
715    fn forward_resize_horizontal_with_alpha<
716        'a,
717        T: Clone + Copy + Debug + Send + Sync + Default + WeightsGenerator<W> + 'static,
718        W,
719        const N: usize,
720    >(
721        &self,
722        store: &ImageStore<'a, T, N>,
723        into: &mut ImageStoreMut<'a, T, N>,
724        premultiply_alpha_requested: bool,
725        nova_thread_pool: &novtb::ThreadPool,
726    ) -> Result<(), PicScaleError>
727    where
728        ImageStore<'a, T, N>: VerticalConvolutionPass<T, W, N>
729            + HorizontalConvolutionPass<T, W, N>
730            + AssociateAlpha<T, N>,
731        ImageStoreMut<'a, T, N>: CheckStoreDensity + UnassociateAlpha<T, N>,
732    {
733        let new_size = into.get_size();
734        let mut src_store = std::borrow::Cow::Borrowed(store);
735
736        let mut has_alpha_premultiplied = true;
737
738        if premultiply_alpha_requested {
739            let is_alpha_premultiplication_reasonable =
740                src_store.is_alpha_premultiplication_needed();
741            if is_alpha_premultiplication_reasonable {
742                let mut target_premultiplied =
743                    try_vec![T::default(); src_store.width * src_store.height * N];
744                let mut new_store = ImageStoreMut::<T, N>::from_slice(
745                    &mut target_premultiplied,
746                    src_store.width,
747                    src_store.height,
748                )?;
749                new_store.bit_depth = into.bit_depth;
750                src_store.premultiply_alpha(&mut new_store, nova_thread_pool);
751                src_store = std::borrow::Cow::Owned(ImageStore::<T, N> {
752                    buffer: std::borrow::Cow::Owned(target_premultiplied),
753                    channels: N,
754                    width: src_store.width,
755                    height: src_store.height,
756                    stride: src_store.width * N,
757                    bit_depth: into.bit_depth,
758                });
759                has_alpha_premultiplied = true;
760            }
761        }
762
763        let horizontal_filters = T::make_weights(self.function, src_store.width, new_size.width)?;
764        let options = ConvolutionOptions::new(self.workload_strategy);
765        src_store.convolve_horizontal(horizontal_filters, into, nova_thread_pool, options);
766
767        if premultiply_alpha_requested && has_alpha_premultiplied {
768            into.unpremultiply_alpha(nova_thread_pool, self.workload_strategy);
769        }
770
771        Ok(())
772    }
773
774    pub(crate) fn generic_resize_with_alpha<
775        'a,
776        T: Clone + Copy + Debug + Send + Sync + Default + WeightsGenerator<W> + 'static,
777        W,
778        const N: usize,
779    >(
780        &self,
781        store: &ImageStore<'a, T, N>,
782        into: &mut ImageStoreMut<'a, T, N>,
783        premultiply_alpha_requested: bool,
784    ) -> Result<(), PicScaleError>
785    where
786        ImageStore<'a, T, N>: VerticalConvolutionPass<T, W, N>
787            + HorizontalConvolutionPass<T, W, N>
788            + AssociateAlpha<T, N>,
789        ImageStoreMut<'a, T, N>: CheckStoreDensity + UnassociateAlpha<T, N>,
790    {
791        let new_size = into.get_size();
792        into.validate()?;
793        store.validate()?;
794        if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
795            return Err(PicScaleError::ZeroImageDimensions);
796        }
797
798        if check_image_size_overflow(store.width, store.height, store.channels) {
799            return Err(PicScaleError::SourceImageIsTooLarge);
800        }
801
802        if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
803            return Err(PicScaleError::DestinationImageIsTooLarge);
804        }
805
806        if into.should_have_bit_depth() && !(1..=16).contains(&into.bit_depth) {
807            return Err(PicScaleError::UnsupportedBitDepth(into.bit_depth));
808        }
809
810        if store.width == new_size.width && store.height == new_size.height {
811            store.copied_to_mut(into);
812            return Ok(());
813        }
814
815        let nova_thread_pool = self
816            .threading_policy
817            .get_nova_pool(ImageSize::new(new_size.width, new_size.height));
818
819        if self.function == ResamplingFunction::Nearest {
820            resize_nearest::<T, N>(
821                store.buffer.as_ref(),
822                store.width,
823                store.height,
824                into.buffer.borrow_mut(),
825                new_size.width,
826                new_size.height,
827                &nova_thread_pool,
828            );
829            return Ok(());
830        }
831
832        let should_do_horizontal = store.width != new_size.width;
833        let should_do_vertical = store.height != new_size.height;
834        assert!(should_do_horizontal || should_do_vertical);
835
836        if should_do_vertical && should_do_horizontal {
837            self.forward_resize_with_alpha(
838                store,
839                into,
840                premultiply_alpha_requested,
841                &nova_thread_pool,
842            )
843        } else if should_do_vertical {
844            self.forward_resize_vertical_with_alpha(
845                store,
846                into,
847                premultiply_alpha_requested,
848                &nova_thread_pool,
849            )
850        } else {
851            assert!(should_do_horizontal);
852            self.forward_resize_horizontal_with_alpha(
853                store,
854                into,
855                premultiply_alpha_requested,
856                &nova_thread_pool,
857            )
858        }
859    }
860}
861
862impl Scaling for Scaler {
863    fn set_threading_policy(&mut self, threading_policy: ThreadingPolicy) {
864        self.threading_policy = threading_policy;
865    }
866
867    fn resize_plane<'a>(
868        &'a self,
869        store: &ImageStore<'a, u8, 1>,
870        into: &mut ImageStoreMut<'a, u8, 1>,
871    ) -> Result<(), PicScaleError> {
872        self.generic_resize(store, into)
873    }
874
875    fn resize_cbcr8<'a>(
876        &'a self,
877        store: &ImageStore<'a, u8, 2>,
878        into: &mut ImageStoreMut<'a, u8, 2>,
879    ) -> Result<(), PicScaleError> {
880        self.generic_resize(store, into)
881    }
882
883    fn resize_gray_alpha<'a>(
884        &'a self,
885        store: &ImageStore<'a, u8, 2>,
886        into: &mut ImageStoreMut<'a, u8, 2>,
887        premultiply_alpha: bool,
888    ) -> Result<(), PicScaleError> {
889        self.generic_resize_with_alpha(store, into, premultiply_alpha)
890    }
891
892    fn resize_rgb<'a>(
893        &'a self,
894        store: &ImageStore<'a, u8, 3>,
895        into: &mut ImageStoreMut<'a, u8, 3>,
896    ) -> Result<(), PicScaleError> {
897        self.generic_resize(store, into)
898    }
899
900    fn resize_rgba<'a>(
901        &'a self,
902        store: &ImageStore<'a, u8, 4>,
903        into: &mut ImageStoreMut<'a, u8, 4>,
904        premultiply_alpha: bool,
905    ) -> Result<(), PicScaleError> {
906        self.generic_resize_with_alpha(store, into, premultiply_alpha)
907    }
908}
909
910impl ScalingF32 for Scaler {
911    fn resize_plane_f32<'a>(
912        &'a self,
913        store: &ImageStore<'a, f32, 1>,
914        into: &mut ImageStoreMut<'a, f32, 1>,
915    ) -> Result<(), PicScaleError> {
916        match self.workload_strategy {
917            WorkloadStrategy::PreferQuality => self.generic_resize::<f32, f64, 1>(store, into),
918            WorkloadStrategy::PreferSpeed => self.generic_resize::<f32, f32, 1>(store, into),
919        }
920    }
921
922    fn resize_cbcr_f32<'a>(
923        &'a self,
924        store: &ImageStore<'a, f32, 2>,
925        into: &mut ImageStoreMut<'a, f32, 2>,
926    ) -> Result<(), PicScaleError> {
927        match self.workload_strategy {
928            WorkloadStrategy::PreferQuality => self.generic_resize::<f32, f64, 2>(store, into),
929            WorkloadStrategy::PreferSpeed => self.generic_resize::<f32, f32, 2>(store, into),
930        }
931    }
932
933    fn resize_gray_alpha_f32<'a>(
934        &'a self,
935        store: &ImageStore<'a, f32, 2>,
936        into: &mut ImageStoreMut<'a, f32, 2>,
937        premultiply_alpha: bool,
938    ) -> Result<(), PicScaleError> {
939        match self.workload_strategy {
940            WorkloadStrategy::PreferQuality => {
941                self.generic_resize_with_alpha::<f32, f64, 2>(store, into, premultiply_alpha)
942            }
943            WorkloadStrategy::PreferSpeed => {
944                self.generic_resize_with_alpha::<f32, f32, 2>(store, into, premultiply_alpha)
945            }
946        }
947    }
948
949    fn resize_rgb_f32<'a>(
950        &'a self,
951        store: &ImageStore<'a, f32, 3>,
952        into: &mut ImageStoreMut<'a, f32, 3>,
953    ) -> Result<(), PicScaleError> {
954        match self.workload_strategy {
955            WorkloadStrategy::PreferQuality => self.generic_resize::<f32, f64, 3>(store, into),
956            WorkloadStrategy::PreferSpeed => self.generic_resize::<f32, f32, 3>(store, into),
957        }
958    }
959
960    fn resize_rgba_f32<'a>(
961        &'a self,
962        store: &ImageStore<'a, f32, 4>,
963        into: &mut ImageStoreMut<'a, f32, 4>,
964        premultiply_alpha: bool,
965    ) -> Result<(), PicScaleError> {
966        match self.workload_strategy {
967            WorkloadStrategy::PreferQuality => {
968                self.generic_resize_with_alpha::<f32, f64, 4>(store, into, premultiply_alpha)
969            }
970            WorkloadStrategy::PreferSpeed => {
971                self.generic_resize_with_alpha::<f32, f32, 4>(store, into, premultiply_alpha)
972            }
973        }
974    }
975}
976
977impl ScalingU16 for Scaler {
978    /// Performs rescaling for RGB
979    ///
980    /// Scales RGB high bit-depth image stored in `u16` type.
981    /// To perform scaling image bit-depth should be set in target image,
982    /// source image expects to have the same one.
983    /// Channel order does not matter.
984    ///
985    /// # Arguments
986    /// `store` - original image store
987    /// `into` - target image store
988    ///
989    /// # Panics
990    /// Method panics if bit-depth < 1 or bit-depth > 16
991    ///
992    /// # Example
993    ///
994    /// #[no_build]
995    /// ```rust
996    ///  use pic_scale::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, ScalingU16};
997    ///  let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
998    ///  let src_store = ImageStore::alloc(100, 100);
999    ///  let mut dst_store = ImageStoreMut::<u16, 3>::alloc_with_depth(50, 50, 10);
1000    ///  scaler.resize_rgb_u16(&src_store, &mut dst_store).unwrap();
1001    /// ```
1002    ///
1003    fn resize_rgb_u16<'a>(
1004        &'a self,
1005        store: &ImageStore<'a, u16, 3>,
1006        into: &mut ImageStoreMut<'a, u16, 3>,
1007    ) -> Result<(), PicScaleError> {
1008        self.generic_resize(store, into)
1009    }
1010
1011    fn resize_cbcr_u16<'a>(
1012        &'a self,
1013        store: &ImageStore<'a, u16, 2>,
1014        into: &mut ImageStoreMut<'a, u16, 2>,
1015    ) -> Result<(), PicScaleError> {
1016        self.generic_resize(store, into)
1017    }
1018
1019    fn resize_gray_alpha16<'a>(
1020        &'a self,
1021        store: &ImageStore<'a, u16, 2>,
1022        into: &mut ImageStoreMut<'a, u16, 2>,
1023        premultiply_alpha: bool,
1024    ) -> Result<(), PicScaleError> {
1025        self.generic_resize_with_alpha(store, into, premultiply_alpha)
1026    }
1027
1028    /// Resizes u16 image
1029    ///
1030    /// # Arguments
1031    /// `store` - original image store
1032    /// `into` - target image store
1033    /// `premultiply_alpha` - flags is alpha is premultiplied
1034    ///
1035    /// # Panics
1036    /// Method panics if bit -depth < 1 or bit-depth > 16
1037    ///
1038    /// # Example
1039    ///
1040    /// #[no_build]
1041    /// ```rust
1042    ///  use pic_scale::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, ScalingU16};
1043    ///  let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
1044    ///  let src_store = ImageStore::alloc(100, 100);
1045    ///  let mut dst_store = ImageStoreMut::<u16, 4>::alloc_with_depth(50, 50, 10);
1046    ///  scaler.resize_rgba_u16(&src_store, &mut dst_store, true).unwrap();
1047    /// ```
1048    ///
1049    fn resize_rgba_u16<'a>(
1050        &'a self,
1051        store: &ImageStore<'a, u16, 4>,
1052        into: &mut ImageStoreMut<'a, u16, 4>,
1053        premultiply_alpha: bool,
1054    ) -> Result<(), PicScaleError> {
1055        self.generic_resize_with_alpha(store, into, premultiply_alpha)
1056    }
1057
1058    /// Performs rescaling for Planar u16
1059    ///
1060    /// Scales planar high bit-depth image stored in `u16` type.
1061    /// To perform scaling image bit-depth should be set in target image,
1062    /// source image expects to have the same one.
1063    ///
1064    /// # Arguments
1065    /// `store` - original image store
1066    /// `into` - target image store
1067    ///
1068    /// # Panic
1069    /// Method panics if bit-depth < 1 or bit-depth > 16
1070    ///
1071    /// # Example
1072    ///
1073    /// #[no_build]
1074    /// ```rust
1075    ///  use pic_scale::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, ScalingU16};
1076    ///  let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1077    ///  let src_store = ImageStore::alloc(100, 100);
1078    ///  let mut dst_store = ImageStoreMut::<u16, 1>::alloc_with_depth(50, 50, 10);
1079    ///  scaler.resize_plane_u16(&src_store, &mut dst_store).unwrap();
1080    /// ```
1081    fn resize_plane_u16<'a>(
1082        &'a self,
1083        store: &ImageStore<'a, u16, 1>,
1084        into: &mut ImageStoreMut<'a, u16, 1>,
1085    ) -> Result<(), PicScaleError> {
1086        self.generic_resize(store, into)
1087    }
1088}
1089
1090impl Scaler {
1091    /// Resizes RGBA2101010 image
1092    ///
1093    /// This method ignores alpha scaling.
1094    ///
1095    /// # Arguments
1096    /// `src_image` - source AR30 image
1097    /// `dst_image` - destination AR30 image
1098    /// `new_size` - New image size
1099    ///
1100    pub fn resize_ar30(
1101        &self,
1102        src_image: &ImageStore<u8, 4>,
1103        dst_image: &mut ImageStoreMut<u8, 4>,
1104        order: Ar30ByteOrder,
1105    ) -> Result<(), PicScaleError> {
1106        src_image.validate()?;
1107        dst_image.validate()?;
1108        let dst_size = dst_image.get_size();
1109        let dst_stride = dst_image.stride();
1110        match order {
1111            Ar30ByteOrder::Host => {
1112                resize_ar30_impl::<{ Rgb30::Ar30 as usize }, { Ar30ByteOrder::Host as usize }>(
1113                    src_image.as_bytes(),
1114                    src_image.stride,
1115                    src_image.get_size(),
1116                    dst_image.buffer.borrow_mut(),
1117                    dst_stride,
1118                    dst_size,
1119                    self,
1120                )
1121            }
1122            Ar30ByteOrder::Network => {
1123                resize_ar30_impl::<{ Rgb30::Ar30 as usize }, { Ar30ByteOrder::Network as usize }>(
1124                    src_image.as_bytes(),
1125                    src_image.stride,
1126                    src_image.get_size(),
1127                    dst_image.buffer.borrow_mut(),
1128                    dst_stride,
1129                    dst_size,
1130                    self,
1131                )
1132            }
1133        }
1134    }
1135
1136    /// Resizes RGBA1010102 image
1137    ///
1138    /// This method ignores alpha scaling.
1139    ///
1140    /// # Arguments
1141    /// `src_image` - source RA30 image
1142    /// `dst_image` - destination RA30 image
1143    ///
1144    pub fn resize_ra30(
1145        &self,
1146        src_image: &ImageStore<u8, 4>,
1147        dst_image: &mut ImageStoreMut<u8, 4>,
1148        order: Ar30ByteOrder,
1149    ) -> Result<(), PicScaleError> {
1150        src_image.validate()?;
1151        dst_image.validate()?;
1152        let dst_size = dst_image.get_size();
1153        let dst_stride = dst_image.stride();
1154        match order {
1155            Ar30ByteOrder::Host => {
1156                resize_ar30_impl::<{ Rgb30::Ra30 as usize }, { Ar30ByteOrder::Host as usize }>(
1157                    src_image.as_bytes(),
1158                    src_image.stride,
1159                    src_image.get_size(),
1160                    dst_image.buffer.borrow_mut(),
1161                    dst_stride,
1162                    dst_size,
1163                    self,
1164                )
1165            }
1166            Ar30ByteOrder::Network => {
1167                resize_ar30_impl::<{ Rgb30::Ra30 as usize }, { Ar30ByteOrder::Network as usize }>(
1168                    src_image.as_bytes(),
1169                    src_image.stride,
1170                    src_image.get_size(),
1171                    dst_image.buffer.borrow_mut(),
1172                    dst_stride,
1173                    dst_size,
1174                    self,
1175                )
1176            }
1177        }
1178    }
1179}
1180
1181/// Declares default scaling options
1182#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)]
1183pub struct ScalingOptions {
1184    pub resampling_function: ResamplingFunction,
1185    pub premultiply_alpha: bool,
1186    pub threading_policy: ThreadingPolicy,
1187}
1188
1189/// Generic trait for [ImageStore] to implement abstract scaling.
1190pub trait ImageStoreScaling<'b, T, const N: usize>
1191where
1192    T: Clone + Copy + Debug,
1193{
1194    fn scale(
1195        &self,
1196        store: &mut ImageStoreMut<'b, T, N>,
1197        options: ScalingOptions,
1198    ) -> Result<(), PicScaleError>;
1199}
1200
1201macro_rules! def_image_scaling_alpha {
1202    ($clazz: ident, $fx_type: ident, $cn: expr) => {
1203        impl<'b> ImageStoreScaling<'b, $fx_type, $cn> for $clazz<'b> {
1204            fn scale(
1205                &self,
1206                store: &mut ImageStoreMut<'b, $fx_type, $cn>,
1207                options: ScalingOptions,
1208            ) -> Result<(), PicScaleError> {
1209                let mut scaler = Scaler::new(options.resampling_function);
1210                scaler.set_threading_policy(options.threading_policy);
1211                scaler.generic_resize_with_alpha::<$fx_type, f32, $cn>(
1212                    self,
1213                    store,
1214                    options.premultiply_alpha,
1215                )
1216            }
1217        }
1218    };
1219}
1220
1221macro_rules! def_image_scaling {
1222    ($clazz: ident, $fx_type: ident, $cn: expr) => {
1223        impl<'b> ImageStoreScaling<'b, $fx_type, $cn> for $clazz<'b> {
1224            fn scale(
1225                &self,
1226                store: &mut ImageStoreMut<'b, $fx_type, $cn>,
1227                options: ScalingOptions,
1228            ) -> Result<(), PicScaleError> {
1229                let mut scaler = Scaler::new(options.resampling_function);
1230                scaler.set_threading_policy(options.threading_policy);
1231                scaler.generic_resize::<$fx_type, f32, $cn>(self, store)
1232            }
1233        }
1234    };
1235}
1236
1237def_image_scaling_alpha!(Rgba8ImageStore, u8, 4);
1238def_image_scaling!(Rgb8ImageStore, u8, 3);
1239def_image_scaling!(CbCr8ImageStore, u8, 2);
1240def_image_scaling!(Planar8ImageStore, u8, 1);
1241def_image_scaling!(Planar16ImageStore, u16, 1);
1242def_image_scaling!(CbCr16ImageStore, u16, 2);
1243def_image_scaling!(Rgb16ImageStore, u16, 3);
1244def_image_scaling_alpha!(Rgba16ImageStore, u16, 4);
1245def_image_scaling!(PlanarF32ImageStore, f32, 1);
1246def_image_scaling!(CbCrF32ImageStore, f32, 2);
1247def_image_scaling!(RgbF32ImageStore, f32, 3);
1248def_image_scaling_alpha!(RgbaF32ImageStore, f32, 4);
1249
1250#[cfg(test)]
1251mod tests {
1252    use super::*;
1253
1254    macro_rules! check_rgba8 {
1255        ($dst: expr, $image_width: expr, $max: expr) => {
1256            {
1257                for (y, row) in $dst.chunks_exact($image_width * 4).enumerate() {
1258                    for (i, dst) in row.chunks_exact(4).enumerate() {
1259                        let diff0 = (dst[0] as i32 - 124).abs();
1260                        let diff1 = (dst[1] as i32 - 41).abs();
1261                        let diff2 = (dst[2] as i32 - 99).abs();
1262                        let diff3 = (dst[3] as i32 - 77).abs();
1263                        assert!(
1264                            diff0 < $max,
1265                            "Diff for channel 0 is expected < {}, but it was {diff0}, at (y: {y}, x: {i})",
1266                            $max
1267                        );
1268                        assert!(
1269                            diff1 < $max,
1270                            "Diff for channel 1 is expected < {}, but it was {diff1}, at (y: {y}, x: {i})",
1271                            $max
1272                        );
1273                        assert!(
1274                            diff2 < $max,
1275                            "Diff for channel 2 is expected < {}, but it was {diff2}, at (y: {y}, x: {i})",
1276                            $max
1277                        );
1278                        assert!(
1279                            diff3 < $max,
1280                            "Diff for channel 3 is expected < {}, but it was {diff3}, at (y: {y}, x: {i})",
1281                            $max
1282                        );
1283                    }
1284                }
1285            }
1286        };
1287    }
1288
1289    macro_rules! check_rgb16 {
1290        ($dst: expr, $image_width: expr, $max: expr) => {
1291            {
1292                for (y, row) in $dst.chunks_exact($image_width * 3).enumerate() {
1293                    for (i, dst) in row.chunks_exact(3).enumerate() {
1294                        let diff0 = (dst[0] as i32 - 124).abs();
1295                        let diff1 = (dst[1] as i32 - 41).abs();
1296                        let diff2 = (dst[2] as i32 - 99).abs();
1297                        assert!(
1298                            diff0 < $max,
1299                            "Diff for channel 0 is expected < {}, but it was {diff0}, at (y: {y}, x: {i})",
1300                            $max
1301                        );
1302                        assert!(
1303                            diff1 < $max,
1304                            "Diff for channel 1 is expected < {}, but it was {diff1}, at (y: {y}, x: {i})",
1305                            $max
1306                        );
1307                        assert!(
1308                            diff2 < $max,
1309                            "Diff for channel 2 is expected < {}, but it was {diff2}, at (y: {y}, x: {i})",
1310                            $max
1311                        );
1312                    }
1313                }
1314            }
1315        };
1316    }
1317
1318    #[test]
1319    fn check_rgba8_resizing_vertical() {
1320        let image_width = 255;
1321        let image_height = 512;
1322        const CN: usize = 4;
1323        let mut image = vec![0u8; image_height * image_width * CN];
1324        for dst in image.chunks_exact_mut(4) {
1325            dst[0] = 124;
1326            dst[1] = 41;
1327            dst[2] = 99;
1328            dst[3] = 77;
1329        }
1330        let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
1331        scaler.set_threading_policy(ThreadingPolicy::Single);
1332        let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1333        let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1334        scaler
1335            .resize_rgba(&src_store, &mut target_store, false)
1336            .unwrap();
1337        let target_data = target_store.buffer.borrow();
1338        check_rgba8!(target_data, image_width, 34);
1339    }
1340
1341    #[test]
1342    fn check_rgba8_resizing_both() {
1343        let image_width = 255;
1344        let image_height = 512;
1345        const CN: usize = 4;
1346        let mut image = vec![0u8; image_height * image_width * CN];
1347        for dst in image.chunks_exact_mut(4) {
1348            dst[0] = 124;
1349            dst[1] = 41;
1350            dst[2] = 99;
1351            dst[3] = 77;
1352        }
1353        image[3] = 78;
1354        let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
1355        scaler.set_threading_policy(ThreadingPolicy::Single);
1356        let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1357        let mut target_store = ImageStoreMut::alloc(image_width / 2, image_height / 2);
1358        scaler
1359            .resize_rgba(&src_store, &mut target_store, false)
1360            .unwrap();
1361        let target_data = target_store.buffer.borrow();
1362        check_rgba8!(target_data, image_width, 34);
1363    }
1364
1365    #[test]
1366    fn check_rgba8_resizing_alpha() {
1367        let image_width = 255;
1368        let image_height = 512;
1369        const CN: usize = 4;
1370        let mut image = vec![0u8; image_height * image_width * CN];
1371        for dst in image.chunks_exact_mut(4) {
1372            dst[0] = 124;
1373            dst[1] = 41;
1374            dst[2] = 99;
1375            dst[3] = 77;
1376        }
1377        image[3] = 78;
1378        let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1379        scaler.set_threading_policy(ThreadingPolicy::Single);
1380        let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1381        let mut target_store = ImageStoreMut::alloc(image_width / 2, image_height / 2);
1382        scaler
1383            .resize_rgba(&src_store, &mut target_store, true)
1384            .unwrap();
1385        let target_data = target_store.buffer.borrow();
1386        check_rgba8!(target_data, image_width, 160);
1387    }
1388
1389    #[test]
1390    fn check_rgb8_resizing_vertical() {
1391        let image_width = 255;
1392        let image_height = 512;
1393        const CN: usize = 3;
1394        let mut image = vec![0u8; image_height * image_width * CN];
1395        for dst in image.chunks_exact_mut(3) {
1396            dst[0] = 124;
1397            dst[1] = 41;
1398            dst[2] = 99;
1399        }
1400        let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
1401        scaler.set_threading_policy(ThreadingPolicy::Single);
1402        let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1403        let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1404        scaler.resize_rgb(&src_store, &mut target_store).unwrap();
1405        let target_data = target_store.buffer.borrow();
1406
1407        check_rgb16!(target_data, image_width, 85);
1408    }
1409
1410    #[test]
1411    fn check_rgb8_resizing_vertical_threading() {
1412        let image_width = 255;
1413        let image_height = 512;
1414        const CN: usize = 3;
1415        let mut image = vec![0u8; image_height * image_width * CN];
1416        for dst in image.chunks_exact_mut(3) {
1417            dst[0] = 124;
1418            dst[1] = 41;
1419            dst[2] = 99;
1420        }
1421        let mut scaler = Scaler::new(ResamplingFunction::Bilinear);
1422        scaler.set_threading_policy(ThreadingPolicy::Adaptive);
1423        let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1424        let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1425        scaler.resize_rgb(&src_store, &mut target_store).unwrap();
1426        let target_data = target_store.buffer.borrow();
1427
1428        check_rgb16!(target_data, image_width, 85);
1429    }
1430
1431    #[test]
1432    fn check_rgba10_resizing_vertical() {
1433        let image_width = 8;
1434        let image_height = 8;
1435        const CN: usize = 4;
1436        let mut image = vec![0u16; image_height * image_width * CN];
1437        for dst in image.chunks_exact_mut(4) {
1438            dst[0] = 124;
1439            dst[1] = 41;
1440            dst[2] = 99;
1441            dst[3] = 77;
1442        }
1443        image[3] = 78;
1444        let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1445        scaler.set_threading_policy(ThreadingPolicy::Single);
1446        let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1447        src_store.bit_depth = 10;
1448        let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 10);
1449        scaler
1450            .resize_rgba_u16(&src_store, &mut target_store, false)
1451            .unwrap();
1452        let target_data = target_store.buffer.borrow();
1453
1454        check_rgba8!(target_data, image_width, 60);
1455    }
1456
1457    #[test]
1458    fn check_rgb10_resizing_vertical() {
1459        let image_width = 8;
1460        let image_height = 4;
1461        const CN: usize = 3;
1462        let mut image = vec![0; image_height * image_width * CN];
1463        for dst in image.chunks_exact_mut(3) {
1464            dst[0] = 124;
1465            dst[1] = 41;
1466            dst[2] = 99;
1467        }
1468        let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1469        scaler.set_threading_policy(ThreadingPolicy::Single);
1470        let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1471        src_store.bit_depth = 10;
1472        let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 10);
1473        scaler
1474            .resize_rgb_u16(&src_store, &mut target_store)
1475            .unwrap();
1476        let target_data = target_store.buffer.borrow();
1477
1478        check_rgb16!(target_data, image_width, 85);
1479    }
1480
1481    #[test]
1482    fn check_rgb10_resizing_vertical_adaptive() {
1483        let image_width = 8;
1484        let image_height = 4;
1485        const CN: usize = 3;
1486        let mut image = vec![0; image_height * image_width * CN];
1487        for dst in image.chunks_exact_mut(3) {
1488            dst[0] = 124;
1489            dst[1] = 41;
1490            dst[2] = 99;
1491        }
1492        let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1493        scaler.set_threading_policy(ThreadingPolicy::Adaptive);
1494        let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1495        src_store.bit_depth = 10;
1496        let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 10);
1497        scaler
1498            .resize_rgb_u16(&src_store, &mut target_store)
1499            .unwrap();
1500        let target_data = target_store.buffer.borrow();
1501
1502        check_rgb16!(target_data, image_width, 85);
1503    }
1504
1505    #[test]
1506    fn check_rgb16_resizing_vertical() {
1507        let image_width = 8;
1508        let image_height = 8;
1509        const CN: usize = 3;
1510        let mut image = vec![164; image_height * image_width * CN];
1511        for dst in image.chunks_exact_mut(3) {
1512            dst[0] = 124;
1513            dst[1] = 41;
1514            dst[2] = 99;
1515        }
1516        let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1517        scaler.set_threading_policy(ThreadingPolicy::Single);
1518        let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1519        src_store.bit_depth = 10;
1520        let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 16);
1521        scaler
1522            .resize_rgb_u16(&src_store, &mut target_store)
1523            .unwrap();
1524        let target_data = target_store.buffer.borrow();
1525
1526        check_rgb16!(target_data, image_width, 100);
1527    }
1528
1529    #[test]
1530    fn check_rgba16_resizing_vertical() {
1531        let image_width = 8;
1532        let image_height = 8;
1533        const CN: usize = 4;
1534        let mut image = vec![0u16; image_height * image_width * CN];
1535        for dst in image.chunks_exact_mut(4) {
1536            dst[0] = 124;
1537            dst[1] = 41;
1538            dst[2] = 99;
1539            dst[3] = 255;
1540        }
1541        let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1542        scaler.set_threading_policy(ThreadingPolicy::Single);
1543        let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1544        src_store.bit_depth = 10;
1545        let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 16);
1546        scaler
1547            .resize_rgba_u16(&src_store, &mut target_store, false)
1548            .unwrap();
1549        let target_data = target_store.buffer.borrow();
1550
1551        check_rgba8!(target_data, image_width, 180);
1552    }
1553
1554    #[test]
1555    fn check_rgba16_resizing_vertical_threading() {
1556        let image_width = 8;
1557        let image_height = 8;
1558        const CN: usize = 4;
1559        let mut image = vec![0u16; image_height * image_width * CN];
1560        for dst in image.chunks_exact_mut(4) {
1561            dst[0] = 124;
1562            dst[1] = 41;
1563            dst[2] = 99;
1564            dst[3] = 255;
1565        }
1566        let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
1567        scaler.set_threading_policy(ThreadingPolicy::Adaptive);
1568        let mut src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1569        src_store.bit_depth = 10;
1570        let mut target_store = ImageStoreMut::alloc_with_depth(image_width, image_height / 2, 16);
1571        scaler
1572            .resize_rgba_u16(&src_store, &mut target_store, false)
1573            .unwrap();
1574        let target_data = target_store.buffer.borrow();
1575
1576        check_rgba8!(target_data, image_width, 180);
1577    }
1578
1579    #[test]
1580    fn check_rgba8_nearest_vertical() {
1581        let image_width = 255;
1582        let image_height = 512;
1583        const CN: usize = 4;
1584        let mut image = vec![0u8; image_height * image_width * CN];
1585        for dst in image.chunks_exact_mut(4) {
1586            dst[0] = 124;
1587            dst[1] = 41;
1588            dst[2] = 99;
1589            dst[3] = 77;
1590        }
1591        let mut scaler = Scaler::new(ResamplingFunction::Nearest);
1592        scaler.set_threading_policy(ThreadingPolicy::Single);
1593        let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1594        let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1595        scaler
1596            .resize_rgba(&src_store, &mut target_store, false)
1597            .unwrap();
1598        let target_data = target_store.buffer.borrow();
1599
1600        check_rgba8!(target_data, image_width, 80);
1601    }
1602
1603    #[test]
1604    fn check_rgba8_nearest_vertical_threading() {
1605        let image_width = 255;
1606        let image_height = 512;
1607        const CN: usize = 4;
1608        let mut image = vec![0u8; image_height * image_width * CN];
1609        for dst in image.chunks_exact_mut(4) {
1610            dst[0] = 124;
1611            dst[1] = 41;
1612            dst[2] = 99;
1613            dst[3] = 77;
1614        }
1615        let mut scaler = Scaler::new(ResamplingFunction::Nearest);
1616        scaler.set_threading_policy(ThreadingPolicy::Adaptive);
1617        let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
1618        let mut target_store = ImageStoreMut::alloc(image_width, image_height / 2);
1619        scaler
1620            .resize_rgba(&src_store, &mut target_store, false)
1621            .unwrap();
1622        let target_data = target_store.buffer.borrow();
1623
1624        check_rgba8!(target_data, image_width, 80);
1625    }
1626}