| 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 | |