Coverage Report

Created: 2026-06-18 07:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/image/src/images/generic_image.rs
Line
Count
Source
1
use crate::error::{ImageError, ImageResult};
2
use crate::flat::{ViewMutOfPixel, ViewOfPixel};
3
use crate::math::Rect;
4
use crate::traits::Pixel;
5
use crate::{ImageBuffer, SubImage};
6
7
/// Trait to inspect an image.
8
///
9
/// ```
10
/// use image::{GenericImageView, Rgb, RgbImage};
11
///
12
/// let buffer = RgbImage::new(10, 10);
13
/// let image: &dyn GenericImageView<Pixel = Rgb<u8>> = &buffer;
14
/// ```
15
pub trait GenericImageView {
16
    /// The type of pixel.
17
    type Pixel: Pixel;
18
19
    /// The width and height of this image.
20
    fn dimensions(&self) -> (u32, u32);
21
22
    /// The width of this image.
23
0
    fn width(&self) -> u32 {
24
0
        let (w, _) = self.dimensions();
25
0
        w
26
0
    }
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::flat::View<&[f32], image::color::Rgb<f32>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::flat::View<&[f32], image::color::Luma<f32>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::flat::View<&[f32], image::color::Rgba<f32>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::flat::View<&[f32], image::color::LumaA<f32>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::flat::View<&[u8], image::color::Rgb<u8>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::flat::View<&[u8], image::color::Luma<u8>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::flat::View<&[u8], image::color::Rgba<u8>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::flat::View<&[u8], image::color::LumaA<u8>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::flat::View<&[u16], image::color::Rgb<u16>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::flat::View<&[u16], image::color::Luma<u16>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::flat::View<&[u16], image::color::Rgba<u16>> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::flat::View<&[u16], image::color::LumaA<u16>> as image::images::generic_image::GenericImageView>::width
27
28
    /// The height of this image.
29
0
    fn height(&self) -> u32 {
30
0
        let (_, h) = self.dimensions();
31
0
        h
32
0
    }
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::flat::View<&[f32], image::color::Rgb<f32>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::flat::View<&[f32], image::color::Luma<f32>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::flat::View<&[f32], image::color::Rgba<f32>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::flat::View<&[f32], image::color::LumaA<f32>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::flat::View<&[u8], image::color::Rgb<u8>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::flat::View<&[u8], image::color::Luma<u8>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::flat::View<&[u8], image::color::Rgba<u8>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::flat::View<&[u8], image::color::LumaA<u8>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::flat::View<&[u16], image::color::Rgb<u16>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::flat::View<&[u16], image::color::Luma<u16>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::flat::View<&[u16], image::color::Rgba<u16>> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::flat::View<&[u16], image::color::LumaA<u16>> as image::images::generic_image::GenericImageView>::height
33
34
    /// Returns true if this x, y coordinate is contained inside the image.
35
0
    fn in_bounds(&self, x: u32, y: u32) -> bool {
36
0
        let (width, height) = self.dimensions();
37
0
        x < width && y < height
38
0
    }
39
40
    /// Returns the pixel located at (x, y). Indexed from top left.
41
    ///
42
    /// # Panics
43
    ///
44
    /// Panics if `(x, y)` is out of bounds.
45
    fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel;
46
47
    /// Returns the pixel located at (x, y). Indexed from top left.
48
    ///
49
    /// This function can be implemented in a way that ignores bounds checking.
50
    /// # Safety
51
    ///
52
    /// The coordinates must be [`in_bounds`] of the image.
53
    ///
54
    /// [`in_bounds`]: Self::in_bounds
55
0
    unsafe fn unsafe_get_pixel(&self, x: u32, y: u32) -> Self::Pixel {
56
0
        self.get_pixel(x, y)
57
0
    }
58
59
    /// Returns an Iterator over the pixels of this image.
60
    /// The iterator yields the coordinates of each pixel
61
    /// along with their value
62
0
    fn pixels(&self) -> Pixels<'_, Self>
63
0
    where
64
0
        Self: Sized,
65
    {
66
0
        let (width, height) = self.dimensions();
67
68
0
        Pixels {
69
0
            image: self,
70
0
            x: 0,
71
0
            y: 0,
72
0
            width,
73
0
            height,
74
0
        }
75
0
    }
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::pixels
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::pixels
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::pixels
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::pixels
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::pixels
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::pixels
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::pixels
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::pixels
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::pixels
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::pixels
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::pixels
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::pixels
76
77
    /// Returns a subimage that is an immutable view into this image.
78
    /// You can use [`GenericImage::sub_image`] if you need a mutable view instead.
79
    /// The coordinates set the position of the top left corner of the view.
80
    ///
81
    ///  # Panics
82
    ///
83
    /// Panics if the dimensions provided fall out of bounds.
84
0
    fn view(&self, rect: Rect) -> SubImage<&Self>
85
0
    where
86
0
        Self: Sized,
87
    {
88
0
        rect.assert_in_bounds_of(self);
89
0
        SubImage::new(self, rect)
90
0
    }
91
92
    /// Returns a subimage that is an immutable view into this image so long as
93
    /// the provided coordinates and dimensions are within the bounds of this Image.
94
0
    fn try_view(&self, rect: Rect) -> Result<SubImage<&Self>, ImageError>
95
0
    where
96
0
        Self: Sized,
97
    {
98
0
        rect.test_in_bounds_of(self)?;
99
0
        Ok(SubImage::new(self, rect))
100
0
    }
101
102
    /// Create an empty [`ImageBuffer`] with the same pixel type as this image.
103
    ///
104
    /// This should ensure metadata such as the color space are transferred without copying any of
105
    /// the pixel data. The idea is to prepare a buffer ready to be filled with a filtered or
106
    /// portion of the channel data from the current image without performing the work of copying
107
    /// the data into that buffer twice.
108
    ///
109
    /// The default implementation defers to [`GenericImageView::buffer_like`].
110
0
    fn buffer_like(&self) -> ImageBuffer<Self::Pixel, Vec<<Self::Pixel as Pixel>::Subpixel>> {
111
0
        let (w, h) = self.dimensions();
112
0
        self.buffer_with_dimensions(w, h)
113
0
    }
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::buffer_like
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::buffer_like
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::buffer_like
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::buffer_like
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::buffer_like
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::buffer_like
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::buffer_like
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::buffer_like
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::buffer_like
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::buffer_like
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::buffer_like
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::buffer_like
114
115
    /// Create an empty [`ImageBuffer`] with different dimensions.
116
    ///
117
    /// See [`GenericImageView::buffer_like`].
118
    ///
119
    /// Uses for this are for instances preparing a buffer for only a portion of the image, or
120
    /// extracting the metadata to prepare a buffer of a different pixel type.
121
0
    fn buffer_with_dimensions(
122
0
        &self,
123
0
        width: u32,
124
0
        height: u32,
125
0
    ) -> ImageBuffer<Self::Pixel, Vec<<Self::Pixel as Pixel>::Subpixel>> {
126
0
        ImageBuffer::new(width, height)
127
0
    }
128
129
    /// If the buffer has a fitting layout, return a canonical view of the samples.
130
    ///
131
    /// This is the basis of optimization and by default return `None`. It lets consumers of
132
    /// generic images access the sample data through a canonical descriptor of its layout directly
133
    /// instead of pixel-by-pixel. This provides more efficient forms of access that the
134
    /// [`GenericImageView`] trait itself does not demand from all its implementations.
135
    ///
136
    /// Implementation of this method should be cheap to call.
137
    ///
138
    /// See [`GenericImage::to_pixel_view_mut`] for images that allow mutating pixels.
139
    ///
140
    /// If implemented, a [`SubImage`] proxy of this image will provide a sample view as well.
141
0
    fn to_pixel_view(&self) -> Option<ViewOfPixel<'_, Self::Pixel>> {
142
0
        None
143
0
    }
