/src/image/src/images/sub_image.rs
Line | Count | Source |
1 | | use crate::{math::Rect, 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, rect: Rect) -> SubImage<I> { |
53 | 0 | SubImage { |
54 | 0 | inner: SubImageInner { |
55 | 0 | image, |
56 | 0 | xoffset: rect.x, |
57 | 0 | yoffset: rect.y, |
58 | 0 | xstride: rect.width, |
59 | 0 | ystride: rect.height, |
60 | 0 | }, |
61 | 0 | } |
62 | 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<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 | 0 | pub fn change_bounds(&mut self, x: u32, y: u32, width: u32, height: u32) { |
66 | 0 | self.inner.xoffset = x; |
67 | 0 | self.inner.yoffset = y; |
68 | 0 | self.inner.xstride = width; |
69 | 0 | self.inner.ystride = height; |
70 | 0 | } |
71 | | |
72 | | /// The offsets of this subimage relative to the underlying image. |
73 | 0 | pub fn offsets(&self) -> (u32, u32) { |
74 | 0 | (self.inner.xoffset, self.inner.yoffset) |
75 | 0 | } |
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<&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 | | /// use image::math::Rect; |
114 | | /// let buffer = RgbImage::new(10, 10); |
115 | | /// |
116 | | /// let selection = Rect::from_xy_ranges(0..10, 0..10); |
117 | | /// let subimage: SubImage<&RgbImage> = buffer.view(selection); |
118 | | /// let subview: SubImage<&RgbImage> = subimage.view(selection); |
119 | | /// |
120 | | /// // Less efficient and NOT &RgbImage |
121 | | /// let _: SubImage<&_> = GenericImageView::view(&*subimage, selection); |
122 | | /// ``` |
123 | 0 | pub fn view(&self, mut rect: Rect) -> SubImage<&I::Target> { |
124 | 0 | rect.assert_in_bounds_of(&self.inner); |
125 | 0 | rect.x = self.inner.xoffset.saturating_add(rect.x); |
126 | 0 | rect.y = self.inner.yoffset.saturating_add(rect.y); |
127 | 0 | SubImage::new(&*self.inner.image, rect) |
128 | 0 | } |
129 | | |
130 | | /// Get a reference to the underlying image. |
131 | 0 | pub fn inner(&self) -> &I::Target { |
132 | 0 | &self.inner.image |
133 | 0 | } |
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 | 0 | pub fn sub_image(&mut self, mut rect: Rect) -> SubImage<&mut I::Target> { |
145 | 0 | rect.assert_in_bounds_of(&self.inner); |
146 | 0 | rect.x = self.inner.xoffset.saturating_add(rect.x); |
147 | 0 | rect.y = self.inner.yoffset.saturating_add(rect.y); |
148 | 0 | SubImage::new(&mut *self.inner.image, rect) |
149 | 0 | } |
150 | | |
151 | | /// Get a mutable reference to the underlying image. |
152 | 0 | pub fn inner_mut(&mut self) -> &mut I::Target { |
153 | 0 | &mut self.inner.image |
154 | 0 | } |
155 | | } |
156 | | |
157 | | impl<I> Deref for SubImage<I> |
158 | | where |
159 | | I: Deref, |
160 | | { |
161 | | type Target = SubImageInner<I>; |
162 | | |
163 | 0 | fn deref(&self) -> &Self::Target { |
164 | 0 | &self.inner |
165 | 0 | } |
166 | | } |
167 | | |
168 | | impl<I> DerefMut for SubImage<I> |
169 | | where |
170 | | I: DerefMut, |
171 | | { |
172 | 0 | fn deref_mut(&mut self) -> &mut Self::Target { |
173 | 0 | &mut self.inner |
174 | 0 | } |
175 | | } |
176 | | |
177 | | #[allow(deprecated)] |
178 | | impl<I> GenericImageView for SubImageInner<I> |
179 | | where |
180 | | I: Deref, |
181 | | I::Target: GenericImageView, |
182 | | { |
183 | | type Pixel = DerefPixel<I>; |
184 | | |
185 | 0 | fn dimensions(&self) -> (u32, u32) { |
186 | 0 | (self.xstride, self.ystride) |
187 | 0 | } |
188 | | |
189 | 0 | fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel { |
190 | 0 | self.image.get_pixel(x + self.xoffset, y + self.yoffset) |
191 | 0 | } |
192 | | |
193 | | /// Create a buffer with the (color) metadata of the underlying image. |
194 | 0 | fn buffer_with_dimensions( |
195 | 0 | &self, |
196 | 0 | width: u32, |
197 | 0 | height: u32, |
198 | 0 | ) -> ImageBuffer< |
199 | 0 | <I::Target as GenericImageView>::Pixel, |
200 | 0 | Vec<<<I::Target as GenericImageView>::Pixel as Pixel>::Subpixel>, |
201 | 0 | > { |
202 | 0 | self.image.buffer_with_dimensions(width, height) |
203 | 0 | } |
204 | | } |
205 | | |
206 | | #[allow(deprecated)] |
207 | | impl<I> GenericImage for SubImageInner<I> |
208 | | where |
209 | | I: DerefMut, |
210 | | I::Target: GenericImage + Sized, |
211 | | { |
212 | 0 | fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel { |
213 | 0 | self.image.get_pixel_mut(x + self.xoffset, y + self.yoffset) |
214 | 0 | } |
215 | | |
216 | 0 | fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) { |
217 | 0 | self.image |
218 | 0 | .put_pixel(x + self.xoffset, y + self.yoffset, pixel); |
219 | 0 | } |
220 | | |
221 | | /// DEPRECATED: This method will be removed. Blend the pixel directly instead. |
222 | 0 | fn blend_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) { |
223 | 0 | self.image |
224 | 0 | .blend_pixel(x + self.xoffset, y + self.yoffset, pixel); |
225 | 0 | } |
226 | | } |
227 | | |
228 | | #[cfg(test)] |
229 | | mod tests { |
230 | | use crate::{math::Rect, metadata::Cicp, GenericImageView, RgbaImage}; |
231 | | |
232 | | #[test] |
233 | | fn preserves_color_space() { |
234 | | let mut buffer = RgbaImage::new(16, 16); |
235 | | buffer[(0, 0)] = crate::Rgba([0xff, 0, 0, 255]); |
236 | | buffer.set_rgb_primaries(Cicp::DISPLAY_P3.primaries); |
237 | | |
238 | | let view = buffer.view(Rect::from_xy_ranges(0..16, 0..16)); |
239 | | let result = view.buffer_like(); |
240 | | |
241 | | assert_eq!(buffer.color_space(), result.color_space()); |
242 | | } |
243 | | |
244 | | #[test] |
245 | | fn deep_preserves_color_space() { |
246 | | let mut buffer = RgbaImage::new(16, 16); |
247 | | buffer[(0, 0)] = crate::Rgba([0xff, 0, 0, 255]); |
248 | | buffer.set_rgb_primaries(Cicp::DISPLAY_P3.primaries); |
249 | | |
250 | | let view = buffer.view(Rect::from_xy_ranges(0..16, 0..16)); |
251 | | let view = view.view(Rect::from_xy_ranges(0..16, 0..16)); |
252 | | let result = view.buffer_like(); |
253 | | |
254 | | assert_eq!(buffer.color_space(), result.color_space()); |
255 | | } |
256 | | } |