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
10use std::fmt::{Debug, Display, Formatter};
11use std::iter::{self, FusedIterator};
12use std::marker::PhantomData;
13use std::mem::size_of;
14use std::ops::{Index, IndexMut, Range};
15
16use aligned_vec::{ABox, AVec, ConstAlign};
17
18use crate::math::*;
19use crate::pixel::*;
20
21#[cfg(feature = "serialize")]
22use serde::{Deserialize, Serialize};
23
24/// Plane-specific configuration.
25#[derive(Debug, Clone, PartialEq, Eq)]
26#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
27pub 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
54impl 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)]
91pub 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))]
102pub 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
109unsafe impl<T: Pixel + Send> Send for PlaneData<T> {}
110unsafe impl<T: Pixel + Sync> Sync for PlaneData<T> {}
111
112impl<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
120impl<T: Pixel> std::ops::DerefMut for PlaneData<T> {
121 fn deref_mut(&mut self) -> &mut [T] {
122 self.data.as_mut()
123 }
124}
125
126impl<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))]
155pub 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
164impl<T: Pixel> Debug for Plane<T>
165where
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
177impl<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)]
613pub struct PlaneIter<'a, T: Pixel> {
614 plane: &'a Plane<T>,
615 y: usize,
616 x: usize,
617}
618
619impl<'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
634impl<'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
652impl<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)]
657pub 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
664pub struct RowsIter<'a, T: Pixel> {
665 plane: &'a Plane<T>,
666 x: isize,
667 y: isize,
668}
669
670impl<'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
693impl<'a, T: Pixel> ExactSizeIterator for RowsIter<'a, T> {}
694impl<'a, T: Pixel> FusedIterator for RowsIter<'a, T> {}
695
696impl<'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
799impl<'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
807pub struct PlaneMutSlice<'a, T: Pixel> {
808 pub plane: &'a mut Plane<T>,
809 pub x: isize,
810 pub y: isize,
811}
812
813pub 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
820impl<'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
847impl<'a, T: Pixel> ExactSizeIterator for RowsIterMut<'a, T> {}
848impl<'a, T: Pixel> FusedIterator for RowsIterMut<'a, T> {}
849
850impl<'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
879impl<'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
887impl<'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)]
895pub 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