144
}
145
146
/// Immutable pixel iterator
147
#[derive(Debug)]
148
pub struct Pixels<'a, I: ?Sized + 'a> {
149
    image: &'a I,
150
    x: u32,
151
    y: u32,
152
    width: u32,
153
    height: u32,
154
}
155
156
impl<I: GenericImageView> Iterator for Pixels<'_, I> {
157
    type Item = (u32, u32, I::Pixel);
158
159
0
    fn next(&mut self) -> Option<(u32, u32, I::Pixel)> {
160
0
        if self.x >= self.width {
161
0
            self.x = 0;
162
0
            self.y += 1;
163
0
        }
164
165
0
        if self.y >= self.height {
166
0
            None
167
        } else {
168
0
            let pixel = self.image.get_pixel(self.x, self.y);
169
0
            let p = (self.x, self.y, pixel);
170
171
0
            self.x += 1;
172
173
0
            Some(p)
174
        }
175
0
    }
Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::Rgb<f32>, alloc::vec::Vec<f32>>> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::Rgb<u8>, alloc::vec::Vec<u8>>> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::Rgb<u16>, alloc::vec::Vec<u16>>> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::Luma<f32>, alloc::vec::Vec<f32>>> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::Luma<u8>, alloc::vec::Vec<u8>>> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::Luma<u16>, alloc::vec::Vec<u16>>> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::Rgba<f32>, alloc::vec::Vec<f32>>> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::Rgba<u8>, alloc::vec::Vec<u8>>> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::Rgba<u16>, alloc::vec::Vec<u16>>> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::LumaA<f32>, alloc::vec::Vec<f32>>> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::LumaA<u8>, alloc::vec::Vec<u8>>> as core::iter::traits::iterator::Iterator>::next
Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::LumaA<u16>, alloc::vec::Vec<u16>>> as core::iter::traits::iterator::Iterator>::next
176
}
177
178
impl<I: ?Sized> Clone for Pixels<'_, I> {
179
0
    fn clone(&self) -> Self {
180
0
        Pixels { ..*self }
181
0
    }
