Skip to main content

ai_image/images/
sub_image.rs

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