]>
Commit | Line | Data |
---|---|---|
92f5a8d4 TL |
1 | // |
2 | // Copyright 2005-2007 Adobe Systems Incorporated | |
3 | // | |
4 | // Distributed under the Boost Software License, Version 1.0 | |
5 | // See accompanying file LICENSE_1_0.txt or copy at | |
6 | // http://www.boost.org/LICENSE_1_0.txt | |
7 | // | |
8 | #ifndef BOOST_GIL_IMAGE_VIEW_HPP | |
9 | #define BOOST_GIL_IMAGE_VIEW_HPP | |
7c673cae | 10 | |
92f5a8d4 TL |
11 | #include <boost/gil/dynamic_step.hpp> |
12 | #include <boost/gil/iterator_from_2d.hpp> | |
7c673cae | 13 | |
92f5a8d4 | 14 | #include <boost/assert.hpp> |
7c673cae FG |
15 | |
16 | #include <cstddef> | |
17 | #include <iterator> | |
7c673cae FG |
18 | |
19 | namespace boost { namespace gil { | |
20 | ||
21 | //////////////////////////////////////////////////////////////////////////////////////// | |
22 | /// \class image_view | |
23 | /// \ingroup ImageViewModel PixelBasedModel | |
24 | /// \brief A lightweight object that interprets memory as a 2D array of pixels. Models ImageViewConcept,PixelBasedConcept,HasDynamicXStepTypeConcept,HasDynamicYStepTypeConcept,HasTransposedTypeConcept | |
25 | /// | |
26 | /// Image view consists of a pixel 2D locator (defining the mechanism for navigating in 2D) | |
27 | /// and the image dimensions. | |
28 | /// | |
29 | /// Image views to images are what ranges are to STL containers. They are lightweight objects, | |
30 | /// that don't own the pixels. It is the user's responsibility that the underlying data remains | |
31 | /// valid for the lifetime of the image view. | |
32 | /// | |
92f5a8d4 TL |
33 | /// Similar to iterators and ranges, constness of views does not extend to constness of pixels. |
34 | /// A const \p image_view does not allow changing its location in memory (resizing, moving) but does | |
7c673cae FG |
35 | /// not prevent one from changing the pixels. The latter requires an image view whose value_type |
36 | /// is const. | |
37 | /// | |
38 | /// Images have interfaces consistent with STL 1D random access containers, so they can be used | |
39 | /// directly in STL algorithms like: | |
40 | /// \code | |
41 | /// std::fill(img.begin(), img.end(), red_pixel); | |
42 | /// \endcode | |
43 | /// | |
44 | /// In addition, horizontal, vertical and 2D random access iterators are provided. | |
45 | /// | |
46 | /// Note also that \p image_view does not require that its element type be a pixel. It could be | |
47 | /// instantiated with a locator whose \p value_type models only \p Regular. In this case the image | |
48 | /// view models the weaker RandomAccess2DImageViewConcept, and does not model PixelBasedConcept. | |
49 | /// Many generic algorithms don't require the elements to be pixels. | |
50 | /// | |
51 | //////////////////////////////////////////////////////////////////////////////////////// | |
52 | template <typename Loc> // Models 2D Pixel Locator | |
92f5a8d4 TL |
53 | class image_view |
54 | { | |
7c673cae | 55 | public: |
92f5a8d4 | 56 | // aliases required by ConstRandomAccessNDImageViewConcept |
7c673cae | 57 | static const std::size_t num_dimensions=2; |
92f5a8d4 TL |
58 | using value_type = typename Loc::value_type; |
59 | using reference = typename Loc::reference; // result of dereferencing | |
60 | using coord_t = typename Loc::coord_t; // 1D difference type (same for all dimensions) | |
61 | using difference_type = coord_t; // result of operator-(1d_iterator,1d_iterator) | |
62 | using point_t = typename Loc::point_t; | |
63 | using locator = Loc; | |
64 | using const_t = image_view<typename Loc::const_t>; // same as this type, but over const values | |
65 | template <std::size_t D> struct axis | |
66 | { | |
67 | using coord_t = typename Loc::template axis<D>::coord_t; // difference_type along each dimension | |
68 | using iterator = typename Loc::template axis<D>::iterator; // 1D iterator type along each dimension | |
7c673cae | 69 | }; |
92f5a8d4 TL |
70 | using iterator = iterator_from_2d<Loc>; // 1D iterator type for each pixel left-to-right inside top-to-bottom |
71 | using const_iterator = typename const_t::iterator; // may be used to examine, but not to modify values | |
72 | using const_reference = typename const_t::reference; // behaves as a const reference | |
73 | using pointer = typename std::iterator_traits<iterator>::pointer; // behaves as a pointer to the value type | |
74 | using reverse_iterator = std::reverse_iterator<iterator>; | |
75 | using size_type = std::size_t; | |
76 | ||
77 | // aliases required by ConstRandomAccess2DImageViewConcept | |
78 | using xy_locator = locator; | |
79 | using x_iterator = typename xy_locator::x_iterator; // pixel iterator along a row | |
80 | using y_iterator = typename xy_locator::y_iterator; // pixel iterator along a column | |
81 | using x_coord_t = typename xy_locator::x_coord_t; | |
82 | using y_coord_t = typename xy_locator::y_coord_t; | |
83 | ||
84 | template <typename Deref> | |
85 | struct add_deref | |
86 | { | |
87 | using type = image_view<typename Loc::template add_deref<Deref>::type>; | |
88 | static type make(image_view<Loc> const& view, Deref const& d) | |
89 | { | |
90 | return type(view.dimensions(), Loc::template add_deref<Deref>::make(view.pixels(), d)); | |
91 | } | |
7c673cae FG |
92 | }; |
93 | ||
94 | image_view() : _dimensions(0,0) {} | |
92f5a8d4 TL |
95 | image_view(image_view const& img_view) |
96 | : _dimensions(img_view.dimensions()), _pixels(img_view.pixels()) | |
97 | {} | |
98 | ||
99 | template <typename View> | |
100 | image_view(View const& view) : _dimensions(view.dimensions()), _pixels(view.pixels()) {} | |
101 | ||
102 | template <typename L2> | |
103 | image_view(point_t const& dims, L2 const& loc) : _dimensions(dims), _pixels(loc) {} | |
104 | ||
105 | template <typename L2> | |
106 | image_view(coord_t width, coord_t height, L2 const& loc) | |
107 | : _dimensions(x_coord_t(width), y_coord_t(height)), _pixels(loc) | |
108 | {} | |
109 | ||
110 | template <typename View> | |
111 | image_view& operator=(View const& view) | |
112 | { | |
113 | _pixels = view.pixels(); | |
114 | _dimensions = view.dimensions(); | |
115 | return *this; | |
116 | } | |
117 | ||
118 | image_view& operator=(image_view const& view) | |
119 | { | |
120 | // TODO: Self-assignment protection? | |
121 | _pixels = view.pixels(); | |
122 | _dimensions = view.dimensions(); | |
123 | return *this; | |
124 | } | |
125 | ||
126 | template <typename View> | |
127 | bool operator==(View const &view) const | |
128 | { | |
129 | return pixels() == view.pixels() && dimensions() == view.dimensions(); | |
130 | } | |
131 | ||
132 | template <typename View> | |
133 | bool operator!=(View const& view) const | |
134 | { | |
135 | return !(*this == view); | |
136 | } | |
137 | ||
138 | template <typename L2> | |
139 | friend void swap(image_view<L2> &lhs, image_view<L2> &rhs); | |
140 | ||
141 | /// \brief Exchanges the elements of the current view with those of \a other | |
142 | /// in constant time. | |
143 | /// | |
144 | /// \note Required by the Collection concept | |
145 | /// \see https://www.boost.org/libs/utility/Collection.html | |
146 | void swap(image_view<Loc>& other) | |
147 | { | |
148 | using boost::gil::swap; | |
149 | swap(*this, other); | |
150 | } | |
151 | ||
152 | auto dimensions() const -> point_t const& | |
153 | { | |
154 | return _dimensions; | |
155 | } | |
156 | ||
157 | auto pixels() const -> locator const& | |
158 | { | |
159 | return _pixels; | |
160 | } | |
161 | ||
162 | auto width() const -> x_coord_t | |
163 | { | |
164 | return dimensions().x; | |
165 | } | |
166 | ||
167 | auto height() const -> y_coord_t | |
168 | { | |
169 | return dimensions().y; | |
170 | } | |
171 | ||
172 | auto num_channels() const -> std::size_t | |
173 | { | |
174 | return gil::num_channels<value_type>::value; | |
175 | } | |
176 | ||
177 | bool is_1d_traversable() const | |
178 | { | |
179 | return _pixels.is_1d_traversable(width()); | |
180 | } | |
181 | ||
182 | /// \brief Returns true if the view has no elements, false otherwise. | |
183 | /// | |
184 | /// \note Required by the Collection concept | |
185 | /// \see https://www.boost.org/libs/utility/Collection.html | |
186 | bool empty() const | |
187 | { | |
188 | return !(width() > 0 && height() > 0); | |
189 | } | |
190 | ||
191 | /// \brief Returns a reference to the first element in raster order. | |
192 | /// | |
193 | /// \note Required by the ForwardCollection, since view model the concept. | |
194 | /// \see https://www.boost.org/libs/utility/Collection.html | |
195 | auto front() const -> reference | |
196 | { | |
197 | BOOST_ASSERT(!empty()); | |
198 | return *begin(); | |
199 | } | |
200 | ||
201 | /// \brief Returns a reference to the last element in raster order. | |
202 | /// | |
203 | /// \note Required by the ForwardCollection, since view model the concept. | |
204 | /// \see https://www.boost.org/libs/utility/Collection.html | |
205 | auto back() const -> reference | |
206 | { | |
207 | BOOST_ASSERT(!empty()); | |
208 | return *rbegin(); | |
209 | } | |
7c673cae FG |
210 | |
211 | //\{@ | |
212 | /// \name 1D navigation | |
92f5a8d4 TL |
213 | auto size() const -> size_type |
214 | { | |
215 | return width() * height(); | |
216 | } | |
217 | ||
218 | auto begin() const -> iterator | |
219 | { | |
220 | return iterator(_pixels, _dimensions.x); | |
221 | } | |
222 | ||
223 | auto end() const -> iterator | |
224 | { | |
225 | // potential performance problem! | |
226 | return begin() + static_cast<difference_type>(size()); | |
227 | } | |
228 | ||
229 | auto rbegin() const -> reverse_iterator | |
230 | { | |
231 | return reverse_iterator(end()); | |
232 | } | |
233 | ||
234 | auto rend() const -> reverse_iterator | |
235 | { | |
236 | return reverse_iterator(begin()); | |
237 | } | |
238 | ||
239 | auto operator[](difference_type i) const -> reference | |
240 | { | |
241 | BOOST_ASSERT(i < static_cast<difference_type>(size())); | |
242 | return begin()[i]; // potential performance problem! | |
243 | } | |
244 | ||
f67539c2 | 245 | auto at(difference_type i) const -> iterator |
92f5a8d4 | 246 | { |
f67539c2 TL |
247 | // UB if the specified increment advances non-incrementable iterator (i.e. past-the-end) |
248 | BOOST_ASSERT(i < static_cast<difference_type>(size())); | |
92f5a8d4 TL |
249 | return begin() + i; |
250 | } | |
251 | ||
252 | auto at(point_t const& p) const -> iterator | |
253 | { | |
f67539c2 | 254 | // UB if the specified coordinates advance non-incrementable iterator (i.e. past-the-end) |
92f5a8d4 TL |
255 | BOOST_ASSERT(0 <= p.x && p.x < width()); |
256 | BOOST_ASSERT(0 <= p.y && p.y < height()); | |
257 | return begin() + p.y * width() + p.x; | |
258 | } | |
259 | ||
260 | auto at(x_coord_t x, y_coord_t y) const -> iterator | |
261 | { | |
f67539c2 | 262 | // UB if the specified coordinates advance non-incrementable iterator (i.e. past-the-end) |
92f5a8d4 TL |
263 | BOOST_ASSERT(0 <= x && x < width()); |
264 | BOOST_ASSERT(0 <= y && y < height()); | |
265 | return begin() + y * width() + x; | |
266 | } | |
7c673cae FG |
267 | //\}@ |
268 | ||
269 | //\{@ | |
270 | /// \name 2-D navigation | |
92f5a8d4 TL |
271 | auto operator()(point_t const& p) const -> reference |
272 | { | |
273 | BOOST_ASSERT(0 <= p.x && p.x < width()); | |
274 | BOOST_ASSERT(0 <= p.y && p.y < height()); | |
275 | return _pixels(p.x, p.y); | |
276 | } | |
277 | ||
278 | auto operator()(x_coord_t x, y_coord_t y) const -> reference | |
279 | { | |
280 | BOOST_ASSERT(0 <= x && x < width()); | |
281 | BOOST_ASSERT(0 <= y && y < height()); | |
282 | return _pixels(x, y); | |
283 | } | |
284 | ||
285 | template <std::size_t D> | |
286 | auto axis_iterator(point_t const& p) const -> typename axis<D>::iterator | |
287 | { | |
f67539c2 | 288 | // Allow request for iterators from inclusive range of [begin, end] |
92f5a8d4 TL |
289 | BOOST_ASSERT(0 <= p.x && p.x <= width()); |
290 | BOOST_ASSERT(0 <= p.y && p.y <= height()); | |
291 | return _pixels.template axis_iterator<D>(p); | |
292 | } | |
293 | ||
294 | auto xy_at(x_coord_t x, y_coord_t y) const -> xy_locator | |
295 | { | |
296 | // TODO: Are relative locations of neighbors with negative offsets valid? Sampling? | |
297 | BOOST_ASSERT(x < width()); | |
f67539c2 | 298 | BOOST_ASSERT(y <= height()); |
92f5a8d4 TL |
299 | return _pixels + point_t(x, y); |
300 | } | |
301 | ||
f67539c2 | 302 | auto xy_at(point_t const& p) const -> xy_locator |
92f5a8d4 TL |
303 | { |
304 | // TODO: Are relative locations of neighbors with negative offsets valid? Sampling? | |
305 | BOOST_ASSERT(p.x < width()); | |
306 | BOOST_ASSERT(p.y < height()); | |
307 | return _pixels + p; | |
308 | } | |
7c673cae FG |
309 | //\}@ |
310 | ||
311 | //\{@ | |
312 | /// \name X navigation | |
92f5a8d4 TL |
313 | auto x_at(x_coord_t x, y_coord_t y) const -> x_iterator |
314 | { | |
315 | BOOST_ASSERT(0 <= x && x <= width()); // allow request for [begin, end] inclusive | |
f67539c2 | 316 | BOOST_ASSERT(0 <= y && y < height()); // TODO: For empty image/view, shouldn't we accept: row_begin(0) == view.row_end(0) ? |
92f5a8d4 TL |
317 | return _pixels.x_at(x, y); |
318 | } | |
319 | ||
320 | auto x_at(point_t const& p) const -> x_iterator | |
321 | { | |
322 | BOOST_ASSERT(0 <= p.x && p.x <= width()); // allow request for [begin, end] inclusive | |
f67539c2 | 323 | BOOST_ASSERT(0 <= p.y && p.y < height()); // TODO: For empty image/view, shouldn't we accept: row_begin(0) == view.row_end(0) ? |
92f5a8d4 TL |
324 | return _pixels.x_at(p); |
325 | } | |
326 | ||
327 | auto row_begin(y_coord_t y) const -> x_iterator | |
328 | { | |
329 | BOOST_ASSERT(0 <= y && y < height()); | |
330 | return x_at(0, y); | |
331 | } | |
332 | ||
333 | auto row_end(y_coord_t y) const -> x_iterator | |
334 | { | |
335 | BOOST_ASSERT(0 <= y && y < height()); | |
336 | return x_at(width(), y); | |
337 | } | |
7c673cae FG |
338 | //\}@ |
339 | ||
340 | //\{@ | |
341 | /// \name Y navigation | |
92f5a8d4 TL |
342 | auto y_at(x_coord_t x, y_coord_t y) const -> y_iterator |
343 | { | |
f67539c2 | 344 | BOOST_ASSERT(0 <= x && x < width()); // TODO: For empty image/view, shouldn't we accept: view.col_begin(0) == view.col_end(0) ? |
92f5a8d4 TL |
345 | BOOST_ASSERT(0 <= y && y <= height()); // allow request for [begin, end] inclusive |
346 | return xy_at(x, y).y(); | |
347 | } | |
348 | ||
349 | auto y_at(point_t const& p) const -> y_iterator | |
350 | { | |
f67539c2 | 351 | BOOST_ASSERT(0 <= p.x && p.x < width()); // TODO: For empty image/view, shouldn't we accept: view.col_begin(0) == view.col_end(0) ? |
92f5a8d4 TL |
352 | BOOST_ASSERT(0 <= p.y && p.y <= height()); // allow request for [begin, end] inclusive |
353 | return xy_at(p).y(); | |
354 | } | |
355 | ||
356 | auto col_begin(x_coord_t x) const -> y_iterator | |
357 | { | |
358 | BOOST_ASSERT(0 <= x && x < width()); | |
359 | return y_at(x, 0); | |
360 | } | |
361 | ||
362 | auto col_end(x_coord_t x) const -> y_iterator | |
363 | { | |
364 | BOOST_ASSERT(0 <= x && x < width()); | |
365 | return y_at(x, height()); | |
366 | } | |
7c673cae FG |
367 | //\}@ |
368 | ||
369 | private: | |
92f5a8d4 TL |
370 | template <typename L2> |
371 | friend class image_view; | |
7c673cae FG |
372 | |
373 | point_t _dimensions; | |
374 | xy_locator _pixels; | |
375 | }; | |
376 | ||
92f5a8d4 TL |
377 | template <typename L2> |
378 | inline void swap(image_view<L2>& x, image_view<L2>& y) { | |
7c673cae | 379 | using std::swap; |
92f5a8d4 | 380 | swap(x._dimensions,y._dimensions); |
7c673cae FG |
381 | swap(x._pixels, y._pixels); // TODO: Extend further |
382 | } | |
383 | ||
384 | ///////////////////////////// | |
385 | // PixelBasedConcept | |
386 | ///////////////////////////// | |
387 | ||
388 | template <typename L> | |
92f5a8d4 | 389 | struct channel_type<image_view<L> > : public channel_type<L> {}; |
7c673cae FG |
390 | |
391 | template <typename L> | |
92f5a8d4 | 392 | struct color_space_type<image_view<L> > : public color_space_type<L> {}; |
7c673cae FG |
393 | |
394 | template <typename L> | |
92f5a8d4 | 395 | struct channel_mapping_type<image_view<L> > : public channel_mapping_type<L> {}; |
7c673cae FG |
396 | |
397 | template <typename L> | |
92f5a8d4 | 398 | struct is_planar<image_view<L> > : public is_planar<L> {}; |
7c673cae FG |
399 | |
400 | ///////////////////////////// | |
401 | // HasDynamicXStepTypeConcept | |
402 | ///////////////////////////// | |
403 | ||
404 | template <typename L> | |
92f5a8d4 TL |
405 | struct dynamic_x_step_type<image_view<L>> |
406 | { | |
407 | using type = image_view<typename gil::dynamic_x_step_type<L>::type>; | |
7c673cae FG |
408 | }; |
409 | ||
410 | ///////////////////////////// | |
411 | // HasDynamicYStepTypeConcept | |
412 | ///////////////////////////// | |
413 | ||
414 | template <typename L> | |
92f5a8d4 TL |
415 | struct dynamic_y_step_type<image_view<L>> |
416 | { | |
417 | using type = image_view<typename gil::dynamic_y_step_type<L>::type>; | |
7c673cae FG |
418 | }; |
419 | ||
420 | ///////////////////////////// | |
421 | // HasTransposedTypeConcept | |
422 | ///////////////////////////// | |
423 | ||
424 | template <typename L> | |
92f5a8d4 TL |
425 | struct transposed_type<image_view<L>> |
426 | { | |
427 | using type = image_view<typename transposed_type<L>::type>; | |
7c673cae FG |
428 | }; |
429 | ||
92f5a8d4 | 430 | }} // namespace boost::gil |
7c673cae FG |
431 | |
432 | #endif |