v_frame/
plane.rs

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: AVec::from_iter(
136                Self::DATA_ALIGNMENT,
137                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, 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<T: Pixel> Iterator for PlaneIter<'_, 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 = 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 = 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 = self.plane.cfg.height as isize - self.y;
686        debug_assert!(remaining >= 0);
687        let remaining = remaining as usize;
688
689        (remaining, Some(remaining))
690    }
691}
692
693impl<T: Pixel> ExactSizeIterator for RowsIter<'_, T> {}
694impl<T: Pixel> FusedIterator for RowsIter<'_, 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<T: Pixel> Index<usize> for PlaneSlice<'_, T> {
800    type Output = [T];
801    fn index(&self, index: usize) -> &Self::Output {
802        let 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<T: Pixel> ExactSizeIterator for RowsIterMut<'_, T> {}
848impl<T: Pixel> FusedIterator for RowsIterMut<'_, T> {}
849
850impl<T: Pixel> PlaneMutSlice<'_, 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<T: Pixel> Index<usize> for PlaneMutSlice<'_, T> {
880    type Output = [T];
881    fn index(&self, index: usize) -> &Self::Output {
882        let range = self.plane.row_range(self.x, self.y + index as isize);
883        &self.plane.data[range]
884    }
885}
886
887impl<T: Pixel> IndexMut<usize> for PlaneMutSlice<'_, T> {
888    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
889        let 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(&[
909            0, 0, 0, 0, 0, 0, 0, 0,
910            0, 0, 0, 0, 0, 0, 0, 0,
911            0, 0, 0, 0, 0, 0, 0, 0,
912            0, 0, 1, 2, 3, 4, 0, 0,
913            0, 0, 8, 7, 6, 5, 0, 0,
914            0, 0, 9, 8, 7, 6, 0, 0,
915            0, 0, 2, 3, 4, 5, 0, 0,
916            0, 0, 0, 0, 0, 0, 0, 0,
917            0, 0, 0, 0, 0, 0, 0, 0,
918        ], 8);
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(&[
934            0, 0, 0, 0, 0, 0, 0, 0,
935            0, 0, 0, 0, 0, 0, 0, 0,
936            0, 0, 0, 0, 0, 0, 0, 0,
937            0, 0, 1, 2, 3, 4, 0, 0,
938            0, 0, 8, 7, 6, 5, 0, 0,
939            0, 0, 9, 8, 7, 6, 0, 0,
940            0, 0, 2, 3, 4, 5, 0, 0,
941            0, 0, 0, 0, 0, 0, 0, 0,
942            0, 0, 0, 0, 0, 0, 0, 0,
943        ], 8);
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            1, 1, 1, 2, 3, 4, 4, 4,
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            8, 8, 8, 7, 6, 5, 5, 5,
1199            9, 9, 9, 8, 7, 6, 6, 6,
1200            2, 2, 2, 3, 4, 5, 5, 5,
1201            2, 2, 2, 3, 4, 5, 5, 5,
1202            2, 2, 2, 3, 4, 5, 5, 5,
1203        ], &plane.data[..]);
1204    }
1205
1206    #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
1207    #[test]
1208    fn test_pixel_iterator() {
1209        #[rustfmt::skip]
1210        let plane = Plane::<u8> {
1211            data: PlaneData::from_slice(&[
1212                0, 0, 0, 0, 0, 0, 0, 0,
1213                0, 0, 0, 0, 0, 0, 0, 0,
1214                0, 0, 0, 0, 0, 0, 0, 0,
1215                0, 0, 1, 2, 3, 4, 0, 0,
1216                0, 0, 8, 7, 6, 5, 0, 0,
1217                0, 0, 9, 8, 7, 6, 0, 0,
1218                0, 0, 2, 3, 4, 5, 0, 0,
1219                0, 0, 0, 0, 0, 0, 0, 0,
1220                0, 0, 0, 0, 0, 0, 0, 0,
1221            ]),
1222            cfg: PlaneConfig {
1223                stride: 8,
1224                alloc_height: 9,
1225                width: 4,
1226                height: 4,
1227                xdec: 0,
1228                ydec: 0,
1229                xpad: 0,
1230                ypad: 0,
1231                xorigin: 2,
1232                yorigin: 3,
1233            },
1234        };
1235
1236        let pixels: Vec<u8> = plane.iter().collect();
1237
1238        assert_eq!(
1239            &[1, 2, 3, 4, 8, 7, 6, 5, 9, 8, 7, 6, 2, 3, 4, 5,][..],
1240            &pixels[..]
1241        );
1242    }
1243}