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#[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#[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 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}