182
}
183
184
/// A trait for manipulating images.
185
pub trait GenericImage: GenericImageView {
186
    /// Put a pixel at location (x, y). Indexed from top left.
187
    ///
188
    /// # Panics
189
    ///
190
    /// Panics if `(x, y)` is out of bounds.
191
    fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel);
192
193
    /// Puts a pixel at location (x, y). Indexed from top left.
194
    ///
195
    /// This function can be implemented in a way that ignores bounds checking.
196
    /// # Safety
197
    ///
198
    /// The coordinates must be [`in_bounds`] of the image.
199
    ///
200
    /// [`in_bounds`]: GenericImageView::in_bounds
201
0
    unsafe fn unsafe_put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
202
0
        self.put_pixel(x, y, pixel);
203
0
    }
204
205
    /// Copies all of the pixels from another image into this image.
206
    ///
207
    /// The other image is copied with the top-left corner of the
208
    /// other image placed at (x, y).
209
    ///
210
    /// In order to copy only a piece of the other image, use [`GenericImageView::view`].
211
    ///
212
    /// You can use [`FlatSamples`] to source pixels from an arbitrary regular raster of channel
213
    /// values, for example from a foreign interface or a fixed image.
214
    ///
215
    /// # Returns
216
    /// Returns an error if the image is too large to be copied at the given position
217
    ///
218
    /// [`FlatSamples`]: crate::FlatSamples
219
0
    fn copy_from<O>(&mut self, other: &O, x: u32, y: u32) -> ImageResult<()>
220
0
    where
221
0
        O: GenericImageView<Pixel = Self::Pixel>,
222
    {
223
        // This makes it easy to keep the default implementation without sacrificing performance.
224
        // It suffices for impls to override `copy_from_samples` for most of the gains.
225
0
        if let Some(flat) = other.to_pixel_view() {
226
0
            return self.copy_from_samples(flat, x, y);
227
0
        }
228
229
        // Note the order: for types that implement an efficient assignment *from* a basic view we
230
        // also expect that they know how to iterate themselves efficiently, they can do their own
231
        // View-To-View performance or may do eve better than that if their view is more complex for
232
        // some reason. Their choice. On the other hand if the source is _not_ a simple view then it
233
        // will likely need to go through individual `GenericImageView::get_pixel` calls. And in
234
        // this case we can still save on iterator calls for the target. The customization point
235
        // however does not exist; any trait impl that intends to make this fast would need to
236
        // provide a full `copy_from` impl. We only need to avoid the recursion here: `ViewMut` will
237
        // override its `copy_from` with the intended effect by providing a non-trait inherent
238
        // method instead.
239
0
        if let Some(mut view) = self.to_pixel_view_mut() {
240
0
            return view.inner_copy_from(other, x, y);
241
0
        }
242
243
        // Do bounds checking here so we can use the non-bounds-checking
244
        // functions to copy pixels.
245
0
        Rect::from_image_at(other, x, y).test_in_bounds_of(self)?;
246
247
0
        for k in 0..other.height() {
248
0
            for i in 0..other.width() {
249
0
                let p = other.get_pixel(i, k);
250
0
                self.put_pixel(i + x, k + y, p);
251
0
            }
252
        }
253
254
0
        Ok(())
255
0
    }
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::Rgb<f32>, alloc::vec::Vec<f32>>>
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::Rgb<u8>, alloc::vec::Vec<u8>>>
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::Rgb<u16>, alloc::vec::Vec<u16>>>
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::Luma<f32>, alloc::vec::Vec<f32>>>
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::Luma<u8>, alloc::vec::Vec<u8>>>
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::Luma<u16>, alloc::vec::Vec<u16>>>
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::Rgba<f32>, alloc::vec::Vec<f32>>>
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::Rgba<u8>, alloc::vec::Vec<u8>>>
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::Rgba<u16>, alloc::vec::Vec<u16>>>
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::LumaA<f32>, alloc::vec::Vec<f32>>>
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::LumaA<u8>, alloc::vec::Vec<u8>>>
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::LumaA<u16>, alloc::vec::Vec<u16>>>
256
257
    /// Copy pixels from a regular strided matrix of pixels.
