1use crate::image::Image;
13use crate::layout;
14use crate::layout::{Layout, MismatchedPixelError, TexelLayout, TryMend};
15use crate::texel::{AsTexel, Texel};
16use core::ops::Range;
17
18#[derive(Clone, Copy, PartialEq, Eq)]
20pub struct StrideSpec {
21 pub width: usize,
23 pub height: usize,
25 pub element: TexelLayout,
30 pub width_stride: usize,
32 pub height_stride: usize,
34 pub offset: usize,
36}
37
38#[derive(Clone, Copy, PartialEq, Eq)]
48pub struct StridedBytes {
49 spec: StrideSpec,
50 total: usize,
52}
53
54#[derive(Clone, Copy, PartialEq, Eq)]
60pub struct Strides<T> {
61 inner: StridedBytes,
62 texel: Texel<T>,
63}
64
65#[derive(Debug)]
67pub struct BadStrideError {
68 #[allow(dead_code)]
71 kind: BadStrideKind,
72}
73
74#[derive(Debug)]
75enum BadStrideKind {
76 UnalignedOffset,
77 UnalignedWidthStride,
78 UnalignedHeightStride,
79 OutOfMemory,
80}
81
82pub struct StridedBufferRef<'data> {
84 layout: StridedBytes,
85 data: &'data [u8],
86}
87
88pub struct StridedBufferMut<'data> {
111 layout: StridedBytes,
112 data: &'data mut [u8],
113}
114
115impl StrideSpec {
116 fn matches(&self, other: &Self) -> bool {
118 self.element.size() == other.element.size()
119 && self.width == other.width
120 && self.height == other.height
121 }
122
123 fn has_contiguous_rows(&self) -> bool {
124 self.element.size() == self.width_stride
125 }
126
127 fn has_contiguous_cols(&self) -> bool {
128 self.element.size() == self.height_stride
129 }
130
131 fn element_start(&self, row: usize, col: usize) -> usize {
132 (row * self.height_stride) + (col * self.width_stride) + self.offset
133 }
134
135 fn element(&self, row: usize, col: usize) -> Range<usize> {
136 let start = self.element_start(row, col);
137 start..start + self.element.size()
138 }
139
140 fn contiguous_row(&self, row: usize) -> Range<usize> {
141 let start = self.element_start(row, 0);
142 let length = self.width * self.element.size();
143 start..start + length
144 }
145
146 fn contiguous_col(&self, col: usize) -> Range<usize> {
147 let start = self.element_start(0, col);
148 let length = self.height * self.element.size();
149 start..start + length
150 }
151
152 fn end(&self) -> Option<usize> {
153 if self.height == 0 || self.width == 0 {
154 return Some(self.offset);
155 }
156
157 let max_w = self.width - 1;
158 let max_h = self.height - 1;
159
160 let max_w_offset = max_w.checked_mul(self.width_stride)?;
161 let max_h_offset = max_h.checked_mul(self.height_stride)?;
162
163 let relative_past_end = self
164 .element
165 .size()
166 .checked_add(max_h_offset)?
167 .checked_add(max_w_offset)?;
168
169 let total = relative_past_end.checked_add(self.offset)?;
173 Some(total)
174 }
175}
176
177impl StridedBytes {
178 pub fn new(spec: StrideSpec) -> Result<Self, BadStrideError> {
184 if spec.offset % spec.element.align() != 0 {
185 return Err(BadStrideKind::UnalignedOffset.into());
186 }
187
188 if spec.width_stride % spec.element.align() != 0 {
189 return Err(BadStrideKind::UnalignedWidthStride.into());
190 }
191
192 if spec.height_stride % spec.element.align() != 0 {
193 return Err(BadStrideKind::UnalignedHeightStride.into());
194 }
195
196 let total = spec.end().ok_or(BadStrideKind::OutOfMemory)?;
197
198 Ok(StridedBytes { spec, total })
199 }
200
201 pub fn with_repeated_width_and_height(
203 element: TexelLayout,
204 width: usize,
205 height: usize,
206 ) -> Self {
207 StridedBytes {
208 spec: StrideSpec {
209 element,
210 width,
211 height,
212 height_stride: 0,
213 width_stride: 0,
214 offset: 0,
215 },
216 total: element.size(),
217 }
218 }
219
220 pub fn with_column_major(matrix: layout::MatrixBytes) -> Self {
225 StridedBytes {
226 spec: StrideSpec {
227 element: matrix.element(),
228 width: matrix.width(),
229 height: matrix.height(),
230 height_stride: matrix.element().size(),
231 width_stride: matrix.height() * matrix.element().size(),
234 offset: 0,
235 },
236 total: matrix.byte_len(),
237 }
238 }
239
240 pub fn with_row_major(matrix: layout::MatrixBytes) -> Self {
245 StridedBytes {
246 spec: StrideSpec {
247 element: matrix.element(),
248 width: matrix.width(),
249 height: matrix.height(),
250 height_stride: matrix.width() * matrix.element().size(),
253 width_stride: matrix.element().size(),
254 offset: 0,
255 },
256 total: matrix.byte_len(),
257 }
258 }
259
260 pub fn spec(&self) -> StrideSpec {
262 self.spec
263 }
264
265 pub fn shrink_element(&mut self, new: TexelLayout) {
269 self.spec.element = self.spec.element.infimum(new);
270 }
271
272 fn matches(&self, other: &Self) -> bool {
273 self.spec.matches(&other.spec)
274 }
275
276 fn contiguous_rows(&self) -> Option<impl Iterator<Item = Range<usize>> + '_> {
277 if self.spec.has_contiguous_rows() {
278 Some((0..self.spec.height).map(move |row| self.spec.contiguous_row(row)))
279 } else {
280 None
281 }
282 }
283
284 fn contiguous_columns(&self) -> Option<impl Iterator<Item = Range<usize>> + '_> {
285 if self.spec.has_contiguous_cols() {
286 Some((0..self.spec.width).map(move |row| self.spec.contiguous_col(row)))
287 } else {
288 None
289 }
290 }
291
292 fn pixel(&self, x: usize, y: usize) -> Range<usize> {
293 self.spec.element(x, y)
294 }
295}
296
297impl<T> Strides<T> {
298 pub fn with_texel(texel: Texel<T>, bytes: StridedBytes) -> Option<Self> {
302 if TexelLayout::from(texel) == bytes.spec.element {
303 Some(Strides {
304 inner: bytes,
305 texel,
306 })
307 } else {
308 None
309 }
310 }
311
312 pub fn spec(&self) -> StrideSpec {
313 self.inner.spec()
314 }
315
316 pub fn texel(&self) -> Texel<T> {
317 self.texel
318 }
319}
320
321impl<'data> StridedBufferRef<'data> {
322 pub fn new(image: &'data Image<impl StridedLayout>) -> Self {
324 let layout = image.layout().strided();
325 let data = &image.as_bytes()[..layout.total];
326 StridedBufferRef { layout, data }
327 }
328
329 pub fn with_bytes(layout: StridedBytes, content: &'data [u8]) -> Option<Self> {
334 let data = content
335 .get(..layout.total)
336 .filter(|data| data.as_ptr() as usize % layout.spec.element.align() == 0)?;
337 Some(StridedBufferRef { layout, data })
338 }
339
340 pub fn with_repeated_element<T: AsTexel>(el: &'data T, width: usize, height: usize) -> Self {
341 let texel = T::texel();
342 let layout = StridedBytes::with_repeated_width_and_height(texel.into(), width, height);
343 let data = texel.to_bytes(core::slice::from_ref(el));
344 StridedBufferRef { layout, data }
345 }
346
347 pub fn shrink_element(&mut self, new: TexelLayout) -> TexelLayout {
349 self.layout.shrink_element(new);
350 self.layout.spec.element
351 }
352
353 pub fn as_ref(&self) -> StridedBufferRef<'_> {
355 StridedBufferRef {
356 layout: self.layout,
357 data: &*self.data,
358 }
359 }
360}
361
362impl<'data> StridedBufferMut<'data> {
363 pub fn new(image: &'data mut Image<impl StridedLayout>) -> Self {
365 let layout = image.layout().strided();
366 let data = &mut image.as_bytes_mut()[..layout.total];
367 StridedBufferMut { layout, data }
368 }
369
370 pub fn with_bytes(layout: StridedBytes, content: &'data mut [u8]) -> Option<Self> {
375 let data = content
376 .get_mut(..layout.total)
377 .filter(|data| data.as_ptr() as usize % layout.spec.element.align() == 0)?;
378 Some(StridedBufferMut { layout, data })
379 }
380
381 pub fn shrink_element(&mut self, new: TexelLayout) -> TexelLayout {
383 self.layout.shrink_element(new);
384 self.layout.spec.element
385 }
386
387 pub fn copy_from_image(&mut self, source: StridedBufferRef<'_>) {
391 assert!(self.layout.matches(&source.layout), "Mismatching layouts.");
392 if let Some(rows) = self.layout.contiguous_rows() {
395 if let Some(src_rows) = source.layout.contiguous_rows() {
396 for (row, src) in rows.zip(src_rows) {
397 self.data[row].copy_from_slice(&source.data[src]);
398 }
399 return;
400 }
401 }
402
403 if let Some(cols) = self.layout.contiguous_columns() {
404 if let Some(src_cols) = source.layout.contiguous_columns() {
405 for (col, src) in cols.zip(src_cols) {
406 self.data[col].copy_from_slice(&source.data[src]);
407 }
408 return;
409 }
410 }
411
412 for x in 0..self.layout.spec.width {
414 for y in 0..self.layout.spec.height {
415 let into = self.layout.pixel(x, y);
416 let from = source.layout.pixel(x, y);
417 self.data[into].copy_from_slice(&source.data[from]);
419 }
420 }
421 }
422
423 pub fn as_ref(&self) -> StridedBufferRef<'_> {
425 StridedBufferRef {
426 layout: self.layout,
427 data: &*self.data,
428 }
429 }
430
431 pub fn into_ref(self) -> StridedBufferRef<'data> {
433 StridedBufferRef {
434 layout: self.layout,
435 data: self.data,
436 }
437 }
438}
439
440pub trait StridedLayout: Layout {
445 fn strided(&self) -> StridedBytes;
450}
451
452impl Layout for StridedBytes {
453 fn byte_len(&self) -> usize {
454 self.total
455 }
456}
457
458impl StridedLayout for StridedBytes {
459 fn strided(&self) -> StridedBytes {
460 *self
461 }
462}
463
464impl<T: StridedLayout> StridedLayout for &'_ T {
465 fn strided(&self) -> StridedBytes {
466 (**self).strided()
467 }
468}
469
470impl<T: StridedLayout> StridedLayout for &'_ mut T {
471 fn strided(&self) -> StridedBytes {
472 (**self).strided()
473 }
474}
475
476impl<T: StridedLayout> layout::Decay<T> for StridedBytes {
477 fn decay(from: T) -> Self {
478 from.strided()
479 }
480}
481
482impl<P: AsTexel> StridedLayout for layout::Matrix<P> {
483 fn strided(&self) -> StridedBytes {
484 let matrix: layout::MatrixBytes = self.clone().into();
485 StridedBytes::with_row_major(matrix)
486 }
487}
488
489impl<P> Layout for Strides<P> {
490 fn byte_len(&self) -> usize {
491 self.inner.total
492 }
493}
494
495impl<P> StridedLayout for Strides<P> {
496 fn strided(&self) -> StridedBytes {
497 self.inner.clone()
498 }
499}
500
501impl From<BadStrideKind> for BadStrideError {
502 fn from(kind: BadStrideKind) -> Self {
503 BadStrideError { kind }
504 }
505}
506
507impl From<&'_ StridedBytes> for StrideSpec {
508 fn from(layout: &'_ StridedBytes) -> Self {
509 layout.spec()
510 }
511}
512
513impl<P> TryMend<StridedBytes> for Texel<P> {
515 type Into = Strides<P>;
516 type Err = MismatchedPixelError;
517
518 fn try_mend(self, matrix: &StridedBytes) -> Result<Strides<P>, Self::Err> {
519 Strides::with_texel(self, *matrix).ok_or_else(MismatchedPixelError::default)
520 }
521}
522
523#[test]
524fn align_validation() {
525 let matrix = layout::MatrixBytes::from_width_height(TexelLayout::from_pixel::<u16>(), 2, 2)
527 .expect("Valid matrix");
528 let layout = StridedBytes::with_row_major(matrix);
529
530 let bad_offset = StrideSpec {
531 offset: 1,
532 ..layout.spec
533 };
534 assert!(StridedBytes::new(bad_offset).is_err());
535 let bad_pitch = StrideSpec {
536 width_stride: 5,
537 ..layout.spec
538 };
539 assert!(StridedBytes::new(bad_pitch).is_err());
540}
541
542#[test]
543fn image_copies() {
544 let matrix = layout::MatrixBytes::from_width_height(TexelLayout::from_pixel::<u8>(), 2, 2)
545 .expect("Valid matrix");
546 let row_layout = StridedBytes::with_row_major(matrix);
547 let col_layout = StridedBytes::with_column_major(matrix);
548
549 let src = Image::with_bytes(row_layout, &[0u8, 1, 2, 3]);
550
551 let mut dst = Image::new(row_layout);
552 StridedBufferMut::new(&mut dst).copy_from_image(StridedBufferRef::new(&src));
553 assert_eq!(dst.as_bytes(), &[0u8, 1, 2, 3], "Still in same order");
554
555 let mut dst = Image::new(col_layout);
556 StridedBufferMut::new(&mut dst).copy_from_image(StridedBufferRef::new(&src));
557 assert_eq!(
558 dst.as_bytes(),
559 &[0u8, 2, 1, 3],
560 "In transposed matrix order"
561 );
562}