rav1e/tiling/
plane_region.rs

1// Copyright (c) 2019-2022, The rav1e contributors. All rights reserved
2//
3// This source code is subject to the terms of the BSD 2 Clause License and
4// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
5// was not distributed with this source code in the LICENSE file, you can
6// obtain it at www.aomedia.org/license/software. If the Alliance for Open
7// Media Patent License 1.0 was not distributed with this source code in the
8// PATENTS file, you can obtain it at www.aomedia.org/license/patent.
9
10#![allow(clippy::iter_nth_zero)]
11
12use crate::context::*;
13use crate::frame::*;
14use crate::util::*;
15
16use std::iter::FusedIterator;
17use std::marker::PhantomData;
18use std::ops::{Index, IndexMut};
19use std::slice;
20
21/// Rectangle of a plane region, in pixels
22#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
23pub struct Rect {
24  // coordinates relative to the plane origin (xorigin, yorigin)
25  pub x: isize,
26  pub y: isize,
27  pub width: usize,
28  pub height: usize,
29}
30
31impl Rect {
32  #[inline(always)]
33  pub const fn decimated(&self, xdec: usize, ydec: usize) -> Self {
34    Self {
35      x: self.x >> xdec,
36      y: self.y >> ydec,
37      width: self.width >> xdec,
38      height: self.height >> ydec,
39    }
40  }
41  pub const fn to_area(&self) -> Area {
42    Area::Rect { x: self.x, y: self.y, width: self.width, height: self.height }
43  }
44}
45
46// Structure to describe a rectangle area in several ways
47//
48// To retrieve a subregion from a region, we need to provide the subregion
49// bounds, relative to its parent region. The subregion must always be included
50// in its parent region.
51//
52// For that purpose, we could just use a rectangle (x, y, width, height), but
53// this would be too cumbersome to use in practice. For example, we often need
54// to pass a subregion from an offset, using the same bottom-right corner as
55// its parent, or to pass a subregion expressed in block offset instead of
56// pixel offset.
57//
58// Area provides a flexible way to describe a subregion.
59#[derive(Debug, Clone, Copy)]
60pub enum Area {
61  /// A well-defined rectangle
62  Rect { x: isize, y: isize, width: usize, height: usize },
63  /// A rectangle starting at offset (x, y) and ending at the bottom-right
64  /// corner of the parent
65  StartingAt { x: isize, y: isize },
66  /// A well-defined rectangle with offset expressed in blocks
67  BlockRect { bo: BlockOffset, width: usize, height: usize },
68  /// a rectangle starting at given block offset until the bottom-right corner
69  /// of the parent
70  BlockStartingAt { bo: BlockOffset },
71}
72
73impl Area {
74  #[inline(always)]
75  /// Convert to a rectangle of pixels.
76  /// For a `BlockRect` and `BlockStartingAt`, for subsampled chroma planes,
77  /// the returned rect will be aligned to a 4x4 chroma block.
78  /// This is necessary for `compute_distortion` and `rdo_cfl_alpha` as
79  /// the subsampled chroma block covers multiple luma blocks.
80  pub const fn to_rect(
81    &self, xdec: usize, ydec: usize, parent_width: usize, parent_height: usize,
82  ) -> Rect {
83    match *self {
84      Area::Rect { x, y, width, height } => Rect { x, y, width, height },
85      Area::StartingAt { x, y } => Rect {
86        x,
87        y,
88        width: (parent_width as isize - x) as usize,
89        height: (parent_height as isize - y) as usize,
90      },
91      Area::BlockRect { bo, width, height } => Rect {
92        x: (bo.x >> xdec << BLOCK_TO_PLANE_SHIFT) as isize,
93        y: (bo.y >> ydec << BLOCK_TO_PLANE_SHIFT) as isize,
94        width,
95        height,
96      },
97      Area::BlockStartingAt { bo } => {
98        let x = (bo.x >> xdec << BLOCK_TO_PLANE_SHIFT) as isize;
99        let y = (bo.y >> ydec << BLOCK_TO_PLANE_SHIFT) as isize;
100        Rect {
101          x,
102          y,
103          width: (parent_width as isize - x) as usize,
104          height: (parent_height as isize - y) as usize,
105        }
106      }
107    }
108  }
109}
110
111/// Bounded region of a plane
112///
113/// This allows to give access to a rectangular area of a plane without
114/// giving access to the whole plane.
115#[derive(Debug)]
116pub struct PlaneRegion<'a, T: Pixel> {
117  data: *const T, // points to (plane_cfg.x, plane_cfg.y)
118  pub plane_cfg: &'a PlaneConfig,
119  // private to guarantee borrowing rules
120  rect: Rect,
121  phantom: PhantomData<&'a T>,
122}
123
124/// Mutable bounded region of a plane
125///
126/// This allows to give mutable access to a rectangular area of the plane
127/// without giving access to the whole plane.
128#[derive(Debug)]
129pub struct PlaneRegionMut<'a, T: Pixel> {
130  data: *mut T, // points to (plane_cfg.x, plane_cfg.y)
131  pub plane_cfg: &'a PlaneConfig,
132  rect: Rect,
133  phantom: PhantomData<&'a mut T>,
134}
135
136// common impl for PlaneRegion and PlaneRegionMut
137macro_rules! plane_region_common {
138  // $name: PlaneRegion or PlaneRegionMut
139  // $as_ptr: as_ptr or as_mut_ptr
140  // $opt_mut: nothing or mut
141  ($name:ident, $as_ptr:ident $(,$opt_mut:tt)?) => {
142    impl<'a, T: Pixel> $name<'a, T> {
143      #[inline(always)]
144      pub fn is_null(&self) -> bool {
145        self.data.is_null()
146      }
147
148      #[cold]
149      pub fn empty(plane_cfg : &'a PlaneConfig) -> Self {
150        return Self {
151          // SAFETY: This is actually pretty unsafe.
152          // This means we need to ensure that no other method on this struct
153          // can access data if the dimensions are 0.
154          data: unsafe { std::ptr::null_mut::<T>() },
155          plane_cfg,
156          rect: Rect::default(),
157          phantom: PhantomData,
158        }
159      }
160
161      /// # Panics
162      ///
163      /// - If the configured dimensions are invalid
164      #[inline(always)]
165      pub fn from_slice(data: &'a $($opt_mut)? [T], cfg: &'a PlaneConfig, rect: Rect) -> Self {
166        if cfg.width == 0 || cfg.height == 0 {
167          return Self::empty(&cfg);
168        }
169        assert!(rect.x >= -(cfg.xorigin as isize));
170        assert!(rect.y >= -(cfg.yorigin as isize));
171        assert!(cfg.xorigin as isize + rect.x + rect.width as isize <= cfg.stride as isize);
172        assert!(cfg.yorigin as isize + rect.y + rect.height as isize <= cfg.alloc_height as isize);
173
174        // SAFETY: The above asserts ensure we do not go OOB.
175        unsafe { Self::from_slice_unsafe(data, cfg, rect)}
176      }
177
178      #[inline(always)]
179      pub unsafe fn from_slice_unsafe(data: &'a $($opt_mut)? [T], cfg: &'a PlaneConfig, rect: Rect) -> Self {
180        debug_assert!(rect.x >= -(cfg.xorigin as isize));
181        debug_assert!(rect.y >= -(cfg.yorigin as isize));
182        debug_assert!(cfg.xorigin as isize + rect.x + rect.width as isize <= cfg.stride as isize);
183        debug_assert!(cfg.yorigin as isize + rect.y + rect.height as isize <= cfg.alloc_height as isize);
184
185        let origin = (cfg.yorigin as isize + rect.y) * cfg.stride as isize + cfg.xorigin as isize + rect.x;
186        Self {
187          data: data.$as_ptr().offset(origin),
188          plane_cfg: cfg,
189          rect,
190          phantom: PhantomData,
191        }
192      }
193
194      #[inline(always)]
195      pub fn new(plane: &'a $($opt_mut)? Plane<T>, rect: Rect) -> Self {
196        Self::from_slice(& $($opt_mut)? plane.data, &plane.cfg, rect)
197      }
198
199      #[inline(always)]
200      pub fn new_from_plane(plane: &'a $($opt_mut)? Plane<T>) -> Self {
201        let rect = Area::StartingAt { x: 0, y: 0 }.to_rect(
202          plane.cfg.xdec,
203          plane.cfg.ydec,
204          plane.cfg.stride - plane.cfg.xorigin,
205          plane.cfg.alloc_height - plane.cfg.yorigin,
206        );
207
208        // SAFETY: Area::StartingAt{}.to_rect is guaranteed to be the entire plane
209        unsafe { Self::from_slice_unsafe(& $($opt_mut)? plane.data, &plane.cfg, rect) }
210      }
211
212      #[inline(always)]
213      pub fn data_ptr(&self) -> *const T {
214        self.data
215      }
216
217      #[inline(always)]
218      pub fn rect(&self) -> &Rect {
219        &self.rect
220      }
221
222      #[inline(always)]
223      pub fn rows_iter(&self) -> RowsIter<'_, T> {
224        RowsIter {
225          data: self.data,
226          stride: self.plane_cfg.stride,
227          width: self.rect.width,
228          remaining: self.rect.height,
229          phantom: PhantomData,
230        }
231      }
232
233      pub fn vert_windows(&self, h: usize) -> VertWindows<'_, T> {
234        VertWindows {
235          data: self.data,
236          plane_cfg: self.plane_cfg,
237          remaining: (self.rect.height as isize - h as isize + 1).max(0) as usize,
238          output_rect: Rect {
239            x: self.rect.x,
240            y: self.rect.y,
241            width: self.rect.width,
242            height: h
243          }
244        }
245      }
246
247      pub fn horz_windows(&self, w: usize) -> HorzWindows<'_, T> {
248        HorzWindows {
249          data: self.data,
250          plane_cfg: self.plane_cfg,
251          remaining: (self.rect.width as isize - w as isize + 1).max(0) as usize,
252          output_rect: Rect {
253            x: self.rect.x,
254            y: self.rect.y,
255            width: w,
256            height: self.rect.height
257          }
258        }
259      }
260
261      /// Return a view to a subregion of the plane
262      ///
263      /// The subregion must be included in (i.e. must not exceed) this region.
264      ///
265      /// It is described by an `Area`, relative to this region.
266      ///
267      /// # Panics
268      ///
269      /// - If the requested dimensions are larger than the plane region size
270      ///
271      /// # Example
272      ///
273      /// ``` ignore
274      /// # use rav1e::tiling::*;
275      /// # fn f(region: &PlaneRegion<'_, u16>) {
276      /// // a subregion from (10, 8) to the end of the region
277      /// let subregion = region.subregion(Area::StartingAt { x: 10, y: 8 });
278      /// # }
279      /// ```
280      ///
281      /// ``` ignore
282      /// # use rav1e::context::*;
283      /// # use rav1e::tiling::*;
284      /// # fn f(region: &PlaneRegion<'_, u16>) {
285      /// // a subregion from the top-left of block (2, 3) having size (64, 64)
286      /// let bo = BlockOffset { x: 2, y: 3 };
287      /// let subregion = region.subregion(Area::BlockRect { bo, width: 64, height: 64 });
288      /// # }
289      /// ```
290      #[inline(always)]
291      pub fn subregion(&self, area: Area) -> PlaneRegion<'_, T> {
292        if self.data.is_null() {
293          return PlaneRegion::empty(&self.plane_cfg);
294        }
295        let rect = area.to_rect(
296          self.plane_cfg.xdec,
297          self.plane_cfg.ydec,
298          self.rect.width,
299          self.rect.height,
300        );
301        assert!(rect.x >= 0 && rect.x as usize <= self.rect.width);
302        assert!(rect.y >= 0 && rect.y as usize <= self.rect.height);
303        // SAFETY: The above asserts ensure we do not go outside the original rectangle.
304        let data = unsafe {
305          self.data.add(rect.y as usize * self.plane_cfg.stride + rect.x as usize)
306        };
307        let absolute_rect = Rect {
308          x: self.rect.x + rect.x,
309          y: self.rect.y + rect.y,
310          width: rect.width,
311          height: rect.height,
312        };
313        PlaneRegion {
314          data,
315          plane_cfg: &self.plane_cfg,
316          rect: absolute_rect,
317          phantom: PhantomData,
318        }
319      }
320
321      // Return an equivalent PlaneRegion with origin homed to 0,0.  Data
322      // pointer is not moved (0,0 points to the same pixel previously
323      // pointed to by old x,y).
324      #[inline(always)]
325      pub fn home(&self) -> Self {
326        let home_rect = Rect {
327          x: 0,
328          y: 0,
329          width: self.rect.width,
330          height: self.rect.height,
331        };
332
333        Self {
334          data: self.data,
335          plane_cfg: &self.plane_cfg,
336          rect: home_rect,
337          phantom: PhantomData,
338        }
339      }
340
341      #[inline(always)]
342      pub fn to_frame_plane_offset(&self, tile_po: PlaneOffset) -> PlaneOffset {
343        PlaneOffset {
344          x: self.rect.x + tile_po.x,
345          y: self.rect.y + tile_po.y,
346        }
347      }
348
349      #[inline(always)]
350      pub fn to_frame_block_offset(&self, tile_bo: TileBlockOffset) -> PlaneBlockOffset {
351        debug_assert!(self.rect.x >= 0);
352        debug_assert!(self.rect.y >= 0);
353        let PlaneConfig { xdec, ydec, .. } = self.plane_cfg;
354        debug_assert!(self.rect.x as usize % (MI_SIZE >> xdec) == 0);
355        debug_assert!(self.rect.y as usize % (MI_SIZE >> ydec) == 0);
356        let bx = self.rect.x as usize >> MI_SIZE_LOG2 - xdec;
357        let by = self.rect.y as usize >> MI_SIZE_LOG2 - ydec;
358        PlaneBlockOffset(BlockOffset {
359          x: bx + tile_bo.0.x,
360          y: by + tile_bo.0.y,
361        })
362      }
363
364      #[inline(always)]
365      pub fn to_frame_super_block_offset(
366        &self,
367        tile_sbo: TileSuperBlockOffset,
368        sb_size_log2: usize
369      ) -> PlaneSuperBlockOffset {
370        debug_assert!(sb_size_log2 == 6 || sb_size_log2 == 7);
371        debug_assert!(self.rect.x >= 0);
372        debug_assert!(self.rect.y >= 0);
373        let PlaneConfig { xdec, ydec, .. } = self.plane_cfg;
374        debug_assert!(self.rect.x as usize % (1 << sb_size_log2 - xdec) == 0);
375        debug_assert!(self.rect.y as usize % (1 << sb_size_log2 - ydec) == 0);
376        let sbx = self.rect.x as usize >> sb_size_log2 - xdec;
377        let sby = self.rect.y as usize >> sb_size_log2 - ydec;
378        PlaneSuperBlockOffset(SuperBlockOffset {
379          x: sbx + tile_sbo.0.x,
380          y: sby + tile_sbo.0.y,
381        })
382      }
383
384      /// Returns the frame block offset of the subregion.
385      #[inline(always)]
386      pub fn frame_block_offset(&self) -> PlaneBlockOffset {
387        self.to_frame_block_offset(TileBlockOffset(BlockOffset { x: 0, y: 0 }))
388      }
389
390      pub(crate) fn scratch_copy(&self) -> Plane<T> {
391        let &Rect { width, height, .. } = self.rect();
392        let &PlaneConfig { xdec, ydec, .. } = self.plane_cfg;
393        let mut ret: Plane<T> = Plane::new(width, height, xdec, ydec, 0, 0);
394        let mut dst: PlaneRegionMut<T> = ret.as_region_mut();
395        for (dst_row, src_row) in dst.rows_iter_mut().zip(self.rows_iter()) {
396          for (out, input) in dst_row.iter_mut().zip(src_row) {
397            *out = *input;
398          }
399        }
400        ret
401      }
402    }
403
404    unsafe impl<T: Pixel> Send for $name<'_, T> {}
405    unsafe impl<T: Pixel> Sync for $name<'_, T> {}
406
407    impl<T: Pixel> Index<usize> for $name<'_, T> {
408      type Output = [T];
409
410      #[inline(always)]
411      fn index(&self, index: usize) -> &Self::Output {
412        assert!(index < self.rect.height);
413        // SAFETY: The above assert ensures we do not access OOB data.
414        unsafe {
415          let ptr = self.data.add(index * self.plane_cfg.stride);
416          slice::from_raw_parts(ptr, self.rect.width)
417        }
418      }
419    }
420  }
421}
422
423plane_region_common!(PlaneRegion, as_ptr);
424plane_region_common!(PlaneRegionMut, as_mut_ptr, mut);
425
426impl<'a, T: Pixel> PlaneRegionMut<'a, T> {
427  #[inline(always)]
428  pub fn data_ptr_mut(&mut self) -> *mut T {
429    self.data
430  }
431
432  #[inline(always)]
433  pub fn rows_iter_mut(&mut self) -> RowsIterMut<'_, T> {
434    RowsIterMut {
435      data: self.data,
436      stride: self.plane_cfg.stride,
437      width: self.rect.width,
438      remaining: self.rect.height,
439      phantom: PhantomData,
440    }
441  }
442
443  /// Return a mutable view to a subregion of the plane
444  ///
445  /// The subregion must be included in (i.e. must not exceed) this region.
446  ///
447  /// It is described by an `Area`, relative to this region.
448  ///
449  /// # Panics
450  ///
451  /// - If the targeted `area` is outside of the bounds of this plane region.
452  ///
453  /// # Example
454  ///
455  /// ``` ignore
456  /// # use rav1e::tiling::*;
457  /// # fn f(region: &mut PlaneRegionMut<'_, u16>) {
458  /// // a mutable subregion from (10, 8) having size (32, 32)
459  /// let subregion = region.subregion_mut(Area::Rect { x: 10, y: 8, width: 32, height: 32 });
460  /// # }
461  /// ```
462  ///
463  /// ``` ignore
464  /// # use rav1e::context::*;
465  /// # use rav1e::tiling::*;
466  /// # fn f(region: &mut PlaneRegionMut<'_, u16>) {
467  /// // a mutable subregion from the top-left of block (2, 3) to the end of the region
468  /// let bo = BlockOffset { x: 2, y: 3 };
469  /// let subregion = region.subregion_mut(Area::BlockStartingAt { bo });
470  /// # }
471  /// ```
472  #[inline(always)]
473  pub fn subregion_mut(&mut self, area: Area) -> PlaneRegionMut<'_, T> {
474    let rect = area.to_rect(
475      self.plane_cfg.xdec,
476      self.plane_cfg.ydec,
477      self.rect.width,
478      self.rect.height,
479    );
480    assert!(rect.x >= 0 && rect.x as usize <= self.rect.width);
481    assert!(rect.y >= 0 && rect.y as usize <= self.rect.height);
482    // SAFETY: The above asserts ensure we do not go outside the original rectangle.
483    let data = unsafe {
484      self.data.add(rect.y as usize * self.plane_cfg.stride + rect.x as usize)
485    };
486    let absolute_rect = Rect {
487      x: self.rect.x + rect.x,
488      y: self.rect.y + rect.y,
489      width: rect.width,
490      height: rect.height,
491    };
492    PlaneRegionMut {
493      data,
494      plane_cfg: self.plane_cfg,
495      rect: absolute_rect,
496      phantom: PhantomData,
497    }
498  }
499
500  #[inline(always)]
501  pub fn as_const(&self) -> PlaneRegion<'_, T> {
502    PlaneRegion {
503      data: self.data,
504      plane_cfg: self.plane_cfg,
505      rect: self.rect,
506      phantom: PhantomData,
507    }
508  }
509}
510
511impl<T: Pixel> IndexMut<usize> for PlaneRegionMut<'_, T> {
512  #[inline(always)]
513  fn index_mut(&mut self, index: usize) -> &mut Self::Output {
514    assert!(index < self.rect.height);
515    // SAFETY: The above assert ensures we do not access OOB data.
516    unsafe {
517      let ptr = self.data.add(index * self.plane_cfg.stride);
518      slice::from_raw_parts_mut(ptr, self.rect.width)
519    }
520  }
521}
522
523/// Iterator over plane region rows
524pub struct RowsIter<'a, T: Pixel> {
525  data: *const T,
526  stride: usize,
527  width: usize,
528  remaining: usize,
529  phantom: PhantomData<&'a T>,
530}
531
532/// Mutable iterator over plane region rows
533pub struct RowsIterMut<'a, T: Pixel> {
534  data: *mut T,
535  stride: usize,
536  width: usize,
537  remaining: usize,
538  phantom: PhantomData<&'a mut T>,
539}
540
541impl<'a, T: Pixel> Iterator for RowsIter<'a, T> {
542  type Item = &'a [T];
543
544  #[inline(always)]
545  fn next(&mut self) -> Option<Self::Item> {
546    if self.remaining > 0 {
547      // SAFETY: We verified that we have enough data left to not go OOB,
548      // assuming that `self.stride` and `self.width` are set correctly.
549      let row = unsafe {
550        let ptr = self.data;
551        self.data = self.data.add(self.stride);
552        slice::from_raw_parts(ptr, self.width)
553      };
554      self.remaining -= 1;
555      Some(row)
556    } else {
557      None
558    }
559  }
560
561  #[inline(always)]
562  fn size_hint(&self) -> (usize, Option<usize>) {
563    (self.remaining, Some(self.remaining))
564  }
565}
566
567impl<'a, T: Pixel> Iterator for RowsIterMut<'a, T> {
568  type Item = &'a mut [T];
569
570  #[inline(always)]
571  fn next(&mut self) -> Option<Self::Item> {
572    if self.remaining > 0 {
573      // SAFETY: We verified that we have enough data left to not go OOB,
574      // assuming that `self.stride` and `self.width` are set correctly.
575      let row = unsafe {
576        let ptr = self.data;
577        self.data = self.data.add(self.stride);
578        slice::from_raw_parts_mut(ptr, self.width)
579      };
580      self.remaining -= 1;
581      Some(row)
582    } else {
583      None
584    }
585  }
586
587  #[inline(always)]
588  fn size_hint(&self) -> (usize, Option<usize>) {
589    (self.remaining, Some(self.remaining))
590  }
591}
592
593impl<T: Pixel> ExactSizeIterator for RowsIter<'_, T> {}
594impl<T: Pixel> FusedIterator for RowsIter<'_, T> {}
595impl<T: Pixel> ExactSizeIterator for RowsIterMut<'_, T> {}
596impl<T: Pixel> FusedIterator for RowsIterMut<'_, T> {}
597
598pub struct VertWindows<'a, T: Pixel> {
599  data: *const T,
600  plane_cfg: &'a PlaneConfig,
601  remaining: usize,
602  output_rect: Rect,
603}
604
605pub struct HorzWindows<'a, T: Pixel> {
606  data: *const T,
607  plane_cfg: &'a PlaneConfig,
608  remaining: usize,
609  output_rect: Rect,
610}
611
612impl<'a, T: Pixel> Iterator for VertWindows<'a, T> {
613  type Item = PlaneRegion<'a, T>;
614
615  #[inline(always)]
616  fn next(&mut self) -> Option<Self::Item> {
617    self.nth(0)
618  }
619
620  #[inline(always)]
621  fn size_hint(&self) -> (usize, Option<usize>) {
622    (self.remaining, Some(self.remaining))
623  }
624
625  #[inline(always)]
626  fn nth(&mut self, n: usize) -> Option<Self::Item> {
627    if self.remaining > n {
628      // SAFETY: We verified that we have enough data left to not go OOB.
629      self.data = unsafe { self.data.add(self.plane_cfg.stride * n) };
630      self.output_rect.y += n as isize;
631      let output = PlaneRegion {
632        data: self.data,
633        plane_cfg: self.plane_cfg,
634        rect: self.output_rect,
635        phantom: PhantomData,
636      };
637      // SAFETY: We verified that we have enough data left to not go OOB.
638      self.data = unsafe { self.data.add(self.plane_cfg.stride) };
639      self.output_rect.y += 1;
640      self.remaining -= (n + 1);
641      Some(output)
642    } else {
643      None
644    }
645  }
646}
647
648impl<'a, T: Pixel> Iterator for HorzWindows<'a, T> {
649  type Item = PlaneRegion<'a, T>;
650
651  #[inline(always)]
652  fn next(&mut self) -> Option<Self::Item> {
653    self.nth(0)
654  }
655
656  #[inline(always)]
657  fn size_hint(&self) -> (usize, Option<usize>) {
658    (self.remaining, Some(self.remaining))
659  }
660
661  #[inline(always)]
662  fn nth(&mut self, n: usize) -> Option<Self::Item> {
663    if self.remaining > n {
664      // SAFETY: We verified that we have enough data left to not go OOB.
665      self.data = unsafe { self.data.add(n) };
666      self.output_rect.x += n as isize;
667      let output = PlaneRegion {
668        data: self.data,
669        plane_cfg: self.plane_cfg,
670        rect: self.output_rect,
671        phantom: PhantomData,
672      };
673      // SAFETY: We verified that we have enough data left to not go OOB.
674      self.data = unsafe { self.data.add(1) };
675      self.output_rect.x += 1;
676      self.remaining -= (n + 1);
677      Some(output)
678    } else {
679      None
680    }
681  }
682}
683
684impl<T: Pixel> ExactSizeIterator for VertWindows<'_, T> {}
685impl<T: Pixel> FusedIterator for VertWindows<'_, T> {}
686impl<T: Pixel> ExactSizeIterator for HorzWindows<'_, T> {}
687impl<T: Pixel> FusedIterator for HorzWindows<'_, T> {}
688
689#[test]
690fn area_test() {
691  assert_eq!(
692    (Area::BlockStartingAt { bo: BlockOffset { x: 0, y: 0 } })
693      .to_rect(0, 0, 100, 100),
694    Rect { x: 0, y: 0, width: 100, height: 100 }
695  );
696  assert_eq!(
697    (Area::BlockStartingAt { bo: BlockOffset { x: 1, y: 1 } })
698      .to_rect(0, 0, 100, 100),
699    Rect { x: 4, y: 4, width: 96, height: 96 }
700  );
701  assert_eq!(
702    (Area::BlockStartingAt { bo: BlockOffset { x: 1, y: 1 } })
703      .to_rect(1, 1, 50, 50),
704    Rect { x: 0, y: 0, width: 50, height: 50 }
705  );
706  assert_eq!(
707    (Area::BlockStartingAt { bo: BlockOffset { x: 2, y: 2 } })
708      .to_rect(1, 1, 50, 50),
709    Rect { x: 4, y: 4, width: 46, height: 46 }
710  );
711  assert_eq!(
712    (Area::BlockRect { bo: BlockOffset { x: 0, y: 0 }, width: 1, height: 1 })
713      .to_rect(0, 0, 100, 100),
714    Rect { x: 0, y: 0, width: 1, height: 1 }
715  );
716  assert_eq!(
717    (Area::BlockRect { bo: BlockOffset { x: 1, y: 1 }, width: 1, height: 1 })
718      .to_rect(0, 0, 100, 100),
719    Rect { x: 4, y: 4, width: 1, height: 1 }
720  );
721  assert_eq!(
722    (Area::BlockRect { bo: BlockOffset { x: 1, y: 1 }, width: 1, height: 1 })
723      .to_rect(1, 1, 50, 50),
724    Rect { x: 0, y: 0, width: 1, height: 1 }
725  );
726  assert_eq!(
727    (Area::BlockRect { bo: BlockOffset { x: 2, y: 2 }, width: 1, height: 1 })
728      .to_rect(1, 1, 50, 50),
729    Rect { x: 4, y: 4, width: 1, height: 1 }
730  );
731}
732
733#[test]
734fn frame_block_offset() {
735  {
736    let p = Plane::<u8>::new(100, 100, 0, 0, 0, 0);
737    let pr =
738      PlaneRegion::new(&p, Rect { x: 0, y: 0, width: 100, height: 100 });
739    let bo = BlockOffset { x: 0, y: 0 };
740    assert_eq!(
741      pr.to_frame_block_offset(TileBlockOffset(bo)),
742      PlaneBlockOffset(bo)
743    );
744    assert_eq!(
745      pr.to_frame_block_offset(TileBlockOffset(bo)),
746      pr.subregion(Area::BlockStartingAt { bo }).frame_block_offset()
747    );
748  }
749  {
750    let p = Plane::<u8>::new(100, 100, 0, 0, 0, 0);
751    let pr =
752      PlaneRegion::new(&p, Rect { x: 0, y: 0, width: 100, height: 100 });
753    let bo = BlockOffset { x: 1, y: 1 };
754    assert_eq!(
755      pr.to_frame_block_offset(TileBlockOffset(bo)),
756      PlaneBlockOffset(bo)
757    );
758    assert_eq!(
759      pr.to_frame_block_offset(TileBlockOffset(bo)),
760      pr.subregion(Area::BlockStartingAt { bo }).frame_block_offset()
761    );
762  }
763  {
764    let p = Plane::<u8>::new(100, 100, 1, 1, 0, 0);
765    let pr =
766      PlaneRegion::new(&p, Rect { x: 0, y: 0, width: 100, height: 100 });
767    let bo = BlockOffset { x: 1, y: 1 };
768    assert_eq!(
769      pr.to_frame_block_offset(TileBlockOffset(bo)),
770      PlaneBlockOffset(bo)
771    );
772  }
773  {
774    let p = Plane::<u8>::new(100, 100, 1, 1, 0, 0);
775    let pr =
776      PlaneRegion::new(&p, Rect { x: 0, y: 0, width: 100, height: 100 });
777    let bo = BlockOffset { x: 2, y: 2 };
778    assert_eq!(
779      pr.to_frame_block_offset(TileBlockOffset(bo)),
780      PlaneBlockOffset(bo)
781    );
782    assert_eq!(
783      pr.to_frame_block_offset(TileBlockOffset(bo)),
784      pr.subregion(Area::BlockStartingAt { bo }).frame_block_offset()
785    );
786  }
787}