258
0
    fn copy_from_samples(
259
0
        &mut self,
260
0
        samples: ViewOfPixel<'_, Self::Pixel>,
261
0
        x: u32,
262
0
        y: u32,
263
0
    ) -> ImageResult<()> {
264
        // Even though the implementation is the same, do not just call `Self::copy_from` here to
265
        // avoid circular dependencies in careless implementations.
266
0
        Rect::from_image_at(&samples, x, y).test_in_bounds_of(self)?;
267
268
0
        for k in 0..samples.height() {
269
0
            for i in 0..samples.width() {
270
0
                let p = samples.get_pixel(i, k);
271
0
                self.put_pixel(i + x, k + y, p);
272
0
            }
273
        }
274
275
0
        Ok(())
276
0
    }
277
278
    /// Copies all of the pixels from one part of this image to another part of this image.
279
    ///
280
    /// The destination rectangle of the copy is specified with the top-left corner placed at (x, y).
281
    ///
282
    /// # Returns
283
    /// `true` if the copy was successful, `false` if the image could not
284
    /// be copied due to size constraints.
285
0
    fn copy_within(&mut self, source: Rect, x: u32, y: u32) -> bool {
286
        let Rect {
287
0
            x: sx,
288
0
            y: sy,
289
0
            width,
290
0
            height,
291
0
        } = source;
292
0
        let dx = x;
293
0
        let dy = y;
294
0
        assert!(sx < self.width() && dx < self.width());
295
0
        assert!(sy < self.height() && dy < self.height());
296
0
        if self.width() - dx.max(sx) < width || self.height() - dy.max(sy) < height {
297
0
            return false;
298
0
        }
299
        // since `.rev()` creates a new dype we would either have to go with dynamic dispatch for the ranges
300
        // or have quite a lot of code bloat. A macro gives us static dispatch with less visible bloat.
301
        macro_rules! copy_within_impl_ {
302
            ($xiter:expr, $yiter:expr) => {
303
                for y in $yiter {
304
                    let sy = sy + y;
305
                    let dy = dy + y;
306
                    for x in $xiter {
307
                        let sx = sx + x;
308
                        let dx = dx + x;
309
                        let pixel = self.get_pixel(sx, sy);
310
                        self.put_pixel(dx, dy, pixel);
311
                    }
312
                }
313
            };
314
        }
315
        // check how target and source rectangles relate to each other so we dont overwrite data before we copied it.
316
0
        match (sx < dx, sy < dy) {
317
0
            (true, true) => copy_within_impl_!((0..width).rev(), (0..height).rev()),
318
0
            (true, false) => copy_within_impl_!((0..width).rev(), 0..height),
319
0
            (false, true) => copy_within_impl_!(0..width, (0..height).rev()),
320
0
            (false, false) => copy_within_impl_!(0..width, 0..height),
321
        }
322
0
        true
323
0
    }
324
325
    /// Returns a mutable subimage that is a view into this image.
326
    /// If you want an immutable subimage instead, use [`GenericImageView::view`]
327
    /// The coordinates set the position of the top left corner of the `SubImage`.
328
0
    fn sub_image(&mut self, rect: Rect) -> SubImage<&mut Self>
329
0
    where
330
0
        Self: Sized,
331
    {
332
0
        rect.assert_in_bounds_of(self);
333
0
        SubImage::new(self, rect)
334
0
    }
335
336
    /// If the buffer has a fitting layout, return a canonical mutable view of the samples.
337
    ///
338
    /// This is the basis of optimization and by default return `None`. It lets consumers of generic
