| 1 | // Copyright (c) 2017-2021, The rav1e contributors. All rights reserved |
| 2 | // |
| 3 | // This source code is subject to the terms of the BSD 2 Clause License and |
| 4 | // the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License |
| 5 | // was not distributed with this source code in the LICENSE file, you can |
| 6 | // obtain it at www.aomedia.org/license/software. If the Alliance for Open |
| 7 | // Media Patent License 1.0 was not distributed with this source code in the |
| 8 | // PATENTS file, you can obtain it at www.aomedia.org/license/patent. |
| 9 | |
| 10 | use std::fmt::{Debug, Display, Formatter}; |
| 11 | use std::iter::{self, FusedIterator}; |
| 12 | use std::marker::PhantomData; |
| 13 | use std::mem::size_of; |
| 14 | use std::ops::{Index, IndexMut, Range}; |
| 15 | |
| 16 | use aligned_vec::{ABox, AVec, ConstAlign}; |
| 17 | |
| 18 | use crate::math::*; |
| 19 | use crate::pixel::*; |
| 20 | |
| 21 | #[cfg (feature = "serialize" )] |
| 22 | use serde::{Deserialize, Serialize}; |
| 23 | |
| 24 | /// Plane-specific configuration. |
| 25 | #[derive (Debug, Clone, PartialEq, Eq)] |
| 26 | #[cfg_attr (feature = "serialize" , derive(Serialize, Deserialize))] |
| 27 | pub struct PlaneConfig { |
| 28 | /// Data stride. |
| 29 | pub stride: usize, |
| 30 | /// Allocated height in pixels. |
| 31 | pub alloc_height: usize, |
| 32 | /// Width in pixels. |
| 33 | pub width: usize, |
| 34 | /// Height in pixels. |
| 35 | pub height: usize, |
| 36 | /// Decimator along the X axis. |
| 37 | /// |
| 38 | /// For example, for chroma planes in a 4:2:0 configuration this would be 1. |
| 39 | pub xdec: usize, |
| 40 | /// Decimator along the Y axis. |
| 41 | /// |
| 42 | /// For example, for chroma planes in a 4:2:0 configuration this would be 1. |
| 43 | pub ydec: usize, |
| 44 | /// Number of padding pixels on the right. |
| 45 | pub xpad: usize, |
| 46 | /// Number of padding pixels on the bottom. |
| 47 | pub ypad: usize, |
| 48 | /// X where the data starts. |
| 49 | pub xorigin: usize, |
| 50 | /// Y where the data starts. |
| 51 | pub yorigin: usize, |
| 52 | } |
| 53 | |
| 54 | impl PlaneConfig { |
| 55 | /// Stride alignment in bytes. |
| 56 | const STRIDE_ALIGNMENT_LOG2: usize = 6; |
| 57 | |
| 58 | #[inline ] |
| 59 | pub fn new( |
| 60 | width: usize, |
| 61 | height: usize, |
| 62 | xdec: usize, |
| 63 | ydec: usize, |
| 64 | xpad: usize, |
| 65 | ypad: usize, |
| 66 | type_size: usize, |
| 67 | ) -> Self { |
| 68 | let xorigin = xpad.align_power_of_two(Self::STRIDE_ALIGNMENT_LOG2 + 1 - type_size); |
| 69 | let yorigin = ypad; |
| 70 | let stride = (xorigin + width + xpad) |
| 71 | .align_power_of_two(Self::STRIDE_ALIGNMENT_LOG2 + 1 - type_size); |
| 72 | let alloc_height = yorigin + height + ypad; |
| 73 | |
| 74 | PlaneConfig { |
| 75 | stride, |
| 76 | alloc_height, |
| 77 | width, |
| 78 | height, |
| 79 | xdec, |
| 80 | ydec, |
| 81 | xpad, |
| 82 | ypad, |
| 83 | xorigin, |
| 84 | yorigin, |
| 85 | } |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | /// Absolute offset in pixels inside a plane |
| 90 | #[derive (Clone, Copy, Debug, Default)] |
| 91 | pub struct PlaneOffset { |
| 92 | pub x: isize, |
| 93 | pub y: isize, |
| 94 | } |
| 95 | |
| 96 | /// Backing buffer for the Plane data |
| 97 | /// |
| 98 | /// The buffer is padded and aligned according to the architecture-specific |
| 99 | /// SIMD constraints. |
| 100 | #[derive (Debug, Clone, PartialEq, Eq)] |
| 101 | #[cfg_attr (feature = "serialize" , derive(Serialize, Deserialize))] |
| 102 | pub struct PlaneData<T: Pixel> { |
| 103 | #[cfg (not(target_arch = "wasm32" ))] |
| 104 | data: ABox<[T], ConstAlign<{ 1 << 6 }>>, |
| 105 | #[cfg (target_arch = "wasm32" )] |
| 106 | data: ABox<[T], ConstAlign<{ 1 << 3 }>>, |
| 107 | } |
| 108 | |
| 109 | unsafe impl<T: Pixel + Send> Send for PlaneData<T> {} |
| 110 | unsafe impl<T: Pixel + Sync> Sync for PlaneData<T> {} |
| 111 | |
| 112 | impl<T: Pixel> std::ops::Deref for PlaneData<T> { |
| 113 | type Target = [T]; |
| 114 | |
| 115 | fn deref(&self) -> &[T] { |
| 116 | self.data.as_ref() |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | impl<T: Pixel> std::ops::DerefMut for PlaneData<T> { |
| 121 | fn deref_mut(&mut self) -> &mut [T] { |
| 122 | self.data.as_mut() |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | impl<T: Pixel> PlaneData<T> { |
| 127 | #[cfg (target_arch = "wasm32" )] |
| 128 | // FIXME: wasm32 allocator fails for alignment larger than 3 |
| 129 | const DATA_ALIGNMENT: usize = 1 << 3; |
| 130 | #[cfg (not(target_arch = "wasm32" ))] |
| 131 | const DATA_ALIGNMENT: usize = 1 << 6; |
| 132 | |
| 133 | pub fn new(len: usize) -> Self { |
| 134 | Self { |
| 135 | data: AVecAVec>::from_iter( |
| 136 | Self::DATA_ALIGNMENT, |
| 137 | iter:iter::repeat(T::cast_from(128)).take(len), |
| 138 | ) |
| 139 | .into_boxed_slice(), |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | fn from_slice(data: &[T]) -> Self { |
| 144 | Self { |
| 145 | data: AVec::from_slice(Self::DATA_ALIGNMENT, slice:data).into_boxed_slice(), |
| 146 | } |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | /// One data plane of a frame. |
| 151 | /// |
| 152 | /// For example, a plane can be a Y luma plane or a U or V chroma plane. |
| 153 | #[derive (Clone, PartialEq, Eq)] |
| 154 | #[cfg_attr (feature = "serialize" , derive(Serialize, Deserialize))] |
| 155 | pub struct Plane<T: Pixel> { |
| 156 | // TODO: it is used by encoder to copy by plane and by tiling, make it |
| 157 | // private again once tiling is moved and a copy_plane fn is added. |
| 158 | // |
| 159 | pub data: PlaneData<T>, |
| 160 | /// Plane configuration. |
| 161 | pub cfg: PlaneConfig, |
| 162 | } |
| 163 | |
| 164 | impl<T: Pixel> Debug for Plane<T> |
| 165 | where |
| 166 | T: Display, |
| 167 | { |
| 168 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
| 169 | write!( |
| 170 | f, |
| 171 | "Plane {{ data: [ {}, ...], cfg: {:?} }}" , |
| 172 | self.data[0], self.cfg |
| 173 | ) |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | impl<T: Pixel> Plane<T> { |
| 178 | /// Allocates and returns a new plane. |
| 179 | pub fn new( |
| 180 | width: usize, |
| 181 | height: usize, |
| 182 | xdec: usize, |
| 183 | ydec: usize, |
| 184 | xpad: usize, |
| 185 | ypad: usize, |
| 186 | ) -> Self { |
| 187 | let cfg = PlaneConfig::new(width, height, xdec, ydec, xpad, ypad, size_of::<T>()); |
| 188 | let data = PlaneData::new(cfg.stride * cfg.alloc_height); |
| 189 | |
| 190 | Plane { data, cfg } |
| 191 | } |
| 192 | |
| 193 | /// # Panics |
| 194 | /// |
| 195 | /// - If `len` is not a multiple of `stride` |
| 196 | pub fn from_slice(data: &[T], stride: usize) -> Self { |
| 197 | let len = data.len(); |
| 198 | |
| 199 | assert!(len % stride == 0); |
| 200 | |
| 201 | Self { |
| 202 | data: PlaneData::from_slice(data), |
| 203 | cfg: PlaneConfig { |
| 204 | stride, |
| 205 | alloc_height: len / stride, |
| 206 | width: stride, |
| 207 | height: len / stride, |
| 208 | xdec: 0, |
| 209 | ydec: 0, |
| 210 | xpad: 0, |
| 211 | ypad: 0, |
| 212 | xorigin: 0, |
| 213 | yorigin: 0, |
| 214 | }, |
| 215 | } |
| 216 | } |
| 217 | |
| 218 | pub fn pad(&mut self, w: usize, h: usize) { |
| 219 | let xorigin = self.cfg.xorigin; |
| 220 | let yorigin = self.cfg.yorigin; |
| 221 | let stride = self.cfg.stride; |
| 222 | let alloc_height = self.cfg.alloc_height; |
| 223 | let width = (w + self.cfg.xdec) >> self.cfg.xdec; |
| 224 | let height = (h + self.cfg.ydec) >> self.cfg.ydec; |
| 225 | |
| 226 | if xorigin > 0 { |
| 227 | for y in 0..height { |
| 228 | let base = (yorigin + y) * stride; |
| 229 | let fill_val = self.data[base + xorigin]; |
| 230 | for val in &mut self.data[base..base + xorigin] { |
| 231 | *val = fill_val; |
| 232 | } |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | if xorigin + width < stride { |
| 237 | for y in 0..height { |
| 238 | let base = (yorigin + y) * stride + xorigin + width; |
| 239 | let fill_val = self.data[base - 1]; |
| 240 | for val in &mut self.data[base..base + stride - (xorigin + width)] { |
| 241 | *val = fill_val; |
| 242 | } |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | if yorigin > 0 { |
| 247 | let (top, bottom) = self.data.split_at_mut(yorigin * stride); |
| 248 | let src = &bottom[..stride]; |
| 249 | for y in 0..yorigin { |
| 250 | let dst = &mut top[y * stride..(y + 1) * stride]; |
| 251 | dst.copy_from_slice(src); |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | if yorigin + height < self.cfg.alloc_height { |
| 256 | let (top, bottom) = self.data.split_at_mut((yorigin + height) * stride); |
| 257 | let src = &top[(yorigin + height - 1) * stride..]; |
| 258 | for y in 0..alloc_height - (yorigin + height) { |
| 259 | let dst = &mut bottom[y * stride..(y + 1) * stride]; |
| 260 | dst.copy_from_slice(src); |
| 261 | } |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | /// Minimally test that the plane has been padded. |
| 266 | pub fn probe_padding(&self, w: usize, h: usize) -> bool { |
| 267 | let PlaneConfig { |
| 268 | xorigin, |
| 269 | yorigin, |
| 270 | stride, |
| 271 | alloc_height, |
| 272 | xdec, |
| 273 | ydec, |
| 274 | .. |
| 275 | } = self.cfg; |
| 276 | let width = (w + xdec) >> xdec; |
| 277 | let height = (h + ydec) >> ydec; |
| 278 | let corner = (yorigin + height - 1) * stride + xorigin + width - 1; |
| 279 | let corner_value = self.data[corner]; |
| 280 | |
| 281 | self.data[(yorigin + height) * stride - 1] == corner_value |
| 282 | && self.data[(alloc_height - 1) * stride + xorigin + width - 1] == corner_value |
| 283 | && self.data[alloc_height * stride - 1] == corner_value |
| 284 | } |
| 285 | |
| 286 | pub fn slice(&self, po: PlaneOffset) -> PlaneSlice<'_, T> { |
| 287 | PlaneSlice { |
| 288 | plane: self, |
| 289 | x: po.x, |
| 290 | y: po.y, |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | pub fn mut_slice(&mut self, po: PlaneOffset) -> PlaneMutSlice<'_, T> { |
| 295 | PlaneMutSlice { |
| 296 | plane: self, |
| 297 | x: po.x, |
| 298 | y: po.y, |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | #[inline ] |
| 303 | fn index(&self, x: usize, y: usize) -> usize { |
| 304 | (y + self.cfg.yorigin) * self.cfg.stride + (x + self.cfg.xorigin) |
| 305 | } |
| 306 | |
| 307 | /// This version of the function crops off the padding on the right side of the image |
| 308 | #[inline ] |
| 309 | pub fn row_range_cropped(&self, x: isize, y: isize) -> Range<usize> { |
| 310 | debug_assert!(self.cfg.yorigin as isize + y >= 0); |
| 311 | debug_assert!(self.cfg.xorigin as isize + x >= 0); |
| 312 | let base_y = (self.cfg.yorigin as isize + y) as usize; |
| 313 | let base_x = (self.cfg.xorigin as isize + x) as usize; |
| 314 | let base = base_y * self.cfg.stride + base_x; |
| 315 | let width = (self.cfg.width as isize - x) as usize; |
| 316 | base..base + width |
| 317 | } |
| 318 | |
| 319 | /// This version of the function includes the padding on the right side of the image |
| 320 | #[inline ] |
| 321 | pub fn row_range(&self, x: isize, y: isize) -> Range<usize> { |
| 322 | debug_assert!(self.cfg.yorigin as isize + y >= 0); |
| 323 | debug_assert!(self.cfg.xorigin as isize + x >= 0); |
| 324 | let base_y = (self.cfg.yorigin as isize + y) as usize; |
| 325 | let base_x = (self.cfg.xorigin as isize + x) as usize; |
| 326 | let base = base_y * self.cfg.stride + base_x; |
| 327 | let width = self.cfg.stride - base_x; |
| 328 | base..base + width |
| 329 | } |
| 330 | |
| 331 | /// Returns the pixel at the given coordinates. |
| 332 | pub fn p(&self, x: usize, y: usize) -> T { |
| 333 | self.data[self.index(x, y)] |
| 334 | } |
| 335 | |
| 336 | /// Returns plane data starting from the origin. |
| 337 | pub fn data_origin(&self) -> &[T] { |
| 338 | &self.data[self.index(0, 0)..] |
| 339 | } |
| 340 | |
| 341 | /// Returns mutable plane data starting from the origin. |
| 342 | pub fn data_origin_mut(&mut self) -> &mut [T] { |
| 343 | let i = self.index(0, 0); |
| 344 | &mut self.data[i..] |
| 345 | } |
| 346 | |
| 347 | /// Copies data into the plane from a pixel array. |
| 348 | /// |
| 349 | /// # Panics |
| 350 | /// |
| 351 | /// - If `source_bytewidth` does not match the generic `T` of `Plane` |
| 352 | pub fn copy_from_raw_u8( |
| 353 | &mut self, |
| 354 | source: &[u8], |
| 355 | source_stride: usize, |
| 356 | source_bytewidth: usize, |
| 357 | ) { |
| 358 | let stride = self.cfg.stride; |
| 359 | |
| 360 | assert!(stride != 0); |
| 361 | assert!(source_stride != 0); |
| 362 | |
| 363 | for (self_row, source_row) in self |
| 364 | .data_origin_mut() |
| 365 | .chunks_exact_mut(stride) |
| 366 | .zip(source.chunks_exact(source_stride)) |
| 367 | { |
| 368 | match source_bytewidth { |
| 369 | 1 => { |
| 370 | for (self_pixel, source_pixel) in self_row.iter_mut().zip(source_row.iter()) { |
| 371 | *self_pixel = T::cast_from(*source_pixel); |
| 372 | } |
| 373 | } |
| 374 | 2 => { |
| 375 | assert!( |
| 376 | size_of::<T>() == 2, |
| 377 | "source bytewidth ( {}) cannot fit in Plane<u8>" , |
| 378 | source_bytewidth |
| 379 | ); |
| 380 | |
| 381 | debug_assert!(T::type_enum() == PixelType::U16); |
| 382 | |
| 383 | // SAFETY: because of the assert it is safe to assume that T == u16 |
| 384 | let self_row: &mut [u16] = unsafe { std::mem::transmute(self_row) }; |
| 385 | // SAFETY: we reinterpret the slice of bytes as a slice of elements of |
| 386 | // [u8; 2] to allow for more efficient codegen with from_le_bytes |
| 387 | let source_row: &[[u8; 2]] = unsafe { |
| 388 | std::slice::from_raw_parts(source_row.as_ptr().cast(), source_row.len() / 2) |
| 389 | }; |
| 390 | |
| 391 | for (self_pixel, bytes) in self_row.iter_mut().zip(source_row) { |
| 392 | *self_pixel = u16::from_le_bytes(*bytes); |
| 393 | } |
| 394 | } |
| 395 | |
| 396 | _ => {} |
| 397 | } |
| 398 | } |
| 399 | } |
| 400 | |
| 401 | /// Copies data from a plane into a pixel array. |
| 402 | /// |
| 403 | /// # Panics |
| 404 | /// |
| 405 | /// - If `dest_bytewidth` does not match the generic `T` of `Plane` |
| 406 | pub fn copy_to_raw_u8(&self, dest: &mut [u8], dest_stride: usize, dest_bytewidth: usize) { |
| 407 | let stride = self.cfg.stride; |
| 408 | for (self_row, dest_row) in self |
| 409 | .data_origin() |
| 410 | .chunks_exact(stride) |
| 411 | .zip(dest.chunks_exact_mut(dest_stride)) |
| 412 | { |
| 413 | match dest_bytewidth { |
| 414 | 1 => { |
| 415 | for (self_pixel, dest_pixel) in |
| 416 | self_row[..self.cfg.width].iter().zip(dest_row.iter_mut()) |
| 417 | { |
| 418 | *dest_pixel = u8::cast_from(*self_pixel); |
| 419 | } |
| 420 | } |
| 421 | 2 => { |
| 422 | assert!( |
| 423 | size_of::<T>() >= 2, |
| 424 | "dest bytewidth ( {}) cannot fit in Plane<u8>" , |
| 425 | dest_bytewidth |
| 426 | ); |
| 427 | |
| 428 | // SAFETY: we reinterpret the slice of bytes as a slice |
| 429 | // of [u8; 2] with half the elements |
| 430 | let dest_row: &mut [[u8; 2]] = unsafe { |
| 431 | std::slice::from_raw_parts_mut( |
| 432 | dest_row.as_mut_ptr().cast(), |
| 433 | dest_row.len() / 2, |
| 434 | ) |
| 435 | }; |
| 436 | |
| 437 | for (self_pixel, bytes) in self_row[..self.cfg.width].iter().zip(dest_row) { |
| 438 | *bytes = u16::cast_from(*self_pixel).to_le_bytes(); |
| 439 | } |
| 440 | } |
| 441 | |
| 442 | _ => {} |
| 443 | } |
| 444 | } |
| 445 | } |
| 446 | |
| 447 | /// Returns plane with half the resolution for width and height. |
| 448 | /// Downscaled with 2x2 box filter. |
| 449 | /// Padded to dimensions with `frame_width` and `frame_height`. |
| 450 | /// |
| 451 | /// # Panics |
| 452 | /// |
| 453 | /// - If the requested width and height are > half the input width or height |
| 454 | pub fn downsampled(&self, frame_width: usize, frame_height: usize) -> Plane<T> { |
| 455 | let src = self; |
| 456 | let mut new = Plane::new( |
| 457 | (src.cfg.width + 1) / 2, |
| 458 | (src.cfg.height + 1) / 2, |
| 459 | src.cfg.xdec + 1, |
| 460 | src.cfg.ydec + 1, |
| 461 | src.cfg.xpad / 2, |
| 462 | src.cfg.ypad / 2, |
| 463 | ); |
| 464 | |
| 465 | let width = new.cfg.width; |
| 466 | let height = new.cfg.height; |
| 467 | |
| 468 | assert!(width * 2 <= src.cfg.stride - src.cfg.xorigin); |
| 469 | assert!(height * 2 <= src.cfg.alloc_height - src.cfg.yorigin); |
| 470 | |
| 471 | let data_origin = src.data_origin(); |
| 472 | for (row_idx, dst_row) in new |
| 473 | .mut_slice(PlaneOffset::default()) |
| 474 | .rows_iter_mut() |
| 475 | .enumerate() |
| 476 | .take(height) |
| 477 | { |
| 478 | let src_top_row = &data_origin[(src.cfg.stride * row_idx * 2)..][..(2 * width)]; |
| 479 | let src_bottom_row = |
| 480 | &data_origin[(src.cfg.stride * (row_idx * 2 + 1))..][..(2 * width)]; |
| 481 | |
| 482 | for ((dst, a), b) in dst_row |
| 483 | .iter_mut() |
| 484 | .zip(src_top_row.chunks_exact(2)) |
| 485 | .zip(src_bottom_row.chunks_exact(2)) |
| 486 | { |
| 487 | let sum = u32::cast_from(a[0]) |
| 488 | + u32::cast_from(a[1]) |
| 489 | + u32::cast_from(b[0]) |
| 490 | + u32::cast_from(b[1]); |
| 491 | let avg = (sum + 2) >> 2; |
| 492 | *dst = T::cast_from(avg); |
| 493 | } |
| 494 | } |
| 495 | |
| 496 | new.pad(frame_width, frame_height); |
| 497 | new |
| 498 | } |
| 499 | |
| 500 | /// Returns a plane downscaled from the source plane by a factor of `scale` (not padded) |
| 501 | pub fn downscale<const SCALE: usize>(&self) -> Plane<T> { |
| 502 | let mut new_plane = Plane::new(self.cfg.width / SCALE, self.cfg.height / SCALE, 0, 0, 0, 0); |
| 503 | |
| 504 | self.downscale_in_place::<SCALE>(&mut new_plane); |
| 505 | |
| 506 | new_plane |
| 507 | } |
| 508 | |
| 509 | /// Downscales the source plane by a factor of `scale`, writing the result to `in_plane` (not padded) |
| 510 | /// |
| 511 | /// # Panics |
| 512 | /// |
| 513 | /// - If the current plane's width and height are not at least `SCALE` times the `in_plane`'s |
| 514 | #[cfg_attr (feature = "profiling" , profiling::function(downscale_in_place))] |
| 515 | pub fn downscale_in_place<const SCALE: usize>(&self, in_plane: &mut Plane<T>) { |
| 516 | let stride = in_plane.cfg.stride; |
| 517 | let width = in_plane.cfg.width; |
| 518 | let height = in_plane.cfg.height; |
| 519 | |
| 520 | if stride == 0 || self.cfg.stride == 0 { |
| 521 | panic!("stride cannot be 0" ); |
| 522 | } |
| 523 | |
| 524 | assert!(width * SCALE <= self.cfg.stride - self.cfg.xorigin); |
| 525 | assert!(height * SCALE <= self.cfg.alloc_height - self.cfg.yorigin); |
| 526 | |
| 527 | // SAFETY: Bounds checks have been removed for performance reasons |
| 528 | unsafe { |
| 529 | let src = self; |
| 530 | let box_pixels = SCALE * SCALE; |
| 531 | let half_box_pixels = box_pixels as u32 / 2; // Used for rounding int division |
| 532 | |
| 533 | let data_origin = src.data_origin(); |
| 534 | let plane_data_mut_slice = &mut *in_plane.data; |
| 535 | |
| 536 | // Iter dst rows |
| 537 | for row_idx in 0..height { |
| 538 | let dst_row = plane_data_mut_slice.get_unchecked_mut(row_idx * stride..); |
| 539 | // Iter dst cols |
| 540 | for (col_idx, dst) in dst_row.get_unchecked_mut(..width).iter_mut().enumerate() { |
| 541 | macro_rules! generate_inner_loop { |
| 542 | ($x:ty) => { |
| 543 | let mut sum = half_box_pixels as $x; |
| 544 | // Sum box of size scale * scale |
| 545 | |
| 546 | // Iter src row |
| 547 | for y in 0..SCALE { |
| 548 | let src_row_idx = row_idx * SCALE + y; |
| 549 | let src_row = |
| 550 | data_origin.get_unchecked((src_row_idx * src.cfg.stride)..); |
| 551 | |
| 552 | // Iter src col |
| 553 | for x in 0..SCALE { |
| 554 | let src_col_idx = col_idx * SCALE + x; |
| 555 | sum += <$x>::cast_from(*src_row.get_unchecked(src_col_idx)); |
| 556 | } |
| 557 | } |
| 558 | |
| 559 | // Box average |
| 560 | let avg = sum as usize / box_pixels; |
| 561 | *dst = T::cast_from(avg); |
| 562 | }; |
| 563 | } |
| 564 | |
| 565 | // Use 16 bit precision if overflow would not happen |
| 566 | if T::type_enum() == PixelType::U8 |
| 567 | && SCALE as u128 * SCALE as u128 * (u8::MAX as u128) |
| 568 | + half_box_pixels as u128 |
| 569 | <= u16::MAX as u128 |
| 570 | { |
| 571 | generate_inner_loop!(u16); |
| 572 | } else { |
| 573 | generate_inner_loop!(u32); |
| 574 | } |
| 575 | } |
| 576 | } |
| 577 | } |
| 578 | } |
| 579 | |
| 580 | /// Iterates over the pixels in the plane, skipping the padding. |
| 581 | pub fn iter(&self) -> PlaneIter<'_, T> { |
| 582 | PlaneIter::new(self) |
| 583 | } |
| 584 | |
| 585 | /// Iterates over the lines of the plane |
| 586 | pub fn rows_iter(&self) -> RowsIter<'_, T> { |
| 587 | RowsIter { |
| 588 | plane: self, |
| 589 | x: 0, |
| 590 | y: 0, |
| 591 | } |
| 592 | } |
| 593 | |
| 594 | pub fn rows_iter_mut(&mut self) -> RowsIterMut<'_, T> { |
| 595 | RowsIterMut { |
| 596 | plane: self as *mut Plane<T>, |
| 597 | x: 0, |
| 598 | y: 0, |
| 599 | phantom: PhantomData, |
| 600 | } |
| 601 | } |
| 602 | |
| 603 | /// Return a line |
| 604 | pub fn row(&self, y: isize) -> &[T] { |
| 605 | let range = self.row_range(0, y); |
| 606 | |
| 607 | &self.data[range] |
| 608 | } |
| 609 | } |
| 610 | |
| 611 | /// Iterator over plane pixels, skipping padding. |
| 612 | #[derive (Debug)] |
| 613 | pub struct PlaneIter<'a, T: Pixel> { |
| 614 | plane: &'a Plane<T>, |
| 615 | y: usize, |
| 616 | x: usize, |
| 617 | } |
| 618 | |
| 619 | impl<'a, T: Pixel> PlaneIter<'a, T> { |
| 620 | /// Creates a new iterator. |
| 621 | pub fn new(plane: &'a Plane<T>) -> Self { |
| 622 | Self { plane, y: 0, x: 0 } |
| 623 | } |
| 624 | |
| 625 | fn width(&self) -> usize { |
| 626 | self.plane.cfg.width |
| 627 | } |
| 628 | |
| 629 | fn height(&self) -> usize { |
| 630 | self.plane.cfg.height |
| 631 | } |
| 632 | } |
| 633 | |
| 634 | impl<'a, T: Pixel> Iterator for PlaneIter<'a, T> { |
| 635 | type Item = T; |
| 636 | |
| 637 | fn next(&mut self) -> Option<<Self as Iterator>::Item> { |
| 638 | if self.y == self.height() { |
| 639 | return None; |
| 640 | } |
| 641 | let pixel: T = self.plane.p(self.x, self.y); |
| 642 | if self.x == self.width() - 1 { |
| 643 | self.x = 0; |
| 644 | self.y += 1; |
| 645 | } else { |
| 646 | self.x += 1; |
| 647 | } |
| 648 | Some(pixel) |
| 649 | } |
| 650 | } |
| 651 | |
| 652 | impl<T: Pixel> FusedIterator for PlaneIter<'_, T> {} |
| 653 | |
| 654 | // A Plane, PlaneSlice, or PlaneRegion is assumed to include or be able to include |
| 655 | // padding on the edge of the frame |
| 656 | #[derive (Clone, Copy, Debug)] |
| 657 | pub struct PlaneSlice<'a, T: Pixel> { |
| 658 | pub plane: &'a Plane<T>, |
| 659 | pub x: isize, |
| 660 | pub y: isize, |
| 661 | } |
| 662 | |
| 663 | // A RowsIter or RowsIterMut is assumed to crop the padding from the frame edges |
| 664 | pub struct RowsIter<'a, T: Pixel> { |
| 665 | plane: &'a Plane<T>, |
| 666 | x: isize, |
| 667 | y: isize, |
| 668 | } |
| 669 | |
| 670 | impl<'a, T: Pixel> Iterator for RowsIter<'a, T> { |
| 671 | type Item = &'a [T]; |
| 672 | |
| 673 | fn next(&mut self) -> Option<Self::Item> { |
| 674 | if self.plane.cfg.height as isize > self.y { |
| 675 | // cannot directly return self.ps.row(row) due to lifetime issue |
| 676 | let range: Range = self.plane.row_range_cropped(self.x, self.y); |
| 677 | self.y += 1; |
| 678 | Some(&self.plane.data[range]) |
| 679 | } else { |
| 680 | None |
| 681 | } |
| 682 | } |
| 683 | |
| 684 | fn size_hint(&self) -> (usize, Option<usize>) { |
| 685 | let remaining: isize = self.plane.cfg.height as isize - self.y; |
| 686 | debug_assert!(remaining >= 0); |
| 687 | let remaining: usize = remaining as usize; |
| 688 | |
| 689 | (remaining, Some(remaining)) |
| 690 | } |
| 691 | } |
| 692 | |
| 693 | impl<'a, T: Pixel> ExactSizeIterator for RowsIter<'a, T> {} |
| 694 | impl<'a, T: Pixel> FusedIterator for RowsIter<'a, T> {} |
| 695 | |
| 696 | impl<'a, T: Pixel> PlaneSlice<'a, T> { |
| 697 | #[allow (unused)] |
| 698 | pub fn as_ptr(&self) -> *const T { |
| 699 | self[0].as_ptr() |
| 700 | } |
| 701 | |
| 702 | pub fn rows_iter(&self) -> RowsIter<'_, T> { |
| 703 | RowsIter { |
| 704 | plane: self.plane, |
| 705 | x: self.x, |
| 706 | y: self.y, |
| 707 | } |
| 708 | } |
| 709 | |
| 710 | pub fn clamp(&self) -> PlaneSlice<'a, T> { |
| 711 | PlaneSlice { |
| 712 | plane: self.plane, |
| 713 | x: self.x.clamp( |
| 714 | -(self.plane.cfg.xorigin as isize), |
| 715 | self.plane.cfg.width as isize, |
| 716 | ), |
| 717 | y: self.y.clamp( |
| 718 | -(self.plane.cfg.yorigin as isize), |
| 719 | self.plane.cfg.height as isize, |
| 720 | ), |
| 721 | } |
| 722 | } |
| 723 | |
| 724 | pub fn subslice(&self, xo: usize, yo: usize) -> PlaneSlice<'a, T> { |
| 725 | PlaneSlice { |
| 726 | plane: self.plane, |
| 727 | x: self.x + xo as isize, |
| 728 | y: self.y + yo as isize, |
| 729 | } |
| 730 | } |
| 731 | |
| 732 | pub fn reslice(&self, xo: isize, yo: isize) -> PlaneSlice<'a, T> { |
| 733 | PlaneSlice { |
| 734 | plane: self.plane, |
| 735 | x: self.x + xo, |
| 736 | y: self.y + yo, |
| 737 | } |
| 738 | } |
| 739 | |
| 740 | /// A slice starting i pixels above the current one. |
| 741 | pub fn go_up(&self, i: usize) -> PlaneSlice<'a, T> { |
| 742 | PlaneSlice { |
| 743 | plane: self.plane, |
| 744 | x: self.x, |
| 745 | y: self.y - i as isize, |
| 746 | } |
| 747 | } |
| 748 | |
| 749 | /// A slice starting i pixels to the left of the current one. |
| 750 | pub fn go_left(&self, i: usize) -> PlaneSlice<'a, T> { |
| 751 | PlaneSlice { |
| 752 | plane: self.plane, |
| 753 | x: self.x - i as isize, |
| 754 | y: self.y, |
| 755 | } |
| 756 | } |
| 757 | |
| 758 | pub fn p(&self, add_x: usize, add_y: usize) -> T { |
| 759 | let new_y = (self.y + add_y as isize + self.plane.cfg.yorigin as isize) as usize; |
| 760 | let new_x = (self.x + add_x as isize + self.plane.cfg.xorigin as isize) as usize; |
| 761 | self.plane.data[new_y * self.plane.cfg.stride + new_x] |
| 762 | } |
| 763 | |
| 764 | /// Checks if `add_y` and `add_x` lies in the allocated bounds of the |
| 765 | /// underlying plane. |
| 766 | pub fn accessible(&self, add_x: usize, add_y: usize) -> bool { |
| 767 | let y = (self.y + add_y as isize + self.plane.cfg.yorigin as isize) as usize; |
| 768 | let x = (self.x + add_x as isize + self.plane.cfg.xorigin as isize) as usize; |
| 769 | y < self.plane.cfg.alloc_height && x < self.plane.cfg.stride |
| 770 | } |
| 771 | |
| 772 | /// Checks if -`sub_x` and -`sub_y` lies in the allocated bounds of the |
| 773 | /// underlying plane. |
| 774 | pub fn accessible_neg(&self, sub_x: usize, sub_y: usize) -> bool { |
| 775 | let y = self.y - sub_y as isize + self.plane.cfg.yorigin as isize; |
| 776 | let x = self.x - sub_x as isize + self.plane.cfg.xorigin as isize; |
| 777 | y >= 0 && x >= 0 |
| 778 | } |
| 779 | |
| 780 | /// This version of the function crops off the padding on the right side of the image |
| 781 | pub fn row_cropped(&self, y: usize) -> &[T] { |
| 782 | let y = (self.y + y as isize + self.plane.cfg.yorigin as isize) as usize; |
| 783 | let x = (self.x + self.plane.cfg.xorigin as isize) as usize; |
| 784 | let start = y * self.plane.cfg.stride + x; |
| 785 | let width = (self.plane.cfg.width as isize - self.x) as usize; |
| 786 | &self.plane.data[start..start + width] |
| 787 | } |
| 788 | |
| 789 | /// This version of the function includes the padding on the right side of the image |
| 790 | pub fn row(&self, y: usize) -> &[T] { |
| 791 | let y = (self.y + y as isize + self.plane.cfg.yorigin as isize) as usize; |
| 792 | let x = (self.x + self.plane.cfg.xorigin as isize) as usize; |
| 793 | let start = y * self.plane.cfg.stride + x; |
| 794 | let width = self.plane.cfg.stride - x; |
| 795 | &self.plane.data[start..start + width] |
| 796 | } |
| 797 | } |
| 798 | |
| 799 | impl<'a, T: Pixel> Index<usize> for PlaneSlice<'a, T> { |
| 800 | type Output = [T]; |
| 801 | fn index(&self, index: usize) -> &Self::Output { |
| 802 | let range: Range = self.plane.row_range(self.x, self.y + index as isize); |
| 803 | &self.plane.data[range] |
| 804 | } |
| 805 | } |
| 806 | |
| 807 | pub struct PlaneMutSlice<'a, T: Pixel> { |
| 808 | pub plane: &'a mut Plane<T>, |
| 809 | pub x: isize, |
| 810 | pub y: isize, |
| 811 | } |
| 812 | |
| 813 | pub struct RowsIterMut<'a, T: Pixel> { |
| 814 | plane: *mut Plane<T>, |
| 815 | x: isize, |
| 816 | y: isize, |
| 817 | phantom: PhantomData<&'a mut Plane<T>>, |
| 818 | } |
| 819 | |
| 820 | impl<'a, T: Pixel> Iterator for RowsIterMut<'a, T> { |
| 821 | type Item = &'a mut [T]; |
| 822 | |
| 823 | fn next(&mut self) -> Option<Self::Item> { |
| 824 | // SAFETY: there could not be a concurrent call using a mutable reference to the plane |
| 825 | let plane = unsafe { &mut *self.plane }; |
| 826 | if plane.cfg.height as isize > self.y { |
| 827 | // cannot directly return self.ps.row(row) due to lifetime issue |
| 828 | let range = plane.row_range_cropped(self.x, self.y); |
| 829 | self.y += 1; |
| 830 | Some(&mut plane.data[range]) |
| 831 | } else { |
| 832 | None |
| 833 | } |
| 834 | } |
| 835 | |
| 836 | fn size_hint(&self) -> (usize, Option<usize>) { |
| 837 | // SAFETY: there could not be a concurrent call using a mutable reference to the plane |
| 838 | let plane = unsafe { &mut *self.plane }; |
| 839 | let remaining = plane.cfg.height as isize - self.y; |
| 840 | debug_assert!(remaining >= 0); |
| 841 | let remaining = remaining as usize; |
| 842 | |
| 843 | (remaining, Some(remaining)) |
| 844 | } |
| 845 | } |
| 846 | |
| 847 | impl<'a, T: Pixel> ExactSizeIterator for RowsIterMut<'a, T> {} |
| 848 | impl<'a, T: Pixel> FusedIterator for RowsIterMut<'a, T> {} |
| 849 | |
| 850 | impl<'a, T: Pixel> PlaneMutSlice<'a, T> { |
| 851 | #[allow (unused)] |
| 852 | pub fn rows_iter(&self) -> RowsIter<'_, T> { |
| 853 | RowsIter { |
| 854 | plane: self.plane, |
| 855 | x: self.x, |
| 856 | y: self.y, |
| 857 | } |
| 858 | } |
| 859 | |
| 860 | pub fn rows_iter_mut(&mut self) -> RowsIterMut<'_, T> { |
| 861 | RowsIterMut { |
| 862 | plane: self.plane as *mut Plane<T>, |
| 863 | x: self.x, |
| 864 | y: self.y, |
| 865 | phantom: PhantomData, |
| 866 | } |
| 867 | } |
| 868 | |
| 869 | #[allow (unused)] |
| 870 | pub fn subslice(&mut self, xo: usize, yo: usize) -> PlaneMutSlice<'_, T> { |
| 871 | PlaneMutSlice { |
| 872 | plane: self.plane, |
| 873 | x: self.x + xo as isize, |
| 874 | y: self.y + yo as isize, |
| 875 | } |
| 876 | } |
| 877 | } |
| 878 | |
| 879 | impl<'a, T: Pixel> Index<usize> for PlaneMutSlice<'a, T> { |
| 880 | type Output = [T]; |
| 881 | fn index(&self, index: usize) -> &Self::Output { |
| 882 | let range: Range = self.plane.row_range(self.x, self.y + index as isize); |
| 883 | &self.plane.data[range] |
| 884 | } |
| 885 | } |
| 886 | |
| 887 | impl<'a, T: Pixel> IndexMut<usize> for PlaneMutSlice<'a, T> { |
| 888 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { |
| 889 | let range: Range = self.plane.row_range(self.x, self.y + index as isize); |
| 890 | &mut self.plane.data[range] |
| 891 | } |
| 892 | } |
| 893 | |
| 894 | #[cfg (test)] |
| 895 | pub mod test { |
| 896 | use super::*; |
| 897 | |
| 898 | #[cfg (all(target_arch = "wasm32" , target_os = "unknown" ))] |
| 899 | use wasm_bindgen_test::*; |
| 900 | |
| 901 | #[cfg (all(target_arch = "wasm32" , target_os = "unknown" ))] |
| 902 | wasm_bindgen_test_configure!(run_in_browser); |
| 903 | |
| 904 | #[cfg_attr (all(target_arch = "wasm32" , target_os = "unknown" ), wasm_bindgen_test)] |
| 905 | #[test ] |
| 906 | fn copy_from_raw_u8() { |
| 907 | #[rustfmt::skip] |
| 908 | let mut plane = Plane::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, |
| 909 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 910 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 911 | 0, 0, 1, 2, 3, 4, 0, 0, |
| 912 | 0, 0, 8, 7, 6, 5, 0, 0, |
| 913 | 0, 0, 9, 8, 7, 6, 0, 0, |
| 914 | 0, 0, 2, 3, 4, 5, 0, 0, |
| 915 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 916 | 0, 0, 0, 0, 0, 0, 0, 0], |
| 917 | 8, |
| 918 | ); |
| 919 | |
| 920 | let input = vec![42u8; 64]; |
| 921 | |
| 922 | plane.copy_from_raw_u8(&input, 8, 1); |
| 923 | |
| 924 | println!("{:?}" , &plane.data[..10]); |
| 925 | |
| 926 | assert_eq!(&input[..64], &plane.data[..64]); |
| 927 | } |
| 928 | |
| 929 | #[cfg_attr (all(target_arch = "wasm32" , target_os = "unknown" ), wasm_bindgen_test)] |
| 930 | #[test ] |
| 931 | fn copy_to_raw_u8() { |
| 932 | #[rustfmt::skip] |
| 933 | let plane = Plane::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, |
| 934 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 935 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 936 | 0, 0, 1, 2, 3, 4, 0, 0, |
| 937 | 0, 0, 8, 7, 6, 5, 0, 0, |
| 938 | 0, 0, 9, 8, 7, 6, 0, 0, |
| 939 | 0, 0, 2, 3, 4, 5, 0, 0, |
| 940 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 941 | 0, 0, 0, 0, 0, 0, 0, 0], |
| 942 | 8, |
| 943 | ); |
| 944 | |
| 945 | let mut output = vec![42u8; 64]; |
| 946 | |
| 947 | plane.copy_to_raw_u8(&mut output, 8, 1); |
| 948 | |
| 949 | println!("{:?}" , &plane.data[..10]); |
| 950 | |
| 951 | assert_eq!(&output[..64], &plane.data[..64]); |
| 952 | } |
| 953 | |
| 954 | #[cfg_attr (all(target_arch = "wasm32" , target_os = "unknown" ), wasm_bindgen_test)] |
| 955 | #[test ] |
| 956 | fn test_plane_downsample() { |
| 957 | #[rustfmt::skip] |
| 958 | let plane = Plane::<u8> { |
| 959 | data: PlaneData::from_slice(&[ |
| 960 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 961 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 962 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 963 | 0, 0, 1, 2, 3, 4, 0, 0, |
| 964 | 0, 0, 8, 7, 6, 5, 0, 0, |
| 965 | 0, 0, 9, 8, 7, 6, 0, 0, |
| 966 | 0, 0, 2, 3, 4, 5, 0, 0, |
| 967 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 968 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 969 | ]), |
| 970 | cfg: PlaneConfig { |
| 971 | stride: 8, |
| 972 | alloc_height: 9, |
| 973 | width: 4, |
| 974 | height: 4, |
| 975 | xdec: 0, |
| 976 | ydec: 0, |
| 977 | xpad: 0, |
| 978 | ypad: 0, |
| 979 | xorigin: 2, |
| 980 | yorigin: 3, |
| 981 | }, |
| 982 | }; |
| 983 | let downsampled = plane.downsampled(4, 4); |
| 984 | |
| 985 | #[rustfmt::skip] |
| 986 | let expected = &[ |
| 987 | 5, 5, |
| 988 | 6, 6, |
| 989 | ]; |
| 990 | |
| 991 | let v: Vec<_> = downsampled.iter().collect(); |
| 992 | |
| 993 | assert_eq!(&expected[..], &v[..]); |
| 994 | } |
| 995 | |
| 996 | #[cfg_attr (all(target_arch = "wasm32" , target_os = "unknown" ), wasm_bindgen_test)] |
| 997 | #[test ] |
| 998 | fn test_plane_downsample_odd() { |
| 999 | #[rustfmt::skip] |
| 1000 | let plane = Plane::<u8> { |
| 1001 | data: PlaneData::from_slice(&[ |
| 1002 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1003 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1004 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1005 | 0, 0, 1, 2, 3, 4, 0, 0, |
| 1006 | 0, 0, 8, 7, 6, 5, 0, 0, |
| 1007 | 0, 0, 9, 8, 7, 6, 0, 0, |
| 1008 | 0, 0, 2, 3, 4, 5, 0, 0, |
| 1009 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1010 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1011 | ]), |
| 1012 | cfg: PlaneConfig { |
| 1013 | stride: 8, |
| 1014 | alloc_height: 9, |
| 1015 | width: 3, |
| 1016 | height: 3, |
| 1017 | xdec: 0, |
| 1018 | ydec: 0, |
| 1019 | xpad: 0, |
| 1020 | ypad: 0, |
| 1021 | xorigin: 2, |
| 1022 | yorigin: 3, |
| 1023 | }, |
| 1024 | }; |
| 1025 | let downsampled = plane.downsampled(3, 3); |
| 1026 | |
| 1027 | #[rustfmt::skip] |
| 1028 | let expected = &[ |
| 1029 | 5, 5, |
| 1030 | 6, 6, |
| 1031 | ]; |
| 1032 | |
| 1033 | let v: Vec<_> = downsampled.iter().collect(); |
| 1034 | assert_eq!(&expected[..], &v[..]); |
| 1035 | } |
| 1036 | |
| 1037 | #[cfg_attr (all(target_arch = "wasm32" , target_os = "unknown" ), wasm_bindgen_test)] |
| 1038 | #[test ] |
| 1039 | fn test_plane_downscale() { |
| 1040 | #[rustfmt::skip] |
| 1041 | let plane = Plane::<u8> { |
| 1042 | data: PlaneData::from_slice(&[ |
| 1043 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1044 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1045 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1046 | 0, 0, 0, 1, 4, 5, 0, 0, |
| 1047 | 0, 0, 2, 3, 6, 7, 0, 0, |
| 1048 | 0, 0, 8, 9, 7, 5, 0, 0, |
| 1049 | 0, 0, 9, 8, 3, 1, 0, 0, |
| 1050 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1051 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1052 | ]), |
| 1053 | cfg: PlaneConfig { |
| 1054 | stride: 8, |
| 1055 | alloc_height: 9, |
| 1056 | width: 4, |
| 1057 | height: 4, |
| 1058 | xdec: 0, |
| 1059 | ydec: 0, |
| 1060 | xpad: 0, |
| 1061 | ypad: 0, |
| 1062 | xorigin: 2, |
| 1063 | yorigin: 3, |
| 1064 | }, |
| 1065 | }; |
| 1066 | let downscaled = plane.downscale::<2>(); |
| 1067 | |
| 1068 | #[rustfmt::skip] |
| 1069 | let expected = &[ |
| 1070 | 2, 6, |
| 1071 | 9, 4 |
| 1072 | ]; |
| 1073 | |
| 1074 | let v: Vec<_> = downscaled.iter().collect(); |
| 1075 | assert_eq!(&expected[..], &v[..]); |
| 1076 | } |
| 1077 | |
| 1078 | #[cfg_attr (all(target_arch = "wasm32" , target_os = "unknown" ), wasm_bindgen_test)] |
| 1079 | #[test ] |
| 1080 | fn test_plane_downscale_odd() { |
| 1081 | #[rustfmt::skip] |
| 1082 | let plane = Plane::<u8> { |
| 1083 | data: PlaneData::from_slice(&[ |
| 1084 | 1, 2, 3, 4, 1, 2, 3, 4, |
| 1085 | 0, 0, 8, 7, 6, 5, 8, 7, |
| 1086 | 6, 5, 8, 7, 6, 5, 8, 7, |
| 1087 | 6, 5, 8, 7, 0, 0, 2, 3, |
| 1088 | 4, 5, 0, 0, 9, 8, 7, 6, |
| 1089 | 0, 0, 0, 0, 2, 3, 4, 5, |
| 1090 | 0, 0, 0, 0, 2, 3, 4, 5, |
| 1091 | ]), |
| 1092 | cfg: PlaneConfig { |
| 1093 | stride: 8, |
| 1094 | alloc_height: 7, |
| 1095 | width: 8, |
| 1096 | height: 7, |
| 1097 | xdec: 0, |
| 1098 | ydec: 0, |
| 1099 | xpad: 0, |
| 1100 | ypad: 0, |
| 1101 | xorigin: 0, |
| 1102 | yorigin: 0, |
| 1103 | }, |
| 1104 | }; |
| 1105 | |
| 1106 | let downscaled = plane.downscale::<3>(); |
| 1107 | |
| 1108 | #[rustfmt::skip] |
| 1109 | let expected = &[ |
| 1110 | 4, 5, |
| 1111 | 3, 3 |
| 1112 | ]; |
| 1113 | |
| 1114 | let v: Vec<_> = downscaled.iter().collect(); |
| 1115 | assert_eq!(&expected[..], &v[..]); |
| 1116 | } |
| 1117 | |
| 1118 | #[cfg_attr (all(target_arch = "wasm32" , target_os = "unknown" ), wasm_bindgen_test)] |
| 1119 | #[test ] |
| 1120 | fn test_plane_downscale_odd_2() { |
| 1121 | #[rustfmt::skip] |
| 1122 | let plane = Plane::<u8> { |
| 1123 | data: PlaneData::from_slice(&[ |
| 1124 | 9, 8, 3, 1, 0, 1, 4, 5, 0, 0, |
| 1125 | 0, 1, 4, 5, 0, 0, 0, 0, 0, 0, |
| 1126 | 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, |
| 1127 | 0, 2, 3, 6, 7, 0, 0, 0, 0, 0, |
| 1128 | 0, 0, 8, 9, 7, 5, 0, 0, 0, 0, |
| 1129 | 9, 8, 3, 1, 0, 1, 4, 5, 0, 0, |
| 1130 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 1131 | 0, 0, 0, 0, 0, 2, 3, 6, 7, 0, |
| 1132 | 0, 0, 0, 0, 0, 0, 8, 9, 7, 5, |
| 1133 | 0, 0, 0, 0, 9, 8, 3, 1, 0, 0 |
| 1134 | ]), |
| 1135 | cfg: PlaneConfig { |
| 1136 | stride: 10, |
| 1137 | alloc_height: 10, |
| 1138 | width: 10, |
| 1139 | height: 10, |
| 1140 | xdec: 0, |
| 1141 | ydec: 0, |
| 1142 | xpad: 0, |
| 1143 | ypad: 0, |
| 1144 | xorigin: 0, |
| 1145 | yorigin: 0, |
| 1146 | }, |
| 1147 | }; |
| 1148 | let downscaled = plane.downscale::<3>(); |
| 1149 | |
| 1150 | #[rustfmt::skip] |
| 1151 | let expected = &[ |
| 1152 | 3, 1, 2, |
| 1153 | 4, 4, 1, |
| 1154 | 0, 0, 4, |
| 1155 | ]; |
| 1156 | |
| 1157 | let v: Vec<_> = downscaled.iter().collect(); |
| 1158 | assert_eq!(&expected[..], &v[..]); |
| 1159 | } |
| 1160 | |
| 1161 | #[cfg_attr (all(target_arch = "wasm32" , target_os = "unknown" ), wasm_bindgen_test)] |
| 1162 | #[test ] |
| 1163 | fn test_plane_pad() { |
| 1164 | #[rustfmt::skip] |
| 1165 | let mut plane = Plane::<u8> { |
| 1166 | data: PlaneData::from_slice(&[ |
| 1167 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1168 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1169 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1170 | 0, 0, 1, 2, 3, 4, 0, 0, |
| 1171 | 0, 0, 8, 7, 6, 5, 0, 0, |
| 1172 | 0, 0, 9, 8, 7, 6, 0, 0, |
| 1173 | 0, 0, 2, 3, 4, 5, 0, 0, |
| 1174 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1175 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1176 | ]), |
| 1177 | cfg: PlaneConfig { |
| 1178 | stride: 8, |
| 1179 | alloc_height: 9, |
| 1180 | width: 4, |
| 1181 | height: 4, |
| 1182 | xdec: 0, |
| 1183 | ydec: 0, |
| 1184 | xpad: 0, |
| 1185 | ypad: 0, |
| 1186 | xorigin: 2, |
| 1187 | yorigin: 3, |
| 1188 | }, |
| 1189 | }; |
| 1190 | plane.pad(4, 4); |
| 1191 | |
| 1192 | #[rustfmt::skip] |
| 1193 | assert_eq!( |
| 1194 | &[ |
| 1195 | 1, 1, 1, 2, 3, 4, 4, 4, |
| 1196 | 1, 1, 1, 2, 3, 4, 4, 4, |
| 1197 | 1, 1, 1, 2, 3, 4, 4, 4, |
| 1198 | 1, 1, 1, 2, 3, 4, 4, 4, |
| 1199 | 8, 8, 8, 7, 6, 5, 5, 5, |
| 1200 | 9, 9, 9, 8, 7, 6, 6, 6, |
| 1201 | 2, 2, 2, 3, 4, 5, 5, 5, |
| 1202 | 2, 2, 2, 3, 4, 5, 5, 5, |
| 1203 | 2, 2, 2, 3, 4, 5, 5, 5, |
| 1204 | ][..], |
| 1205 | &plane.data[..] |
| 1206 | ); |
| 1207 | } |
| 1208 | |
| 1209 | #[cfg_attr (all(target_arch = "wasm32" , target_os = "unknown" ), wasm_bindgen_test)] |
| 1210 | #[test ] |
| 1211 | fn test_pixel_iterator() { |
| 1212 | #[rustfmt::skip] |
| 1213 | let plane = Plane::<u8> { |
| 1214 | data: PlaneData::from_slice(&[ |
| 1215 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1216 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1217 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1218 | 0, 0, 1, 2, 3, 4, 0, 0, |
| 1219 | 0, 0, 8, 7, 6, 5, 0, 0, |
| 1220 | 0, 0, 9, 8, 7, 6, 0, 0, |
| 1221 | 0, 0, 2, 3, 4, 5, 0, 0, |
| 1222 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1223 | 0, 0, 0, 0, 0, 0, 0, 0, |
| 1224 | ]), |
| 1225 | cfg: PlaneConfig { |
| 1226 | stride: 8, |
| 1227 | alloc_height: 9, |
| 1228 | width: 4, |
| 1229 | height: 4, |
| 1230 | xdec: 0, |
| 1231 | ydec: 0, |
| 1232 | xpad: 0, |
| 1233 | ypad: 0, |
| 1234 | xorigin: 2, |
| 1235 | yorigin: 3, |
| 1236 | }, |
| 1237 | }; |
| 1238 | |
| 1239 | let pixels: Vec<u8> = plane.iter().collect(); |
| 1240 | |
| 1241 | assert_eq!( |
| 1242 | &[1, 2, 3, 4, 8, 7, 6, 5, 9, 8, 7, 6, 2, 3, 4, 5,][..], |
| 1243 | &pixels[..] |
| 1244 | ); |
| 1245 | } |
| 1246 | } |
| 1247 | |