Coverage Report

Created: 2025-10-12 08:06

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, ParameterError, ParameterErrorKind};
2
use crate::math::Rect;
3
use crate::traits::Pixel;
4
use crate::{ImageBuffer, SubImage};
5
6
/// Trait to inspect an image.
7
///
8
/// ```
9
/// use image::{GenericImageView, Rgb, RgbImage};
10
///
11
/// let buffer = RgbImage::new(10, 10);
12
/// let image: &dyn GenericImageView<Pixel = Rgb<u8>> = &buffer;
13
/// ```
14
pub trait GenericImageView {
15
    /// The type of pixel.
16
    type Pixel: Pixel;
17
18
    /// The width and height of this image.
19
    fn dimensions(&self) -> (u32, u32);
20
21
    /// The width of this image.
22
0
    fn width(&self) -> u32 {
23
0
        let (w, _) = self.dimensions();
24
0
        w
25
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<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<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::buffer::ImageBuffer<image::color::Rgb<u8>, &[u8]> as image::images::generic_image::GenericImageView>::width
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u8>, &[u8]> as image::images::generic_image::GenericImageView>::width
26
27
    /// The height of this image.
28
0
    fn height(&self) -> u32 {
29
0
        let (_, h) = self.dimensions();
30
0
        h
31
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<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<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::buffer::ImageBuffer<image::color::Rgb<u8>, &[u8]> as image::images::generic_image::GenericImageView>::height
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u8>, &[u8]> as image::images::generic_image::GenericImageView>::height
32
33
    /// Returns true if this x, y coordinate is contained inside the image.
34
0
    fn in_bounds(&self, x: u32, y: u32) -> bool {
35
0
        let (width, height) = self.dimensions();
36
0
        x < width && y < height
37
0
    }
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u8>, &[u8]> as image::images::generic_image::GenericImageView>::in_bounds
Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u8>, &[u8]> as image::images::generic_image::GenericImageView>::in_bounds
38
39
    /// Returns the pixel located at (x, y). Indexed from top left.
40
    ///
41
    /// # Panics
42
    ///
43
    /// Panics if `(x, y)` is out of bounds.
44
    fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel;
45
46
    /// Returns the pixel located at (x, y). Indexed from top left.
47
    ///
48
    /// This function can be implemented in a way that ignores bounds checking.
49
    /// # Safety
50
    ///
51
    /// The coordinates must be [`in_bounds`] of the image.
52
    ///
53
    /// [`in_bounds`]: #method.in_bounds
54
    unsafe fn unsafe_get_pixel(&self, x: u32, y: u32) -> Self::Pixel {
55
        self.get_pixel(x, y)
56
    }
57
58
    /// Returns an Iterator over the pixels of this image.
59
    /// The iterator yields the coordinates of each pixel
60
    /// along with their value
61
0
    fn pixels(&self) -> Pixels<'_, Self>
62
0
    where
63
0
        Self: Sized,
64
    {
65
0
        let (width, height) = self.dimensions();
66
67
0
        Pixels {
68
0
            image: self,
69
0
            x: 0,
70
0
            y: 0,
71
0
            width,
72
0
            height,
73
0
        }
74
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<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<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
75
76
    /// Returns a subimage that is an immutable view into this image.
77
    /// You can use [`GenericImage::sub_image`] if you need a mutable view instead.
78
    /// The coordinates set the position of the top left corner of the view.
79
    ///
80
    ///  # Panics
81
    ///
82
    /// Panics if the dimensions provided fall out of bounds.
83
    fn view(&self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&Self>
84
    where
85
        Self: Sized,
86
    {
87
        assert!(u64::from(x) + u64::from(width) <= u64::from(self.width()));
88
        assert!(u64::from(y) + u64::from(height) <= u64::from(self.height()));
89
        SubImage::new(self, x, y, width, height)
90
    }
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
    fn try_view(
95
        &self,
96
        x: u32,
97
        y: u32,
98
        width: u32,
99
        height: u32,
100
    ) -> Result<SubImage<&Self>, ImageError>
101
    where
102
        Self: Sized,
103
    {
104
        if u64::from(x) + u64::from(width) > u64::from(self.width())
105
            || u64::from(y) + u64::from(height) > u64::from(self.height())
106
        {
107
            Err(ImageError::Parameter(ParameterError::from_kind(
108
                ParameterErrorKind::DimensionMismatch,
109
            )))
110
        } else {
111
            Ok(SubImage::new(self, x, y, width, height))
112
        }
113
    }
114
115
    /// Create an empty [`ImageBuffer`] with the same pixel type as this image.
116
    ///
117
    /// This should ensure metadata such as the color space are transferred without copying any of
118
    /// the pixel data. The idea is to prepare a buffer ready to be filled with a filtered or
119
    /// portion of the channel data from the current image without performing the work of copying
120
    /// the data into that buffer twice.
121
    ///
122
    /// The default implementation defers to [`GenericImageView::buffer_like`].
123
0
    fn buffer_like(&self) -> ImageBuffer<Self::Pixel, Vec<<Self::Pixel as Pixel>::Subpixel>> {
124
0
        let (w, h) = self.dimensions();
125
0
        self.buffer_with_dimensions(w, h)
126
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<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<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
127
128
    /// Create an empty [`ImageBuffer`] with different dimensions.
129
    ///
130
    /// See [`GenericImageView::buffer_like`].
131
    ///
132
    /// Uses for this are for instances preparing a buffer for only a portion of the image, or
133
    /// extracting the metadata to prepare a buffer of a different pixel type.
134
    fn buffer_with_dimensions(
135
        &self,
136
        width: u32,
137
        height: u32,
138
    ) -> ImageBuffer<Self::Pixel, Vec<<Self::Pixel as Pixel>::Subpixel>> {
139
        ImageBuffer::new(width, height)
140
    }
141
}
142
143
/// Immutable pixel iterator
144
#[derive(Debug)]
145
pub struct Pixels<'a, I: ?Sized + 'a> {
146
    image: &'a I,
147
    x: u32,
148
    y: u32,
149
    width: u32,
150
    height: u32,
151
}
152
153
impl<I: GenericImageView> Iterator for Pixels<'_, I> {
154
    type Item = (u32, u32, I::Pixel);
155
156
0
    fn next(&mut self) -> Option<(u32, u32, I::Pixel)> {
157
0
        if self.x >= self.width {
158
0
            self.x = 0;
159
0
            self.y += 1;
160
0
        }
161
162
0
        if self.y >= self.height {
163
0
            None
164
        } else {
165
0
            let pixel = self.image.get_pixel(self.x, self.y);
166
0
            let p = (self.x, self.y, pixel);
167
168
0
            self.x += 1;
169
170
0
            Some(p)
171
        }
172
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<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<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
173
}
174
175
impl<I: ?Sized> Clone for Pixels<'_, I> {
176
    fn clone(&self) -> Self {
177
        Pixels { ..*self }
178
    }
179
}
180
181
/// A trait for manipulating images.
182
pub trait GenericImage: GenericImageView {
183
    /// Gets a reference to the mutable pixel at location `(x, y)`. Indexed from top left.
184
    ///
185
    /// # Panics
186
    ///
187
    /// Panics if `(x, y)` is out of bounds.
188
    ///
189
    /// Panics for dynamic images (this method is deprecated and will be removed).
190
    ///
191
    /// ## Known issues
192
    ///
193
    /// This requires the buffer to contain a unique set of continuous channels in the exact order
194
    /// and byte representation that the pixel type requires. This is somewhat restrictive.
195
    ///
196
    /// TODO: Maybe use some kind of entry API? this would allow pixel type conversion on the fly
197
    /// while still doing only one array lookup:
198
    ///
199
    /// ```ignore
200
    /// let px = image.pixel_entry_at(x,y);
201
    /// px.set_from_rgba(rgba)
202
    /// ```
203
    #[deprecated(since = "0.24.0", note = "Use `get_pixel` and `put_pixel` instead.")]
204
    fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel;
205
206
    /// Put a pixel at location (x, y). Indexed from top left.
207
    ///
208
    /// # Panics
209
    ///
210
    /// Panics if `(x, y)` is out of bounds.
211
    fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel);
212
213
    /// Puts a pixel at location (x, y). Indexed from top left.
214
    ///
215
    /// This function can be implemented in a way that ignores bounds checking.
216
    /// # Safety
217
    ///
218
    /// The coordinates must be [`in_bounds`] of the image.
219
    ///
220
    /// [`in_bounds`]: traits.GenericImageView.html#method.in_bounds
221
    unsafe fn unsafe_put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
222
        self.put_pixel(x, y, pixel);
223
    }
224
225
    /// Put a pixel at location (x, y), taking into account alpha channels
226
    #[deprecated(
227
        since = "0.24.0",
228
        note = "Use iterator `pixels_mut` to blend the pixels directly"
229
    )]
230
    fn blend_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel);
