Coverage Report

Created: 2026-06-10 07:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/image/src/images/sub_image.rs
Line
Count
Source
1
use crate::{
2
    flat::{ViewMutOfPixel, ViewOfPixel},
3
    math::Rect,
4
    GenericImage, GenericImageView, ImageBuffer, Pixel,
5
};
6
7
use std::ops::{Deref, DerefMut};
8
9
/// A View into another image
10
///
11
/// Instances of this struct can be created using:
12
///   - [`GenericImage::sub_image`] to create a mutable view,
13
///   - [`GenericImageView::view`] to create an immutable view,
14
///   - [`SubImage::new`] to instantiate the struct directly.
15
///
16
/// Note that this does _not_ implement `GenericImage`, but it dereferences to one which allows you
17
/// to use it as if it did. See [Design Considerations](#Design-Considerations) below for details.
18
///
19
/// # Design Considerations
20
///
21
/// For reasons relating to coherence, this is not itself a `GenericImage` or a `GenericImageView`.
22
/// In short, we want to reserve the ability of adding traits implemented for _all_ generic images
23
/// but in a different manner for `SubImage`. This may be required to ensure that stacking
24
/// sub-images comes at no double indirect cost.
25
///
26
/// If, ultimately, this is not needed then a directly implementation of `GenericImage` can and
27
/// will get added. This inconvenience may alternatively get resolved if Rust allows some forms of
28
/// specialization, which might make this trick unnecessary and thus also allows for a direct
29
/// implementation.
30
#[derive(Copy, Clone)]
31
pub struct SubImage<I> {
32
    inner: SubImageInner<I>,
33
}
34
35
/// The inner type of `SubImage` that implements `GenericImage{,View}`.
36
///
37
/// This type is _nominally_ `pub` but it is not exported from the crate. It should be regarded as
38
/// an existential type in any case.
39
#[derive(Copy, Clone)]
40
pub struct SubImageInner<I> {
41
    image: I,
42
    xoffset: u32,
43
    yoffset: u32,
44
    xstride: u32,
45
    ystride: u32,
46
}
47
48
/// Alias to access Pixel behind a reference
49
type DerefPixel<I> = <<I as Deref>::Target as GenericImageView>::Pixel;
50
51
/// Alias to access Subpixel behind a reference
52
type DerefSubpixel<I> = <DerefPixel<I> as Pixel>::Subpixel;
53
54
impl<I> SubImage<I> {
55
    /// Construct a new subimage
56
    /// The coordinates set the position of the top left corner of the `SubImage`.
57
0
    pub fn new(image: I, rect: Rect) -> SubImage<I> {
58
0
        SubImage {
59
0
            inner: SubImageInner {
60
0
                image,
61
0
                xoffset: rect.x,
62
0
                yoffset: rect.y,
63
0
                xstride: rect.width,
64
0
                ystride: rect.height,
65
0
            },
66
0
        }
67
0
    }
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Rgb<f32>, alloc::vec::Vec<f32>>>>::new
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Rgb<u8>, alloc::vec::Vec<u8>>>>::new
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Rgb<u16>, alloc::vec::Vec<u16>>>>::new
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Luma<f32>, alloc::vec::Vec<f32>>>>::new
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Luma<u8>, alloc::vec::Vec<u8>>>>::new
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Luma<u16>, alloc::vec::Vec<u16>>>>::new
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Rgba<f32>, alloc::vec::Vec<f32>>>>::new
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Rgba<u8>, alloc::vec::Vec<u8>>>>::new
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Rgba<u16>, alloc::vec::Vec<u16>>>>::new
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::LumaA<f32>, alloc::vec::Vec<f32>>>>::new
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::LumaA<u8>, alloc::vec::Vec<u8>>>>::new
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::LumaA<u16>, alloc::vec::Vec<u16>>>>::new
68
69
    /// Change the coordinates of this subimage.
70
0
    pub fn change_bounds(&mut self, x: u32, y: u32, width: u32, height: u32) {
71
0
        self.inner.xoffset = x;
72
0
        self.inner.yoffset = y;
73
0
        self.inner.xstride = width;
74
0
        self.inner.ystride = height;
75
0
    }
76
77
    /// The offsets of this subimage relative to the underlying image.
78
0
    pub fn offsets(&self) -> (u32, u32) {
79
0
        (self.inner.xoffset, self.inner.yoffset)
80
0
    }
81
82
    /// Convert this subimage to an `ImageBuffer`
83
0
    pub fn to_image(&self) -> ImageBuffer<DerefPixel<I>, Vec<DerefSubpixel<I>>>
84
0
    where
85
0
        I: Deref,
86
0
        I::Target: GenericImageView + 'static,
87
    {
88
0
        let w = self.inner.xstride;
89
0
        let h = self.inner.ystride;
90
0
        let borrowed = &*self.inner.image;
91
0
        let mut out = borrowed.buffer_with_dimensions(w, h);
92
93
        // fast path for row-major packed views
94
0
        if let Some(view) = self.to_pixel_view() {
95
0
            if let Some(row_iter) = view.iter_rows() {
96
0
                for (row, out_row) in row_iter.zip(out.rows_mut()) {
97
0
                    Pixel::pixels_as_channels_mut(out_row).copy_from_slice(row);
98
0
                }
99
0
                return out;
100
0
            }
101
0
        }
102
103
0
        for y in 0..h {
104
0
            for x in 0..w {
105
0
                let p = borrowed.get_pixel(x + self.inner.xoffset, y + self.inner.yoffset);
106
0
                out.put_pixel(x, y, p);
107
0
            }
108
        }
109
110
0
        out
111
0
    }
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Rgb<f32>, alloc::vec::Vec<f32>>>>::to_image
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Rgb<u8>, alloc::vec::Vec<u8>>>>::to_image
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Rgb<u16>, alloc::vec::Vec<u16>>>>::to_image
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Luma<f32>, alloc::vec::Vec<f32>>>>::to_image
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Luma<u8>, alloc::vec::Vec<u8>>>>::to_image
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Luma<u16>, alloc::vec::Vec<u16>>>>::to_image
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Rgba<f32>, alloc::vec::Vec<f32>>>>::to_image
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Rgba<u8>, alloc::vec::Vec<u8>>>>::to_image
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Rgba<u16>, alloc::vec::Vec<u16>>>>::to_image
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::LumaA<f32>, alloc::vec::Vec<f32>>>>::to_image
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::LumaA<u8>, alloc::vec::Vec<u8>>>>::to_image
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::LumaA<u16>, alloc::vec::Vec<u16>>>>::to_image
112
}
113
114
/// Methods for readable images.
115
impl<I> SubImage<I>
116
where
117
    I: Deref,
