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