231
232
    /// Copies all of the pixels from another image into this image.
233
    ///
234
    /// The other image is copied with the top-left corner of the
235
    /// other image placed at (x, y).
236
    ///
237
    /// In order to copy only a piece of the other image, use [`GenericImageView::view`].
238
    ///
239
    /// You can use [`FlatSamples`] to source pixels from an arbitrary regular raster of channel
240
    /// values, for example from a foreign interface or a fixed image.
241
    ///
242
    /// # Returns
243
    /// Returns an error if the image is too large to be copied at the given position
244
    ///
245
    /// [`GenericImageView::view`]: trait.GenericImageView.html#method.view
246
    /// [`FlatSamples`]: flat/struct.FlatSamples.html
247
0
    fn copy_from<O>(&mut self, other: &O, x: u32, y: u32) -> ImageResult<()>
248
0
    where
249
0
        O: GenericImageView<Pixel = Self::Pixel>,
250
    {
251
        // Do bounds checking here so we can use the non-bounds-checking
252
        // functions to copy pixels.
253
0
        if self.width() < other.width() + x || self.height() < other.height() + y {
254
0
            return Err(ImageError::Parameter(ParameterError::from_kind(
255
0
                ParameterErrorKind::DimensionMismatch,
256
0
            )));
257
0
        }
258
259
0
        for k in 0..other.height() {
260
0
            for i in 0..other.width() {
261
0
                let p = other.get_pixel(i, k);
262
0
                self.put_pixel(i + x, k + y, p);
263
0
            }
264
        }
265
0
        Ok(())
266
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<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<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>>>
267
268
    /// Copies all of the pixels from one part of this image to another part of this image.
269
    ///
270
    /// The destination rectangle of the copy is specified with the top-left corner placed at (x, y).
271
    ///
272
    /// # Returns
273
    /// `true` if the copy was successful, `false` if the image could not
274
    /// be copied due to size constraints.
275
    fn copy_within(&mut self, source: Rect, x: u32, y: u32) -> bool {
276
        let Rect {
277
            x: sx,
278
            y: sy,
279
            width,
280
            height,
281
        } = source;
282
        let dx = x;
283
        let dy = y;
284
        assert!(sx < self.width() && dx < self.width());
285
        assert!(sy < self.height() && dy < self.height());
286
        if self.width() - dx.max(sx) < width || self.height() - dy.max(sy) < height {
287
            return false;
288
        }
289
        // since `.rev()` creates a new dype we would either have to go with dynamic dispatch for the ranges
290
        // or have quite a lot of code bloat. A macro gives us static dispatch with less visible bloat.
291
        macro_rules! copy_within_impl_ {
292
            ($xiter:expr, $yiter:expr) => {
293
                for y in $yiter {
294
                    let sy = sy + y;
295
                    let dy = dy + y;
296
                    for x in $xiter {
297
                        let sx = sx + x;
298
                        let dx = dx + x;
299
                        let pixel = self.get_pixel(sx, sy);
300
                        self.put_pixel(dx, dy, pixel);
301
                    }
302
                }
303
            };
304
        }
305
        // check how target and source rectangles relate to each other so we dont overwrite data before we copied it.
306
        match (sx < dx, sy < dy) {
307
            (true, true) => copy_within_impl_!((0..width).rev(), (0..height).rev()),
308
            (true, false) => copy_within_impl_!((0..width).rev(), 0..height),
309
            (false, true) => copy_within_impl_!(0..width, (0..height).rev()),
310
            (false, false) => copy_within_impl_!(0..width, 0..height),
311
        }
312
        true
313
    }
314
315
    /// Returns a mutable subimage that is a view into this image.
316
    /// If you want an immutable subimage instead, use [`GenericImageView::view`]
317
    /// The coordinates set the position of the top left corner of the `SubImage`.
318
    fn sub_image(&mut self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&mut Self>
319
    where
320
        Self: Sized,
321
    {
322
        assert!(u64::from(x) + u64::from(width) <= u64::from(self.width()));
323
        assert!(u64::from(y) + u64::from(height) <= u64::from(self.height()));
324
        SubImage::new(self, x, y, width, height)
325
    }
326
}
327
328
#[cfg(test)]
329
mod tests {
330
    use super::{GenericImage, GenericImageView};
331
332
    use crate::color::Rgba;
333
    use crate::math::Rect;
334
    use crate::{GrayImage, ImageBuffer};
335
336
    #[test]
337
    #[allow(deprecated)]
338
    /// Test that alpha blending works as expected
339
    fn test_image_alpha_blending() {
340
        let mut target = ImageBuffer::new(1, 1);
341
        target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255]));
