ai_image/images/flat.rs
1//! Image representations for ffi.
2//!
3//! # Usage
4//!
5//! Imagine you want to offer a very simple ffi interface: The caller provides an image buffer and
6//! your program creates a thumbnail from it and dumps that image as `png`. This module is designed
7//! to help you transition from raw memory data to Rust representation.
8//!
9//! ```no_run
10//! use std::ptr;
11//! use core::slice;
12//! use ai_image::Rgb;
13//! use ai_image::flat::{FlatSamples, SampleLayout};
14//! use ai_image::imageops::thumbnail;
15//!
16//! #[no_mangle]
17//! pub extern "C" fn store_rgb8_compressed(
18//! data: *const u8, len: usize,
19//! layout: *const SampleLayout
20//! )
21//! -> bool
22//! {
23//! let samples = unsafe { slice::from_raw_parts(data, len) };
24//! let layout = unsafe { ptr::read(layout) };
25//!
26//! let buffer = FlatSamples {
27//! samples,
28//! layout,
29//! color_hint: None,
30//! };
31//!
32//! let view = match buffer.as_view::<Rgb<u8>>() {
33//! Err(_) => return false, // Invalid layout.
34//! Ok(view) => view,
35//! };
36//!
37//! thumbnail(&view, 64, 64)
38//! .save("output.png")
39//! .map(|_| true)
40//! .unwrap_or_else(|_| false)
41//! }
42//! ```
43//!
44use alloc::vec::Vec;
45use core::marker::PhantomData;
46use core::ops::{Deref, Index, IndexMut};
47use core::{cmp, error, fmt};
48
49use num_traits::Zero;
50
51use crate::color::ColorType;
52use crate::error::{
53 DecodingError, ImageError, ImageFormatHint, ParameterError, ParameterErrorKind,
54 UnsupportedError, UnsupportedErrorKind,
55};
56use crate::traits::Pixel;
57use crate::{GenericImage, GenericImageView, ImageBuffer};
58
59/// A flat buffer over a (multi channel) image.
60///
61/// In contrast to `ImageBuffer`, this representation of a sample collection is much more lenient
62/// in the layout thereof. It also allows grouping by color planes instead of by pixel as long as
63/// the strides of each extent are constant. This struct itself has no invariants on the strides
64/// but not every possible configuration can be interpreted as a [`GenericImageView`] or
65/// [`GenericImage`]. The methods [`as_view`] and [`as_view_mut`] construct the actual implementors
66/// of these traits and perform necessary checks. To manually perform this and other layout checks
67/// use [`is_normal`] or [`has_aliased_samples`].
68///
69/// Instances can be constructed not only by hand. The buffer instances returned by library
70/// functions such as [`ImageBuffer::as_flat_samples`] guarantee that the conversion to a generic
71/// image or generic view succeeds. A very different constructor is [`with_monocolor`]. It uses a
72/// single pixel as the backing storage for an arbitrarily sized read-only raster by mapping each
73/// pixel to the same samples by setting some strides to `0`.
74///
75/// [`GenericImage`]: ../trait.GenericImage.html
76/// [`GenericImageView`]: ../trait.GenericImageView.html
77/// [`ImageBuffer::as_flat_samples`]: ../struct.ImageBuffer.html#method.as_flat_samples
78/// [`is_normal`]: #method.is_normal
79/// [`has_aliased_samples`]: #method.has_aliased_samples
80/// [`as_view`]: #method.as_view
81/// [`as_view_mut`]: #method.as_view_mut
82/// [`with_monocolor`]: #method.with_monocolor
83#[derive(Clone, Debug)]
84pub struct FlatSamples<Buffer> {
85 /// Underlying linear container holding sample values.
86 pub samples: Buffer,
87
88 /// A `repr(C)` description of the layout of buffer samples.
89 pub layout: SampleLayout,
90
91 /// Supplementary color information.
92 ///
93 /// You may keep this as `None` in most cases. This is NOT checked in `View` or other
94 /// converters. It is intended mainly as a way for types that convert to this buffer type to
95 /// attach their otherwise static color information. A dynamic image representation could
96 /// however use this to resolve representational ambiguities such as the order of RGB channels.
97 pub color_hint: Option<ColorType>,
98}
99
100/// A ffi compatible description of a sample buffer.
101#[repr(C)]
102#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
103pub struct SampleLayout {
104 /// The number of channels in the color representation of the image.
105 pub channels: u8,
106
107 /// Add this to an index to get to the sample in the next channel.
108 pub channel_stride: usize,
109
110 /// The width of the represented image.
111 pub width: u32,
112
113 /// Add this to an index to get to the next sample in x-direction.
114 pub width_stride: usize,
115
116 /// The height of the represented image.
117 pub height: u32,
118
119 /// Add this to an index to get to the next sample in y-direction.
120 pub height_stride: usize,
121}
122
123/// Helper struct for an unnamed (stride, length) pair.
124#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
125struct Dim(usize, usize);
126
127impl SampleLayout {
128 /// Describe a row-major image packed in all directions.
129 ///
130 /// The resulting will surely be `NormalForm::RowMajorPacked`. It can therefore be converted to
131 /// safely to an `ImageBuffer` with a large enough underlying buffer.
132 ///
133 /// ```
134 /// # use ai_image::flat::{NormalForm, SampleLayout};
135 /// let layout = SampleLayout::row_major_packed(3, 640, 480);
136 /// assert!(layout.is_normal(NormalForm::RowMajorPacked));
137 /// ```
138 ///
139 /// # Panics
140 ///
141 /// On platforms where `usize` has the same size as `u32` this panics when the resulting stride
142 /// in the `height` direction would be larger than `usize::MAX`. On other platforms
143 /// where it can surely accommodate `u8::MAX * u32::MAX`, this can never happen.
144 #[must_use]
145 pub fn row_major_packed(channels: u8, width: u32, height: u32) -> Self {
146 let height_stride = (channels as usize).checked_mul(width as usize).expect(
147 "Row major packed image can not be described because it does not fit into memory",
148 );
149 SampleLayout {
150 channels,
151 channel_stride: 1,
152 width,
153 width_stride: channels as usize,
154 height,
155 height_stride,
156 }
157 }
158
159 /// Describe a column-major image packed in all directions.
160 ///
161 /// The resulting will surely be `NormalForm::ColumnMajorPacked`. This is not particularly
162 /// useful for conversion but can be used to describe such a buffer without pitfalls.
163 ///
164 /// ```
165 /// # use ai_image::flat::{NormalForm, SampleLayout};
166 /// let layout = SampleLayout::column_major_packed(3, 640, 480);
167 /// assert!(layout.is_normal(NormalForm::ColumnMajorPacked));
168 /// ```
169 ///
170 /// # Panics
171 ///
172 /// On platforms where `usize` has the same size as `u32` this panics when the resulting stride
173 /// in the `width` direction would be larger than `usize::MAX`. On other platforms
174 /// where it can surely accommodate `u8::MAX * u32::MAX`, this can never happen.
175 #[must_use]
176 pub fn column_major_packed(channels: u8, width: u32, height: u32) -> Self {
177 let width_stride = (channels as usize).checked_mul(height as usize).expect(
178 "Column major packed image can not be described because it does not fit into memory",
179 );
180 SampleLayout {
181 channels,
182 channel_stride: 1,
183 height,
184 height_stride: channels as usize,
185 width,
186 width_stride,
187 }
188 }
189
190 /// Get the strides for indexing matrix-like `[(c, w, h)]`.
191 ///
192 /// For a row-major layout with grouped samples, this tuple is strictly
193 /// increasing.
194 #[must_use]
195 pub fn strides_cwh(&self) -> (usize, usize, usize) {
196 (self.channel_stride, self.width_stride, self.height_stride)
197 }
198
199 /// Get the dimensions `(channels, width, height)`.
200 ///
201 /// The interface is optimized for use with `strides_cwh` instead. The channel extent will be
202 /// before width and height.
203 #[must_use]
204 pub fn extents(&self) -> (usize, usize, usize) {
205 (
206 self.channels as usize,
207 self.width as usize,
208 self.height as usize,
209 )
210 }
211
212 /// Tuple of bounds in the order of coordinate inputs.
213 ///
214 /// This function should be used whenever working with image coordinates opposed to buffer
215 /// coordinates. The only difference compared to `extents` is the output type.
216 #[must_use]
217 pub fn bounds(&self) -> (u8, u32, u32) {
218 (self.channels, self.width, self.height)
219 }
220
221 /// Get the minimum length of a buffer such that all in-bounds samples have valid indices.
222 ///
223 /// This method will allow zero strides, allowing compact representations of monochrome images.
224 /// To check that no aliasing occurs, try `check_alias_invariants`. For compact images (no
225 /// aliasing and no unindexed samples) this is `width*height*channels`. But for both of the
226 /// other cases, the reasoning is slightly more involved.
227 ///
228 /// # Explanation
229 ///
230 /// Note that there is a difference between `min_length` and the index of the sample
231 /// 'one-past-the-end'. This is due to strides that may be larger than the dimension below.
232 ///
233 /// ## Example with holes
234 ///
235 /// Let's look at an example of a grayscale image with
236 /// * `width_stride = 1`
237 /// * `width = 2`
238 /// * `height_stride = 3`
239 /// * `height = 2`
240 ///
241 /// ```text
242 /// | x x | x x m | $
243 /// min_length m ^
244 /// ^ one-past-the-end $
245 /// ```
246 ///
247 /// The difference is also extreme for empty images with large strides. The one-past-the-end
248 /// sample index is still as large as the largest of these strides while `min_length = 0`.
249 ///
250 /// ## Example with aliasing
251 ///
252 /// The concept gets even more important when you allow samples to alias each other. Here we
253 /// have the buffer of a small grayscale image where this is the case, this time we will first
254 /// show the buffer and then the individual rows below.
255 ///
256 /// * `width_stride = 1`
257 /// * `width = 3`
258 /// * `height_stride = 2`
259 /// * `height = 2`
260 ///
261 /// ```text
262 /// 1 2 3 4 5 m
263 /// |1 2 3| row one
264 /// |3 4 5| row two
265 /// ^ m min_length
266 /// ^ ??? one-past-the-end
267 /// ```
268 ///
269 /// This time 'one-past-the-end' is not even simply the largest stride times the extent of its
270 /// dimension. That still points inside the image because `height*height_stride = 4` but also
271 /// `index_of(1, 2) = 4`.
272 #[must_use]
273 pub fn min_length(&self) -> Option<usize> {
274 if self.width == 0 || self.height == 0 || self.channels == 0 {
275 return Some(0);
276 }
277
278 self.index(self.channels - 1, self.width - 1, self.height - 1)
279 .and_then(|idx| idx.checked_add(1))
280 }
281
282 /// Check if a buffer of length `len` is large enough.
283 #[must_use]
284 pub fn fits(&self, len: usize) -> bool {
285 self.min_length().is_some_and(|min| len >= min)
286 }
287
288 /// The extents of this array, in order of increasing strides.
289 fn increasing_stride_dims(&self) -> [Dim; 3] {
290 // Order extents by strides, then check that each is less equal than the next stride.
291 let mut grouped: [Dim; 3] = [
292 Dim(self.channel_stride, self.channels as usize),
293 Dim(self.width_stride, self.width as usize),
294 Dim(self.height_stride, self.height as usize),
295 ];
296
297 grouped.sort();
298
299 let (min_dim, mid_dim, max_dim) = (grouped[0], grouped[1], grouped[2]);
300 assert!(min_dim.stride() <= mid_dim.stride() && mid_dim.stride() <= max_dim.stride());
301
302 grouped
303 }
304
305 /// If there are any samples aliasing each other.
306 ///
307 /// If this is not the case, it would always be safe to allow mutable access to two different
308 /// samples at the same time. Otherwise, this operation would need additional checks. When one
309 /// dimension overflows `usize` with its stride we also consider this aliasing.
310 #[must_use]
311 pub fn has_aliased_samples(&self) -> bool {
312 let grouped = self.increasing_stride_dims();
313 let (min_dim, mid_dim, max_dim) = (grouped[0], grouped[1], grouped[2]);
314
315 let min_size = match min_dim.checked_len() {
316 None => return true,
317 Some(size) => size,
318 };
319
320 let mid_size = match mid_dim.checked_len() {
321 None => return true,
322 Some(size) => size,
323 };
324
325 if max_dim.checked_len().is_none() {
326 return true;
327 }
328
329 // Each higher dimension must walk over all of one lower dimension.
330 min_size > mid_dim.stride() || mid_size > max_dim.stride()
331 }
332
333 /// Check if a buffer fulfills the requirements of a normal form.
334 ///
335 /// Certain conversions have preconditions on the structure of the sample buffer that are not
336 /// captured (by design) by the type system. These are then checked before the conversion. Such
337 /// checks can all be done in constant time and will not inspect the buffer content. You can
338 /// perform these checks yourself when the conversion is not required at this moment but maybe
339 /// still performed later.
340 #[must_use]
341 pub fn is_normal(&self, form: NormalForm) -> bool {
342 if self.has_aliased_samples() {
343 return false;
344 }
345
346 if form >= NormalForm::PixelPacked && self.channel_stride != 1 {
347 return false;
348 }
349
350 if form >= NormalForm::ImagePacked {
351 // has aliased already checked for overflows.
352 let grouped = self.increasing_stride_dims();
353 let (min_dim, mid_dim, max_dim) = (grouped[0], grouped[1], grouped[2]);
354
355 if 1 != min_dim.stride() {
356 return false;
357 }
358
359 if min_dim.len() != mid_dim.stride() {
360 return false;
361 }
362
363 if mid_dim.len() != max_dim.stride() {
364 return false;
365 }
366 }
367
368 if form >= NormalForm::RowMajorPacked {
369 if self.width_stride != self.channels as usize {
370 return false;
371 }
372
373 if self.width as usize * self.width_stride != self.height_stride {
374 return false;
375 }
376 }
377
378 if form >= NormalForm::ColumnMajorPacked {
379 if self.height_stride != self.channels as usize {
380 return false;
381 }
382
383 if self.height as usize * self.height_stride != self.width_stride {
384 return false;
385 }
386 }
387
388 true
389 }
390
391 /// Check that the pixel and the channel index are in bounds.
392 ///
393 /// An in-bound coordinate does not yet guarantee that the corresponding calculation of a
394 /// buffer index does not overflow. However, if such a buffer large enough to hold all samples
395 /// actually exists in memory, this property of course follows.
396 #[must_use]
397 pub fn in_bounds(&self, channel: u8, x: u32, y: u32) -> bool {
398 channel < self.channels && x < self.width && y < self.height
399 }
400
401 /// Resolve the index of a particular sample.
402 ///
403 /// `None` if the index is outside the bounds or does not fit into a `usize`.
404 #[must_use]
405 pub fn index(&self, channel: u8, x: u32, y: u32) -> Option<usize> {
406 if !self.in_bounds(channel, x, y) {
407 return None;
408 }
409
410 self.index_ignoring_bounds(channel as usize, x as usize, y as usize)
411 }
412
413 /// Get the theoretical position of sample (channel, x, y).
414 ///
415 /// The 'check' is for overflow during index calculation, not that it is contained in the
416 /// image. Two samples may return the same index, even when one of them is out of bounds. This
417 /// happens when all strides are `0`, i.e. the image is an arbitrarily large monochrome image.
418 #[must_use]
419 pub fn index_ignoring_bounds(&self, channel: usize, x: usize, y: usize) -> Option<usize> {
420 let idx_c = channel.checked_mul(self.channel_stride);
421 let idx_x = x.checked_mul(self.width_stride);
422 let idx_y = y.checked_mul(self.height_stride);
423
424 let (Some(idx_c), Some(idx_x), Some(idx_y)) = (idx_c, idx_x, idx_y) else {
425 return None;
426 };
427
428 Some(0usize)
429 .and_then(|b| b.checked_add(idx_c))
430 .and_then(|b| b.checked_add(idx_x))
431 .and_then(|b| b.checked_add(idx_y))
432 }
433
434 /// Get an index provided it is inbouds.
435 ///
436 /// Assumes that the image is backed by some sufficiently large buffer. Then computation can
437 /// not overflow as we could represent the maximum coordinate. Since overflow is defined either
438 /// way, this method can not be unsafe.
439 ///
440 /// Behavior is *unspecified* if the index is out of bounds or this sample layout would require
441 /// a buffer larger than `isize::MAX` bytes.
442 #[must_use]
443 pub fn in_bounds_index(&self, c: u8, x: u32, y: u32) -> usize {
444 let (c_stride, x_stride, y_stride) = self.strides_cwh();
445 (y as usize * y_stride) + (x as usize * x_stride) + (c as usize * c_stride)
446 }
447
448 /// Shrink the image to the minimum of current and given extents.
449 ///
450 /// This does not modify the strides, so that the resulting sample buffer may have holes
451 /// created by the shrinking operation. Shrinking could also lead to an non-aliasing image when
452 /// samples had aliased each other before.
453 pub fn shrink_to(&mut self, channels: u8, width: u32, height: u32) {
454 self.channels = self.channels.min(channels);
455 self.width = self.width.min(width);
456 self.height = self.height.min(height);
457 }
458}
459
460impl Dim {
461 fn stride(self) -> usize {
462 self.0
463 }
464
465 /// Length of this dimension in memory.
466 fn checked_len(self) -> Option<usize> {
467 self.0.checked_mul(self.1)
468 }
469
470 fn len(self) -> usize {
471 self.0 * self.1
472 }
473}
474
475impl<Buffer> FlatSamples<Buffer> {
476 /// Get the strides for indexing matrix-like `[(c, w, h)]`.
477 ///
478 /// For a row-major layout with grouped samples, this tuple is strictly
479 /// increasing.
480 pub fn strides_cwh(&self) -> (usize, usize, usize) {
481 self.layout.strides_cwh()
482 }
483
484 /// Get the dimensions `(channels, width, height)`.
485 ///
486 /// The interface is optimized for use with `strides_cwh` instead. The channel extent will be
487 /// before width and height.
488 pub fn extents(&self) -> (usize, usize, usize) {
489 self.layout.extents()
490 }
491
492 /// Tuple of bounds in the order of coordinate inputs.
493 ///
494 /// This function should be used whenever working with image coordinates opposed to buffer
495 /// coordinates. The only difference compared to `extents` is the output type.
496 pub fn bounds(&self) -> (u8, u32, u32) {
497 self.layout.bounds()
498 }
499
500 /// Get a reference based version.
501 pub fn as_ref<T>(&self) -> FlatSamples<&[T]>
502 where
503 Buffer: AsRef<[T]>,
504 {
505 FlatSamples {
506 samples: self.samples.as_ref(),
507 layout: self.layout,
508 color_hint: self.color_hint,
509 }
510 }
511
512 /// Get a mutable reference based version.
513 pub fn as_mut<T>(&mut self) -> FlatSamples<&mut [T]>
514 where
515 Buffer: AsMut<[T]>,
516 {
517 FlatSamples {
518 samples: self.samples.as_mut(),
519 layout: self.layout,
520 color_hint: self.color_hint,
521 }
522 }
523
524 /// Copy the data into an owned vector.
525 pub fn to_vec<T>(&self) -> FlatSamples<Vec<T>>
526 where
527 T: Clone,
528 Buffer: AsRef<[T]>,
529 {
530 FlatSamples {
531 samples: self.samples.as_ref().to_vec(),
532 layout: self.layout,
533 color_hint: self.color_hint,
534 }
535 }
536
537 /// Get a reference to a single sample.
538 ///
539 /// This more restrictive than the method based on `std::ops::Index` but guarantees to properly
540 /// check all bounds and not panic as long as `Buffer::as_ref` does not do so.
541 ///
542 /// ```
543 /// # use ai_image::{RgbImage};
544 /// let flat = RgbImage::new(480, 640).into_flat_samples();
545 ///
546 /// // Get the blue channel at (10, 10).
547 /// assert!(flat.get_sample(1, 10, 10).is_some());
548 ///
549 /// // There is no alpha channel.
550 /// assert!(flat.get_sample(3, 10, 10).is_none());
551 /// ```
552 ///
553 /// For cases where a special buffer does not provide `AsRef<[T]>`, consider encapsulating
554 /// bounds checks with `min_length` in a type similar to `View`. Then you may use
555 /// `in_bounds_index` as a small speedup over the index calculation of this method which relies
556 /// on `index_ignoring_bounds` since it can not have a-priori knowledge that the sample
557 /// coordinate is in fact backed by any memory buffer.
558 pub fn get_sample<T>(&self, channel: u8, x: u32, y: u32) -> Option<&T>
559 where
560 Buffer: AsRef<[T]>,
561 {
562 self.index(channel, x, y)
563 .and_then(|idx| self.samples.as_ref().get(idx))
564 }
565
566 /// Get a mutable reference to a single sample.
567 ///
568 /// This more restrictive than the method based on `std::ops::IndexMut` but guarantees to
569 /// properly check all bounds and not panic as long as `Buffer::as_ref` does not do so.
570 /// Contrary to conversion to `ViewMut`, this does not require that samples are packed since it
571 /// does not need to convert samples to a color representation.
572 ///
573 /// **WARNING**: Note that of course samples may alias, so that the mutable reference returned
574 /// here can in fact modify more than the coordinate in the argument.
575 ///
576 /// ```
577 /// # use ai_image::{RgbImage};
578 /// let mut flat = RgbImage::new(480, 640).into_flat_samples();
579 ///
580 /// // Assign some new color to the blue channel at (10, 10).
581 /// *flat.get_mut_sample(1, 10, 10).unwrap() = 255;
582 ///
583 /// // There is no alpha channel.
584 /// assert!(flat.get_mut_sample(3, 10, 10).is_none());
585 /// ```
586 ///
587 /// For cases where a special buffer does not provide `AsRef<[T]>`, consider encapsulating
588 /// bounds checks with `min_length` in a type similar to `View`. Then you may use
589 /// `in_bounds_index` as a small speedup over the index calculation of this method which relies
590 /// on `index_ignoring_bounds` since it can not have a-priori knowledge that the sample
591 /// coordinate is in fact backed by any memory buffer.
592 pub fn get_mut_sample<T>(&mut self, channel: u8, x: u32, y: u32) -> Option<&mut T>
593 where
594 Buffer: AsMut<[T]>,
595 {
596 match self.index(channel, x, y) {
597 None => None,
598 Some(idx) => self.samples.as_mut().get_mut(idx),
599 }
600 }
601
602 /// View this buffer as an image over some type of pixel.
603 ///
604 /// This first ensures that all in-bounds coordinates refer to valid indices in the sample
605 /// buffer. It also checks that the specified pixel format expects the same number of channels
606 /// that are present in this buffer. Neither are larger nor a smaller number will be accepted.
607 /// There is no automatic conversion.
608 pub fn as_view<P>(&self) -> Result<View<&[P::Subpixel], P>, Error>
609 where
610 P: Pixel,
611 Buffer: AsRef<[P::Subpixel]>,
612 {
613 FlatSamples {
614 samples: self.samples.as_ref(),
615 layout: self.layout,
616 color_hint: self.color_hint,
617 }
618 .into_view()
619 }
620
621 /// Convert this descriptor into a readable image.
622 ///
623 /// An owned version of [`Self::as_view`] that uses the original buffer type.
624 pub(crate) fn into_view<P>(self) -> Result<View<Buffer, P>, Error>
625 where
626 P: Pixel,
627 Buffer: AsRef<[P::Subpixel]>,
628 {
629 if self.layout.channels != P::CHANNEL_COUNT {
630 return Err(Error::ChannelCountMismatch(
631 self.layout.channels,
632 P::CHANNEL_COUNT,
633 ));
634 }
635
636 if !self.layout.fits(self.samples.as_ref().len()) {
637 return Err(Error::TooLarge);
638 }
639
640 Ok(View {
641 inner: self,
642 phantom: PhantomData,
643 })
644 }
645
646 /// View this buffer but keep mutability at a sample level.
647 ///
648 /// This is similar to `as_view` but subtly different from `as_view_mut`. The resulting type
649 /// can be used as a `GenericImage` with the same prior invariants needed as for `as_view`.
650 /// It can not be used as a mutable `GenericImage` but does not need channels to be packed in
651 /// their pixel representation.
652 ///
653 /// This first ensures that all in-bounds coordinates refer to valid indices in the sample
654 /// buffer. It also checks that the specified pixel format expects the same number of channels
655 /// that are present in this buffer. Neither are larger nor a smaller number will be accepted.
656 /// There is no automatic conversion.
657 ///
658 /// **WARNING**: Note that of course samples may alias, so that the mutable reference returned
659 /// for one sample can in fact modify other samples as well. Sometimes exactly this is
660 /// intended.
661 pub fn as_view_with_mut_samples<P>(&mut self) -> Result<View<&mut [P::Subpixel], P>, Error>
662 where
663 P: Pixel,
664 Buffer: AsMut<[P::Subpixel]>,
665 {
666 if self.layout.channels != P::CHANNEL_COUNT {
667 return Err(Error::ChannelCountMismatch(
668 self.layout.channels,
669 P::CHANNEL_COUNT,
670 ));
671 }
672
673 let as_mut = self.samples.as_mut();
674 if !self.layout.fits(as_mut.len()) {
675 return Err(Error::TooLarge);
676 }
677
678 Ok(View {
679 inner: FlatSamples {
680 samples: as_mut,
681 layout: self.layout,
682 color_hint: self.color_hint,
683 },
684 phantom: PhantomData,
685 })
686 }
687
688 /// Interpret this buffer as a mutable image.
689 ///
690 /// To succeed, the pixels in this buffer may not alias each other and the samples of each
691 /// pixel must be packed (i.e. `channel_stride` is `1`). The number of channels must be
692 /// consistent with the channel count expected by the pixel format.
693 ///
694 /// This is similar to an `ImageBuffer` except it is a temporary view that is not normalized as
695 /// strongly. To get an owning version, consider copying the data into an `ImageBuffer`. This
696 /// provides many more operations, is possibly faster (if not you may want to open an issue) is
697 /// generally polished. You can also try to convert this buffer inline, see
698 /// `ImageBuffer::from_raw`.
699 pub fn as_view_mut<P>(&mut self) -> Result<ViewMut<&mut [P::Subpixel], P>, Error>
700 where
701 P: Pixel,
702 Buffer: AsMut<[P::Subpixel]>,
703 {
704 if !self.layout.is_normal(NormalForm::PixelPacked) {
705 return Err(Error::NormalFormRequired(NormalForm::PixelPacked));
706 }
707
708 if self.layout.channels != P::CHANNEL_COUNT {
709 return Err(Error::ChannelCountMismatch(
710 self.layout.channels,
711 P::CHANNEL_COUNT,
712 ));
713 }
714
715 let as_mut = self.samples.as_mut();
716 if !self.layout.fits(as_mut.len()) {
717 return Err(Error::TooLarge);
718 }
719
720 Ok(ViewMut {
721 inner: FlatSamples {
722 samples: as_mut,
723 layout: self.layout,
724 color_hint: self.color_hint,
725 },
726 phantom: PhantomData,
727 })
728 }
729
730 /// View the samples as a slice.
731 ///
732 /// The slice is not limited to the region of the image and not all sample indices are valid
733 /// indices into this buffer. See `image_mut_slice` as an alternative.
734 pub fn as_slice<T>(&self) -> &[T]
735 where
736 Buffer: AsRef<[T]>,
737 {
738 self.samples.as_ref()
739 }
740
741 /// View the samples as a slice.
742 ///
743 /// The slice is not limited to the region of the image and not all sample indices are valid
744 /// indices into this buffer. See `image_mut_slice` as an alternative.
745 pub fn as_mut_slice<T>(&mut self) -> &mut [T]
746 where
747 Buffer: AsMut<[T]>,
748 {
749 self.samples.as_mut()
750 }
751
752 /// Return the portion of the buffer that holds sample values.
753 ///
754 /// This may fail when the coordinates in this image are either out-of-bounds of the underlying
755 /// buffer or can not be represented. Note that the slice may have holes that do not correspond
756 /// to any sample in the image represented by it.
757 pub fn image_slice<T>(&self) -> Option<&[T]>
758 where
759 Buffer: AsRef<[T]>,
760 {
761 let min_length = self.min_length()?;
762
763 let slice = self.samples.as_ref();
764 if slice.len() < min_length {
765 return None;
766 }
767
768 Some(&slice[..min_length])
769 }
770
771 /// Mutable portion of the buffer that holds sample values.
772 pub fn image_mut_slice<T>(&mut self) -> Option<&mut [T]>
773 where
774 Buffer: AsMut<[T]>,
775 {
776 let min_length = self.min_length()?;
777
778 let slice = self.samples.as_mut();
779 if slice.len() < min_length {
780 return None;
781 }
782
783 Some(&mut slice[..min_length])
784 }
785
786 /// Move the data into an image buffer.
787 ///
788 /// This does **not** convert the sample layout. The buffer needs to be in packed row-major form
789 /// before calling this function. In case of an error, returns the buffer again so that it does
790 /// not release any allocation.
791 pub fn try_into_buffer<P>(self) -> Result<ImageBuffer<P, Buffer>, (Error, Self)>
792 where
793 P: Pixel + 'static,
794 P::Subpixel: 'static,
795 Buffer: Deref<Target = [P::Subpixel]>,
796 {
797 if !self.is_normal(NormalForm::RowMajorPacked) {
798 return Err((Error::NormalFormRequired(NormalForm::RowMajorPacked), self));
799 }
800
801 if self.layout.channels != P::CHANNEL_COUNT {
802 return Err((
803 Error::ChannelCountMismatch(self.layout.channels, P::CHANNEL_COUNT),
804 self,
805 ));
806 }
807
808 if !self.fits(self.samples.deref().len()) {
809 return Err((Error::TooLarge, self));
810 }
811
812 Ok(
813 ImageBuffer::from_raw(self.layout.width, self.layout.height, self.samples)
814 .unwrap_or_else(|| {
815 panic!("Preconditions should have been ensured before conversion")
816 }),
817 )
818 }
819
820 /// Get the minimum length of a buffer such that all in-bounds samples have valid indices.
821 ///
822 /// This method will allow zero strides, allowing compact representations of monochrome images.
823 /// To check that no aliasing occurs, try `check_alias_invariants`. For compact images (no
824 /// aliasing and no unindexed samples) this is `width*height*channels`. But for both of the
825 /// other cases, the reasoning is slightly more involved.
826 ///
827 /// # Explanation
828 ///
829 /// Note that there is a difference between `min_length` and the index of the sample
830 /// 'one-past-the-end'. This is due to strides that may be larger than the dimension below.
831 ///
832 /// ## Example with holes
833 ///
834 /// Let's look at an example of a grayscale image with
835 /// * `width_stride = 1`
836 /// * `width = 2`
837 /// * `height_stride = 3`
838 /// * `height = 2`
839 ///
840 /// ```text
841 /// | x x | x x m | $
842 /// min_length m ^
843 /// ^ one-past-the-end $
844 /// ```
845 ///
846 /// The difference is also extreme for empty images with large strides. The one-past-the-end
847 /// sample index is still as large as the largest of these strides while `min_length = 0`.
848 ///
849 /// ## Example with aliasing
850 ///
851 /// The concept gets even more important when you allow samples to alias each other. Here we
852 /// have the buffer of a small grayscale image where this is the case, this time we will first
853 /// show the buffer and then the individual rows below.
854 ///
855 /// * `width_stride = 1`
856 /// * `width = 3`
857 /// * `height_stride = 2`
858 /// * `height = 2`
859 ///
860 /// ```text
861 /// 1 2 3 4 5 m
862 /// |1 2 3| row one
863 /// |3 4 5| row two
864 /// ^ m min_length
865 /// ^ ??? one-past-the-end
866 /// ```
867 ///
868 /// This time 'one-past-the-end' is not even simply the largest stride times the extent of its
869 /// dimension. That still points inside the image because `height*height_stride = 4` but also
870 /// `index_of(1, 2) = 4`.
871 pub fn min_length(&self) -> Option<usize> {
872 self.layout.min_length()
873 }
874
875 /// Check if a buffer of length `len` is large enough.
876 pub fn fits(&self, len: usize) -> bool {
877 self.layout.fits(len)
878 }
879
880 /// If there are any samples aliasing each other.
881 ///
882 /// If this is not the case, it would always be safe to allow mutable access to two different
883 /// samples at the same time. Otherwise, this operation would need additional checks. When one
884 /// dimension overflows `usize` with its stride we also consider this aliasing.
885 pub fn has_aliased_samples(&self) -> bool {
886 self.layout.has_aliased_samples()
887 }
888
889 /// Check if a buffer fulfills the requirements of a normal form.
890 ///
891 /// Certain conversions have preconditions on the structure of the sample buffer that are not
892 /// captured (by design) by the type system. These are then checked before the conversion. Such
893 /// checks can all be done in constant time and will not inspect the buffer content. You can
894 /// perform these checks yourself when the conversion is not required at this moment but maybe
895 /// still performed later.
896 pub fn is_normal(&self, form: NormalForm) -> bool {
897 self.layout.is_normal(form)
898 }
899
900 /// Check that the pixel and the channel index are in bounds.
901 ///
902 /// An in-bound coordinate does not yet guarantee that the corresponding calculation of a
903 /// buffer index does not overflow. However, if such a buffer large enough to hold all samples
904 /// actually exists in memory, this property of course follows.
905 pub fn in_bounds(&self, channel: u8, x: u32, y: u32) -> bool {
906 self.layout.in_bounds(channel, x, y)
907 }
908
909 /// Resolve the index of a particular sample.
910 ///
911 /// `None` if the index is outside the bounds or does not fit into a `usize`.
912 pub fn index(&self, channel: u8, x: u32, y: u32) -> Option<usize> {
913 self.layout.index(channel, x, y)
914 }
915
916 /// Get the theoretical position of sample (x, y, channel).
917 ///
918 /// The 'check' is for overflow during index calculation, not that it is contained in the
919 /// image. Two samples may return the same index, even when one of them is out of bounds. This
920 /// happens when all strides are `0`, i.e. the image is an arbitrarily large monochrome image.
921 pub fn index_ignoring_bounds(&self, channel: usize, x: usize, y: usize) -> Option<usize> {
922 self.layout.index_ignoring_bounds(channel, x, y)
923 }
924
925 /// Get an index provided it is inbouds.
926 ///
927 /// Assumes that the image is backed by some sufficiently large buffer. Then computation can
928 /// not overflow as we could represent the maximum coordinate. Since overflow is defined either
929 /// way, this method can not be unsafe.
930 pub fn in_bounds_index(&self, channel: u8, x: u32, y: u32) -> usize {
931 self.layout.in_bounds_index(channel, x, y)
932 }
933
934 /// Shrink the image to the minimum of current and given extents.
935 ///
936 /// This does not modify the strides, so that the resulting sample buffer may have holes
937 /// created by the shrinking operation. Shrinking could also lead to an non-aliasing image when
938 /// samples had aliased each other before.
939 pub fn shrink_to(&mut self, channels: u8, width: u32, height: u32) {
940 self.layout.shrink_to(channels, width, height);
941 }
942}
943
944impl<'buf, Subpixel> FlatSamples<&'buf [Subpixel]> {
945 /// Create a monocolor image from a single pixel.
946 ///
947 /// This can be used as a very cheap source of a `GenericImageView` with an arbitrary number of
948 /// pixels of a single color, without any dynamic allocation.
949 ///
950 /// ## Examples
951 ///
952 /// ```
953 /// # fn paint_something<T>(_: T) {}
954 /// use ai_image::{flat::FlatSamples, GenericImage, RgbImage, Rgb};
955 ///
956 /// let background = Rgb([20, 20, 20]);
957 /// let bg = FlatSamples::with_monocolor(&background, 200, 200);
958 ///
959 /// let mut image = RgbImage::new(200, 200);
960 /// paint_something(&mut image);
961 ///
962 /// // Reset the canvas
963 /// image.copy_from(&bg.as_view().unwrap(), 0, 0);
964 /// ```
965 pub fn with_monocolor<P>(pixel: &'buf P, width: u32, height: u32) -> Self
966 where
967 P: Pixel<Subpixel = Subpixel>,
968 Subpixel: crate::Primitive,
969 {
970 FlatSamples {
971 samples: pixel.channels(),
972 layout: SampleLayout {
973 channels: P::CHANNEL_COUNT,
974 channel_stride: 1,
975 width,
976 width_stride: 0,
977 height,
978 height_stride: 0,
979 },
980
981 // TODO this value is never set. It should be set in all places where the Pixel type implements PixelWithColorType
982 color_hint: None,
983 }
984 }
985}
986
987/// A flat buffer that can be used as an image view.
988///
989/// This is a nearly trivial wrapper around a buffer but at least sanitizes by checking the buffer
990/// length first and constraining the pixel type.
991///
992/// Note that this does not eliminate panics as the `AsRef<[T]>` implementation of `Buffer` may be
993/// unreliable, i.e. return different buffers at different times. This of course is a non-issue for
994/// all common collections where the bounds check once must be enough.
995///
996/// # Inner invariants
997///
998/// * For all indices inside bounds, the corresponding index is valid in the buffer
999/// * `P::channel_count()` agrees with `self.inner.layout.channels`
1000#[derive(Clone, Debug)]
1001pub struct View<Buffer, P: Pixel>
1002where
1003 Buffer: AsRef<[P::Subpixel]>,
1004{
1005 inner: FlatSamples<Buffer>,
1006 phantom: PhantomData<P>,
1007}
1008
1009/// Type alias for a view based on a pixel's channels.
1010pub type ViewOfPixel<'lt, P> = View<&'lt [<P as Pixel>::Subpixel], P>;
1011
1012/// A mutable owning version of a flat buffer.
1013///
1014/// While this wraps a buffer similar to `ImageBuffer`, this is mostly intended as a utility. The
1015/// library endorsed normalized representation is still `ImageBuffer`. Also, the implementation of
1016/// `AsMut<[P::Subpixel]>` must always yield the same buffer. Therefore there is no public way to
1017/// construct this with an owning buffer.
1018///
1019/// # Inner invariants
1020///
1021/// * For all indices inside bounds, the corresponding index is valid in the buffer
1022/// * There is no aliasing of samples
1023/// * The samples are packed, i.e. `self.inner.layout.sample_stride == 1`
1024/// * `P::channel_count()` agrees with `self.inner.layout.channels`
1025#[derive(Clone, Debug)]
1026pub struct ViewMut<Buffer, P: Pixel>
1027where
1028 Buffer: AsMut<[P::Subpixel]>,
1029{
1030 inner: FlatSamples<Buffer>,
1031 phantom: PhantomData<P>,
1032}
1033
1034/// Type alias for a mutable view based on a pixel's channels.
1035pub type ViewMutOfPixel<'lt, P> = ViewMut<&'lt mut [<P as Pixel>::Subpixel], P>;
1036
1037/// Denotes invalid flat sample buffers when trying to convert to stricter types.
1038///
1039/// The biggest use case being `ImageBuffer` which expects closely packed
1040/// samples in a row major matrix representation. But this error type may be
1041/// reused for other import functions. A more versatile user may also try to
1042/// correct the underlying representation depending on the error variant.
1043#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1044pub enum Error {
1045 /// The represented image was too large.
1046 ///
1047 /// The optional value denotes a possibly accepted maximal bound.
1048 TooLarge,
1049
1050 /// The represented image can not use this representation.
1051 ///
1052 /// Has an additional value of the normalized form that would be accepted.
1053 NormalFormRequired(NormalForm),
1054
1055 /// The color format did not match the channel count.
1056 ///
1057 /// In some cases you might be able to fix this by lowering the reported pixel count of the
1058 /// buffer without touching the strides.
1059 ///
1060 /// In very special circumstances you *may* do the opposite. This is **VERY** dangerous but not
1061 /// directly memory unsafe although that will likely alias pixels. One scenario is when you
1062 /// want to construct an `Rgba` image but have only 3 bytes per pixel and for some reason don't
1063 /// care about the value of the alpha channel even though you need `Rgba`.
1064 ChannelCountMismatch(u8, u8),
1065
1066 /// Deprecated - `ChannelCountMismatch` is used instead
1067 WrongColor(ColorType),
1068}
1069
1070/// Different normal forms of buffers.
1071///
1072/// A normal form is an unaliased buffer with some additional constraints. The `ÌmageBuffer` uses
1073/// row major form with packed samples.
1074#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1075pub enum NormalForm {
1076 /// No pixel aliases another.
1077 ///
1078 /// Unaliased also guarantees that all index calculations in the image bounds using
1079 /// `dim_index*dim_stride` (such as `x*width_stride + y*height_stride`) do not overflow.
1080 Unaliased,
1081
1082 /// At least pixels are packed.
1083 ///
1084 /// Images of these types can wrap `[T]`-slices into the standard color types. This is a
1085 /// precondition for `GenericImage` which requires by-reference access to pixels.
1086 PixelPacked,
1087
1088 /// All samples are packed.
1089 ///
1090 /// This is orthogonal to `PixelPacked`. It requires that there are no holes in the image but
1091 /// it is not necessary that the pixel samples themselves are adjacent. An example of this
1092 /// behaviour is a planar image layout.
1093 ImagePacked,
1094
1095 /// The samples are in row-major form and all samples are packed.
1096 ///
1097 /// In addition to `PixelPacked` and `ImagePacked` this also asserts that the pixel matrix is
1098 /// in row-major form.
1099 RowMajorPacked,
1100
1101 /// The samples are in column-major form and all samples are packed.
1102 ///
1103 /// In addition to `PixelPacked` and `ImagePacked` this also asserts that the pixel matrix is
1104 /// in column-major form.
1105 ColumnMajorPacked,
1106}
1107
1108impl<Buffer, P: Pixel> View<Buffer, P>
1109where
1110 Buffer: AsRef<[P::Subpixel]>,
1111{
1112 /// Take out the sample buffer.
1113 ///
1114 /// Gives up the normalization invariants on the buffer format.
1115 pub fn into_inner(self) -> FlatSamples<Buffer> {
1116 self.inner
1117 }
1118
1119 /// Get a reference on the inner sample descriptor.
1120 ///
1121 /// There is no mutable counterpart as modifying the buffer format, including strides and
1122 /// lengths, could invalidate the accessibility invariants of the `View`. It is not specified
1123 /// if the inner buffer is the same as the buffer of the image from which this view was
1124 /// created. It might have been truncated as an optimization.
1125 pub fn flat(&self) -> &FlatSamples<Buffer> {
1126 &self.inner
1127 }
1128
1129 /// Get a reference on the inner buffer.
1130 ///
1131 /// There is no mutable counter part since it is not intended to allow you to reassign the
1132 /// buffer or otherwise change its size or properties.
1133 pub fn samples(&self) -> &Buffer {
1134 &self.inner.samples
1135 }
1136
1137 /// Get a reference to a selected subpixel if it is in-bounds.
1138 ///
1139 /// This method will return `None` when the sample is out-of-bounds. All errors that could
1140 /// occur due to overflow have been eliminated while construction the `View`.
1141 pub fn get_sample(&self, channel: u8, x: u32, y: u32) -> Option<&P::Subpixel> {
1142 if !self.inner.in_bounds(channel, x, y) {
1143 return None;
1144 }
1145
1146 let index = self.inner.in_bounds_index(channel, x, y);
1147 // Should always be `Some(_)` but checking is more costly.
1148 self.samples().as_ref().get(index)
1149 }
1150
1151 /// Get a mutable reference to a selected subpixel if it is in-bounds.
1152 ///
1153 /// This is relevant only when constructed with `FlatSamples::as_view_with_mut_samples`. This
1154 /// method will return `None` when the sample is out-of-bounds. All errors that could occur due
1155 /// to overflow have been eliminated while construction the `View`.
1156 ///
1157 /// **WARNING**: Note that of course samples may alias, so that the mutable reference returned
1158 /// here can in fact modify more than the coordinate in the argument.
1159 pub fn get_mut_sample(&mut self, channel: u8, x: u32, y: u32) -> Option<&mut P::Subpixel>
1160 where
1161 Buffer: AsMut<[P::Subpixel]>,
1162 {
1163 if !self.inner.in_bounds(channel, x, y) {
1164 return None;
1165 }
1166
1167 let index = self.inner.in_bounds_index(channel, x, y);
1168 // Should always be `Some(_)` but checking is more costly.
1169 self.inner.samples.as_mut().get_mut(index)
1170 }
1171
1172 /// Get the minimum length of a buffer such that all in-bounds samples have valid indices.
1173 ///
1174 /// See `FlatSamples::min_length`. This method will always succeed.
1175 pub fn min_length(&self) -> usize {
1176 self.inner.min_length().unwrap()
1177 }
1178
1179 /// Return the portion of the buffer that holds sample values.
1180 ///
1181 /// While this can not fail–the validity of all coordinates has been validated during the
1182 /// conversion from `FlatSamples`–the resulting slice may still contain holes.
1183 pub fn image_slice(&self) -> &[P::Subpixel] {
1184 &self.samples().as_ref()[..self.min_length()]
1185 }
1186
1187 pub(crate) fn strides_wh(&self) -> (usize, usize) {
1188 // Note `c` stride must be `1` for a valid `View` so we can ignore it here.
1189 let (_, w, h) = self.inner.layout.strides_cwh();
1190 (w, h)
1191 }
1192
1193 /// Return the mutable portion of the buffer that holds sample values.
1194 ///
1195 /// This is relevant only when constructed with `FlatSamples::as_view_with_mut_samples`. While
1196 /// this can not fail–the validity of all coordinates has been validated during the conversion
1197 /// from `FlatSamples`–the resulting slice may still contain holes.
1198 pub fn image_mut_slice(&mut self) -> &mut [P::Subpixel]
1199 where
1200 Buffer: AsMut<[P::Subpixel]>,
1201 {
1202 let min_length = self.min_length();
1203 &mut self.inner.samples.as_mut()[..min_length]
1204 }
1205
1206 /// Shrink the inner image.
1207 ///
1208 /// The new dimensions will be the minimum of the previous dimensions. Since the set of
1209 /// in-bounds pixels afterwards is a subset of the current ones, this is allowed on a `View`.
1210 /// Note that you can not change the number of channels as an intrinsic property of `P`.
1211 pub fn shrink_to(&mut self, width: u32, height: u32) {
1212 let channels = self.inner.layout.channels;
1213 self.inner.shrink_to(channels, width, height);
1214 }
1215
1216 /// Try to convert this into an image with mutable pixels.
1217 ///
1218 /// The resulting image implements `GenericImage` in addition to `GenericImageView`. While this
1219 /// has mutable samples, it does not enforce that pixel can not alias and that samples are
1220 /// packed enough for a mutable pixel reference. This is slightly cheaper than the chain
1221 /// `self.into_inner().as_view_mut()` and keeps the `View` alive on failure.
1222 ///
1223 /// ```
1224 /// # use ai_image::RgbImage;
1225 /// # use ai_image::Rgb;
1226 /// let mut buffer = RgbImage::new(480, 640).into_flat_samples();
1227 /// let view = buffer.as_view_with_mut_samples::<Rgb<u8>>().unwrap();
1228 ///
1229 /// // Inspect some pixels, …
1230 ///
1231 /// // Doesn't fail because it was originally an `RgbImage`.
1232 /// let view_mut = view.try_upgrade().unwrap();
1233 /// ```
1234 pub fn try_upgrade(self) -> Result<ViewMut<Buffer, P>, (Error, Self)>
1235 where
1236 Buffer: AsMut<[P::Subpixel]>,
1237 {
1238 if !self.inner.is_normal(NormalForm::PixelPacked) {
1239 return Err((Error::NormalFormRequired(NormalForm::PixelPacked), self));
1240 }
1241
1242 // No length check or channel count check required, all the same.
1243 Ok(ViewMut {
1244 inner: self.inner,
1245 phantom: PhantomData,
1246 })
1247 }
1248}
1249
1250impl<Buffer, P: Pixel> ViewMut<Buffer, P>
1251where
1252 Buffer: AsMut<[P::Subpixel]>,
1253{
1254 /// Take out the sample buffer.
1255 ///
1256 /// Gives up the normalization invariants on the buffer format.
1257 pub fn into_inner(self) -> FlatSamples<Buffer> {
1258 self.inner
1259 }
1260
1261 /// Get a reference on the sample buffer descriptor.
1262 ///
1263 /// There is no mutable counterpart as modifying the buffer format, including strides and
1264 /// lengths, could invalidate the accessibility invariants of the `View`. It is not specified
1265 /// if the inner buffer is the same as the buffer of the image from which this view was
1266 /// created. It might have been truncated as an optimization.
1267 pub fn flat(&self) -> &FlatSamples<Buffer> {
1268 &self.inner
1269 }
1270
1271 /// Get a reference on the inner buffer.
1272 ///
1273 /// There is no mutable counter part since it is not intended to allow you to reassign the
1274 /// buffer or otherwise change its size or properties. However, its contents can be accessed
1275 /// mutable through a slice with `image_mut_slice`.
1276 pub fn samples(&self) -> &Buffer {
1277 &self.inner.samples
1278 }
1279
1280 /// Get the minimum length of a buffer such that all in-bounds samples have valid indices.
1281 ///
1282 /// See `FlatSamples::min_length`. This method will always succeed.
1283 pub fn min_length(&self) -> usize {
1284 self.inner.min_length().unwrap()
1285 }
1286
1287 /// Get a reference to a selected subpixel.
1288 ///
1289 /// This method will return `None` when the sample is out-of-bounds. All errors that could
1290 /// occur due to overflow have been eliminated while construction the `View`.
1291 pub fn get_sample(&self, channel: u8, x: u32, y: u32) -> Option<&P::Subpixel>
1292 where
1293 Buffer: AsRef<[P::Subpixel]>,
1294 {
1295 if !self.inner.in_bounds(channel, x, y) {
1296 return None;
1297 }
1298
1299 let index = self.inner.in_bounds_index(channel, x, y);
1300 // Should always be `Some(_)` but checking is more costly.
1301 self.samples().as_ref().get(index)
1302 }
1303
1304 /// Get a mutable reference to a selected sample.
1305 ///
1306 /// This method will return `None` when the sample is out-of-bounds. All errors that could
1307 /// occur due to overflow have been eliminated while construction the `View`.
1308 pub fn get_mut_sample(&mut self, channel: u8, x: u32, y: u32) -> Option<&mut P::Subpixel> {
1309 if !self.inner.in_bounds(channel, x, y) {
1310 return None;
1311 }
1312
1313 let index = self.inner.in_bounds_index(channel, x, y);
1314 // Should always be `Some(_)` but checking is more costly.
1315 self.inner.samples.as_mut().get_mut(index)
1316 }
1317
1318 /// Return the portion of the buffer that holds sample values.
1319 ///
1320 /// While this can not fail–the validity of all coordinates has been validated during the
1321 /// conversion from `FlatSamples`–the resulting slice may still contain holes.
1322 pub fn image_slice(&self) -> &[P::Subpixel]
1323 where
1324 Buffer: AsRef<[P::Subpixel]>,
1325 {
1326 &self.inner.samples.as_ref()[..self.min_length()]
1327 }
1328
1329 /// Return the mutable buffer that holds sample values.
1330 pub fn image_mut_slice(&mut self) -> &mut [P::Subpixel] {
1331 let length = self.min_length();
1332 &mut self.inner.samples.as_mut()[..length]
1333 }
1334
1335 /// Shrink the inner image.
1336 ///
1337 /// The new dimensions will be the minimum of the previous dimensions. Since the set of
1338 /// in-bounds pixels afterwards is a subset of the current ones, this is allowed on a `View`.
1339 /// Note that you can not change the number of channels as an intrinsic property of `P`.
1340 pub fn shrink_to(&mut self, width: u32, height: u32) {
1341 let channels = self.inner.layout.channels;
1342 self.inner.shrink_to(channels, width, height);
1343 }
1344}
1345
1346// The out-of-bounds panic for single sample access similar to `slice::index`.
1347#[inline(never)]
1348#[cold]
1349fn panic_cwh_out_of_bounds(
1350 (c, x, y): (u8, u32, u32),
1351 bounds: (u8, u32, u32),
1352 strides: (usize, usize, usize),
1353) -> ! {
1354 panic!(
1355 "Sample coordinates {:?} out of sample matrix bounds {:?} with strides {:?}",
1356 (c, x, y),
1357 bounds,
1358 strides
1359 )
1360}
1361
1362// The out-of-bounds panic for pixel access similar to `slice::index`.
1363#[inline(never)]
1364#[cold]
1365fn panic_pixel_out_of_bounds((x, y): (u32, u32), bounds: (u32, u32)) -> ! {
1366 panic!("Image index {:?} out of bounds {:?}", (x, y), bounds)
1367}
1368
1369impl<Buffer> Index<(u8, u32, u32)> for FlatSamples<Buffer>
1370where
1371 Buffer: Index<usize>,
1372{
1373 type Output = Buffer::Output;
1374
1375 /// Return a reference to a single sample at specified coordinates.
1376 ///
1377 /// # Panics
1378 ///
1379 /// When the coordinates are out of bounds or the index calculation fails.
1380 fn index(&self, (c, x, y): (u8, u32, u32)) -> &Self::Output {
1381 let bounds = self.bounds();
1382 let strides = self.strides_cwh();
1383 let index = self
1384 .index(c, x, y)
1385 .unwrap_or_else(|| panic_cwh_out_of_bounds((c, x, y), bounds, strides));
1386 &self.samples[index]
1387 }
1388}
1389
1390impl<Buffer> IndexMut<(u8, u32, u32)> for FlatSamples<Buffer>
1391where
1392 Buffer: IndexMut<usize>,
1393{
1394 /// Return a mutable reference to a single sample at specified coordinates.
1395 ///
1396 /// # Panics
1397 ///
1398 /// When the coordinates are out of bounds or the index calculation fails.
1399 fn index_mut(&mut self, (c, x, y): (u8, u32, u32)) -> &mut Self::Output {
1400 let bounds = self.bounds();
1401 let strides = self.strides_cwh();
1402 let index = self
1403 .index(c, x, y)
1404 .unwrap_or_else(|| panic_cwh_out_of_bounds((c, x, y), bounds, strides));
1405 &mut self.samples[index]
1406 }
1407}
1408
1409impl<Buffer, P: Pixel> GenericImageView for View<Buffer, P>
1410where
1411 Buffer: AsRef<[P::Subpixel]>,
1412{
1413 type Pixel = P;
1414
1415 fn dimensions(&self) -> (u32, u32) {
1416 (self.inner.layout.width, self.inner.layout.height)
1417 }
1418
1419 fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel {
1420 if !self.inner.in_bounds(0, x, y) {
1421 panic_pixel_out_of_bounds((x, y), self.dimensions())
1422 }
1423
1424 let image = self.inner.samples.as_ref();
1425 let base_index = self.inner.in_bounds_index(0, x, y);
1426 let channels = P::CHANNEL_COUNT as usize;
1427
1428 let mut buffer = [Zero::zero(); 256];
1429 buffer
1430 .iter_mut()
1431 .enumerate()
1432 .take(channels)
1433 .for_each(|(c, to)| {
1434 let index = base_index + c * self.inner.layout.channel_stride;
1435 *to = image[index];
1436 });
1437
1438 *P::from_slice(&buffer[..channels])
1439 }
1440
1441 fn to_pixel_view(&self) -> Option<ViewOfPixel<'_, Self::Pixel>> {
1442 Some(View {
1443 inner: FlatSamples {
1444 samples: self.inner.samples.as_ref(),
1445 layout: self.inner.layout,
1446 color_hint: None,
1447 },
1448 phantom: PhantomData,
1449 })
1450 }
1451}
1452
1453impl<Buffer, P: Pixel> GenericImageView for ViewMut<Buffer, P>
1454where
1455 Buffer: AsMut<[P::Subpixel]> + AsRef<[P::Subpixel]>,
1456{
1457 type Pixel = P;
1458
1459 fn dimensions(&self) -> (u32, u32) {
1460 (self.inner.layout.width, self.inner.layout.height)
1461 }
1462
1463 fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel {
1464 if !self.inner.in_bounds(0, x, y) {
1465 panic_pixel_out_of_bounds((x, y), self.dimensions())
1466 }
1467
1468 let image = self.inner.samples.as_ref();
1469 let base_index = self.inner.in_bounds_index(0, x, y);
1470 let channels = P::CHANNEL_COUNT as usize;
1471
1472 let mut buffer = [Zero::zero(); 256];
1473 buffer
1474 .iter_mut()
1475 .enumerate()
1476 .take(channels)
1477 .for_each(|(c, to)| {
1478 let index = base_index + c * self.inner.layout.channel_stride;
1479 *to = image[index];
1480 });
1481
1482 *P::from_slice(&buffer[..channels])
1483 }
1484
1485 fn to_pixel_view(&self) -> Option<ViewOfPixel<'_, Self::Pixel>> {
1486 Some(View {
1487 inner: FlatSamples {
1488 samples: self.inner.samples.as_ref(),
1489 layout: self.inner.layout,
1490 color_hint: None,
1491 },
1492 phantom: PhantomData,
1493 })
1494 }
1495}
1496
1497impl<Buffer, P: Pixel> GenericImage for ViewMut<Buffer, P>
1498where
1499 Buffer: AsMut<[P::Subpixel]> + AsRef<[P::Subpixel]>,
1500{
1501 fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel {
1502 if !self.inner.in_bounds(0, x, y) {
1503 panic_pixel_out_of_bounds((x, y), self.dimensions())
1504 }
1505
1506 let base_index = self.inner.in_bounds_index(0, x, y);
1507 let channel_count = <P as Pixel>::CHANNEL_COUNT as usize;
1508 let pixel_range = base_index..base_index + channel_count;
1509 P::from_slice_mut(&mut self.inner.samples.as_mut()[pixel_range])
1510 }
1511
1512 #[allow(deprecated)]
1513 fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
1514 *self.get_pixel_mut(x, y) = pixel;
1515 }
1516
1517 #[allow(deprecated)]
1518 fn blend_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
1519 self.get_pixel_mut(x, y).blend(&pixel);
1520 }
1521}
1522
1523impl From<Error> for ImageError {
1524 fn from(error: Error) -> ImageError {
1525 #[derive(Debug)]
1526 struct NormalFormRequiredError(NormalForm);
1527 impl fmt::Display for NormalFormRequiredError {
1528 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1529 write!(f, "Required sample buffer in normal form {:?}", self.0)
1530 }
1531 }
1532 impl error::Error for NormalFormRequiredError {}
1533
1534 match error {
1535 Error::TooLarge => ImageError::Parameter(ParameterError::from_kind(
1536 ParameterErrorKind::DimensionMismatch,
1537 )),
1538 Error::NormalFormRequired(form) => ImageError::Decoding(DecodingError::new(
1539 ImageFormatHint::Unknown,
1540 NormalFormRequiredError(form),
1541 )),
1542 Error::ChannelCountMismatch(_lc, _pc) => ImageError::Parameter(
1543 ParameterError::from_kind(ParameterErrorKind::DimensionMismatch),
1544 ),
1545 Error::WrongColor(color) => {
1546 ImageError::Unsupported(UnsupportedError::from_format_and_kind(
1547 ImageFormatHint::Unknown,
1548 UnsupportedErrorKind::Color(color.into()),
1549 ))
1550 }
1551 }
1552 }
1553}
1554
1555impl fmt::Display for Error {
1556 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1557 match self {
1558 Error::TooLarge => write!(f, "The layout is too large"),
1559 Error::NormalFormRequired(form) => write!(
1560 f,
1561 "The layout needs to {}",
1562 match form {
1563 NormalForm::ColumnMajorPacked => "be packed and in column major form",
1564 NormalForm::ImagePacked => "be fully packed",
1565 NormalForm::PixelPacked => "have packed pixels",
1566 NormalForm::RowMajorPacked => "be packed and in row major form",
1567 NormalForm::Unaliased => "not have any aliasing channels",
1568 }
1569 ),
1570 Error::ChannelCountMismatch(layout_channels, pixel_channels) => {
1571 write!(f, "The channel count of the chosen pixel (={pixel_channels}) does agree with the layout (={layout_channels})")
1572 }
1573 Error::WrongColor(color) => {
1574 write!(f, "The chosen color type does not match the hint {color:?}")
1575 }
1576 }
1577 }
1578}
1579
1580impl error::Error for Error {}
1581
1582impl PartialOrd for NormalForm {
1583 /// Compares the logical preconditions.
1584 ///
1585 /// `a < b` if the normal form `a` has less preconditions than `b`.
1586 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1587 match (*self, *other) {
1588 (NormalForm::Unaliased, NormalForm::Unaliased) => Some(cmp::Ordering::Equal),
1589 (NormalForm::PixelPacked, NormalForm::PixelPacked) => Some(cmp::Ordering::Equal),
1590 (NormalForm::ImagePacked, NormalForm::ImagePacked) => Some(cmp::Ordering::Equal),
1591 (NormalForm::RowMajorPacked, NormalForm::RowMajorPacked) => Some(cmp::Ordering::Equal),
1592 (NormalForm::ColumnMajorPacked, NormalForm::ColumnMajorPacked) => {
1593 Some(cmp::Ordering::Equal)
1594 }
1595
1596 (NormalForm::Unaliased, _) => Some(cmp::Ordering::Less),
1597 (_, NormalForm::Unaliased) => Some(cmp::Ordering::Greater),
1598
1599 (NormalForm::PixelPacked, NormalForm::ColumnMajorPacked) => Some(cmp::Ordering::Less),
1600 (NormalForm::PixelPacked, NormalForm::RowMajorPacked) => Some(cmp::Ordering::Less),
1601 (NormalForm::RowMajorPacked, NormalForm::PixelPacked) => Some(cmp::Ordering::Greater),
1602 (NormalForm::ColumnMajorPacked, NormalForm::PixelPacked) => {
1603 Some(cmp::Ordering::Greater)
1604 }
1605
1606 (NormalForm::ImagePacked, NormalForm::ColumnMajorPacked) => Some(cmp::Ordering::Less),
1607 (NormalForm::ImagePacked, NormalForm::RowMajorPacked) => Some(cmp::Ordering::Less),
1608 (NormalForm::RowMajorPacked, NormalForm::ImagePacked) => Some(cmp::Ordering::Greater),
1609 (NormalForm::ColumnMajorPacked, NormalForm::ImagePacked) => {
1610 Some(cmp::Ordering::Greater)
1611 }
1612
1613 (NormalForm::ImagePacked, NormalForm::PixelPacked) => None,
1614 (NormalForm::PixelPacked, NormalForm::ImagePacked) => None,
1615 (NormalForm::RowMajorPacked, NormalForm::ColumnMajorPacked) => None,
1616 (NormalForm::ColumnMajorPacked, NormalForm::RowMajorPacked) => None,
1617 }
1618 }
1619}
1620
1621#[cfg(test)]
1622mod tests {
1623 use super::*;
1624 use crate::color::{LumaA, Rgb};
1625 use crate::images::buffer::GrayAlphaImage;
1626
1627 #[test]
1628 fn aliasing_view() {
1629 let buffer = FlatSamples {
1630 samples: &[42],
1631 layout: SampleLayout {
1632 channels: 3,
1633 channel_stride: 0,
1634 width: 100,
1635 width_stride: 0,
1636 height: 100,
1637 height_stride: 0,
1638 },
1639 color_hint: None,
1640 };
1641
1642 let view = buffer.as_view::<Rgb<u8>>().expect("This is a valid view");
1643 let pixel_count = view
1644 .pixels()
1645 .inspect(|pixel| assert!(pixel.2 == Rgb([42, 42, 42])))
1646 .count();
1647 assert_eq!(pixel_count, 100 * 100);
1648 }
1649
1650 #[test]
1651 fn mutable_view() {
1652 let mut buffer = FlatSamples {
1653 samples: [0; 18],
1654 layout: SampleLayout {
1655 channels: 2,
1656 channel_stride: 1,
1657 width: 3,
1658 width_stride: 2,
1659 height: 3,
1660 height_stride: 6,
1661 },
1662 color_hint: None,
1663 };
1664
1665 {
1666 let mut view = buffer
1667 .as_view_mut::<LumaA<u16>>()
1668 .expect("This should be a valid mutable buffer");
1669 assert_eq!(view.dimensions(), (3, 3));
1670 #[allow(deprecated)]
1671 for i in 0..9 {
1672 *view.get_pixel_mut(i % 3, i / 3) = LumaA([2 * i as u16, 2 * i as u16 + 1]);
1673 }
1674 }
1675
1676 buffer
1677 .samples
1678 .iter()
1679 .enumerate()
1680 .for_each(|(idx, sample)| assert_eq!(idx, *sample as usize));
1681 }
1682
1683 #[test]
1684 fn normal_forms() {
1685 assert!(FlatSamples {
1686 samples: [0u8; 0],
1687 layout: SampleLayout {
1688 channels: 2,
1689 channel_stride: 1,
1690 width: 3,
1691 width_stride: 9,
1692 height: 3,
1693 height_stride: 28,
1694 },
1695 color_hint: None,
1696 }
1697 .is_normal(NormalForm::PixelPacked));
1698
1699 assert!(FlatSamples {
1700 samples: [0u8; 0],
1701 layout: SampleLayout {
1702 channels: 2,
1703 channel_stride: 8,
1704 width: 4,
1705 width_stride: 1,
1706 height: 2,
1707 height_stride: 4,
1708 },
1709 color_hint: None,
1710 }
1711 .is_normal(NormalForm::ImagePacked));
1712
1713 assert!(FlatSamples {
1714 samples: [0u8; 0],
1715 layout: SampleLayout {
1716 channels: 2,
1717 channel_stride: 1,
1718 width: 4,
1719 width_stride: 2,
1720 height: 2,
1721 height_stride: 8,
1722 },
1723 color_hint: None,
1724 }
1725 .is_normal(NormalForm::RowMajorPacked));
1726
1727 assert!(FlatSamples {
1728 samples: [0u8; 0],
1729 layout: SampleLayout {
1730 channels: 2,
1731 channel_stride: 1,
1732 width: 4,
1733 width_stride: 4,
1734 height: 2,
1735 height_stride: 2,
1736 },
1737 color_hint: None,
1738 }
1739 .is_normal(NormalForm::ColumnMajorPacked));
1740 }
1741
1742 #[test]
1743 fn image_buffer_conversion() {
1744 let expected_layout = SampleLayout {
1745 channels: 2,
1746 channel_stride: 1,
1747 width: 4,
1748 width_stride: 2,
1749 height: 2,
1750 height_stride: 8,
1751 };
1752
1753 let initial = GrayAlphaImage::new(expected_layout.width, expected_layout.height);
1754 let buffer = initial.into_flat_samples();
1755
1756 assert_eq!(buffer.layout, expected_layout);
1757
1758 let _: GrayAlphaImage = buffer
1759 .try_into_buffer()
1760 .unwrap_or_else(|(error, _)| panic!("Expected buffer to be convertible but {error:?}"));
1761 }
1762}