Skip to main content

ezk_image/
resize.rs

1use crate::{ImageMut, ImageRef, PixelFormat};
2use fir::{IntoImageView, IntoImageViewMut, pixels::InnerPixel};
3#[cfg(feature = "multi-thread")]
4use rayon::scope;
5#[cfg(not(feature = "multi-thread"))]
6use rayon_stub::scope;
7use std::{cmp, error::Error, fmt, marker::PhantomData};
8
9pub use fir::{Filter, FilterType, ResizeAlg};
10
11/// Everything that can go wrong when calling [`Resizer::resize`]
12#[derive(Debug, PartialEq)]
13pub enum ResizeError {
14    DifferentFormats(PixelFormat, PixelFormat),
15}
16
17impl fmt::Display for ResizeError {
18    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19        match self {
20            ResizeError::DifferentFormats(src, dst) => write!(
21                f,
22                "source and destination images have different pixel formats (source image is {src:?} and destination is {dst:?})"
23            ),
24        }
25    }
26}
27
28impl Error for ResizeError {}
29
30/// Wrapper over [`fast_image_resize`](fir)
31#[derive(Clone)]
32pub struct Resizer {
33    alg: fir::ResizeAlg,
34    fir: Vec<fir::Resizer>,
35}
36
37impl Resizer {
38    pub fn new(alg: fir::ResizeAlg) -> Self {
39        Self { alg, fir: vec![] }
40    }
41
42    /// Resize an image. `src` and `dst` must have the same pixel format.
43    ///
44    /// Color characteristics of the images are ignored.
45    pub fn resize(
46        &mut self,
47        src: &dyn ImageRef,
48        dst: &mut dyn ImageMut,
49    ) -> Result<(), ResizeError> {
50        if src.format() != dst.format() {
51            return Err(ResizeError::DifferentFormats(src.format(), dst.format()));
52        }
53
54        let alg = self.alg;
55
56        let desc = src.format().plane_desc();
57
58        let src_width = src.width();
59        let dst_width = dst.width();
60
61        let src_planes: Vec<(&[u8], usize)> = src.planes().collect();
62        let dst_planes: Vec<(&mut [u8], usize)> = dst.planes_mut().collect();
63
64        self.fir.resize_with(
65            cmp::max(self.fir.len(), src_planes.len()),
66            fir::Resizer::new,
67        );
68
69        scope(|s| {
70            for ((plane_desc, ((src_plane, src_stride), (dst_plane, dst_stride))), fir_resizer) in
71                desc.iter()
72                    .zip(src_planes.into_iter().zip(dst_planes))
73                    .zip(&mut self.fir)
74            {
75                s.spawn(move |_| {
76                    let src_fir_width = plane_desc.width_op.op(src_width)
77                        / (plane_desc.pixel_type.size() / plane_desc.bytes_per_primitive);
78                    let dst_fir_width = plane_desc.width_op.op(dst_width)
79                        / (plane_desc.pixel_type.size() / plane_desc.bytes_per_primitive);
80
81                    let src_height = src_plane.len() / src_stride;
82                    let dst_height = dst_plane.len() / dst_stride;
83
84                    let src = FirAdapterIntoImageView {
85                        pixel_type: plane_desc.pixel_type,
86                        width: src_fir_width,
87                        height: src_height,
88                        stride: src_stride,
89                        plane: src_plane,
90                    };
91
92                    let mut dst = FirAdapterIntoImageView {
93                        pixel_type: plane_desc.pixel_type,
94                        width: dst_fir_width,
95                        height: dst_height,
96                        stride: dst_stride,
97                        plane: dst_plane,
98                    };
99
100                    fir_resizer
101                        .resize(
102                            &src,
103                            &mut dst,
104                            Some(&fir::ResizeOptions {
105                                algorithm: alg,
106                                cropping: fir::SrcCropping::None,
107                                mul_div_alpha: false,
108                            }),
109                        )
110                        .expect(
111                            "Pixel type must be assured to be the same before calling fir's resize",
112                        );
113                })
114            }
115        });
116
117        Ok(())
118    }
119}
120
121#[cfg(not(feature = "multi-thread"))]
122mod rayon_stub {
123    pub(super) struct Scope {}
124
125    impl Scope {
126        pub(super) fn spawn(&self, f: impl FnOnce(&Scope)) {
127            scope(f)
128        }
129    }
130
131    pub(super) fn scope(f: impl FnOnce(&Scope)) {
132        f(&Scope {})
133    }
134}
135
136struct FirAdapterIntoImageView<T> {
137    pixel_type: fir::PixelType,
138    width: usize,
139    height: usize,
140    stride: usize,
141    plane: T,
142}
143
144impl<T: AsRef<[u8]>> IntoImageView for FirAdapterIntoImageView<T> {
145    fn pixel_type(&self) -> Option<fir::PixelType> {
146        Some(self.pixel_type)
147    }
148
149    fn width(&self) -> u32 {
150        self.width as u32
151    }
152
153    fn height(&self) -> u32 {
154        self.height as u32
155    }
156
157    fn image_view<P: fir::PixelTrait>(&self) -> Option<impl fir::ImageView<Pixel = P>> {
158        if P::pixel_type() != self.pixel_type {
159            return None;
160        }
161
162        Some(FirAdapterImageView {
163            width: self.width,
164            height: self.height,
165            stride: self.stride,
166            plane: self.plane.as_ref(),
167            _pixel_type: PhantomData,
168        })
169    }
170}
171
172impl<T: AsRef<[u8]> + AsMut<[u8]> + Send + Sync> IntoImageViewMut for FirAdapterIntoImageView<T> {
173    fn image_view_mut<P: fir::PixelTrait>(&mut self) -> Option<impl fir::ImageViewMut<Pixel = P>> {
174        if P::pixel_type() != self.pixel_type {
175            return None;
176        }
177
178        Some(FirAdapterImageView {
179            width: self.width,
180            height: self.height,
181            stride: self.stride,
182            plane: self.plane.as_mut(),
183            _pixel_type: PhantomData,
184        })
185    }
186}
187
188struct FirAdapterImageView<T, P> {
189    width: usize,
190    height: usize,
191    stride: usize,
192    plane: T,
193    _pixel_type: PhantomData<P>,
194}
195
196unsafe impl<T: AsRef<[u8]> + Send + Sync, P: InnerPixel> fir::ImageView
197    for FirAdapterImageView<T, P>
198{
199    type Pixel = P;
200
201    fn width(&self) -> u32 {
202        self.width as u32
203    }
204
205    fn height(&self) -> u32 {
206        self.height as u32
207    }
208
209    fn iter_rows(&self, start_row: u32) -> impl Iterator<Item = &[Self::Pixel]> {
210        self.plane
211            .as_ref()
212            .chunks_exact(self.stride)
213            .skip(start_row as usize)
214            .map(|row| {
215                let row = &row[..self.width * P::pixel_type().size()];
216
217                let (unwanted1, row, unwanted2) = unsafe { row.align_to::<P>() };
218
219                assert_eq!(row.len(), self.width);
220                assert!(unwanted1.is_empty());
221                assert!(unwanted2.is_empty());
222
223                row
224            })
225    }
226}
227
228unsafe impl<T: AsRef<[u8]> + AsMut<[u8]> + Send + Sync, P: InnerPixel> fir::ImageViewMut
229    for FirAdapterImageView<T, P>
230{
231    fn iter_rows_mut(&mut self, start_row: u32) -> impl Iterator<Item = &mut [Self::Pixel]> {
232        self.plane
233            .as_mut()
234            .chunks_exact_mut(self.stride)
235            .skip(start_row as usize)
236            .map(|row| {
237                let row = &mut row[..self.width * P::pixel_type().size()];
238
239                let (unwanted1, row, unwanted2) = unsafe { row.align_to_mut::<P>() };
240
241                assert_eq!(row.len(), self.width);
242                assert!(unwanted1.is_empty());
243                assert!(unwanted2.is_empty());
244
245                row
246            })
247    }
248}