342
        assert!(*target.get_pixel(0, 0) == Rgba([255, 0, 0, 255]));
343
        target.blend_pixel(0, 0, Rgba([0, 255, 0, 255]));
344
        assert!(*target.get_pixel(0, 0) == Rgba([0, 255, 0, 255]));
345
346
        // Blending an alpha channel onto a solid background
347
        target.blend_pixel(0, 0, Rgba([255, 0, 0, 127]));
348
        assert!(*target.get_pixel(0, 0) == Rgba([127, 127, 0, 255]));
349
350
        // Blending two alpha channels
351
        target.put_pixel(0, 0, Rgba([0, 255, 0, 127]));
352
        target.blend_pixel(0, 0, Rgba([255, 0, 0, 127]));
353
        assert!(*target.get_pixel(0, 0) == Rgba([169, 85, 0, 190]));
354
    }
355
356
    #[test]
357
    fn test_in_bounds() {
358
        let mut target = ImageBuffer::new(2, 2);
359
        target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255]));
360
361
        assert!(target.in_bounds(0, 0));
362
        assert!(target.in_bounds(1, 0));
363
        assert!(target.in_bounds(0, 1));
364
        assert!(target.in_bounds(1, 1));
365
366
        assert!(!target.in_bounds(2, 0));
367
        assert!(!target.in_bounds(0, 2));
