Skip to main content

av_scenechange/data/
plane.rs

1use std::{
2    iter::FusedIterator,
3    marker::PhantomData,
4    ops::{Index, IndexMut},
5    slice,
6};
7
8use v_frame::{
9    pixel::Pixel,
10    plane::{Plane, PlaneConfig, PlaneOffset},
11};
12
13use super::block::{BlockOffset, BLOCK_TO_PLANE_SHIFT};
14
15/// Bounded region of a plane
16///
17/// This allows giving access to a rectangular area of a plane without
18/// giving access to the whole plane.
19#[derive(Debug)]
20pub struct PlaneRegion<'a, T: Pixel> {
21    data: *const T, // points to (plane_cfg.x, plane_cfg.y)
22    pub plane_cfg: &'a PlaneConfig,
23    // private to guarantee borrowing rules
24    rect: Rect,
25    phantom: PhantomData<&'a T>,
26}
27
28/// Mutable bounded region of a plane
29///
30/// This allows to give mutable access to a rectangular area of the plane
31/// without giving access to the whole plane.
32#[derive(Debug)]
33pub struct PlaneRegionMut<'a, T: Pixel> {
34    data: *mut T, // points to (plane_cfg.x, plane_cfg.y)
35    pub plane_cfg: &'a PlaneConfig,
36    rect: Rect,
37    phantom: PhantomData<&'a mut T>,
38}
39
40// common impl for PlaneRegion and PlaneRegionMut
41macro_rules! plane_region_common {
42  // $name: PlaneRegion or PlaneRegionMut
43  // $as_ptr: as_ptr or as_mut_ptr
44  // $opt_mut: nothing or mut
45  ($name:ident, $as_ptr:ident $(,$opt_mut:tt)?) => {
46    impl<'a, T: Pixel> $name<'a, T> {
47      #[cold]
48      pub fn empty(plane_cfg : &'a PlaneConfig) -> Self {
49        return Self {
50          // SAFETY: This is actually pretty unsafe.
51          // This means we need to ensure that no other method on this struct
52          // can access data if the dimensions are 0.
53          data: std::ptr::null_mut::<T>(),
54          plane_cfg,
55          rect: Rect::default(),
56          phantom: PhantomData,
57        }
58      }
59
60      /// # Panics
61      ///
62      /// - If the configured dimensions are invalid
63      pub fn from_slice(data: &'a $($opt_mut)? [T], cfg: &'a PlaneConfig, rect: Rect) -> Self {
64        if cfg.width == 0 || cfg.height == 0 {
65          return Self::empty(&cfg);
66        }
67        assert!(rect.x >= -(cfg.xorigin as isize));
68        assert!(rect.y >= -(cfg.yorigin as isize));
69        assert!(cfg.xorigin as isize + rect.x + rect.width as isize <= cfg.stride as isize);
70        assert!(cfg.yorigin as isize + rect.y + rect.height as isize <= cfg.alloc_height as isize);
71
72        // SAFETY: The above asserts ensure we do not go OOB.
73        unsafe { Self::from_slice_unsafe(data, cfg, rect)}
74      }
75
76      unsafe fn from_slice_unsafe(data: &'a $($opt_mut)? [T], cfg: &'a PlaneConfig, rect: Rect) -> Self {
77        let origin = (cfg.yorigin as isize + rect.y) * cfg.stride as isize + cfg.xorigin as isize + rect.x;
78        Self {
79          data: data.$as_ptr().offset(origin),
80          plane_cfg: cfg,
81          rect,
82          phantom: PhantomData,
83        }
84      }
85
86      pub fn new(plane: &'a $($opt_mut)? Plane<T>, rect: Rect) -> Self {
87        Self::from_slice(& $($opt_mut)? plane.data, &plane.cfg, rect)
88      }
89
90      #[allow(dead_code)]
91      pub fn new_from_plane(plane: &'a $($opt_mut)? Plane<T>) -> Self {
92        let rect = Rect {
93            x: 0,
94            y: 0,
95            width: plane.cfg.stride - plane.cfg.xorigin,
96            height: plane.cfg.alloc_height - plane.cfg.yorigin,
97        };
98
99        // SAFETY: Area::StartingAt{}.to_rect is guaranteed to be the entire plane
100        unsafe { Self::from_slice_unsafe(& $($opt_mut)? plane.data, &plane.cfg, rect) }
101      }
102
103      #[allow(dead_code)]
104      pub fn data_ptr(&self) -> *const T {
105        self.data
106      }
107
108      pub fn rect(&self) -> &Rect {
109        &self.rect
110      }
111
112      #[allow(dead_code)]
113      pub fn rows_iter(&self) -> PlaneRegionRowsIter<'_, T> {
114        PlaneRegionRowsIter {
115          data: self.data,
116          stride: self.plane_cfg.stride,
117          width: self.rect.width,
118          remaining: self.rect.height,
119          phantom: PhantomData,
120        }
121      }
122
123      #[allow(dead_code)]
124      pub fn vert_windows(&self, h: usize) -> VertWindows<'_, T> {
125        VertWindows {
126          data: self.data,
127          plane_cfg: self.plane_cfg,
128          remaining: (self.rect.height as isize - h as isize + 1).max(0) as usize,
129          output_rect: Rect {
130            x: self.rect.x,
131            y: self.rect.y,
132            width: self.rect.width,
133            height: h
134          }
135        }
136      }
137
138      #[allow(dead_code)]
139      pub fn horz_windows(&self, w: usize) -> HorzWindows<'_, T> {
140        HorzWindows {
141          data: self.data,
142          plane_cfg: self.plane_cfg,
143          remaining: (self.rect.width as isize - w as isize + 1).max(0) as usize,
144          output_rect: Rect {
145            x: self.rect.x,
146            y: self.rect.y,
147            width: w,
148            height: self.rect.height
149          }
150        }
151      }
152
153      /// Return a view to a subregion of the plane
154      ///
155      /// The subregion must be included in (i.e. must not exceed) this region.
156      ///
157      /// It is described by an `Area`, relative to this region.
158      ///
159      /// # Panics
160      ///
161      /// - If the requested dimensions are larger than the plane region size
162      ///
163      /// # Example
164      ///
165      /// ``` ignore
166      /// # use rav1e::tiling::*;
167      /// # fn f(region: &PlaneRegion<'_, u16>) {
168      /// // a subregion from (10, 8) to the end of the region
169      /// let subregion = region.subregion(Area::StartingAt { x: 10, y: 8 });
170      /// # }
171      /// ```
172      ///
173      /// ``` ignore
174      /// # use rav1e::context::*;
175      /// # use rav1e::tiling::*;
176      /// # fn f(region: &PlaneRegion<'_, u16>) {
177      /// // a subregion from the top-left of block (2, 3) having size (64, 64)
178      /// let bo = BlockOffset { x: 2, y: 3 };
179      /// let subregion = region.subregion(Area::BlockRect { bo, width: 64, height: 64 });
180      /// # }
181      /// ```
182      #[allow(dead_code)]
183      pub fn subregion(&self, area: Area) -> PlaneRegion<'_, T> {
184        if self.data.is_null() {
185          return PlaneRegion::empty(&self.plane_cfg);
186        }
187        let rect = area.to_rect(
188          self.plane_cfg.xdec,
189          self.plane_cfg.ydec,
190          self.rect.width,
191          self.rect.height,
192        );
193        assert!(rect.x >= 0 && rect.x as usize <= self.rect.width);
194        assert!(rect.y >= 0 && rect.y as usize <= self.rect.height);
195        // SAFETY: The above asserts ensure we do not go outside the original rectangle.
196        let data = unsafe {
197          self.data.add(rect.y as usize * self.plane_cfg.stride + rect.x as usize)
198        };
199        let absolute_rect = Rect {
200          x: self.rect.x + rect.x,
201          y: self.rect.y + rect.y,
202          width: rect.width,
203          height: rect.height,
204        };
205        PlaneRegion {
206          data,
207          plane_cfg: &self.plane_cfg,
208          rect: absolute_rect,
209          phantom: PhantomData,
210        }
211      }
212    }
213
214    unsafe impl<T: Pixel> Send for $name<'_, T> {}
215    unsafe impl<T: Pixel> Sync for $name<'_, T> {}
216
217    impl<T: Pixel> Index<usize> for $name<'_, T> {
218      type Output = [T];
219
220      fn index(&self, index: usize) -> &Self::Output {
221        assert!(index < self.rect.height);
222        // SAFETY: The above assert ensures we do not access OOB data.
223        unsafe {
224          let ptr = self.data.add(index * self.plane_cfg.stride);
225          slice::from_raw_parts(ptr, self.rect.width)
226        }
227      }
228    }
229  }
230}
231
232plane_region_common!(PlaneRegion, as_ptr);
233plane_region_common!(PlaneRegionMut, as_mut_ptr, mut);
234
235impl<T: Pixel> PlaneRegionMut<'_, T> {
236    pub fn data_ptr_mut(&mut self) -> *mut T {
237        self.data
238    }
239
240    pub fn rows_iter_mut(&mut self) -> PlaneRegionRowsIterMut<'_, T> {
241        PlaneRegionRowsIterMut {
242            data: self.data,
243            stride: self.plane_cfg.stride,
244            width: self.rect.width,
245            remaining: self.rect.height,
246            phantom: PhantomData,
247        }
248    }
249
250    pub fn as_const(&self) -> PlaneRegion<'_, T> {
251        PlaneRegion {
252            data: self.data,
253            plane_cfg: self.plane_cfg,
254            rect: self.rect,
255            phantom: PhantomData,
256        }
257    }
258}
259
260impl<T: Pixel> IndexMut<usize> for PlaneRegionMut<'_, T> {
261    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
262        assert!(index < self.rect.height);
263        // SAFETY: The above assert ensures we do not access OOB data.
264        unsafe {
265            let ptr = self.data.add(index * self.plane_cfg.stride);
266            slice::from_raw_parts_mut(ptr, self.rect.width)
267        }
268    }
269}
270
271/// Iterator over plane region rows
272pub struct PlaneRegionRowsIter<'a, T: Pixel> {
273    data: *const T,
274    stride: usize,
275    width: usize,
276    remaining: usize,
277    phantom: PhantomData<&'a T>,
278}
279
280impl<'a, T: Pixel> Iterator for PlaneRegionRowsIter<'a, T> {
281    type Item = &'a [T];
282
283    fn next(&mut self) -> Option<Self::Item> {
284        if self.remaining > 0 {
285            // SAFETY: We verified that we have enough data left to not go OOB,
286            // assuming that `self.stride` and `self.width` are set correctly.
287            let row = unsafe {
288                let ptr = self.data;
289                self.data = self.data.add(self.stride);
290                slice::from_raw_parts(ptr, self.width)
291            };
292            self.remaining -= 1;
293            Some(row)
294        } else {
295            None
296        }
297    }
298
299    fn size_hint(&self) -> (usize, Option<usize>) {
300        (self.remaining, Some(self.remaining))
301    }
302}
303
304/// Mutable iterator over plane region rows
305pub struct PlaneRegionRowsIterMut<'a, T: Pixel> {
306    data: *mut T,
307    stride: usize,
308    width: usize,
309    remaining: usize,
310    phantom: PhantomData<&'a mut T>,
311}
312
313impl<'a, T: Pixel> Iterator for PlaneRegionRowsIterMut<'a, T> {
314    type Item = &'a mut [T];
315
316    fn next(&mut self) -> Option<Self::Item> {
317        if self.remaining > 0 {
318            // SAFETY: We verified that we have enough data left to not go OOB,
319            // assuming that `self.stride` and `self.width` are set correctly.
320            let row = unsafe {
321                let ptr = self.data;
322                self.data = self.data.add(self.stride);
323                slice::from_raw_parts_mut(ptr, self.width)
324            };
325            self.remaining -= 1;
326            Some(row)
327        } else {
328            None
329        }
330    }
331
332    fn size_hint(&self) -> (usize, Option<usize>) {
333        (self.remaining, Some(self.remaining))
334    }
335}
336
337impl<T: Pixel> ExactSizeIterator for PlaneRegionRowsIter<'_, T> {
338}
339impl<T: Pixel> FusedIterator for PlaneRegionRowsIter<'_, T> {
340}
341impl<T: Pixel> ExactSizeIterator for PlaneRegionRowsIterMut<'_, T> {
342}
343impl<T: Pixel> FusedIterator for PlaneRegionRowsIterMut<'_, T> {
344}
345
346pub struct VertWindows<'a, T: Pixel> {
347    data: *const T,
348    plane_cfg: &'a PlaneConfig,
349    remaining: usize,
350    output_rect: Rect,
351}
352
353pub struct HorzWindows<'a, T: Pixel> {
354    data: *const T,
355    plane_cfg: &'a PlaneConfig,
356    remaining: usize,
357    output_rect: Rect,
358}
359
360impl<'a, T: Pixel> Iterator for VertWindows<'a, T> {
361    type Item = PlaneRegion<'a, T>;
362
363    fn next(&mut self) -> Option<Self::Item> {
364        self.nth(0)
365    }
366
367    fn size_hint(&self) -> (usize, Option<usize>) {
368        (self.remaining, Some(self.remaining))
369    }
370
371    fn nth(&mut self, n: usize) -> Option<Self::Item> {
372        if self.remaining > n {
373            // SAFETY: We verified that we have enough data left to not go OOB.
374            self.data = unsafe { self.data.add(self.plane_cfg.stride * n) };
375            self.output_rect.y += n as isize;
376            let output = PlaneRegion {
377                data: self.data,
378                plane_cfg: self.plane_cfg,
379                rect: self.output_rect,
380                phantom: PhantomData,
381            };
382            // SAFETY: We verified that we have enough data left to not go OOB.
383            self.data = unsafe { self.data.add(self.plane_cfg.stride) };
384            self.output_rect.y += 1;
385            self.remaining -= n + 1;
386            Some(output)
387        } else {
388            None
389        }
390    }
391}
392
393impl<'a, T: Pixel> Iterator for HorzWindows<'a, T> {
394    type Item = PlaneRegion<'a, T>;
395
396    fn next(&mut self) -> Option<Self::Item> {
397        self.nth(0)
398    }
399
400    fn size_hint(&self) -> (usize, Option<usize>) {
401        (self.remaining, Some(self.remaining))
402    }
403
404    fn nth(&mut self, n: usize) -> Option<Self::Item> {
405        if self.remaining > n {
406            // SAFETY: We verified that we have enough data left to not go OOB.
407            self.data = unsafe { self.data.add(n) };
408            self.output_rect.x += n as isize;
409            let output = PlaneRegion {
410                data: self.data,
411                plane_cfg: self.plane_cfg,
412                rect: self.output_rect,
413                phantom: PhantomData,
414            };
415            // SAFETY: We verified that we have enough data left to not go OOB.
416            self.data = unsafe { self.data.add(1) };
417            self.output_rect.x += 1;
418            self.remaining -= n + 1;
419            Some(output)
420        } else {
421            None
422        }
423    }
424}
425
426impl<T: Pixel> ExactSizeIterator for VertWindows<'_, T> {
427}
428impl<T: Pixel> FusedIterator for VertWindows<'_, T> {
429}
430impl<T: Pixel> ExactSizeIterator for HorzWindows<'_, T> {
431}
432impl<T: Pixel> FusedIterator for HorzWindows<'_, T> {
433}
434
435/// Rectangle of a plane region, in pixels
436#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
437pub struct Rect {
438    // coordinates relative to the plane origin (xorigin, yorigin)
439    pub x: isize,
440    pub y: isize,
441    pub width: usize,
442    pub height: usize,
443}
444
445// Structure to describe a rectangle area in several ways
446//
447// To retrieve a subregion from a region, we need to provide the subregion
448// bounds, relative to its parent region. The subregion must always be included
449// in its parent region.
450//
451// For that purpose, we could just use a rectangle (x, y, width, height), but
452// this would be too cumbersome to use in practice. For example, we often need
453// to pass a subregion from an offset, using the same bottom-right corner as
454// its parent, or to pass a subregion expressed in block offset instead of
455// pixel offset.
456//
457// Area provides a flexible way to describe a subregion.
458#[derive(Debug, Clone, Copy)]
459pub enum Area {
460    /// A well-defined rectangle
461    Rect(Rect),
462    /// A rectangle starting at offset (x, y) and ending at the bottom-right
463    /// corner of the parent
464    StartingAt { x: isize, y: isize },
465    /// a rectangle starting at given block offset until the bottom-right corner
466    /// of the parent
467    BlockStartingAt { bo: BlockOffset },
468}
469
470impl Area {
471    /// Convert to a rectangle of pixels.
472    /// For a `BlockRect` and `BlockStartingAt`, for subsampled chroma planes,
473    /// the returned rect will be aligned to a 4x4 chroma block.
474    /// This is necessary for `compute_distortion` and `rdo_cfl_alpha` as
475    /// the subsampled chroma block covers multiple luma blocks.
476    pub const fn to_rect(
477        self,
478        xdec: usize,
479        ydec: usize,
480        parent_width: usize,
481        parent_height: usize,
482    ) -> Rect {
483        match self {
484            Area::Rect(rect) => rect,
485            Area::StartingAt { x, y } => Rect {
486                x,
487                y,
488                width: (parent_width as isize - x) as usize,
489                height: (parent_height as isize - y) as usize,
490            },
491            Area::BlockStartingAt { bo } => {
492                let x = (bo.x >> xdec << BLOCK_TO_PLANE_SHIFT) as isize;
493                let y = (bo.y >> ydec << BLOCK_TO_PLANE_SHIFT) as isize;
494                Rect {
495                    x,
496                    y,
497                    width: (parent_width as isize - x) as usize,
498                    height: (parent_height as isize - y) as usize,
499                }
500            }
501        }
502    }
503}
504
505pub trait AsRegion<T: Pixel> {
506    fn as_region(&self) -> PlaneRegion<'_, T>;
507    fn region(&self, area: Area) -> PlaneRegion<'_, T>;
508    fn region_mut(&mut self, area: Area) -> PlaneRegionMut<'_, T>;
509}
510
511impl<T: Pixel> AsRegion<T> for Plane<T> {
512    fn as_region(&self) -> PlaneRegion<'_, T> {
513        PlaneRegion::new_from_plane(self)
514    }
515
516    fn region(&self, area: Area) -> PlaneRegion<'_, T> {
517        let rect = area.to_rect(
518            self.cfg.xdec,
519            self.cfg.ydec,
520            self.cfg.stride - self.cfg.xorigin,
521            self.cfg.alloc_height - self.cfg.yorigin,
522        );
523        PlaneRegion::new(self, rect)
524    }
525
526    fn region_mut(&mut self, area: Area) -> PlaneRegionMut<'_, T> {
527        let rect = area.to_rect(
528            self.cfg.xdec,
529            self.cfg.ydec,
530            self.cfg.stride - self.cfg.xorigin,
531            self.cfg.alloc_height - self.cfg.yorigin,
532        );
533        PlaneRegionMut::new(self, rect)
534    }
535}
536
537/// Absolute offset in blocks inside a plane, where a block is defined
538/// to be an `N*N` square where `N == (1 << BLOCK_TO_PLANE_SHIFT)`.
539#[derive(Clone, Copy, Debug, PartialEq, Eq)]
540pub struct PlaneBlockOffset(pub BlockOffset);
541
542impl PlaneBlockOffset {
543    /// Convert to plane offset without decimation.
544    pub const fn to_luma_plane_offset(self) -> PlaneOffset {
545        self.0.to_luma_plane_offset()
546    }
547}