]>
Commit | Line | Data |
---|---|---|
1 | use super::*; | |
2 | use super::kernel::Kernel; | |
3 | ||
4 | trait ResizeLine<T> { | |
5 | fn resize_line(&mut self, src: &[T], src_len: usize, sstep: usize, dst: &mut [T], dst_len: usize, dstep: usize); | |
6 | } | |
7 | ||
8 | trait CustomFrom<T> { | |
9 | fn cvt_from(val: T) -> Self; | |
10 | } | |
11 | ||
12 | impl CustomFrom<usize> for u8 { | |
13 | fn cvt_from(val: usize) -> Self { val as u8 } | |
14 | } | |
15 | impl CustomFrom<usize> for u16 { | |
16 | fn cvt_from(val: usize) -> Self { val as u16 } | |
17 | } | |
18 | impl CustomFrom<u8> for usize { | |
19 | fn cvt_from(val: u8) -> Self { usize::from(val) } | |
20 | } | |
21 | impl CustomFrom<u16> for usize { | |
22 | fn cvt_from(val: u16) -> Self { usize::from(val) } | |
23 | } | |
24 | impl CustomFrom<f32> for u8 { | |
25 | fn cvt_from(val: f32) -> Self { val.max(0.0).min(255.0) as u8 } | |
26 | } | |
27 | impl CustomFrom<f32> for u16 { | |
28 | fn cvt_from(val: f32) -> Self { val.max(0.0).min(65535.0) as u16 } | |
29 | } | |
30 | impl CustomFrom<u8> for f32 { | |
31 | fn cvt_from(val: u8) -> Self { val as f32 } | |
32 | } | |
33 | impl CustomFrom<u16> for f32 { | |
34 | fn cvt_from(val: u16) -> Self { val as f32 } | |
35 | } | |
36 | ||
37 | trait CustomInto<T:Copy> { | |
38 | fn cvt_into(self) -> T; | |
39 | } | |
40 | ||
41 | impl<T:Copy, U:Copy> CustomInto<U> for T where U: CustomFrom<T> { | |
42 | fn cvt_into(self) -> U { U::cvt_from(self) } | |
43 | } | |
44 | ||
45 | #[derive(Clone,Copy,Default)] | |
46 | struct FracPos { | |
47 | ipos: usize, | |
48 | frac: usize, | |
49 | } | |
50 | ||
51 | impl FracPos { | |
52 | fn new() -> Self { Self::default() } | |
53 | fn add(&mut self, frac: Fraction) { | |
54 | self.frac += frac.num; | |
55 | while self.frac >= frac.den { | |
56 | self.frac -= frac.den; | |
57 | self.ipos += 1; | |
58 | } | |
59 | } | |
60 | } | |
61 | ||
62 | #[derive(Clone,Copy,PartialEq)] | |
63 | struct Fraction { | |
64 | num: usize, | |
65 | den: usize, | |
66 | } | |
67 | ||
68 | impl Fraction { | |
69 | fn new(a: usize, b: usize) -> Self { | |
70 | let cur_gcd = gcd(a, b); | |
71 | Self { | |
72 | num: a / cur_gcd, | |
73 | den: b / cur_gcd, | |
74 | } | |
75 | } | |
76 | fn is_one(self) -> bool { self.num == 1 && self.den == 1 } | |
77 | } | |
78 | ||
79 | fn gcd(mut a: usize, mut b: usize) -> usize { | |
80 | while a != 0 && b != 0 { | |
81 | if a > b { | |
82 | a -= b; | |
83 | } else { | |
84 | b -= a; | |
85 | } | |
86 | } | |
87 | a.max(b) | |
88 | } | |
89 | ||
90 | ||
91 | #[derive(Clone)] | |
92 | struct NNResampler {} | |
93 | ||
94 | impl NNResampler { | |
95 | fn new() -> Self { Self{} } | |
96 | } | |
97 | ||
98 | impl<T:Copy> ResizeLine<T> for NNResampler { | |
99 | fn resize_line(&mut self, src: &[T], src_len: usize, sstep: usize, dst: &mut [T], dst_len: usize, dstep: usize) { | |
100 | let frac = Fraction::new(src_len, dst_len); | |
101 | let mut pos = FracPos::new(); | |
102 | for el in dst.chunks_mut(dstep).take(dst_len) { | |
103 | el[0] = src[pos.ipos * sstep]; | |
104 | pos.add(frac); | |
105 | } | |
106 | } | |
107 | } | |
108 | ||
109 | #[derive(Clone)] | |
110 | struct BilinResize {} | |
111 | ||
112 | impl BilinResize { | |
113 | fn new() -> Self { Self {} } | |
114 | } | |
115 | ||
116 | impl<T> ResizeLine<T> for BilinResize | |
117 | where | |
118 | T: Copy+CustomFrom<usize>+CustomInto<usize> | |
119 | { | |
120 | fn resize_line(&mut self, src: &[T], src_len: usize, sstep: usize, dst: &mut [T], dst_len: usize, dstep: usize) { | |
121 | let mut pos = FracPos::new(); | |
122 | let frac = Fraction::new(src_len, dst_len); | |
123 | ||
124 | for el in dst.chunks_mut(dstep).take(dst_len) { | |
125 | let spos0 = pos.ipos * sstep; | |
126 | if pos.frac == 0 { | |
127 | el[0] = src[spos0]; | |
128 | } else { | |
129 | let spos1 = (pos.ipos + 1).min(src_len - 1) * sstep; | |
130 | let s0: usize = T::cvt_into(src[spos0]); | |
131 | let s1: usize = T::cvt_into(src[spos1]); | |
132 | el[0] = usize::cvt_into((s0 * (frac.den - pos.frac) + s1 * pos.frac) / frac.den); | |
133 | } | |
134 | pos.add(frac); | |
135 | } | |
136 | } | |
137 | } | |
138 | ||
139 | #[derive(Clone)] | |
140 | struct BicubicResize { | |
141 | last_frac: Fraction, | |
142 | ccache: Vec<[f32; 4]>, | |
143 | } | |
144 | ||
145 | impl BicubicResize { | |
146 | fn new() -> Self { | |
147 | Self { | |
148 | last_frac: Fraction::new(1, 1), | |
149 | ccache: Vec::new(), | |
150 | } | |
151 | } | |
152 | fn gen_coeffs(frac: f32) -> [f32; 4] { | |
153 | let frac2 = frac * frac; | |
154 | let frac3 = frac2 * frac; | |
155 | ||
156 | [ -0.5 * frac + frac2 - 0.5 * frac3, | |
157 | 1.0 - 2.5 * frac2 + 1.5 * frac3, | |
158 | 0.5 * frac + 2.0 * frac2 - 1.5 * frac3, | |
159 | -0.5 * frac2 + 0.5 * frac3 ] | |
160 | } | |
161 | fn gen_cache(den: usize) -> Vec<[f32; 4]> { | |
162 | let mut cache = Vec::with_capacity(den); | |
163 | for i in 1..den { | |
164 | cache.push(Self::gen_coeffs((i as f32) / (den as f32))); | |
165 | } | |
166 | cache | |
167 | } | |
168 | } | |
169 | ||
170 | impl<T> ResizeLine<T> for BicubicResize | |
171 | where | |
172 | T: Copy+CustomFrom<f32>+CustomInto<f32> | |
173 | { | |
174 | fn resize_line(&mut self, src: &[T], src_len: usize, sstep: usize, dst: &mut [T], dst_len: usize, dstep: usize) { | |
175 | let frac = Fraction::new(src_len, dst_len); | |
176 | if frac != self.last_frac { | |
177 | self.last_frac = frac; | |
178 | self.ccache = Self::gen_cache(frac.den); | |
179 | } | |
180 | let mut pos = FracPos::new(); | |
181 | let end = (src_len - 1) * sstep; | |
182 | for el in dst.chunks_mut(dstep).take(dst_len) { | |
183 | let spos0 = pos.ipos * sstep; | |
184 | el[0] = if pos.frac == 0 { | |
185 | src[spos0] | |
186 | } else { | |
187 | let spos1 = (spos0 + sstep).min(end); | |
188 | let spos2 = (spos1 + sstep).min(end); | |
189 | let sposm = spos0.saturating_sub(sstep); | |
190 | let sm = T::cvt_into(src[sposm]); | |
191 | let s0 = T::cvt_into(src[spos0]); | |
192 | let s1 = T::cvt_into(src[spos1]); | |
193 | let s2 = T::cvt_into(src[spos2]); | |
194 | ||
195 | let coeffs = &self.ccache[pos.frac - 1]; | |
196 | T::cvt_from(sm * coeffs[0] + s0 * coeffs[1] + s1 * coeffs[2] + s2 * coeffs[3]) | |
197 | }; | |
198 | pos.add(frac); | |
199 | } | |
200 | } | |
201 | } | |
202 | ||
203 | #[derive(Clone)] | |
204 | struct LanczosResize { | |
205 | last_frac: Fraction, | |
206 | order: usize, | |
207 | ccache: Vec<Vec<f32>>, | |
208 | } | |
209 | ||
210 | impl LanczosResize { | |
211 | fn new(order: usize) -> Self { | |
212 | Self { | |
213 | last_frac: Fraction::new(1, 1), | |
214 | order, | |
215 | ccache: Vec::new(), | |
216 | } | |
217 | } | |
218 | fn get_coeffs(num: usize, den: usize, order: usize, coeffs: &mut [f32]) { | |
219 | let norm = std::f32::consts::PI * std::f32::consts::PI; | |
220 | let frac = (num as f32) / (den as f32); | |
221 | let a = order as f32; | |
222 | for i in 0..(order * 2) { | |
223 | let x = frac - ((i as f32) + 1.0 - a); | |
224 | let fp = std::f32::consts::PI * x; | |
225 | coeffs[i] = a * fp.sin() * (fp / a).sin() / (norm * x * x); | |
226 | } | |
227 | } | |
228 | fn create_cache(order: usize, den: usize) -> Vec<Vec<f32>> { | |
229 | let mut cache = Vec::with_capacity(den); | |
230 | for i in 1..den { | |
231 | let mut entry = vec![0.0; order * 2]; | |
232 | Self::get_coeffs(i, den, order, &mut entry); | |
233 | cache.push(entry); | |
234 | } | |
235 | cache | |
236 | } | |
237 | } | |
238 | ||
239 | impl<T> ResizeLine<T> for LanczosResize | |
240 | where | |
241 | T: Copy+CustomFrom<f32>+CustomInto<f32> | |
242 | { | |
243 | fn resize_line(&mut self, src: &[T], src_len: usize, sstep: usize, dst: &mut [T], dst_len: usize, dstep: usize) { | |
244 | let frac = Fraction::new(src_len, dst_len); | |
245 | if frac != self.last_frac { | |
246 | self.last_frac = frac; | |
247 | self.ccache = Self::create_cache(self.order, frac.den); | |
248 | } | |
249 | ||
250 | let mut pos = FracPos::new(); | |
251 | ||
252 | for el in dst.chunks_mut(dstep).take(dst_len) { | |
253 | if pos.frac == 0 { | |
254 | el[0] = src[pos.ipos * sstep]; | |
255 | } else { | |
256 | let coeffs = &self.ccache[pos.frac - 1]; | |
257 | let mut sum = 0.0; | |
258 | for (x, &coef) in coeffs.iter().enumerate() { | |
259 | let cpos = (pos.ipos + 1 + x).saturating_sub(self.order).min(src_len - 1) * sstep; | |
260 | sum += T::cvt_into(src[cpos]) * coef; | |
261 | } | |
262 | el[0] = T::cvt_from(sum); | |
263 | } | |
264 | pos.add(frac); | |
265 | } | |
266 | } | |
267 | } | |
268 | ||
269 | macro_rules! scale_loop { | |
270 | ($sbuf:expr, $tbuf:expr, $dbuf:expr, $scalers:expr) => { | |
271 | let fmt = $sbuf.get_info().get_format(); | |
272 | let dfmt = $dbuf.get_info().get_format(); | |
273 | let ndcomp = dfmt.get_num_comp(); | |
274 | let ncomp = fmt.get_num_comp().min(ndcomp); | |
275 | ||
276 | for comp in 0..ncomp { | |
277 | let istride = $sbuf.get_stride(comp); | |
278 | let dstride = $dbuf.get_stride(comp); | |
279 | let (sw, sh) = $sbuf.get_dimensions(comp); | |
280 | let (dw, dh) = $dbuf.get_dimensions(comp); | |
281 | let ioff = $sbuf.get_offset(comp); | |
282 | let doff = $dbuf.get_offset(comp); | |
283 | let src = $sbuf.get_data(); | |
284 | let dst = $dbuf.get_data_mut().unwrap(); | |
285 | let tstride = (dw + 15) & !15; | |
286 | ||
287 | let cur_frac = Fraction::new(sw, dw); | |
288 | if !cur_frac.is_one() { | |
289 | let mut idx = $scalers.len(); | |
290 | for (i, &(frac, _)) in $scalers.iter().enumerate() { | |
291 | if frac == cur_frac { | |
292 | idx = i; | |
293 | break; | |
294 | } | |
295 | } | |
296 | let resizer = &mut $scalers[idx].1; | |
297 | for (dline, sline) in $tbuf.chunks_mut(tstride).zip(src[ioff..].chunks(istride)).take(sh) { | |
298 | resizer.resize_line(sline, sw, 1, dline, dw, 1); | |
299 | } | |
300 | } else { | |
301 | for (dline, sline) in $tbuf.chunks_mut(tstride).zip(src[ioff..].chunks(istride)).take(sh) { | |
302 | dline[..dw].copy_from_slice(&sline[..sw]); | |
303 | } | |
304 | } | |
305 | ||
306 | let cur_frac = Fraction::new(sh, dh); | |
307 | if !cur_frac.is_one() { | |
308 | let mut idx = $scalers.len(); | |
309 | for (i, &(frac, _)) in $scalers.iter().enumerate() { | |
310 | if frac == cur_frac { | |
311 | idx = i; | |
312 | break; | |
313 | } | |
314 | } | |
315 | let resizer = &mut $scalers[idx].1; | |
316 | for x in 0..dw { | |
317 | resizer.resize_line(&$tbuf[x..], sh, tstride, &mut dst[doff + x..], dh, dstride); | |
318 | } | |
319 | } else { | |
320 | for (dline, sline) in dst[doff..].chunks_mut(dstride).zip($tbuf.chunks(tstride)).take(dh) { | |
321 | dline[..dw].copy_from_slice(&sline[..dw]); | |
322 | } | |
323 | } | |
324 | } | |
325 | }; | |
326 | } | |
327 | ||
328 | type Resizer<T> = (Fraction, Box<dyn ResizeLine<T>>); | |
329 | ||
330 | struct Scaler { | |
331 | resizers8: Vec<Resizer<u8>>, | |
332 | resizers16: Vec<Resizer<u16>>, | |
333 | tmp8: Vec<u8>, | |
334 | tmp16: Vec<u16>, | |
335 | } | |
336 | ||
337 | fn set_resizer<T, F>(dst: &mut Vec<Resizer<T>>, new_resizer: F, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) | |
338 | where F: Fn() -> Box<dyn ResizeLine<T>> | |
339 | { | |
340 | let ncomp = in_fmt.fmt.get_num_comp().min(dest_fmt.fmt.get_num_comp()); | |
341 | for comp in 0..ncomp { | |
342 | if let (Some(sfmt), Some(dfmt)) = (in_fmt.fmt.get_chromaton(comp), dest_fmt.fmt.get_chromaton(comp)) { | |
343 | let sw = sfmt.get_width(in_fmt.width); | |
344 | let sh = sfmt.get_height(in_fmt.height); | |
345 | let dw = dfmt.get_width(dest_fmt.width); | |
346 | let dh = dfmt.get_height(dest_fmt.height); | |
347 | let frac1 = Fraction::new(sw, dw); | |
348 | if !frac1.is_one() { | |
349 | let frac1_present = dst.iter().any(|(frac, _)| *frac == frac1); | |
350 | if !frac1_present { | |
351 | dst.push((frac1, new_resizer())); | |
352 | } | |
353 | } | |
354 | let frac2 = Fraction::new(sh, dh); | |
355 | if !frac2.is_one() { | |
356 | let frac2_present = dst.iter().any(|(frac, _)| *frac == frac2); | |
357 | ||
358 | if !frac2_present { | |
359 | dst.push((frac2, new_resizer())); | |
360 | } | |
361 | } | |
362 | } | |
363 | } | |
364 | } | |
365 | ||
366 | impl Scaler { | |
367 | fn new() -> Self { | |
368 | Self { | |
369 | resizers8: Vec::new(), | |
370 | resizers16: Vec::new(), | |
371 | tmp8: Vec::new(), | |
372 | tmp16: Vec::new(), | |
373 | } | |
374 | } | |
375 | } | |
376 | ||
377 | impl Kernel for Scaler { | |
378 | fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult<NABufferType> { | |
379 | let is16 = in_fmt.fmt.get_max_depth() > 8; | |
380 | for (name, value) in options.iter() { | |
381 | if name.as_str() == "scaler" { | |
382 | match value.as_str() { | |
383 | "nn" => { | |
384 | if !is16 { | |
385 | set_resizer(&mut self.resizers8, || Box::new(NNResampler::new()), in_fmt, dest_fmt); | |
386 | } else { | |
387 | set_resizer(&mut self.resizers16, || Box::new(NNResampler::new()), in_fmt, dest_fmt); | |
388 | } | |
389 | }, | |
390 | "bilin" => { | |
391 | if !is16 { | |
392 | set_resizer(&mut self.resizers8, || Box::new(BilinResize::new()), in_fmt, dest_fmt); | |
393 | } else { | |
394 | set_resizer(&mut self.resizers16, || Box::new(BilinResize::new()), in_fmt, dest_fmt); | |
395 | } | |
396 | }, | |
397 | "bicubic" => { | |
398 | if !is16 { | |
399 | set_resizer(&mut self.resizers8, || Box::new(BicubicResize::new()), in_fmt, dest_fmt); | |
400 | } else { | |
401 | set_resizer(&mut self.resizers16, || Box::new(BicubicResize::new()), in_fmt, dest_fmt); | |
402 | } | |
403 | }, | |
404 | _ => {}, | |
405 | }; | |
406 | if value.as_str().starts_with("lanczos") { | |
407 | let tail = &value[7..]; | |
408 | ||
409 | let mut filt_len = if let Ok(val) = tail.parse::<usize>() { | |
410 | if (2..=16).contains(&val) { | |
411 | val | |
412 | } else { | |
413 | 0 | |
414 | } | |
415 | } else { | |
416 | 0 | |
417 | }; | |
418 | if filt_len == 0 { | |
419 | filt_len = 3; | |
420 | } | |
421 | if !is16 { | |
422 | set_resizer(&mut self.resizers8, || Box::new(LanczosResize::new(filt_len)), in_fmt, dest_fmt); | |
423 | } else { | |
424 | set_resizer(&mut self.resizers16, || Box::new(LanczosResize::new(filt_len)), in_fmt, dest_fmt); | |
425 | } | |
426 | } | |
427 | } | |
428 | } | |
429 | if !is16 && self.resizers8.is_empty() { | |
430 | set_resizer(&mut self.resizers8, || Box::new(NNResampler::new()), in_fmt, dest_fmt); | |
431 | } | |
432 | if is16 && self.resizers16.is_empty() { | |
433 | set_resizer(&mut self.resizers16, || Box::new(NNResampler::new()), in_fmt, dest_fmt); | |
434 | } | |
435 | ||
436 | let mut max_size = 0; | |
437 | let ncomp = in_fmt.fmt.get_num_comp().min(dest_fmt.fmt.get_num_comp()); | |
438 | for comp in 0..ncomp { | |
439 | if let (Some(sfmt), Some(dfmt)) = (in_fmt.fmt.get_chromaton(comp), dest_fmt.fmt.get_chromaton(comp)) { | |
440 | let sh = sfmt.get_height(in_fmt.height); | |
441 | let dw = dfmt.get_width(dest_fmt.width); | |
442 | let tmp_size = sh * ((dw + 15) & !15); | |
443 | max_size = max_size.max(tmp_size); | |
444 | } | |
445 | } | |
446 | if !is16 { | |
447 | self.tmp8.resize(max_size, 0); | |
448 | } else { | |
449 | self.tmp16.resize(max_size, 0); | |
450 | } | |
451 | ||
452 | let res = alloc_video_buffer(NAVideoInfo::new(dest_fmt.width, dest_fmt.height, false, in_fmt.fmt), 3); | |
453 | if res.is_err() { return Err(ScaleError::AllocError); } | |
454 | Ok(res.unwrap()) | |
455 | } | |
456 | fn process(&mut self, pic_in: &NABufferType, pic_out: &mut NABufferType) { | |
457 | if let (Some(ref sbuf), Some(ref mut dbuf)) = (pic_in.get_vbuf(), pic_out.get_vbuf()) { | |
458 | scale_loop!(sbuf, self.tmp8, dbuf, self.resizers8); | |
459 | } else if let (Some(ref sbuf), Some(ref mut dbuf)) = (pic_in.get_vbuf16(), pic_out.get_vbuf16()) { | |
460 | scale_loop!(sbuf, self.tmp16, dbuf, self.resizers16); | |
461 | } else { | |
462 | unreachable!(); | |
463 | } | |
464 | } | |
465 | } | |
466 | ||
467 | pub fn create_scale() -> Box<dyn Kernel> { | |
468 | Box::new(Scaler::new()) | |
469 | } | |
470 |