118
    I::Target: GenericImageView,
119
{
120
    /// Create a sub-view of the image.
121
    ///
122
    /// The coordinates given are relative to the current view on the underlying image.
123
    ///
124
    /// Note that this method is preferred to the one from `GenericImageView`. This is accessible
125
    /// with the explicit method call syntax but it should rarely be needed due to causing an
126
    /// extra level of indirection.
127
    ///
128
    /// ```
129
    /// use image::{GenericImageView, RgbImage, SubImage};
130
    /// use image::math::Rect;
131
    /// let buffer = RgbImage::new(10, 10);
132
    ///
133
    /// let selection = Rect::from_xy_ranges(0..10, 0..10);
134
    /// let subimage: SubImage<&RgbImage> = buffer.view(selection);
135
    /// let subview: SubImage<&RgbImage> = subimage.view(selection);
136
    ///
137
    /// // Less efficient and NOT &RgbImage
138
    /// let _: SubImage<&_> = GenericImageView::view(&*subimage, selection);
139
    /// ```
140
0
    pub fn view(&self, mut rect: Rect) -> SubImage<&I::Target> {
141
0
        rect.assert_in_bounds_of(&self.inner);
142
0
        rect.x = self.inner.xoffset.saturating_add(rect.x);
143
0
        rect.y = self.inner.yoffset.saturating_add(rect.y);
144
0
        SubImage::new(&*self.inner.image, rect)
145
0
    }
146
147
    /// Get a reference to the underlying image.
148
0
    pub fn inner(&self) -> &I::Target {
149
0
        &self.inner.image
150
0
    }
151
}
152
153
impl<I> SubImage<I>
154
where
155
    I: DerefMut,
156
    I::Target: GenericImage,
157
{
158
    /// Create a mutable sub-view of the image.
159
    ///
160
    /// The coordinates given are relative to the current view on the underlying image.
161
0
    pub fn sub_image(&mut self, mut rect: Rect) -> SubImage<&mut I::Target> {
162
0
        rect.assert_in_bounds_of(&self.inner);
163
0
        rect.x = self.inner.xoffset.saturating_add(rect.x);
164
0
        rect.y = self.inner.yoffset.saturating_add(rect.y);
165
0
        SubImage::new(&mut *self.inner.image, rect)
166
0
    }
167
168
    /// Get a mutable reference to the underlying image.
169
0
    pub fn inner_mut(&mut self) -> &mut I::Target {
170
0
        &mut self.inner.image
171
0
    }
172
}
173
174
impl<I> Deref for SubImage<I>
175
where
176
    I: Deref,
