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: *mut T = self.data.add(count:index * self.plane_cfg.stride);
518 slice::from_raw_parts_mut(data: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}
788