339
    /// images access the sample data through a canonical descriptor of its layout directly instead
340
    /// of pixel-by-pixel. This provides more efficient, batched, forms of access that the
341
    /// [`GenericImage`] trait itself does not demand from all its implementations.
342
    ///
343
    /// Implementation of this method should be cheap to call.
344
    ///
345
    /// See [`GenericImageView::to_pixel_view`].
346
    ///
347
    /// If implemented, a [`SubImage`] proxy of this image will provide a sample view as well.
348
0
    fn to_pixel_view_mut(&mut self) -> Option<ViewMutOfPixel<'_, Self::Pixel>> {
349
0
        None
350
0
    }
351
}
352
353
#[cfg(test)]
354
mod tests {
355
    use super::{GenericImage, GenericImageView};
356
357
    use crate::color::Rgba;
358
    use crate::math::Rect;
359
    use crate::{GrayImage, ImageBuffer};
360
361
    #[test]
362
    fn test_image_put_pixel() {
363
        let mut target = ImageBuffer::new(1, 1);
364
        let pixel: Rgba<u8> = Rgba([255, 0, 0, 255]);
365
        target.put_pixel(0, 0, pixel);
366
        assert_eq!(*target.get_pixel(0, 0), pixel);
367
    }
368
369
    #[test]
370
    fn test_in_bounds() {
371
        let mut target = ImageBuffer::new(2, 2);
372
        target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255]));
373
374
        assert!(target.in_bounds(0, 0));
375
        assert!(target.in_bounds(1, 0));
376
        assert!(target.in_bounds(0, 1));
377
        assert!(target.in_bounds(1, 1));
378
379
        assert!(!target.in_bounds(2, 0));
380
        assert!(!target.in_bounds(0, 2));
381
        assert!(!target.in_bounds(2, 2));
382
    }
383
384
    #[test]
385
    fn test_can_subimage_clone_nonmut() {
386
        let mut source = ImageBuffer::new(3, 3);
387
        source.put_pixel(1, 1, Rgba([255u8, 0, 0, 255]));
388
389
        // A non-mutable copy of the source image
390
        let source = source.clone();
391
392
        // Clone a view into non-mutable to a separate buffer
393
        let cloned = source.view(Rect::from_xy_ranges(1..2, 1..2)).to_image();
394
395
        assert!(cloned.get_pixel(0, 0) == source.get_pixel(1, 1));
396
    }
397
398
    #[test]
399
    fn test_can_nest_views() {
400
        let mut source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
401
402
        {
403
            let mut sub1 = source.sub_image(Rect::from_xy_ranges(0..2, 0..2));
404
            let mut sub2 = sub1.sub_image(Rect::from_xy_ranges(1..2, 1..2));
405
            sub2.put_pixel(0, 0, Rgba([0, 0, 0, 0]));
406
        }
407
408
        assert_eq!(*source.get_pixel(1, 1), Rgba([0, 0, 0, 0]));
409
410
        let view1 = source.view(Rect::from_xy_ranges(0..2, 0..2));
411
        assert_eq!(*source.get_pixel(1, 1), view1.get_pixel(1, 1));
412
413
        let view2 = view1.view(Rect::from_xy_ranges(1..2, 1..2));
414
        assert_eq!(*source.get_pixel(1, 1), view2.get_pixel(0, 0));
415
    }
416
417
    #[test]
418
    #[should_panic]
419
    fn test_view_out_of_bounds() {
420
        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
421
        source.view(Rect::from_xy_ranges(1..4, 1..4));
422
    }
423
424
    #[test]
425
    #[should_panic]
426
    fn test_view_coordinates_out_of_bounds() {
427
        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
428
        source.view(Rect::from_xy_ranges(3..6, 3..6));
429
    }
430
431
    #[test]
432
    #[should_panic]
433
    fn test_view_width_out_of_bounds() {
434
        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
435
        source.view(Rect::from_xy_ranges(1..4, 1..3));
436
    }
437
438
    #[test]
439
    #[should_panic]
440
    fn test_view_height_out_of_bounds() {
441
        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
442
        source.view(Rect::from_xy_ranges(1..3, 1..4));
443
    }
444
445
    #[test]
446
    #[should_panic]
447
    fn test_view_x_out_of_bounds() {
448
        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
449
        source.view(Rect::from_xy_ranges(3..6, 1..3));
450
    }
451
452
    #[test]
453
    #[should_panic]