177
{
178
    type Target = SubImageInner<I>;
179
180
0
    fn deref(&self) -> &Self::Target {
181
0
        &self.inner
182
0
    }
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Rgb<f32>, alloc::vec::Vec<f32>>> as core::ops::deref::Deref>::deref
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Rgb<u8>, alloc::vec::Vec<u8>>> as core::ops::deref::Deref>::deref
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Rgb<u16>, alloc::vec::Vec<u16>>> as core::ops::deref::Deref>::deref
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Luma<f32>, alloc::vec::Vec<f32>>> as core::ops::deref::Deref>::deref
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Luma<u8>, alloc::vec::Vec<u8>>> as core::ops::deref::Deref>::deref
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Luma<u16>, alloc::vec::Vec<u16>>> as core::ops::deref::Deref>::deref
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Rgba<f32>, alloc::vec::Vec<f32>>> as core::ops::deref::Deref>::deref
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Rgba<u8>, alloc::vec::Vec<u8>>> as core::ops::deref::Deref>::deref
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::Rgba<u16>, alloc::vec::Vec<u16>>> as core::ops::deref::Deref>::deref
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::LumaA<f32>, alloc::vec::Vec<f32>>> as core::ops::deref::Deref>::deref
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::LumaA<u8>, alloc::vec::Vec<u8>>> as core::ops::deref::Deref>::deref
Unexecuted instantiation: <image::images::sub_image::SubImage<&image::images::buffer::ImageBuffer<image::color::LumaA<u16>, alloc::vec::Vec<u16>>> as core::ops::deref::Deref>::deref
183
}
184
185
impl<I> DerefMut for SubImage<I>
186
where
187
    I: DerefMut,
188
{
189
0
    fn deref_mut(&mut self) -> &mut Self::Target {
190
0
        &mut self.inner
191
0
    }
192
}
193
194
impl<I> GenericImageView for SubImageInner<I>
195
where
196
    I: Deref,
197
    I::Target: GenericImageView,
198
{
199
    type Pixel = DerefPixel<I>;
200
201
0
    fn dimensions(&self) -> (u32, u32) {
202
0
        (self.xstride, self.ystride)
203
0
    }
204
205
0
    fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel {
206
0
        self.image.get_pixel(x + self.xoffset, y + self.yoffset)
207
0
    }
208
209
    /// Create a buffer with the (color) metadata of the underlying image.
210
0
    fn buffer_with_dimensions(
211
0
        &self,
212
0
        width: u32,
213
0
        height: u32,
214
0
    ) -> ImageBuffer<
215
0
        <I::Target as GenericImageView>::Pixel,
216
0
        Vec<<<I::Target as GenericImageView>::Pixel as Pixel>::Subpixel>,
217
0
    > {
218
0
        self.image.buffer_with_dimensions(width, height)
219
0
    }
220
221
0
    fn to_pixel_view(&self) -> Option<ViewOfPixel<'_, Self::Pixel>> {
222
0
        let inner = self.image.to_pixel_view()?;
223
224
        // Now pivot the inner descriptor.
225
0
        let mut descriptor = inner.into_inner();
226
227
0
        let offset = descriptor.index(0, self.xoffset, self.yoffset)?;
228
0
        descriptor.samples = descriptor.samples.get(offset..)?;
229
0
        descriptor.layout.width = self.xstride;
230
0
        descriptor.layout.height = self.ystride;
231
232
0
        descriptor.into_view().ok()
233
0
    }