368
        assert!(!target.in_bounds(2, 2));
369
    }
370
371
    #[test]
372
    fn test_can_subimage_clone_nonmut() {
373
        let mut source = ImageBuffer::new(3, 3);
374
        source.put_pixel(1, 1, Rgba([255u8, 0, 0, 255]));
375
376
        // A non-mutable copy of the source image
377
        let source = source.clone();
378
379
        // Clone a view into non-mutable to a separate buffer
380
        let cloned = source.view(1, 1, 1, 1).to_image();
381
382
        assert!(cloned.get_pixel(0, 0) == source.get_pixel(1, 1));
383
    }
384
385
    #[test]
386
    fn test_can_nest_views() {
387
        let mut source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
388
389
        {
390
            let mut sub1 = source.sub_image(0, 0, 2, 2);
391
            let mut sub2 = sub1.sub_image(1, 1, 1, 1);
392
            sub2.put_pixel(0, 0, Rgba([0, 0, 0, 0]));
393
        }
394
395
        assert_eq!(*source.get_pixel(1, 1), Rgba([0, 0, 0, 0]));
396
397
        let view1 = source.view(0, 0, 2, 2);
398
        assert_eq!(*source.get_pixel(1, 1), view1.get_pixel(1, 1));
399
400
        let view2 = view1.view(1, 1, 1, 1);
401
        assert_eq!(*source.get_pixel(1, 1), view2.get_pixel(0, 0));
402
    }
403
404
    #[test]
405
    #[should_panic]
406
    fn test_view_out_of_bounds() {
407
        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
408
        source.view(1, 1, 3, 3);
409
    }
410
411
    #[test]
412
    #[should_panic]
413
    fn test_view_coordinates_out_of_bounds() {
414
        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
415
        source.view(3, 3, 3, 3);
416
    }
417
418
    #[test]
419
    #[should_panic]
420
    fn test_view_width_out_of_bounds() {
421
        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
422
        source.view(1, 1, 3, 2);
423
    }
424
425
    #[test]
426
    #[should_panic]
427
    fn test_view_height_out_of_bounds() {
428
        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
429
        source.view(1, 1, 2, 3);
430
    }
431
432
    #[test]
433
    #[should_panic]
434
    fn test_view_x_out_of_bounds() {
435
        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
436
        source.view(3, 1, 3, 3);
437
    }
438
439
    #[test]
440
    #[should_panic]
441
    fn test_view_y_out_of_bounds() {
442
        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
443
        source.view(1, 3, 3, 3);
444
    }
445
446
    #[test]
447
    fn test_view_in_bounds() {
448
        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
449
        source.view(0, 0, 3, 3);
450
        source.view(1, 1, 2, 2);
451
        source.view(2, 2, 0, 0);
452
    }
