ae39ef33de2f1efb6bfadacb8d92fac0da5f0a13
[nihav.git] / nihav-core / src / scale / scale / mod.rs
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 as f32);
225 coeffs[i] = a * fp.sin() * (fp / a).sin() / (norm * (x as f32) * (x as f32));
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