1 | // Copyright (c) 2017-2021, The rav1e contributors. All rights reserved |
2 | // |
3 | // This source code is subject to the terms of the BSD 2 Clause License and |
4 | // the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License |
5 | // was not distributed with this source code in the LICENSE file, you can |
6 | // obtain it at www.aomedia.org/license/software. If the Alliance for Open |
7 | // Media Patent License 1.0 was not distributed with this source code in the |
8 | // PATENTS file, you can obtain it at www.aomedia.org/license/patent. |
9 | |
10 | use std::fmt::{Debug, Display, Formatter}; |
11 | use std::iter::{self, FusedIterator}; |
12 | use std::marker::PhantomData; |
13 | use std::mem::size_of; |
14 | use std::ops::{Index, IndexMut, Range}; |
15 | |
16 | use aligned_vec::{ABox, AVec, ConstAlign}; |
17 | |
18 | use crate::math::*; |
19 | use crate::pixel::*; |
20 | |
21 | #[cfg (feature = "serialize" )] |
22 | use serde::{Deserialize, Serialize}; |
23 | |
24 | /// Plane-specific configuration. |
25 | #[derive (Debug, Clone, PartialEq, Eq)] |
26 | #[cfg_attr (feature = "serialize" , derive(Serialize, Deserialize))] |
27 | pub struct PlaneConfig { |
28 | /// Data stride. |
29 | pub stride: usize, |
30 | /// Allocated height in pixels. |
31 | pub alloc_height: usize, |
32 | /// Width in pixels. |
33 | pub width: usize, |
34 | /// Height in pixels. |
35 | pub height: usize, |
36 | /// Decimator along the X axis. |
37 | /// |
38 | /// For example, for chroma planes in a 4:2:0 configuration this would be 1. |
39 | pub xdec: usize, |
40 | /// Decimator along the Y axis. |
41 | /// |
42 | /// For example, for chroma planes in a 4:2:0 configuration this would be 1. |
43 | pub ydec: usize, |
44 | /// Number of padding pixels on the right. |
45 | pub xpad: usize, |
46 | /// Number of padding pixels on the bottom. |
47 | pub ypad: usize, |
48 | /// X where the data starts. |
49 | pub xorigin: usize, |
50 | /// Y where the data starts. |
51 | pub yorigin: usize, |
52 | } |
53 | |
54 | impl PlaneConfig { |
55 | /// Stride alignment in bytes. |
56 | const STRIDE_ALIGNMENT_LOG2: usize = 6; |
57 | |
58 | #[inline ] |
59 | pub fn new( |
60 | width: usize, |
61 | height: usize, |
62 | xdec: usize, |
63 | ydec: usize, |
64 | xpad: usize, |
65 | ypad: usize, |
66 | type_size: usize, |
67 | ) -> Self { |
68 | let xorigin = xpad.align_power_of_two(Self::STRIDE_ALIGNMENT_LOG2 + 1 - type_size); |
69 | let yorigin = ypad; |
70 | let stride = (xorigin + width + xpad) |
71 | .align_power_of_two(Self::STRIDE_ALIGNMENT_LOG2 + 1 - type_size); |
72 | let alloc_height = yorigin + height + ypad; |
73 | |
74 | PlaneConfig { |
75 | stride, |
76 | alloc_height, |
77 | width, |
78 | height, |
79 | xdec, |
80 | ydec, |
81 | xpad, |
82 | ypad, |
83 | xorigin, |
84 | yorigin, |
85 | } |
86 | } |
87 | } |
88 | |
89 | /// Absolute offset in pixels inside a plane |
90 | #[derive (Clone, Copy, Debug, Default)] |
91 | pub struct PlaneOffset { |
92 | pub x: isize, |
93 | pub y: isize, |
94 | } |
95 | |
96 | /// Backing buffer for the Plane data |
97 | /// |
98 | /// The buffer is padded and aligned according to the architecture-specific |
99 | /// SIMD constraints. |
100 | #[derive (Debug, Clone, PartialEq, Eq)] |
101 | #[cfg_attr (feature = "serialize" , derive(Serialize, Deserialize))] |
102 | pub struct PlaneData<T: Pixel> { |
103 | #[cfg (not(target_arch = "wasm32" ))] |
104 | data: ABox<[T], ConstAlign<{ 1 << 6 }>>, |
105 | #[cfg (target_arch = "wasm32" )] |
106 | data: ABox<[T], ConstAlign<{ 1 << 3 }>>, |
107 | } |
108 | |
109 | unsafe impl<T: Pixel + Send> Send for PlaneData<T> {} |
110 | unsafe impl<T: Pixel + Sync> Sync for PlaneData<T> {} |
111 | |
112 | impl<T: Pixel> std::ops::Deref for PlaneData<T> { |
113 | type Target = [T]; |
114 | |
115 | fn deref(&self) -> &[T] { |
116 | self.data.as_ref() |
117 | } |
118 | } |
119 | |
120 | impl<T: Pixel> std::ops::DerefMut for PlaneData<T> { |
121 | fn deref_mut(&mut self) -> &mut [T] { |
122 | self.data.as_mut() |
123 | } |
124 | } |
125 | |
126 | impl<T: Pixel> PlaneData<T> { |
127 | #[cfg (target_arch = "wasm32" )] |
128 | // FIXME: wasm32 allocator fails for alignment larger than 3 |
129 | const DATA_ALIGNMENT: usize = 1 << 3; |
130 | #[cfg (not(target_arch = "wasm32" ))] |
131 | const DATA_ALIGNMENT: usize = 1 << 6; |
132 | |
133 | pub fn new(len: usize) -> Self { |
134 | Self { |
135 | data: AVecAVec>::from_iter( |
136 | Self::DATA_ALIGNMENT, |
137 | iter:iter::repeat(T::cast_from(128)).take(len), |
138 | ) |
139 | .into_boxed_slice(), |
140 | } |
141 | } |
142 | |
143 | fn from_slice(data: &[T]) -> Self { |
144 | Self { |
145 | data: AVec::from_slice(Self::DATA_ALIGNMENT, slice:data).into_boxed_slice(), |
146 | } |
147 | } |
148 | } |
149 | |
150 | /// One data plane of a frame. |
151 | /// |
152 | /// For example, a plane can be a Y luma plane or a U or V chroma plane. |
153 | #[derive (Clone, PartialEq, Eq)] |
154 | #[cfg_attr (feature = "serialize" , derive(Serialize, Deserialize))] |
155 | pub struct Plane<T: Pixel> { |
156 | // TODO: it is used by encoder to copy by plane and by tiling, make it |
157 | // private again once tiling is moved and a copy_plane fn is added. |
158 | // |
159 | pub data: PlaneData<T>, |
160 | /// Plane configuration. |
161 | pub cfg: PlaneConfig, |
162 | } |
163 | |
164 | impl<T: Pixel> Debug for Plane<T> |
165 | where |
166 | T: Display, |
167 | { |
168 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
169 | write!( |
170 | f, |
171 | "Plane {{ data: [ {}, ...], cfg: {:?} }}" , |
172 | self.data[0], self.cfg |
173 | ) |
174 | } |
175 | } |
176 | |
177 | impl<T: Pixel> Plane<T> { |
178 | /// Allocates and returns a new plane. |
179 | pub fn new( |
180 | width: usize, |
181 | height: usize, |
182 | xdec: usize, |
183 | ydec: usize, |
184 | xpad: usize, |
185 | ypad: usize, |
186 | ) -> Self { |
187 | let cfg = PlaneConfig::new(width, height, xdec, ydec, xpad, ypad, size_of::<T>()); |
188 | let data = PlaneData::new(cfg.stride * cfg.alloc_height); |
189 | |
190 | Plane { data, cfg } |
191 | } |
192 | |
193 | /// # Panics |
194 | /// |
195 | /// - If `len` is not a multiple of `stride` |
196 | pub fn from_slice(data: &[T], stride: usize) -> Self { |
197 | let len = data.len(); |
198 | |
199 | assert!(len % stride == 0); |
200 | |
201 | Self { |
202 | data: PlaneData::from_slice(data), |
203 | cfg: PlaneConfig { |
204 | stride, |
205 | alloc_height: len / stride, |
206 | width: stride, |
207 | height: len / stride, |
208 | xdec: 0, |
209 | ydec: 0, |
210 | xpad: 0, |
211 | ypad: 0, |
212 | xorigin: 0, |
213 | yorigin: 0, |
214 | }, |
215 | } |
216 | } |
217 | |
218 | pub fn pad(&mut self, w: usize, h: usize) { |
219 | let xorigin = self.cfg.xorigin; |
220 | let yorigin = self.cfg.yorigin; |
221 | let stride = self.cfg.stride; |
222 | let alloc_height = self.cfg.alloc_height; |
223 | let width = (w + self.cfg.xdec) >> self.cfg.xdec; |
224 | let height = (h + self.cfg.ydec) >> self.cfg.ydec; |
225 | |
226 | if xorigin > 0 { |
227 | for y in 0..height { |
228 | let base = (yorigin + y) * stride; |
229 | let fill_val = self.data[base + xorigin]; |
230 | for val in &mut self.data[base..base + xorigin] { |
231 | *val = fill_val; |
232 | } |
233 | } |
234 | } |
235 | |
236 | if xorigin + width < stride { |
237 | for y in 0..height { |
238 | let base = (yorigin + y) * stride + xorigin + width; |
239 | let fill_val = self.data[base - 1]; |
240 | for val in &mut self.data[base..base + stride - (xorigin + width)] { |
241 | *val = fill_val; |
242 | } |
243 | } |
244 | } |
245 | |
246 | if yorigin > 0 { |
247 | let (top, bottom) = self.data.split_at_mut(yorigin * stride); |
248 | let src = &bottom[..stride]; |
249 | for y in 0..yorigin { |
250 | let dst = &mut top[y * stride..(y + 1) * stride]; |
251 | dst.copy_from_slice(src); |
252 | } |
253 | } |
254 | |
255 | if yorigin + height < self.cfg.alloc_height { |
256 | let (top, bottom) = self.data.split_at_mut((yorigin + height) * stride); |
257 | let src = &top[(yorigin + height - 1) * stride..]; |
258 | for y in 0..alloc_height - (yorigin + height) { |
259 | let dst = &mut bottom[y * stride..(y + 1) * stride]; |
260 | dst.copy_from_slice(src); |
261 | } |
262 | } |
263 | } |
264 | |
265 | /// Minimally test that the plane has been padded. |
266 | pub fn probe_padding(&self, w: usize, h: usize) -> bool { |
267 | let PlaneConfig { |
268 | xorigin, |
269 | yorigin, |
270 | stride, |
271 | alloc_height, |
272 | xdec, |
273 | ydec, |
274 | .. |
275 | } = self.cfg; |
276 | let width = (w + xdec) >> xdec; |
277 | let height = (h + ydec) >> ydec; |
278 | let corner = (yorigin + height - 1) * stride + xorigin + width - 1; |
279 | let corner_value = self.data[corner]; |
280 | |
281 | self.data[(yorigin + height) * stride - 1] == corner_value |
282 | && self.data[(alloc_height - 1) * stride + xorigin + width - 1] == corner_value |
283 | && self.data[alloc_height * stride - 1] == corner_value |
284 | } |
285 | |
286 | pub fn slice(&self, po: PlaneOffset) -> PlaneSlice<'_, T> { |
287 | PlaneSlice { |
288 | plane: self, |
289 | x: po.x, |
290 | y: po.y, |
291 | } |
292 | } |
293 | |
294 | pub fn mut_slice(&mut self, po: PlaneOffset) -> PlaneMutSlice<'_, T> { |
295 | PlaneMutSlice { |
296 | plane: self, |
297 | x: po.x, |
298 | y: po.y, |
299 | } |
300 | } |
301 | |
302 | #[inline ] |
303 | fn index(&self, x: usize, y: usize) -> usize { |
304 | (y + self.cfg.yorigin) * self.cfg.stride + (x + self.cfg.xorigin) |
305 | } |
306 | |
307 | /// This version of the function crops off the padding on the right side of the image |
308 | #[inline ] |
309 | pub fn row_range_cropped(&self, x: isize, y: isize) -> Range<usize> { |
310 | debug_assert!(self.cfg.yorigin as isize + y >= 0); |
311 | debug_assert!(self.cfg.xorigin as isize + x >= 0); |
312 | let base_y = (self.cfg.yorigin as isize + y) as usize; |
313 | let base_x = (self.cfg.xorigin as isize + x) as usize; |
314 | let base = base_y * self.cfg.stride + base_x; |
315 | let width = (self.cfg.width as isize - x) as usize; |
316 | base..base + width |
317 | } |
318 | |
319 | /// This version of the function includes the padding on the right side of the image |
320 | #[inline ] |
321 | pub fn row_range(&self, x: isize, y: isize) -> Range<usize> { |
322 | debug_assert!(self.cfg.yorigin as isize + y >= 0); |
323 | debug_assert!(self.cfg.xorigin as isize + x >= 0); |
324 | let base_y = (self.cfg.yorigin as isize + y) as usize; |
325 | let base_x = (self.cfg.xorigin as isize + x) as usize; |
326 | let base = base_y * self.cfg.stride + base_x; |
327 | let width = self.cfg.stride - base_x; |
328 | base..base + width |
329 | } |
330 | |
331 | /// Returns the pixel at the given coordinates. |
332 | pub fn p(&self, x: usize, y: usize) -> T { |
333 | self.data[self.index(x, y)] |
334 | } |
335 | |
336 | /// Returns plane data starting from the origin. |
337 | pub fn data_origin(&self) -> &[T] { |
338 | &self.data[self.index(0, 0)..] |
339 | } |
340 | |
341 | /// Returns mutable plane data starting from the origin. |
342 | pub fn data_origin_mut(&mut self) -> &mut [T] { |
343 | let i = self.index(0, 0); |
344 | &mut self.data[i..] |
345 | } |
346 | |
347 | /// Copies data into the plane from a pixel array. |
348 | /// |
349 | /// # Panics |
350 | /// |
351 | /// - If `source_bytewidth` does not match the generic `T` of `Plane` |
352 | pub fn copy_from_raw_u8( |
353 | &mut self, |
354 | source: &[u8], |
355 | source_stride: usize, |
356 | source_bytewidth: usize, |
357 | ) { |
358 | let stride = self.cfg.stride; |
359 | |
360 | assert!(stride != 0); |
361 | assert!(source_stride != 0); |
362 | |
363 | for (self_row, source_row) in self |
364 | .data_origin_mut() |
365 | .chunks_exact_mut(stride) |
366 | .zip(source.chunks_exact(source_stride)) |
367 | { |
368 | match source_bytewidth { |
369 | 1 => { |
370 | for (self_pixel, source_pixel) in self_row.iter_mut().zip(source_row.iter()) { |
371 | *self_pixel = T::cast_from(*source_pixel); |
372 | } |
373 | } |
374 | 2 => { |
375 | assert!( |
376 | size_of::<T>() == 2, |
377 | "source bytewidth ( {}) cannot fit in Plane<u8>" , |
378 | source_bytewidth |
379 | ); |
380 | |
381 | debug_assert!(T::type_enum() == PixelType::U16); |
382 | |
383 | // SAFETY: because of the assert it is safe to assume that T == u16 |
384 | let self_row: &mut [u16] = unsafe { std::mem::transmute(self_row) }; |
385 | // SAFETY: we reinterpret the slice of bytes as a slice of elements of |
386 | // [u8; 2] to allow for more efficient codegen with from_le_bytes |
387 | let source_row: &[[u8; 2]] = unsafe { |
388 | std::slice::from_raw_parts(source_row.as_ptr().cast(), source_row.len() / 2) |
389 | }; |
390 | |
391 | for (self_pixel, bytes) in self_row.iter_mut().zip(source_row) { |
392 | *self_pixel = u16::from_le_bytes(*bytes); |
393 | } |
394 | } |
395 | |
396 | _ => {} |
397 | } |
398 | } |
399 | } |
400 | |
401 | /// Copies data from a plane into a pixel array. |
402 | /// |
403 | /// # Panics |
404 | /// |
405 | /// - If `dest_bytewidth` does not match the generic `T` of `Plane` |
406 | pub fn copy_to_raw_u8(&self, dest: &mut [u8], dest_stride: usize, dest_bytewidth: usize) { |
407 | let stride = self.cfg.stride; |
408 | for (self_row, dest_row) in self |
409 | .data_origin() |
410 | .chunks_exact(stride) |
411 | .zip(dest.chunks_exact_mut(dest_stride)) |
412 | { |
413 | match dest_bytewidth { |
414 | 1 => { |
415 | for (self_pixel, dest_pixel) in |
416 | self_row[..self.cfg.width].iter().zip(dest_row.iter_mut()) |
417 | { |
418 | *dest_pixel = u8::cast_from(*self_pixel); |
419 | } |
420 | } |
421 | 2 => { |
422 | assert!( |
423 | size_of::<T>() >= 2, |
424 | "dest bytewidth ( {}) cannot fit in Plane<u8>" , |
425 | dest_bytewidth |
426 | ); |
427 | |
428 | // SAFETY: we reinterpret the slice of bytes as a slice |
429 | // of [u8; 2] with half the elements |
430 | let dest_row: &mut [[u8; 2]] = unsafe { |
431 | std::slice::from_raw_parts_mut( |
432 | dest_row.as_mut_ptr().cast(), |
433 | dest_row.len() / 2, |
434 | ) |
435 | }; |
436 | |
437 | for (self_pixel, bytes) in self_row[..self.cfg.width].iter().zip(dest_row) { |
438 | *bytes = u16::cast_from(*self_pixel).to_le_bytes(); |
439 | } |
440 | } |
441 | |
442 | _ => {} |
443 | } |
444 | } |
445 | } |
446 | |
447 | /// Returns plane with half the resolution for width and height. |
448 | /// Downscaled with 2x2 box filter. |
449 | /// Padded to dimensions with `frame_width` and `frame_height`. |
450 | /// |
451 | /// # Panics |
452 | /// |
453 | /// - If the requested width and height are > half the input width or height |
454 | pub fn downsampled(&self, frame_width: usize, frame_height: usize) -> Plane<T> { |
455 | let src = self; |
456 | let mut new = Plane::new( |
457 | (src.cfg.width + 1) / 2, |
458 | (src.cfg.height + 1) / 2, |
459 | src.cfg.xdec + 1, |
460 | src.cfg.ydec + 1, |
461 | src.cfg.xpad / 2, |
462 | src.cfg.ypad / 2, |
463 | ); |
464 | |
465 | let width = new.cfg.width; |
466 | let height = new.cfg.height; |
467 | |
468 | assert!(width * 2 <= src.cfg.stride - src.cfg.xorigin); |
469 | assert!(height * 2 <= src.cfg.alloc_height - src.cfg.yorigin); |
470 | |
471 | let data_origin = src.data_origin(); |
472 | for (row_idx, dst_row) in new |
473 | .mut_slice(PlaneOffset::default()) |
474 | .rows_iter_mut() |
475 | .enumerate() |
476 | .take(height) |
477 | { |
478 | let src_top_row = &data_origin[(src.cfg.stride * row_idx * 2)..][..(2 * width)]; |
479 | let src_bottom_row = |
480 | &data_origin[(src.cfg.stride * (row_idx * 2 + 1))..][..(2 * width)]; |
481 | |
482 | for ((dst, a), b) in dst_row |
483 | .iter_mut() |
484 | .zip(src_top_row.chunks_exact(2)) |
485 | .zip(src_bottom_row.chunks_exact(2)) |
486 | { |
487 | let sum = u32::cast_from(a[0]) |
488 | + u32::cast_from(a[1]) |
489 | + u32::cast_from(b[0]) |
490 | + u32::cast_from(b[1]); |
491 | let avg = (sum + 2) >> 2; |
492 | *dst = T::cast_from(avg); |
493 | } |
494 | } |
495 | |
496 | new.pad(frame_width, frame_height); |
497 | new |
498 | } |
499 | |
500 | /// Returns a plane downscaled from the source plane by a factor of `scale` (not padded) |
501 | pub fn downscale<const SCALE: usize>(&self) -> Plane<T> { |
502 | let mut new_plane = Plane::new(self.cfg.width / SCALE, self.cfg.height / SCALE, 0, 0, 0, 0); |
503 | |
504 | self.downscale_in_place::<SCALE>(&mut new_plane); |
505 | |
506 | new_plane |
507 | } |
508 | |
509 | /// Downscales the source plane by a factor of `scale`, writing the result to `in_plane` (not padded) |
510 | /// |
511 | /// # Panics |
512 | /// |
513 | /// - If the current plane's width and height are not at least `SCALE` times the `in_plane`'s |
514 | #[cfg_attr (feature = "profiling" , profiling::function(downscale_in_place))] |
515 | pub fn downscale_in_place<const SCALE: usize>(&self, in_plane: &mut Plane<T>) { |
516 | let stride = in_plane.cfg.stride; |
517 | let width = in_plane.cfg.width; |
518 | let height = in_plane.cfg.height; |
519 | |
520 | if stride == 0 || self.cfg.stride == 0 { |
521 | panic!("stride cannot be 0" ); |
522 | } |
523 | |
524 | assert!(width * SCALE <= self.cfg.stride - self.cfg.xorigin); |
525 | assert!(height * SCALE <= self.cfg.alloc_height - self.cfg.yorigin); |
526 | |
527 | // SAFETY: Bounds checks have been removed for performance reasons |
528 | unsafe { |
529 | let src = self; |
530 | let box_pixels = SCALE * SCALE; |
531 | let half_box_pixels = box_pixels as u32 / 2; // Used for rounding int division |
532 | |
533 | let data_origin = src.data_origin(); |
534 | let plane_data_mut_slice = &mut *in_plane.data; |
535 | |
536 | // Iter dst rows |
537 | for row_idx in 0..height { |
538 | let dst_row = plane_data_mut_slice.get_unchecked_mut(row_idx * stride..); |
539 | // Iter dst cols |
540 | for (col_idx, dst) in dst_row.get_unchecked_mut(..width).iter_mut().enumerate() { |
541 | macro_rules! generate_inner_loop { |
542 | ($x:ty) => { |
543 | let mut sum = half_box_pixels as $x; |
544 | // Sum box of size scale * scale |
545 | |
546 | // Iter src row |
547 | for y in 0..SCALE { |
548 | let src_row_idx = row_idx * SCALE + y; |
549 | let src_row = |
550 | data_origin.get_unchecked((src_row_idx * src.cfg.stride)..); |
551 | |
552 | // Iter src col |
553 | for x in 0..SCALE { |
554 | let src_col_idx = col_idx * SCALE + x; |
555 | sum += <$x>::cast_from(*src_row.get_unchecked(src_col_idx)); |
556 | } |
557 | } |
558 | |
559 | // Box average |
560 | let avg = sum as usize / box_pixels; |
561 | *dst = T::cast_from(avg); |
562 | }; |
563 | } |
564 | |
565 | // Use 16 bit precision if overflow would not happen |
566 | if T::type_enum() == PixelType::U8 |
567 | && SCALE as u128 * SCALE as u128 * (u8::MAX as u128) |
568 | + half_box_pixels as u128 |
569 | <= u16::MAX as u128 |
570 | { |
571 | generate_inner_loop!(u16); |
572 | } else { |
573 | generate_inner_loop!(u32); |
574 | } |
575 | } |
576 | } |
577 | } |
578 | } |
579 | |
580 | /// Iterates over the pixels in the plane, skipping the padding. |
581 | pub fn iter(&self) -> PlaneIter<'_, T> { |
582 | PlaneIter::new(self) |
583 | } |
584 | |
585 | /// Iterates over the lines of the plane |
586 | pub fn rows_iter(&self) -> RowsIter<'_, T> { |
587 | RowsIter { |
588 | plane: self, |
589 | x: 0, |
590 | y: 0, |
591 | } |
592 | } |
593 | |
594 | pub fn rows_iter_mut(&mut self) -> RowsIterMut<'_, T> { |
595 | RowsIterMut { |
596 | plane: self as *mut Plane<T>, |
597 | x: 0, |
598 | y: 0, |
599 | phantom: PhantomData, |
600 | } |
601 | } |
602 | |
603 | /// Return a line |
604 | pub fn row(&self, y: isize) -> &[T] { |
605 | let range = self.row_range(0, y); |
606 | |
607 | &self.data[range] |
608 | } |
609 | } |
610 | |
611 | /// Iterator over plane pixels, skipping padding. |
612 | #[derive (Debug)] |
613 | pub struct PlaneIter<'a, T: Pixel> { |
614 | plane: &'a Plane<T>, |
615 | y: usize, |
616 | x: usize, |
617 | } |
618 | |
619 | impl<'a, T: Pixel> PlaneIter<'a, T> { |
620 | /// Creates a new iterator. |
621 | pub fn new(plane: &'a Plane<T>) -> Self { |
622 | Self { plane, y: 0, x: 0 } |
623 | } |
624 | |
625 | fn width(&self) -> usize { |
626 | self.plane.cfg.width |
627 | } |
628 | |
629 | fn height(&self) -> usize { |
630 | self.plane.cfg.height |
631 | } |
632 | } |
633 | |
634 | impl<'a, T: Pixel> Iterator for PlaneIter<'a, T> { |
635 | type Item = T; |
636 | |
637 | fn next(&mut self) -> Option<<Self as Iterator>::Item> { |
638 | if self.y == self.height() { |
639 | return None; |
640 | } |
641 | let pixel: T = self.plane.p(self.x, self.y); |
642 | if self.x == self.width() - 1 { |
643 | self.x = 0; |
644 | self.y += 1; |
645 | } else { |
646 | self.x += 1; |
647 | } |
648 | Some(pixel) |
649 | } |
650 | } |
651 | |
652 | impl<T: Pixel> FusedIterator for PlaneIter<'_, T> {} |
653 | |
654 | // A Plane, PlaneSlice, or PlaneRegion is assumed to include or be able to include |
655 | // padding on the edge of the frame |
656 | #[derive (Clone, Copy, Debug)] |
657 | pub struct PlaneSlice<'a, T: Pixel> { |
658 | pub plane: &'a Plane<T>, |
659 | pub x: isize, |
660 | pub y: isize, |
661 | } |
662 | |
663 | // A RowsIter or RowsIterMut is assumed to crop the padding from the frame edges |
664 | pub struct RowsIter<'a, T: Pixel> { |
665 | plane: &'a Plane<T>, |
666 | x: isize, |
667 | y: isize, |
668 | } |
669 | |
670 | impl<'a, T: Pixel> Iterator for RowsIter<'a, T> { |
671 | type Item = &'a [T]; |
672 | |
673 | fn next(&mut self) -> Option<Self::Item> { |
674 | if self.plane.cfg.height as isize > self.y { |
675 | // cannot directly return self.ps.row(row) due to lifetime issue |
676 | let range: Range = self.plane.row_range_cropped(self.x, self.y); |
677 | self.y += 1; |
678 | Some(&self.plane.data[range]) |
679 | } else { |
680 | None |
681 | } |
682 | } |
683 | |
684 | fn size_hint(&self) -> (usize, Option<usize>) { |
685 | let remaining: isize = self.plane.cfg.height as isize - self.y; |
686 | debug_assert!(remaining >= 0); |
687 | let remaining: usize = remaining as usize; |
688 | |
689 | (remaining, Some(remaining)) |
690 | } |
691 | } |
692 | |
693 | impl<'a, T: Pixel> ExactSizeIterator for RowsIter<'a, T> {} |
694 | impl<'a, T: Pixel> FusedIterator for RowsIter<'a, T> {} |
695 | |
696 | impl<'a, T: Pixel> PlaneSlice<'a, T> { |
697 | #[allow (unused)] |
698 | pub fn as_ptr(&self) -> *const T { |
699 | self[0].as_ptr() |
700 | } |
701 | |
702 | pub fn rows_iter(&self) -> RowsIter<'_, T> { |
703 | RowsIter { |
704 | plane: self.plane, |
705 | x: self.x, |
706 | y: self.y, |
707 | } |
708 | } |
709 | |
710 | pub fn clamp(&self) -> PlaneSlice<'a, T> { |
711 | PlaneSlice { |
712 | plane: self.plane, |
713 | x: self.x.clamp( |
714 | -(self.plane.cfg.xorigin as isize), |
715 | self.plane.cfg.width as isize, |
716 | ), |
717 | y: self.y.clamp( |
718 | -(self.plane.cfg.yorigin as isize), |
719 | self.plane.cfg.height as isize, |
720 | ), |
721 | } |
722 | } |
723 | |
724 | pub fn subslice(&self, xo: usize, yo: usize) -> PlaneSlice<'a, T> { |
725 | PlaneSlice { |
726 | plane: self.plane, |
727 | x: self.x + xo as isize, |
728 | y: self.y + yo as isize, |
729 | } |
730 | } |
731 | |
732 | pub fn reslice(&self, xo: isize, yo: isize) -> PlaneSlice<'a, T> { |
733 | PlaneSlice { |
734 | plane: self.plane, |
735 | x: self.x + xo, |
736 | y: self.y + yo, |
737 | } |
738 | } |
739 | |
740 | /// A slice starting i pixels above the current one. |
741 | pub fn go_up(&self, i: usize) -> PlaneSlice<'a, T> { |
742 | PlaneSlice { |
743 | plane: self.plane, |
744 | x: self.x, |
745 | y: self.y - i as isize, |
746 | } |
747 | } |
748 | |
749 | /// A slice starting i pixels to the left of the current one. |
750 | pub fn go_left(&self, i: usize) -> PlaneSlice<'a, T> { |
751 | PlaneSlice { |
752 | plane: self.plane, |
753 | x: self.x - i as isize, |
754 | y: self.y, |
755 | } |
756 | } |
757 | |
758 | pub fn p(&self, add_x: usize, add_y: usize) -> T { |
759 | let new_y = (self.y + add_y as isize + self.plane.cfg.yorigin as isize) as usize; |
760 | let new_x = (self.x + add_x as isize + self.plane.cfg.xorigin as isize) as usize; |
761 | self.plane.data[new_y * self.plane.cfg.stride + new_x] |
762 | } |
763 | |
764 | /// Checks if `add_y` and `add_x` lies in the allocated bounds of the |
765 | /// underlying plane. |
766 | pub fn accessible(&self, add_x: usize, add_y: usize) -> bool { |
767 | let y = (self.y + add_y as isize + self.plane.cfg.yorigin as isize) as usize; |
768 | let x = (self.x + add_x as isize + self.plane.cfg.xorigin as isize) as usize; |
769 | y < self.plane.cfg.alloc_height && x < self.plane.cfg.stride |
770 | } |
771 | |
772 | /// Checks if -`sub_x` and -`sub_y` lies in the allocated bounds of the |
773 | /// underlying plane. |
774 | pub fn accessible_neg(&self, sub_x: usize, sub_y: usize) -> bool { |
775 | let y = self.y - sub_y as isize + self.plane.cfg.yorigin as isize; |
776 | let x = self.x - sub_x as isize + self.plane.cfg.xorigin as isize; |
777 | y >= 0 && x >= 0 |
778 | } |
779 | |
780 | /// This version of the function crops off the padding on the right side of the image |
781 | pub fn row_cropped(&self, y: usize) -> &[T] { |
782 | let y = (self.y + y as isize + self.plane.cfg.yorigin as isize) as usize; |
783 | let x = (self.x + self.plane.cfg.xorigin as isize) as usize; |
784 | let start = y * self.plane.cfg.stride + x; |
785 | let width = (self.plane.cfg.width as isize - self.x) as usize; |
786 | &self.plane.data[start..start + width] |
787 | } |
788 | |
789 | /// This version of the function includes the padding on the right side of the image |
790 | pub fn row(&self, y: usize) -> &[T] { |
791 | let y = (self.y + y as isize + self.plane.cfg.yorigin as isize) as usize; |
792 | let x = (self.x + self.plane.cfg.xorigin as isize) as usize; |
793 | let start = y * self.plane.cfg.stride + x; |
794 | let width = self.plane.cfg.stride - x; |
795 | &self.plane.data[start..start + width] |
796 | } |
797 | } |
798 | |
799 | impl<'a, T: Pixel> Index<usize> for PlaneSlice<'a, T> { |
800 | type Output = [T]; |
801 | fn index(&self, index: usize) -> &Self::Output { |
802 | let range: Range = self.plane.row_range(self.x, self.y + index as isize); |
803 | &self.plane.data[range] |
804 | } |
805 | } |
806 | |
807 | pub struct PlaneMutSlice<'a, T: Pixel> { |
808 | pub plane: &'a mut Plane<T>, |
809 | pub x: isize, |
810 | pub y: isize, |
811 | } |
812 | |
813 | pub struct RowsIterMut<'a, T: Pixel> { |
814 | plane: *mut Plane<T>, |
815 | x: isize, |
816 | y: isize, |
817 | phantom: PhantomData<&'a mut Plane<T>>, |
818 | } |
819 | |
820 | impl<'a, T: Pixel> Iterator for RowsIterMut<'a, T> { |
821 | type Item = &'a mut [T]; |
822 | |
823 | fn next(&mut self) -> Option<Self::Item> { |
824 | // SAFETY: there could not be a concurrent call using a mutable reference to the plane |
825 | let plane = unsafe { &mut *self.plane }; |
826 | if plane.cfg.height as isize > self.y { |
827 | // cannot directly return self.ps.row(row) due to lifetime issue |
828 | let range = plane.row_range_cropped(self.x, self.y); |
829 | self.y += 1; |
830 | Some(&mut plane.data[range]) |
831 | } else { |
832 | None |
833 | } |
834 | } |
835 | |
836 | fn size_hint(&self) -> (usize, Option<usize>) { |
837 | // SAFETY: there could not be a concurrent call using a mutable reference to the plane |
838 | let plane = unsafe { &mut *self.plane }; |
839 | let remaining = plane.cfg.height as isize - self.y; |
840 | debug_assert!(remaining >= 0); |
841 | let remaining = remaining as usize; |
842 | |
843 | (remaining, Some(remaining)) |
844 | } |
845 | } |
846 | |
847 | impl<'a, T: Pixel> ExactSizeIterator for RowsIterMut<'a, T> {} |
848 | impl<'a, T: Pixel> FusedIterator for RowsIterMut<'a, T> {} |
849 | |
850 | impl<'a, T: Pixel> PlaneMutSlice<'a, T> { |
851 | #[allow (unused)] |
852 | pub fn rows_iter(&self) -> RowsIter<'_, T> { |
853 | RowsIter { |
854 | plane: self.plane, |
855 | x: self.x, |
856 | y: self.y, |
857 | } |
858 | } |
859 | |
860 | pub fn rows_iter_mut(&mut self) -> RowsIterMut<'_, T> { |
861 | RowsIterMut { |
862 | plane: self.plane as *mut Plane<T>, |
863 | x: self.x, |
864 | y: self.y, |
865 | phantom: PhantomData, |
866 | } |
867 | } |
868 | |
869 | #[allow (unused)] |
870 | pub fn subslice(&mut self, xo: usize, yo: usize) -> PlaneMutSlice<'_, T> { |
871 | PlaneMutSlice { |
872 | plane: self.plane, |
873 | x: self.x + xo as isize, |
874 | y: self.y + yo as isize, |
875 | } |
876 | } |
877 | } |
878 | |
879 | impl<'a, T: Pixel> Index<usize> for PlaneMutSlice<'a, T> { |
880 | type Output = [T]; |
881 | fn index(&self, index: usize) -> &Self::Output { |
882 | let range: Range = self.plane.row_range(self.x, self.y + index as isize); |
883 | &self.plane.data[range] |
884 | } |
885 | } |
886 | |
887 | impl<'a, T: Pixel> IndexMut<usize> for PlaneMutSlice<'a, T> { |
888 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { |
889 | let range: Range = self.plane.row_range(self.x, self.y + index as isize); |
890 | &mut self.plane.data[range] |
891 | } |
892 | } |
893 | |
894 | #[cfg (test)] |
895 | pub mod test { |
896 | use super::*; |
897 | |
898 | #[cfg (all(target_arch = "wasm32" , target_os = "unknown" ))] |
899 | use wasm_bindgen_test::*; |
900 | |
901 | #[cfg (all(target_arch = "wasm32" , target_os = "unknown" ))] |
902 | wasm_bindgen_test_configure!(run_in_browser); |
903 | |
904 | #[cfg_attr (all(target_arch = "wasm32" , target_os = "unknown" ), wasm_bindgen_test)] |
905 | #[test ] |
906 | fn copy_from_raw_u8() { |
907 | #[rustfmt::skip] |
908 | let mut plane = Plane::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, |
909 | 0, 0, 0, 0, 0, 0, 0, 0, |
910 | 0, 0, 0, 0, 0, 0, 0, 0, |
911 | 0, 0, 1, 2, 3, 4, 0, 0, |
912 | 0, 0, 8, 7, 6, 5, 0, 0, |
913 | 0, 0, 9, 8, 7, 6, 0, 0, |
914 | 0, 0, 2, 3, 4, 5, 0, 0, |
915 | 0, 0, 0, 0, 0, 0, 0, 0, |
916 | 0, 0, 0, 0, 0, 0, 0, 0], |
917 | 8, |
918 | ); |
919 | |
920 | let input = vec![42u8; 64]; |
921 | |
922 | plane.copy_from_raw_u8(&input, 8, 1); |
923 | |
924 | println!("{:?}" , &plane.data[..10]); |
925 | |
926 | assert_eq!(&input[..64], &plane.data[..64]); |
927 | } |
928 | |
929 | #[cfg_attr (all(target_arch = "wasm32" , target_os = "unknown" ), wasm_bindgen_test)] |
930 | #[test ] |
931 | fn copy_to_raw_u8() { |
932 | #[rustfmt::skip] |
933 | let plane = Plane::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, |
934 | 0, 0, 0, 0, 0, 0, 0, 0, |
935 | 0, 0, 0, 0, 0, 0, 0, 0, |
936 | 0, 0, 1, 2, 3, 4, 0, 0, |
937 | 0, 0, 8, 7, 6, 5, 0, 0, |
938 | 0, 0, 9, 8, 7, 6, 0, 0, |
939 | 0, 0, 2, 3, 4, 5, 0, 0, |
940 | 0, 0, 0, 0, 0, 0, 0, 0, |
941 | 0, 0, 0, 0, 0, 0, 0, 0], |
942 | 8, |
943 | ); |
944 | |
945 | let mut output = vec![42u8; 64]; |
946 | |
947 | plane.copy_to_raw_u8(&mut output, 8, 1); |
948 | |
949 | println!("{:?}" , &plane.data[..10]); |
950 | |
951 | assert_eq!(&output[..64], &plane.data[..64]); |
952 | } |
953 | |
954 | #[cfg_attr (all(target_arch = "wasm32" , target_os = "unknown" ), wasm_bindgen_test)] |
955 | #[test ] |
956 | fn test_plane_downsample() { |
957 | #[rustfmt::skip] |
958 | let plane = Plane::<u8> { |
959 | data: PlaneData::from_slice(&[ |
960 | 0, 0, 0, 0, 0, 0, 0, 0, |
961 | 0, 0, 0, 0, 0, 0, 0, 0, |
962 | 0, 0, 0, 0, 0, 0, 0, 0, |
963 | 0, 0, 1, 2, 3, 4, 0, 0, |
964 | 0, 0, 8, 7, 6, 5, 0, 0, |
965 | 0, 0, 9, 8, 7, 6, 0, 0, |
966 | 0, 0, 2, 3, 4, 5, 0, 0, |
967 | 0, 0, 0, 0, 0, 0, 0, 0, |
968 | 0, 0, 0, 0, 0, 0, 0, 0, |
969 | ]), |
970 | cfg: PlaneConfig { |
971 | stride: 8, |
972 | alloc_height: 9, |
973 | width: 4, |
974 | height: 4, |
975 | xdec: 0, |
976 | ydec: 0, |
977 | xpad: 0, |
978 | ypad: 0, |
979 | xorigin: 2, |
980 | yorigin: 3, |
981 | }, |
982 | }; |
983 | let downsampled = plane.downsampled(4, 4); |
984 | |
985 | #[rustfmt::skip] |
986 | let expected = &[ |
987 | 5, 5, |
988 | 6, 6, |
989 | ]; |
990 | |
991 | let v: Vec<_> = downsampled.iter().collect(); |
992 | |
993 | assert_eq!(&expected[..], &v[..]); |
994 | } |
995 | |
996 | #[cfg_attr (all(target_arch = "wasm32" , target_os = "unknown" ), wasm_bindgen_test)] |
997 | #[test ] |
998 | fn test_plane_downsample_odd() { |
999 | #[rustfmt::skip] |
1000 | let plane = Plane::<u8> { |
1001 | data: PlaneData::from_slice(&[ |
1002 | 0, 0, 0, 0, 0, 0, 0, 0, |
1003 | 0, 0, 0, 0, 0, 0, 0, 0, |
1004 | 0, 0, 0, 0, 0, 0, 0, 0, |
1005 | 0, 0, 1, 2, 3, 4, 0, 0, |
1006 | 0, 0, 8, 7, 6, 5, 0, 0, |
1007 | 0, 0, 9, 8, 7, 6, 0, 0, |
1008 | 0, 0, 2, 3, 4, 5, 0, 0, |
1009 | 0, 0, 0, 0, 0, 0, 0, 0, |
1010 | 0, 0, 0, 0, 0, 0, 0, 0, |
1011 | ]), |
1012 | cfg: PlaneConfig { |
1013 | stride: 8, |
1014 | alloc_height: 9, |
1015 | width: 3, |
1016 | height: 3, |
1017 | xdec: 0, |
1018 | ydec: 0, |
1019 | xpad: 0, |
1020 | ypad: 0, |
1021 | xorigin: 2, |
1022 | yorigin: 3, |
1023 | }, |
1024 | }; |
1025 | let downsampled = plane.downsampled(3, 3); |
1026 | |
1027 | #[rustfmt::skip] |
1028 | let expected = &[ |
1029 | 5, 5, |
1030 | 6, 6, |
1031 | ]; |
1032 | |
1033 | let v: Vec<_> = downsampled.iter().collect(); |
1034 | assert_eq!(&expected[..], &v[..]); |
1035 | } |
1036 | |
1037 | #[cfg_attr (all(target_arch = "wasm32" , target_os = "unknown" ), wasm_bindgen_test)] |
1038 | #[test ] |
1039 | fn test_plane_downscale() { |
1040 | #[rustfmt::skip] |
1041 | let plane = Plane::<u8> { |
1042 | data: PlaneData::from_slice(&[ |
1043 | 0, 0, 0, 0, 0, 0, 0, 0, |
1044 | 0, 0, 0, 0, 0, 0, 0, 0, |
1045 | 0, 0, 0, 0, 0, 0, 0, 0, |
1046 | 0, 0, 0, 1, 4, 5, 0, 0, |
1047 | 0, 0, 2, 3, 6, 7, 0, 0, |
1048 | 0, 0, 8, 9, 7, 5, 0, 0, |
1049 | 0, 0, 9, 8, 3, 1, 0, 0, |
1050 | 0, 0, 0, 0, 0, 0, 0, 0, |
1051 | 0, 0, 0, 0, 0, 0, 0, 0, |
1052 | ]), |
1053 | cfg: PlaneConfig { |
1054 | stride: 8, |
1055 | alloc_height: 9, |
1056 | width: 4, |
1057 | height: 4, |
1058 | xdec: 0, |
1059 | ydec: 0, |
1060 | xpad: 0, |
1061 | ypad: 0, |
1062 | xorigin: 2, |
1063 | yorigin: 3, |
1064 | }, |
1065 | }; |
1066 | let downscaled = plane.downscale::<2>(); |
1067 | |
1068 | #[rustfmt::skip] |
1069 | let expected = &[ |
1070 | 2, 6, |
1071 | 9, 4 |
1072 | ]; |
1073 | |
1074 | let v: Vec<_> = downscaled.iter().collect(); |
1075 | assert_eq!(&expected[..], &v[..]); |
1076 | } |
1077 | |
1078 | #[cfg_attr (all(target_arch = "wasm32" , target_os = "unknown" ), wasm_bindgen_test)] |
1079 | #[test ] |
1080 | fn test_plane_downscale_odd() { |
1081 | #[rustfmt::skip] |
1082 | let plane = Plane::<u8> { |
1083 | data: PlaneData::from_slice(&[ |
1084 | 1, 2, 3, 4, 1, 2, 3, 4, |
1085 | 0, 0, 8, 7, 6, 5, 8, 7, |
1086 | 6, 5, 8, 7, 6, 5, 8, 7, |
1087 | 6, 5, 8, 7, 0, 0, 2, 3, |
1088 | 4, 5, 0, 0, 9, 8, 7, 6, |
1089 | 0, 0, 0, 0, 2, 3, 4, 5, |
1090 | 0, 0, 0, 0, 2, 3, 4, 5, |
1091 | ]), |
1092 | cfg: PlaneConfig { |
1093 | stride: 8, |
1094 | alloc_height: 7, |
1095 | width: 8, |
1096 | height: 7, |
1097 | xdec: 0, |
1098 | ydec: 0, |
1099 | xpad: 0, |
1100 | ypad: 0, |
1101 | xorigin: 0, |
1102 | yorigin: 0, |
1103 | }, |
1104 | }; |
1105 | |
1106 | let downscaled = plane.downscale::<3>(); |
1107 | |
1108 | #[rustfmt::skip] |
1109 | let expected = &[ |
1110 | 4, 5, |
1111 | 3, 3 |
1112 | ]; |
1113 | |
1114 | let v: Vec<_> = downscaled.iter().collect(); |
1115 | assert_eq!(&expected[..], &v[..]); |
1116 | } |
1117 | |
1118 | #[cfg_attr (all(target_arch = "wasm32" , target_os = "unknown" ), wasm_bindgen_test)] |
1119 | #[test ] |
1120 | fn test_plane_downscale_odd_2() { |
1121 | #[rustfmt::skip] |
1122 | let plane = Plane::<u8> { |
1123 | data: PlaneData::from_slice(&[ |
1124 | 9, 8, 3, 1, 0, 1, 4, 5, 0, 0, |
1125 | 0, 1, 4, 5, 0, 0, 0, 0, 0, 0, |
1126 | 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, |
1127 | 0, 2, 3, 6, 7, 0, 0, 0, 0, 0, |
1128 | 0, 0, 8, 9, 7, 5, 0, 0, 0, 0, |
1129 | 9, 8, 3, 1, 0, 1, 4, 5, 0, 0, |
1130 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1131 | 0, 0, 0, 0, 0, 2, 3, 6, 7, 0, |
1132 | 0, 0, 0, 0, 0, 0, 8, 9, 7, 5, |
1133 | 0, 0, 0, 0, 9, 8, 3, 1, 0, 0 |
1134 | ]), |
1135 | cfg: PlaneConfig { |
1136 | stride: 10, |
1137 | alloc_height: 10, |
1138 | width: 10, |
1139 | height: 10, |
1140 | xdec: 0, |
1141 | ydec: 0, |
1142 | xpad: 0, |
1143 | ypad: 0, |
1144 | xorigin: 0, |
1145 | yorigin: 0, |
1146 | }, |
1147 | }; |
1148 | let downscaled = plane.downscale::<3>(); |
1149 | |
1150 | #[rustfmt::skip] |
1151 | let expected = &[ |
1152 | 3, 1, 2, |
1153 | 4, 4, 1, |
1154 | 0, 0, 4, |
1155 | ]; |
1156 | |
1157 | let v: Vec<_> = downscaled.iter().collect(); |
1158 | assert_eq!(&expected[..], &v[..]); |
1159 | } |
1160 | |
1161 | #[cfg_attr (all(target_arch = "wasm32" , target_os = "unknown" ), wasm_bindgen_test)] |
1162 | #[test ] |
1163 | fn test_plane_pad() { |
1164 | #[rustfmt::skip] |
1165 | let mut plane = Plane::<u8> { |
1166 | data: PlaneData::from_slice(&[ |
1167 | 0, 0, 0, 0, 0, 0, 0, 0, |
1168 | 0, 0, 0, 0, 0, 0, 0, 0, |
1169 | 0, 0, 0, 0, 0, 0, 0, 0, |
1170 | 0, 0, 1, 2, 3, 4, 0, 0, |
1171 | 0, 0, 8, 7, 6, 5, 0, 0, |
1172 | 0, 0, 9, 8, 7, 6, 0, 0, |
1173 | 0, 0, 2, 3, 4, 5, 0, 0, |
1174 | 0, 0, 0, 0, 0, 0, 0, 0, |
1175 | 0, 0, 0, 0, 0, 0, 0, 0, |
1176 | ]), |
1177 | cfg: PlaneConfig { |
1178 | stride: 8, |
1179 | alloc_height: 9, |
1180 | width: 4, |
1181 | height: 4, |
1182 | xdec: 0, |
1183 | ydec: 0, |
1184 | xpad: 0, |
1185 | ypad: 0, |
1186 | xorigin: 2, |
1187 | yorigin: 3, |
1188 | }, |
1189 | }; |
1190 | plane.pad(4, 4); |
1191 | |
1192 | #[rustfmt::skip] |
1193 | assert_eq!( |
1194 | &[ |
1195 | 1, 1, 1, 2, 3, 4, 4, 4, |
1196 | 1, 1, 1, 2, 3, 4, 4, 4, |
1197 | 1, 1, 1, 2, 3, 4, 4, 4, |
1198 | 1, 1, 1, 2, 3, 4, 4, 4, |
1199 | 8, 8, 8, 7, 6, 5, 5, 5, |
1200 | 9, 9, 9, 8, 7, 6, 6, 6, |
1201 | 2, 2, 2, 3, 4, 5, 5, 5, |
1202 | 2, 2, 2, 3, 4, 5, 5, 5, |
1203 | 2, 2, 2, 3, 4, 5, 5, 5, |
1204 | ][..], |
1205 | &plane.data[..] |
1206 | ); |
1207 | } |
1208 | |
1209 | #[cfg_attr (all(target_arch = "wasm32" , target_os = "unknown" ), wasm_bindgen_test)] |
1210 | #[test ] |
1211 | fn test_pixel_iterator() { |
1212 | #[rustfmt::skip] |
1213 | let plane = Plane::<u8> { |
1214 | data: PlaneData::from_slice(&[ |
1215 | 0, 0, 0, 0, 0, 0, 0, 0, |
1216 | 0, 0, 0, 0, 0, 0, 0, 0, |
1217 | 0, 0, 0, 0, 0, 0, 0, 0, |
1218 | 0, 0, 1, 2, 3, 4, 0, 0, |
1219 | 0, 0, 8, 7, 6, 5, 0, 0, |
1220 | 0, 0, 9, 8, 7, 6, 0, 0, |
1221 | 0, 0, 2, 3, 4, 5, 0, 0, |
1222 | 0, 0, 0, 0, 0, 0, 0, 0, |
1223 | 0, 0, 0, 0, 0, 0, 0, 0, |
1224 | ]), |
1225 | cfg: PlaneConfig { |
1226 | stride: 8, |
1227 | alloc_height: 9, |
1228 | width: 4, |
1229 | height: 4, |
1230 | xdec: 0, |
1231 | ydec: 0, |
1232 | xpad: 0, |
1233 | ypad: 0, |
1234 | xorigin: 2, |
1235 | yorigin: 3, |
1236 | }, |
1237 | }; |
1238 | |
1239 | let pixels: Vec<u8> = plane.iter().collect(); |
1240 | |
1241 | assert_eq!( |
1242 | &[1, 2, 3, 4, 8, 7, 6, 5, 9, 8, 7, 6, 2, 3, 4, 5,][..], |
1243 | &pixels[..] |
1244 | ); |
1245 | } |
1246 | } |
1247 | |