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 | |
12 | use crate::context::*; |
13 | use crate::frame::*; |
14 | use crate::util::*; |
15 | |
16 | use std::iter::FusedIterator; |
17 | use std::marker::PhantomData; |
18 | use std::ops::{Index, IndexMut}; |
19 | use std::slice; |
20 | |
21 | /// Rectangle of a plane region, in pixels |
22 | #[derive (Debug, Clone, Copy, Default, PartialEq, Eq)] |
23 | pub 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 | |
31 | impl 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)] |
60 | pub 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 | |
73 | impl 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)] |
116 | pub 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)] |
129 | pub 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 |
137 | macro_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 | |
423 | plane_region_common!(PlaneRegion, as_ptr); |
424 | plane_region_common!(PlaneRegionMut, as_mut_ptr, mut); |
425 | |
426 | impl<'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 | |
511 | impl<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 |
524 | pub 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 |
533 | pub 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 | |
541 | impl<'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 | |
567 | impl<'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 | |
593 | impl<T: Pixel> ExactSizeIterator for RowsIter<'_, T> {} |
594 | impl<T: Pixel> FusedIterator for RowsIter<'_, T> {} |
595 | impl<T: Pixel> ExactSizeIterator for RowsIterMut<'_, T> {} |
596 | impl<T: Pixel> FusedIterator for RowsIterMut<'_, T> {} |
597 | |
598 | pub struct VertWindows<'a, T: Pixel> { |
599 | data: *const T, |
600 | plane_cfg: &'a PlaneConfig, |
601 | remaining: usize, |
602 | output_rect: Rect, |
603 | } |
604 | |
605 | pub struct HorzWindows<'a, T: Pixel> { |
606 | data: *const T, |
607 | plane_cfg: &'a PlaneConfig, |
608 | remaining: usize, |
609 | output_rect: Rect, |
610 | } |
611 | |
612 | impl<'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 | |
648 | impl<'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 | |
684 | impl<T: Pixel> ExactSizeIterator for VertWindows<'_, T> {} |
685 | impl<T: Pixel> FusedIterator for VertWindows<'_, T> {} |
686 | impl<T: Pixel> ExactSizeIterator for HorzWindows<'_, T> {} |
687 | impl<T: Pixel> FusedIterator for HorzWindows<'_, T> {} |
688 | |
689 | #[test ] |
690 | fn 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 ] |
734 | fn 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 | |