453
454
    #[test]
455
    fn test_copy_sub_image() {
456
        let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255]));
457
        let view = source.view(0, 0, 3, 3);
458
        let _view2 = view;
459
        view.to_image();
460
    }
461
462
    #[test]
463
    fn test_generic_image_copy_within_oob() {
464
        let mut image: GrayImage = ImageBuffer::from_raw(4, 4, vec![0u8; 16]).unwrap();
465
        assert!(!image.sub_image(0, 0, 4, 4).copy_within(
466
            Rect {
467
                x: 0,
468
                y: 0,
469
                width: 5,
470
                height: 4
471
            },
472
            0,
473
            0
474
        ));
475
        assert!(!image.sub_image(0, 0, 4, 4).copy_within(
476
            Rect {
477
                x: 0,
478
                y: 0,
479
                width: 4,
480
                height: 5
481
            },
482
            0,
483
            0
484
        ));
485
        assert!(!image.sub_image(0, 0, 4, 4).copy_within(
486
            Rect {
487
                x: 1,
488
                y: 0,
489
                width: 4,
490
                height: 4
491
            },
492
            0,
493
            0
494
        ));
495
        assert!(!image.sub_image(0, 0, 4, 4).copy_within(
496
            Rect {
497
                x: 0,
498
                y: 0,
499
                width: 4,
500
                height: 4
501
            },
502
            1,
503
            0
504
        ));
505
        assert!(!image.sub_image(0, 0, 4, 4).copy_within(
506
            Rect {
507
                x: 0,
508
                y: 1,
509
                width: 4,
510
                height: 4
511
            },
512
            0,
513
            0
514
        ));
515
        assert!(!image.sub_image(0, 0, 4, 4).copy_within(
516
            Rect {
517
                x: 0,
518
                y: 0,
519
                width: 4,
520
                height: 4
521
            },
522
            0,
523
            1
524
        ));
525
        assert!(!image.sub_image(0, 0, 4, 4).copy_within(
526
            Rect {
527
                x: 1,
528
                y: 1,
529
                width: 4,
530
                height: 4
531
            },
532
            0,
533
            0
534
        ));
535
    }
536
537
    #[test]
538
    fn test_generic_image_copy_within_tl() {
539
        let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
540
        let expected = [0, 1, 2, 3, 4, 0, 1, 2, 8, 4, 5, 6, 12, 8, 9, 10];
541
        let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
542
        assert!(image.sub_image(0, 0, 4, 4).copy_within(
543
            Rect {
544
                x: 0,
545
                y: 0,
546
                width: 3,
547
                height: 3
548
            },
549
            1,
550
            1
551
        ));
552
        assert_eq!(&image.into_raw(), &expected);
553
    }
554
555
    #[test]
556
    fn test_generic_image_copy_within_tr() {
557
        let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
558
        let expected = [0, 1, 2, 3, 1, 2, 3, 7, 5, 6, 7, 11, 9, 10, 11, 15];
559
        let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
560
        assert!(image.sub_image(0, 0, 4, 4).copy_within(
561
            Rect {
562
                x: 1,
563
                y: 0,
564
                width: 3,
565
                height: 3
566
            },
567
            0,
568
            1
569
        ));
570
        assert_eq!(&image.into_raw(), &expected);
571
    }
572
573
    #[test]
574
    fn test_generic_image_copy_within_bl() {
575
        let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
576
        let expected = [0, 4, 5, 6, 4, 8, 9, 10, 8, 12, 13, 14, 12, 13, 14, 15];
577
        let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
578
        assert!(image.sub_image(0, 0, 4, 4).copy_within(
579
            Rect {
580
                x: 0,
581
                y: 1,
582
                width: 3,
583
                height: 3
584
            },
585
            1,
586
            0
587
        ));
588
        assert_eq!(&image.into_raw(), &expected);
589
    }
590
591
    #[test]
592
    fn test_generic_image_copy_within_br() {
593
        let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
594
        let expected = [5, 6, 7, 3, 9, 10, 11, 7, 13, 14, 15, 11, 12, 13, 14, 15];
595
        let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
596
        assert!(image.sub_image(0, 0, 4, 4).copy_within(
597
            Rect {
598
                x: 1,
599
                y: 1,
600
                width: 3,
601
                height: 3
602
            },
603
            0,
604
            0
605
        ));
606
        assert_eq!(&image.into_raw(), &expected);
607
    }
608
}