/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 | | } |