Coverage Report

Created: 2025-10-14 06:57

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