454
    fn test_view_y_out_of_bounds() {
455
        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
456
        source.view(Rect::from_xy_ranges(1..3, 3..6));
457
    }
458
459
    #[test]
460
    fn test_view_in_bounds() {
461
        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
462
        source.view(Rect::from_xy_ranges(0..3, 0..3));
463
        source.view(Rect::from_xy_ranges(1..3, 1..3));
464
        source.view(Rect::from_xy_ranges(2..2, 2..2));
465
    }
466
467
    #[test]
468
    fn test_copy_sub_image() {
469
        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
470
        let view = source.view(Rect::from_xy_ranges(0..3, 0..3));
471
        let _view2 = view;
472
        view.to_image();
473
    }
474
475
    #[test]
476
    fn test_generic_image_copy_within_oob() {
477
        let mut image: GrayImage = ImageBuffer::from_raw(4, 4, vec![0u8; 16]).unwrap();
478
        assert!(!image
479
            .sub_image(Rect::from_xy_ranges(0..4, 0..4))
480
            .copy_within(Rect::from_xy_ranges(0..5, 0..4), 0, 0));
481
        assert!(!image
482
            .sub_image(Rect::from_xy_ranges(0..4, 0..4))
483
            .copy_within(Rect::from_xy_ranges(0..4, 0..5), 0, 0));
484
        assert!(!image
485
            .sub_image(Rect::from_xy_ranges(0..4, 0..4))
486
            .copy_within(Rect::from_xy_ranges(1..5, 0..4), 0, 0));
487
        assert!(!image
488
            .sub_image(Rect::from_xy_ranges(0..4, 0..4))
489
            .copy_within(Rect::from_xy_ranges(0..4, 0..4), 1, 0));
490
        assert!(!image
491
            .sub_image(Rect::from_xy_ranges(0..4, 0..4))
492
            .copy_within(Rect::from_xy_ranges(0..4, 1..5), 0, 0));
493
        assert!(!image
494
            .sub_image(Rect::from_xy_ranges(0..4, 0..4))
495
            .copy_within(Rect::from_xy_ranges(0..4, 0..4), 0, 1));
496
        assert!(!image
497
            .sub_image(Rect::from_xy_ranges(0..4, 0..4))
498
            .copy_within(Rect::from_xy_ranges(1..5, 0..4), 0, 0));
499
    }
500
501
    #[test]
502
    fn test_generic_image_copy_within_tl() {
503
        let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
504
        let expected = [0, 1, 2, 3, 4, 0, 1, 2, 8, 4, 5, 6, 12, 8, 9, 10];
505
        let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
506
        assert!(image
507
            .sub_image(Rect::from_xy_ranges(0..4, 0..4))
508
            .copy_within(Rect::from_xy_ranges(0..3, 0..3), 1, 1));
509
        assert_eq!(&image.into_raw(), &expected);
510
    }
511
512
    #[test]
513
    fn test_generic_image_copy_within_tr() {
514
        let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
515
        let expected = [0, 1, 2, 3, 1, 2, 3, 7, 5, 6, 7, 11, 9, 10, 11, 15];
516
        let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
517
        assert!(image
518
            .sub_image(Rect::from_xy_ranges(0..4, 0..4))
519
            .copy_within(Rect::from_xy_ranges(1..4, 0..3), 0, 1));
520
        assert_eq!(&image.into_raw(), &expected);
521
    }
522
523
    #[test]
524
    fn test_generic_image_copy_within_bl() {
525
        let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
526
        let expected = [0, 4, 5, 6, 4, 8, 9, 10, 8, 12, 13, 14, 12, 13, 14, 15];
527
        let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
528
        assert!(image
529
            .sub_image(Rect::from_xy_ranges(0..4, 0..4))
530
            .copy_within(Rect::from_xy_ranges(0..3, 1..4), 1, 0));
531
        assert_eq!(&image.into_raw(), &expected);
532
    }
533
534
    #[test]
535
    fn test_generic_image_copy_within_br() {
536
        let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
537
        let expected = [5, 6, 7, 3, 9, 10, 11, 7, 13, 14, 15, 11, 12, 13, 14, 15];
538
        let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
539
        assert!(image
540
            .sub_image(Rect::from_xy_ranges(0..4, 0..4))
541
            .copy_within(Rect::from_xy_ranges(1..4, 1..4), 0, 0));
542
        assert_eq!(&image.into_raw(), &expected);
543
    }
544
}