/src/image/src/images/generic_image.rs
Line | Count | Source |
1 | | use crate::error::{ImageError, ImageResult}; |
2 | | use crate::flat::{ViewMutOfPixel, ViewOfPixel}; |
3 | | use crate::math::Rect; |
4 | | use crate::traits::Pixel; |
5 | | use crate::{ImageBuffer, SubImage}; |
6 | | |
7 | | /// Trait to inspect an image. |
8 | | /// |
9 | | /// ``` |
10 | | /// use image::{GenericImageView, Rgb, RgbImage}; |
11 | | /// |
12 | | /// let buffer = RgbImage::new(10, 10); |
13 | | /// let image: &dyn GenericImageView<Pixel = Rgb<u8>> = &buffer; |
14 | | /// ``` |
15 | | pub trait GenericImageView { |
16 | | /// The type of pixel. |
17 | | type Pixel: Pixel; |
18 | | |
19 | | /// The width and height of this image. |
20 | | fn dimensions(&self) -> (u32, u32); |
21 | | |
22 | | /// The width of this image. |
23 | 0 | fn width(&self) -> u32 { |
24 | 0 | let (w, _) = self.dimensions(); |
25 | 0 | w |
26 | 0 | } Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::flat::View<&[f32], image::color::Rgb<f32>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::flat::View<&[f32], image::color::Luma<f32>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::flat::View<&[f32], image::color::Rgba<f32>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::flat::View<&[f32], image::color::LumaA<f32>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::flat::View<&[u8], image::color::Rgb<u8>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::flat::View<&[u8], image::color::Luma<u8>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::flat::View<&[u8], image::color::Rgba<u8>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::flat::View<&[u8], image::color::LumaA<u8>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::flat::View<&[u16], image::color::Rgb<u16>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::flat::View<&[u16], image::color::Luma<u16>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::flat::View<&[u16], image::color::Rgba<u16>> as image::images::generic_image::GenericImageView>::width Unexecuted instantiation: <image::images::flat::View<&[u16], image::color::LumaA<u16>> as image::images::generic_image::GenericImageView>::width |
27 | | |
28 | | /// The height of this image. |
29 | 0 | fn height(&self) -> u32 { |
30 | 0 | let (_, h) = self.dimensions(); |
31 | 0 | h |
32 | 0 | } Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::flat::View<&[f32], image::color::Rgb<f32>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::flat::View<&[f32], image::color::Luma<f32>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::flat::View<&[f32], image::color::Rgba<f32>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::flat::View<&[f32], image::color::LumaA<f32>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::flat::View<&[u8], image::color::Rgb<u8>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::flat::View<&[u8], image::color::Luma<u8>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::flat::View<&[u8], image::color::Rgba<u8>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::flat::View<&[u8], image::color::LumaA<u8>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::flat::View<&[u16], image::color::Rgb<u16>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::flat::View<&[u16], image::color::Luma<u16>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::flat::View<&[u16], image::color::Rgba<u16>> as image::images::generic_image::GenericImageView>::height Unexecuted instantiation: <image::images::flat::View<&[u16], image::color::LumaA<u16>> as image::images::generic_image::GenericImageView>::height |
33 | | |
34 | | /// Returns true if this x, y coordinate is contained inside the image. |
35 | 0 | fn in_bounds(&self, x: u32, y: u32) -> bool { |
36 | 0 | let (width, height) = self.dimensions(); |
37 | 0 | x < width && y < height |
38 | 0 | } |
39 | | |
40 | | /// Returns the pixel located at (x, y). Indexed from top left. |
41 | | /// |
42 | | /// # Panics |
43 | | /// |
44 | | /// Panics if `(x, y)` is out of bounds. |
45 | | fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel; |
46 | | |
47 | | /// Returns the pixel located at (x, y). Indexed from top left. |
48 | | /// |
49 | | /// This function can be implemented in a way that ignores bounds checking. |
50 | | /// # Safety |
51 | | /// |
52 | | /// The coordinates must be [`in_bounds`] of the image. |
53 | | /// |
54 | | /// [`in_bounds`]: Self::in_bounds |
55 | 0 | unsafe fn unsafe_get_pixel(&self, x: u32, y: u32) -> Self::Pixel { |
56 | 0 | self.get_pixel(x, y) |
57 | 0 | } |
58 | | |
59 | | /// Returns an Iterator over the pixels of this image. |
60 | | /// The iterator yields the coordinates of each pixel |
61 | | /// along with their value |
62 | 0 | fn pixels(&self) -> Pixels<'_, Self> |
63 | 0 | where |
64 | 0 | Self: Sized, |
65 | | { |
66 | 0 | let (width, height) = self.dimensions(); |
67 | | |
68 | 0 | Pixels { |
69 | 0 | image: self, |
70 | 0 | x: 0, |
71 | 0 | y: 0, |
72 | 0 | width, |
73 | 0 | height, |
74 | 0 | } |
75 | 0 | } Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::pixels Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::pixels Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::pixels Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::pixels Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::pixels Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::pixels Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::pixels Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::pixels Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::pixels Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::pixels Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::pixels Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::pixels |
76 | | |
77 | | /// Returns a subimage that is an immutable view into this image. |
78 | | /// You can use [`GenericImage::sub_image`] if you need a mutable view instead. |
79 | | /// The coordinates set the position of the top left corner of the view. |
80 | | /// |
81 | | /// # Panics |
82 | | /// |
83 | | /// Panics if the dimensions provided fall out of bounds. |
84 | 0 | fn view(&self, rect: Rect) -> SubImage<&Self> |
85 | 0 | where |
86 | 0 | Self: Sized, |
87 | | { |
88 | 0 | rect.assert_in_bounds_of(self); |
89 | 0 | SubImage::new(self, rect) |
90 | 0 | } |
91 | | |
92 | | /// Returns a subimage that is an immutable view into this image so long as |
93 | | /// the provided coordinates and dimensions are within the bounds of this Image. |
94 | 0 | fn try_view(&self, rect: Rect) -> Result<SubImage<&Self>, ImageError> |
95 | 0 | where |
96 | 0 | Self: Sized, |
97 | | { |
98 | 0 | rect.test_in_bounds_of(self)?; |
99 | 0 | Ok(SubImage::new(self, rect)) |
100 | 0 | } |
101 | | |
102 | | /// Create an empty [`ImageBuffer`] with the same pixel type as this image. |
103 | | /// |
104 | | /// This should ensure metadata such as the color space are transferred without copying any of |
105 | | /// the pixel data. The idea is to prepare a buffer ready to be filled with a filtered or |
106 | | /// portion of the channel data from the current image without performing the work of copying |
107 | | /// the data into that buffer twice. |
108 | | /// |
109 | | /// The default implementation defers to [`GenericImageView::buffer_like`]. |
110 | 0 | fn buffer_like(&self) -> ImageBuffer<Self::Pixel, Vec<<Self::Pixel as Pixel>::Subpixel>> { |
111 | 0 | let (w, h) = self.dimensions(); |
112 | 0 | self.buffer_with_dimensions(w, h) |
113 | 0 | } Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::buffer_like Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::buffer_like Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::buffer_like Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::buffer_like Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::buffer_like Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::buffer_like Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::buffer_like Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::buffer_like Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::buffer_like Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImageView>::buffer_like Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImageView>::buffer_like Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImageView>::buffer_like |
114 | | |
115 | | /// Create an empty [`ImageBuffer`] with different dimensions. |
116 | | /// |
117 | | /// See [`GenericImageView::buffer_like`]. |
118 | | /// |
119 | | /// Uses for this are for instances preparing a buffer for only a portion of the image, or |
120 | | /// extracting the metadata to prepare a buffer of a different pixel type. |
121 | 0 | fn buffer_with_dimensions( |
122 | 0 | &self, |
123 | 0 | width: u32, |
124 | 0 | height: u32, |
125 | 0 | ) -> ImageBuffer<Self::Pixel, Vec<<Self::Pixel as Pixel>::Subpixel>> { |
126 | 0 | ImageBuffer::new(width, height) |
127 | 0 | } |
128 | | |
129 | | /// If the buffer has a fitting layout, return a canonical view of the samples. |
130 | | /// |
131 | | /// This is the basis of optimization and by default return `None`. It lets consumers of |
132 | | /// generic images access the sample data through a canonical descriptor of its layout directly |
133 | | /// instead of pixel-by-pixel. This provides more efficient forms of access that the |
134 | | /// [`GenericImageView`] trait itself does not demand from all its implementations. |
135 | | /// |
136 | | /// Implementation of this method should be cheap to call. |
137 | | /// |
138 | | /// See [`GenericImage::to_pixel_view_mut`] for images that allow mutating pixels. |
139 | | /// |
140 | | /// If implemented, a [`SubImage`] proxy of this image will provide a sample view as well. |
141 | 0 | fn to_pixel_view(&self) -> Option<ViewOfPixel<'_, Self::Pixel>> { |
142 | 0 | None |
143 | 0 | } |
144 | | } |
145 | | |
146 | | /// Immutable pixel iterator |
147 | | #[derive(Debug)] |
148 | | pub struct Pixels<'a, I: ?Sized + 'a> { |
149 | | image: &'a I, |
150 | | x: u32, |
151 | | y: u32, |
152 | | width: u32, |
153 | | height: u32, |
154 | | } |
155 | | |
156 | | impl<I: GenericImageView> Iterator for Pixels<'_, I> { |
157 | | type Item = (u32, u32, I::Pixel); |
158 | | |
159 | 0 | fn next(&mut self) -> Option<(u32, u32, I::Pixel)> { |
160 | 0 | if self.x >= self.width { |
161 | 0 | self.x = 0; |
162 | 0 | self.y += 1; |
163 | 0 | } |
164 | | |
165 | 0 | if self.y >= self.height { |
166 | 0 | None |
167 | | } else { |
168 | 0 | let pixel = self.image.get_pixel(self.x, self.y); |
169 | 0 | let p = (self.x, self.y, pixel); |
170 | | |
171 | 0 | self.x += 1; |
172 | | |
173 | 0 | Some(p) |
174 | | } |
175 | 0 | } Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::Rgb<f32>, alloc::vec::Vec<f32>>> as core::iter::traits::iterator::Iterator>::next Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::Rgb<u8>, alloc::vec::Vec<u8>>> as core::iter::traits::iterator::Iterator>::next Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::Rgb<u16>, alloc::vec::Vec<u16>>> as core::iter::traits::iterator::Iterator>::next Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::Luma<f32>, alloc::vec::Vec<f32>>> as core::iter::traits::iterator::Iterator>::next Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::Luma<u8>, alloc::vec::Vec<u8>>> as core::iter::traits::iterator::Iterator>::next Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::Luma<u16>, alloc::vec::Vec<u16>>> as core::iter::traits::iterator::Iterator>::next Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::Rgba<f32>, alloc::vec::Vec<f32>>> as core::iter::traits::iterator::Iterator>::next Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::Rgba<u8>, alloc::vec::Vec<u8>>> as core::iter::traits::iterator::Iterator>::next Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::Rgba<u16>, alloc::vec::Vec<u16>>> as core::iter::traits::iterator::Iterator>::next Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::LumaA<f32>, alloc::vec::Vec<f32>>> as core::iter::traits::iterator::Iterator>::next Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::LumaA<u8>, alloc::vec::Vec<u8>>> as core::iter::traits::iterator::Iterator>::next Unexecuted instantiation: <image::images::generic_image::Pixels<image::images::buffer::ImageBuffer<image::color::LumaA<u16>, alloc::vec::Vec<u16>>> as core::iter::traits::iterator::Iterator>::next |
176 | | } |
177 | | |
178 | | impl<I: ?Sized> Clone for Pixels<'_, I> { |
179 | 0 | fn clone(&self) -> Self { |
180 | 0 | Pixels { ..*self } |
181 | 0 | } |
182 | | } |
183 | | |
184 | | /// A trait for manipulating images. |
185 | | pub trait GenericImage: GenericImageView { |
186 | | /// Put a pixel at location (x, y). Indexed from top left. |
187 | | /// |
188 | | /// # Panics |
189 | | /// |
190 | | /// Panics if `(x, y)` is out of bounds. |
191 | | fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel); |
192 | | |
193 | | /// Puts a pixel at location (x, y). Indexed from top left. |
194 | | /// |
195 | | /// This function can be implemented in a way that ignores bounds checking. |
196 | | /// # Safety |
197 | | /// |
198 | | /// The coordinates must be [`in_bounds`] of the image. |
199 | | /// |
200 | | /// [`in_bounds`]: GenericImageView::in_bounds |
201 | 0 | unsafe fn unsafe_put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) { |
202 | 0 | self.put_pixel(x, y, pixel); |
203 | 0 | } |
204 | | |
205 | | /// Copies all of the pixels from another image into this image. |
206 | | /// |
207 | | /// The other image is copied with the top-left corner of the |
208 | | /// other image placed at (x, y). |
209 | | /// |
210 | | /// In order to copy only a piece of the other image, use [`GenericImageView::view`]. |
211 | | /// |
212 | | /// You can use [`FlatSamples`] to source pixels from an arbitrary regular raster of channel |
213 | | /// values, for example from a foreign interface or a fixed image. |
214 | | /// |
215 | | /// # Returns |
216 | | /// Returns an error if the image is too large to be copied at the given position |
217 | | /// |
218 | | /// [`FlatSamples`]: crate::FlatSamples |
219 | 0 | fn copy_from<O>(&mut self, other: &O, x: u32, y: u32) -> ImageResult<()> |
220 | 0 | where |
221 | 0 | O: GenericImageView<Pixel = Self::Pixel>, |
222 | | { |
223 | | // This makes it easy to keep the default implementation without sacrificing performance. |
224 | | // It suffices for impls to override `copy_from_samples` for most of the gains. |
225 | 0 | if let Some(flat) = other.to_pixel_view() { |
226 | 0 | return self.copy_from_samples(flat, x, y); |
227 | 0 | } |
228 | | |
229 | | // Note the order: for types that implement an efficient assignment *from* a basic view we |
230 | | // also expect that they know how to iterate themselves efficiently, they can do their own |
231 | | // View-To-View performance or may do eve better than that if their view is more complex for |
232 | | // some reason. Their choice. On the other hand if the source is _not_ a simple view then it |
233 | | // will likely need to go through individual `GenericImageView::get_pixel` calls. And in |
234 | | // this case we can still save on iterator calls for the target. The customization point |
235 | | // however does not exist; any trait impl that intends to make this fast would need to |
236 | | // provide a full `copy_from` impl. We only need to avoid the recursion here: `ViewMut` will |
237 | | // override its `copy_from` with the intended effect by providing a non-trait inherent |
238 | | // method instead. |
239 | 0 | if let Some(mut view) = self.to_pixel_view_mut() { |
240 | 0 | return view.inner_copy_from(other, x, y); |
241 | 0 | } |
242 | | |
243 | | // Do bounds checking here so we can use the non-bounds-checking |
244 | | // functions to copy pixels. |
245 | 0 | Rect::from_image_at(other, x, y).test_in_bounds_of(self)?; |
246 | | |
247 | 0 | for k in 0..other.height() { |
248 | 0 | for i in 0..other.width() { |
249 | 0 | let p = other.get_pixel(i, k); |
250 | 0 | self.put_pixel(i + x, k + y, p); |
251 | 0 | } |
252 | | } |
253 | | |
254 | 0 | Ok(()) |
255 | 0 | } Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::Rgb<f32>, alloc::vec::Vec<f32>>> Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::Rgb<u8>, alloc::vec::Vec<u8>>> Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgb<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::Rgb<u16>, alloc::vec::Vec<u16>>> Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::Luma<f32>, alloc::vec::Vec<f32>>> Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::Luma<u8>, alloc::vec::Vec<u8>>> Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Luma<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::Luma<u16>, alloc::vec::Vec<u16>>> Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::Rgba<f32>, alloc::vec::Vec<f32>>> Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::Rgba<u8>, alloc::vec::Vec<u8>>> Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::Rgba<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::Rgba<u16>, alloc::vec::Vec<u16>>> Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<f32>, alloc::vec::Vec<f32>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::LumaA<f32>, alloc::vec::Vec<f32>>> Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u8>, alloc::vec::Vec<u8>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::LumaA<u8>, alloc::vec::Vec<u8>>> Unexecuted instantiation: <image::images::buffer::ImageBuffer<image::color::LumaA<u16>, alloc::vec::Vec<u16>> as image::images::generic_image::GenericImage>::copy_from::<image::images::buffer::ImageBuffer<image::color::LumaA<u16>, alloc::vec::Vec<u16>>> |
256 | | |
257 | | /// Copy pixels from a regular strided matrix of pixels. |
258 | 0 | fn copy_from_samples( |
259 | 0 | &mut self, |
260 | 0 | samples: ViewOfPixel<'_, Self::Pixel>, |
261 | 0 | x: u32, |
262 | 0 | y: u32, |
263 | 0 | ) -> ImageResult<()> { |
264 | | // Even though the implementation is the same, do not just call `Self::copy_from` here to |
265 | | // avoid circular dependencies in careless implementations. |
266 | 0 | Rect::from_image_at(&samples, x, y).test_in_bounds_of(self)?; |
267 | | |
268 | 0 | for k in 0..samples.height() { |
269 | 0 | for i in 0..samples.width() { |
270 | 0 | let p = samples.get_pixel(i, k); |
271 | 0 | self.put_pixel(i + x, k + y, p); |
272 | 0 | } |
273 | | } |
274 | | |
275 | 0 | Ok(()) |
276 | 0 | } |
277 | | |
278 | | /// Copies all of the pixels from one part of this image to another part of this image. |
279 | | /// |
280 | | /// The destination rectangle of the copy is specified with the top-left corner placed at (x, y). |
281 | | /// |
282 | | /// # Returns |
283 | | /// `true` if the copy was successful, `false` if the image could not |
284 | | /// be copied due to size constraints. |
285 | 0 | fn copy_within(&mut self, source: Rect, x: u32, y: u32) -> bool { |
286 | | let Rect { |
287 | 0 | x: sx, |
288 | 0 | y: sy, |
289 | 0 | width, |
290 | 0 | height, |
291 | 0 | } = source; |
292 | 0 | let dx = x; |
293 | 0 | let dy = y; |
294 | 0 | assert!(sx < self.width() && dx < self.width()); |
295 | 0 | assert!(sy < self.height() && dy < self.height()); |
296 | 0 | if self.width() - dx.max(sx) < width || self.height() - dy.max(sy) < height { |
297 | 0 | return false; |
298 | 0 | } |
299 | | // since `.rev()` creates a new dype we would either have to go with dynamic dispatch for the ranges |
300 | | // or have quite a lot of code bloat. A macro gives us static dispatch with less visible bloat. |
301 | | macro_rules! copy_within_impl_ { |
302 | | ($xiter:expr, $yiter:expr) => { |
303 | | for y in $yiter { |
304 | | let sy = sy + y; |
305 | | let dy = dy + y; |
306 | | for x in $xiter { |
307 | | let sx = sx + x; |
308 | | let dx = dx + x; |
309 | | let pixel = self.get_pixel(sx, sy); |
310 | | self.put_pixel(dx, dy, pixel); |
311 | | } |
312 | | } |
313 | | }; |
314 | | } |
315 | | // check how target and source rectangles relate to each other so we dont overwrite data before we copied it. |
316 | 0 | match (sx < dx, sy < dy) { |
317 | 0 | (true, true) => copy_within_impl_!((0..width).rev(), (0..height).rev()), |
318 | 0 | (true, false) => copy_within_impl_!((0..width).rev(), 0..height), |
319 | 0 | (false, true) => copy_within_impl_!(0..width, (0..height).rev()), |
320 | 0 | (false, false) => copy_within_impl_!(0..width, 0..height), |
321 | | } |
322 | 0 | true |
323 | 0 | } |
324 | | |
325 | | /// Returns a mutable subimage that is a view into this image. |
326 | | /// If you want an immutable subimage instead, use [`GenericImageView::view`] |
327 | | /// The coordinates set the position of the top left corner of the `SubImage`. |
328 | 0 | fn sub_image(&mut self, rect: Rect) -> SubImage<&mut Self> |
329 | 0 | where |
330 | 0 | Self: Sized, |
331 | | { |
332 | 0 | rect.assert_in_bounds_of(self); |
333 | 0 | SubImage::new(self, rect) |
334 | 0 | } |
335 | | |
336 | | /// If the buffer has a fitting layout, return a canonical mutable view of the samples. |
337 | | /// |
338 | | /// This is the basis of optimization and by default return `None`. It lets consumers of generic |
339 | | /// images access the sample data through a canonical descriptor of its layout directly instead |
340 | | /// of pixel-by-pixel. This provides more efficient, batched, forms of access that the |
341 | | /// [`GenericImage`] trait itself does not demand from all its implementations. |
342 | | /// |
343 | | /// Implementation of this method should be cheap to call. |
344 | | /// |
345 | | /// See [`GenericImageView::to_pixel_view`]. |
346 | | /// |
347 | | /// If implemented, a [`SubImage`] proxy of this image will provide a sample view as well. |
348 | 0 | fn to_pixel_view_mut(&mut self) -> Option<ViewMutOfPixel<'_, Self::Pixel>> { |
349 | 0 | None |
350 | 0 | } |
351 | | } |
352 | | |
353 | | #[cfg(test)] |
354 | | mod tests { |
355 | | use super::{GenericImage, GenericImageView}; |
356 | | |
357 | | use crate::color::Rgba; |
358 | | use crate::math::Rect; |
359 | | use crate::{GrayImage, ImageBuffer}; |
360 | | |
361 | | #[test] |
362 | | fn test_image_put_pixel() { |
363 | | let mut target = ImageBuffer::new(1, 1); |
364 | | let pixel: Rgba<u8> = Rgba([255, 0, 0, 255]); |
365 | | target.put_pixel(0, 0, pixel); |
366 | | assert_eq!(*target.get_pixel(0, 0), pixel); |
367 | | } |
368 | | |
369 | | #[test] |
370 | | fn test_in_bounds() { |
371 | | let mut target = ImageBuffer::new(2, 2); |
372 | | target.put_pixel(0, 0, Rgba([255u8, 0, 0, 255])); |
373 | | |
374 | | assert!(target.in_bounds(0, 0)); |
375 | | assert!(target.in_bounds(1, 0)); |
376 | | assert!(target.in_bounds(0, 1)); |
377 | | assert!(target.in_bounds(1, 1)); |
378 | | |
379 | | assert!(!target.in_bounds(2, 0)); |
380 | | assert!(!target.in_bounds(0, 2)); |
381 | | assert!(!target.in_bounds(2, 2)); |
382 | | } |
383 | | |
384 | | #[test] |
385 | | fn test_can_subimage_clone_nonmut() { |
386 | | let mut source = ImageBuffer::new(3, 3); |
387 | | source.put_pixel(1, 1, Rgba([255u8, 0, 0, 255])); |
388 | | |
389 | | // A non-mutable copy of the source image |
390 | | let source = source.clone(); |
391 | | |
392 | | // Clone a view into non-mutable to a separate buffer |
393 | | let cloned = source.view(Rect::from_xy_ranges(1..2, 1..2)).to_image(); |
394 | | |
395 | | assert!(cloned.get_pixel(0, 0) == source.get_pixel(1, 1)); |
396 | | } |
397 | | |
398 | | #[test] |
399 | | fn test_can_nest_views() { |
400 | | let mut source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); |
401 | | |
402 | | { |
403 | | let mut sub1 = source.sub_image(Rect::from_xy_ranges(0..2, 0..2)); |
404 | | let mut sub2 = sub1.sub_image(Rect::from_xy_ranges(1..2, 1..2)); |
405 | | sub2.put_pixel(0, 0, Rgba([0, 0, 0, 0])); |
406 | | } |
407 | | |
408 | | assert_eq!(*source.get_pixel(1, 1), Rgba([0, 0, 0, 0])); |
409 | | |
410 | | let view1 = source.view(Rect::from_xy_ranges(0..2, 0..2)); |
411 | | assert_eq!(*source.get_pixel(1, 1), view1.get_pixel(1, 1)); |
412 | | |
413 | | let view2 = view1.view(Rect::from_xy_ranges(1..2, 1..2)); |
414 | | assert_eq!(*source.get_pixel(1, 1), view2.get_pixel(0, 0)); |
415 | | } |
416 | | |
417 | | #[test] |
418 | | #[should_panic] |
419 | | fn test_view_out_of_bounds() { |
420 | | let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); |
421 | | source.view(Rect::from_xy_ranges(1..4, 1..4)); |
422 | | } |
423 | | |
424 | | #[test] |
425 | | #[should_panic] |
426 | | fn test_view_coordinates_out_of_bounds() { |
427 | | let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); |
428 | | source.view(Rect::from_xy_ranges(3..6, 3..6)); |
429 | | } |
430 | | |
431 | | #[test] |
432 | | #[should_panic] |
433 | | fn test_view_width_out_of_bounds() { |
434 | | let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); |
435 | | source.view(Rect::from_xy_ranges(1..4, 1..3)); |
436 | | } |
437 | | |
438 | | #[test] |
439 | | #[should_panic] |
440 | | fn test_view_height_out_of_bounds() { |
441 | | let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); |
442 | | source.view(Rect::from_xy_ranges(1..3, 1..4)); |
443 | | } |
444 | | |
445 | | #[test] |
446 | | #[should_panic] |
447 | | fn test_view_x_out_of_bounds() { |
448 | | let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); |
449 | | source.view(Rect::from_xy_ranges(3..6, 1..3)); |
450 | | } |
451 | | |
452 | | #[test] |
453 | | #[should_panic] |
454 | | fn test_view_y_out_of_bounds() { |
455 | | let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); |
456 | | source.view(Rect::from_xy_ranges(1..3, 3..6)); |
457 | | } |
458 | | |
459 | | #[test] |
460 | | fn test_view_in_bounds() { |
461 | | let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); |
462 | | source.view(Rect::from_xy_ranges(0..3, 0..3)); |
463 | | source.view(Rect::from_xy_ranges(1..3, 1..3)); |
464 | | source.view(Rect::from_xy_ranges(2..2, 2..2)); |
465 | | } |
466 | | |
467 | | #[test] |
468 | | fn test_copy_sub_image() { |
469 | | let source = ImageBuffer::from_pixel(3, 3, Rgba([255u8, 0, 0, 255])); |
470 | | let view = source.view(Rect::from_xy_ranges(0..3, 0..3)); |
471 | | let _view2 = view; |
472 | | view.to_image(); |
473 | | } |
474 | | |
475 | | #[test] |
476 | | fn test_generic_image_copy_within_oob() { |
477 | | let mut image: GrayImage = ImageBuffer::from_raw(4, 4, vec![0u8; 16]).unwrap(); |
478 | | assert!(!image |
479 | | .sub_image(Rect::from_xy_ranges(0..4, 0..4)) |
480 | | .copy_within(Rect::from_xy_ranges(0..5, 0..4), 0, 0)); |
481 | | assert!(!image |
482 | | .sub_image(Rect::from_xy_ranges(0..4, 0..4)) |
483 | | .copy_within(Rect::from_xy_ranges(0..4, 0..5), 0, 0)); |
484 | | assert!(!image |
485 | | .sub_image(Rect::from_xy_ranges(0..4, 0..4)) |
486 | | .copy_within(Rect::from_xy_ranges(1..5, 0..4), 0, 0)); |
487 | | assert!(!image |
488 | | .sub_image(Rect::from_xy_ranges(0..4, 0..4)) |
489 | | .copy_within(Rect::from_xy_ranges(0..4, 0..4), 1, 0)); |
490 | | assert!(!image |
491 | | .sub_image(Rect::from_xy_ranges(0..4, 0..4)) |
492 | | .copy_within(Rect::from_xy_ranges(0..4, 1..5), 0, 0)); |
493 | | assert!(!image |
494 | | .sub_image(Rect::from_xy_ranges(0..4, 0..4)) |
495 | | .copy_within(Rect::from_xy_ranges(0..4, 0..4), 0, 1)); |
496 | | assert!(!image |
497 | | .sub_image(Rect::from_xy_ranges(0..4, 0..4)) |
498 | | .copy_within(Rect::from_xy_ranges(1..5, 0..4), 0, 0)); |
499 | | } |
500 | | |
501 | | #[test] |
502 | | fn test_generic_image_copy_within_tl() { |
503 | | let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; |
504 | | let expected = [0, 1, 2, 3, 4, 0, 1, 2, 8, 4, 5, 6, 12, 8, 9, 10]; |
505 | | let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap(); |
506 | | assert!(image |
507 | | .sub_image(Rect::from_xy_ranges(0..4, 0..4)) |
508 | | .copy_within(Rect::from_xy_ranges(0..3, 0..3), 1, 1)); |
509 | | assert_eq!(&image.into_raw(), &expected); |
510 | | } |
511 | | |
512 | | #[test] |
513 | | fn test_generic_image_copy_within_tr() { |
514 | | let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; |
515 | | let expected = [0, 1, 2, 3, 1, 2, 3, 7, 5, 6, 7, 11, 9, 10, 11, 15]; |
516 | | let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap(); |
517 | | assert!(image |
518 | | .sub_image(Rect::from_xy_ranges(0..4, 0..4)) |
519 | | .copy_within(Rect::from_xy_ranges(1..4, 0..3), 0, 1)); |
520 | | assert_eq!(&image.into_raw(), &expected); |
521 | | } |
522 | | |
523 | | #[test] |
524 | | fn test_generic_image_copy_within_bl() { |
525 | | let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; |
526 | | let expected = [0, 4, 5, 6, 4, 8, 9, 10, 8, 12, 13, 14, 12, 13, 14, 15]; |
527 | | let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap(); |
528 | | assert!(image |
529 | | .sub_image(Rect::from_xy_ranges(0..4, 0..4)) |
530 | | .copy_within(Rect::from_xy_ranges(0..3, 1..4), 1, 0)); |
531 | | assert_eq!(&image.into_raw(), &expected); |
532 | | } |
533 | | |
534 | | #[test] |
535 | | fn test_generic_image_copy_within_br() { |
536 | | let data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; |
537 | | let expected = [5, 6, 7, 3, 9, 10, 11, 7, 13, 14, 15, 11, 12, 13, 14, 15]; |
538 | | let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap(); |
539 | | assert!(image |
540 | | .sub_image(Rect::from_xy_ranges(0..4, 0..4)) |
541 | | .copy_within(Rect::from_xy_ranges(1..4, 1..4), 0, 0)); |
542 | | assert_eq!(&image.into_raw(), &expected); |
543 | | } |
544 | | } |