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