Unexecuted instantiation: <image::images::sub_image::SubImageInner<&image::images::buffer::ImageBuffer<image::color::Rgb<f32>, alloc::vec::Vec<f32>>> as image::images::generic_image::GenericImageView>::to_pixel_view
Unexecuted instantiation: <image::images::sub_image::SubImageInner<&image::images::buffer::ImageBuffer<image::color::Rgb<u8>, alloc::vec::Vec<u8>>> as image::images::generic_image::GenericImageView>::to_pixel_view
Unexecuted instantiation: <image::images::sub_image::SubImageInner<&image::images::buffer::ImageBuffer<image::color::Rgb<u16>, alloc::vec::Vec<u16>>> as image::images::generic_image::GenericImageView>::to_pixel_view
Unexecuted instantiation: <image::images::sub_image::SubImageInner<&image::images::buffer::ImageBuffer<image::color::Luma<f32>, alloc::vec::Vec<f32>>> as image::images::generic_image::GenericImageView>::to_pixel_view
Unexecuted instantiation: <image::images::sub_image::SubImageInner<&image::images::buffer::ImageBuffer<image::color::Luma<u8>, alloc::vec::Vec<u8>>> as image::images::generic_image::GenericImageView>::to_pixel_view
Unexecuted instantiation: <image::images::sub_image::SubImageInner<&image::images::buffer::ImageBuffer<image::color::Luma<u16>, alloc::vec::Vec<u16>>> as image::images::generic_image::GenericImageView>::to_pixel_view
Unexecuted instantiation: <image::images::sub_image::SubImageInner<&image::images::buffer::ImageBuffer<image::color::Rgba<f32>, alloc::vec::Vec<f32>>> as image::images::generic_image::GenericImageView>::to_pixel_view
Unexecuted instantiation: <image::images::sub_image::SubImageInner<&image::images::buffer::ImageBuffer<image::color::Rgba<u8>, alloc::vec::Vec<u8>>> as image::images::generic_image::GenericImageView>::to_pixel_view
Unexecuted instantiation: <image::images::sub_image::SubImageInner<&image::images::buffer::ImageBuffer<image::color::Rgba<u16>, alloc::vec::Vec<u16>>> as image::images::generic_image::GenericImageView>::to_pixel_view
Unexecuted instantiation: <image::images::sub_image::SubImageInner<&image::images::buffer::ImageBuffer<image::color::LumaA<f32>, alloc::vec::Vec<f32>>> as image::images::generic_image::GenericImageView>::to_pixel_view
Unexecuted instantiation: <image::images::sub_image::SubImageInner<&image::images::buffer::ImageBuffer<image::color::LumaA<u8>, alloc::vec::Vec<u8>>> as image::images::generic_image::GenericImageView>::to_pixel_view
Unexecuted instantiation: <image::images::sub_image::SubImageInner<&image::images::buffer::ImageBuffer<image::color::LumaA<u16>, alloc::vec::Vec<u16>>> as image::images::generic_image::GenericImageView>::to_pixel_view
234
}
235
236
impl<I> GenericImage for SubImageInner<I>
237
where
238
    I: DerefMut,
239
    I::Target: GenericImage + Sized,
240
{
241
0
    fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
242
0
        self.image
243
0
            .put_pixel(x + self.xoffset, y + self.yoffset, pixel);
244
0
    }
245
246
0
    fn copy_from<O>(&mut self, other: &O, x: u32, y: u32) -> Result<(), crate::ImageError>
247
0
    where
248
0
        O: GenericImageView<Pixel = Self::Pixel>,
249
    {
250
0
        Rect::from_image_at(other, x, y).test_in_bounds_of(self)?;
251
        // Dispatch the inner images `copy_from` method with adjusted offsets. this ensures its
252
        // potentially optimized implementation gets used.
253
0
        self.image
254
0
            .copy_from(other, x + self.xoffset, y + self.yoffset)
255
0
    }
256
257
0
    fn to_pixel_view_mut(&mut self) -> Option<ViewMutOfPixel<'_, Self::Pixel>> {
258
0
        let inner = self.image.to_pixel_view_mut()?;
259
260
        // Now pivot the inner descriptor.
261
0
        let mut descriptor = inner.into_inner();
262
263
0
        let offset = descriptor.index(0, self.xoffset, self.yoffset)?;
264
0
        descriptor.samples = descriptor.samples.get_mut(offset..)?;
265
0
        descriptor.layout.width = self.xstride;
266
0
        descriptor.layout.height = self.ystride;
267
268
0
        descriptor.into_view_mut().ok()
269
0
    }
270
}
271
272
#[cfg(test)]
273
mod tests {
274
    use crate::{math::Rect, metadata::Cicp, GenericImageView, RgbaImage};
275
276
    #[test]
277
    fn preserves_color_space() {
278
        let mut buffer = RgbaImage::new(16, 16);
279
        buffer[(0, 0)] = crate::Rgba([0xff, 0, 0, 255]);
280
        buffer.set_rgb_primaries(Cicp::DISPLAY_P3.primaries);
281
282
        let view = buffer.view(Rect::from_xy_ranges(0..16, 0..16));
283
        let result = view.buffer_like();
284
285
        assert_eq!(buffer.color_space(), result.color_space());
286
    }
287
288
    #[test]
289
    fn deep_preserves_color_space() {
290
        let mut buffer = RgbaImage::new(16, 16);
291
        buffer[(0, 0)] = crate::Rgba([0xff, 0, 0, 255]);
292
        buffer.set_rgb_primaries(Cicp::DISPLAY_P3.primaries);
293
294
        let view = buffer.view(Rect::from_xy_ranges(0..16, 0..16));
295
        let view = view.view(Rect::from_xy_ranges(0..16, 0..16));
296
        let result = view.buffer_like();
297
298
        assert_eq!(buffer.color_space(), result.color_space());
299
    }
300
}