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
10 
11 #include <boost/gil/dynamic_step.hpp>
12 #include <boost/gil/iterator_from_2d.hpp>
13 
14 #include <boost/assert.hpp>
15 
16 #include <cstddef>
17 #include <iterator>
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 ///
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
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
53 class image_view
54 {
55 public:
56     // aliases required by ConstRandomAccessNDImageViewConcept
57     static const std::size_t num_dimensions=2;
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
69     };
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>;
makeboost::gil::image_view::add_deref88         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         }
92     };
93 
image_view()94     image_view() : _dimensions(0,0) {}
image_view(image_view const & img_view)95     image_view(image_view const& img_view)
96         : _dimensions(img_view.dimensions()), _pixels(img_view.pixels())
97     {}
98 
99     template <typename View>
image_view(View const & view)100     image_view(View const& view) : _dimensions(view.dimensions()), _pixels(view.pixels()) {}
101 
102     template <typename L2>
image_view(point_t const & dims,L2 const & loc)103     image_view(point_t const& dims, L2 const& loc) : _dimensions(dims), _pixels(loc) {}
104 
105     template <typename L2>
image_view(coord_t width,coord_t height,L2 const & loc)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>
operator =(View const & view)111     image_view& operator=(View const& view)
112     {
113         _pixels = view.pixels();
114         _dimensions = view.dimensions();
115         return *this;
116     }
117 
operator =(image_view const & view)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>
operator ==(View const & view) const127     bool operator==(View const &view) const
128     {
129         return pixels() == view.pixels() && dimensions() == view.dimensions();
130     }
131 
132     template <typename View>
operator !=(View const & view) const133     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
swap(image_view<Loc> & other)146     void swap(image_view<Loc>& other)
147     {
148         using boost::gil::swap;
149         swap(*this, other);
150     }
151 
dimensions() const152     auto dimensions() const -> point_t const&
153     {
154         return _dimensions;
155     }
156 
pixels() const157     auto pixels() const -> locator const&
158     {
159         return _pixels;
160     }
161 
width() const162     auto width() const -> x_coord_t
163     {
164         return dimensions().x;
165     }
166 
height() const167     auto height() const -> y_coord_t
168     {
169         return dimensions().y;
170     }
171 
num_channels() const172     auto num_channels() const -> std::size_t
173     {
174         return gil::num_channels<value_type>::value;
175     }
176 
is_1d_traversable() const177     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
empty() const186     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
front() const195     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
back() const205     auto back() const -> reference
206     {
207         BOOST_ASSERT(!empty());
208         return *rbegin();
209     }
210 
211     //\{@
212     /// \name 1D navigation
size() const213     auto size() const -> size_type
214     {
215         return width() * height();
216     }
217 
begin() const218     auto begin() const -> iterator
219     {
220         return iterator(_pixels, _dimensions.x);
221     }
222 
end() const223     auto end() const -> iterator
224     {
225         // potential performance problem!
226         return begin() + static_cast<difference_type>(size());
227     }
228 
rbegin() const229     auto rbegin() const -> reverse_iterator
230     {
231         return reverse_iterator(end());
232     }
233 
rend() const234     auto rend() const -> reverse_iterator
235     {
236         return reverse_iterator(begin());
237     }
238 
operator [](difference_type i) const239     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 
at(difference_type i) const245     auto at(difference_type i) const -> iterator
246     {
247         // UB if the specified increment advances non-incrementable iterator (i.e. past-the-end)
248         BOOST_ASSERT(i < static_cast<difference_type>(size()));
249         return begin() + i;
250     }
251 
at(point_t const & p) const252     auto at(point_t const& p) const -> iterator
253     {
254         // UB if the specified coordinates advance non-incrementable iterator (i.e. past-the-end)
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 
at(x_coord_t x,y_coord_t y) const260     auto at(x_coord_t x, y_coord_t y) const -> iterator
261     {
262         // UB if the specified coordinates advance non-incrementable iterator (i.e. past-the-end)
263         BOOST_ASSERT(0 <= x && x < width());
264         BOOST_ASSERT(0 <= y && y < height());
265         return begin() + y * width() + x;
266     }
267     //\}@
268 
269     //\{@
270     /// \name 2-D navigation
operator ()(point_t const & p) const271     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 
operator ()(x_coord_t x,y_coord_t y) const278     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>
axis_iterator(point_t const & p) const286     auto axis_iterator(point_t const& p) const -> typename axis<D>::iterator
287     {
288         // Allow request for iterators from inclusive range of [begin, end]
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 
xy_at(x_coord_t x,y_coord_t y) const294     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());
298         BOOST_ASSERT(y <= height());
299         return _pixels + point_t(x, y);
300     }
301 
xy_at(point_t const & p) const302     auto xy_at(point_t const& p) const -> xy_locator
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     }
309     //\}@
310 
311     //\{@
312     /// \name X navigation
x_at(x_coord_t x,y_coord_t y) const313     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
316         BOOST_ASSERT(0 <= y && y < height()); // TODO: For empty image/view, shouldn't we accept: row_begin(0) == view.row_end(0) ?
317         return _pixels.x_at(x, y);
318     }
319 
x_at(point_t const & p) const320     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
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) ?
324         return _pixels.x_at(p);
325     }
326 
row_begin(y_coord_t y) const327     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 
row_end(y_coord_t y) const333     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     }
338     //\}@
339 
340     //\{@
341     /// \name Y navigation
y_at(x_coord_t x,y_coord_t y) const342     auto y_at(x_coord_t x, y_coord_t y) const -> y_iterator
343     {
344         BOOST_ASSERT(0 <= x && x < width()); // TODO: For empty image/view, shouldn't we accept: view.col_begin(0) == view.col_end(0) ?
345         BOOST_ASSERT(0 <= y && y <= height()); // allow request for [begin, end] inclusive
346         return xy_at(x, y).y();
347     }
348 
y_at(point_t const & p) const349     auto y_at(point_t const& p) const -> y_iterator
350     {
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) ?
352         BOOST_ASSERT(0 <= p.y && p.y <= height()); // allow request for [begin, end] inclusive
353         return xy_at(p).y();
354     }
355 
col_begin(x_coord_t x) const356     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 
col_end(x_coord_t x) const362     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     }
367     //\}@
368 
369 private:
370     template <typename L2>
371     friend class image_view;
372 
373     point_t    _dimensions;
374     xy_locator _pixels;
375 };
376 
377 template <typename L2>
swap(image_view<L2> & x,image_view<L2> & y)378 inline void swap(image_view<L2>& x, image_view<L2>& y) {
379     using std::swap;
380     swap(x._dimensions,y._dimensions);
381     swap(x._pixels, y._pixels);            // TODO: Extend further
382 }
383 
384 /////////////////////////////
385 //  PixelBasedConcept
386 /////////////////////////////
387 
388 template <typename L>
389 struct channel_type<image_view<L> > : public channel_type<L> {};
390 
391 template <typename L>
392 struct color_space_type<image_view<L> > : public color_space_type<L> {};
393 
394 template <typename L>
395 struct channel_mapping_type<image_view<L> > : public channel_mapping_type<L> {};
396 
397 template <typename L>
398 struct is_planar<image_view<L> > : public is_planar<L> {};
399 
400 /////////////////////////////
401 //  HasDynamicXStepTypeConcept
402 /////////////////////////////
403 
404 template <typename L>
405 struct dynamic_x_step_type<image_view<L>>
406 {
407     using type = image_view<typename gil::dynamic_x_step_type<L>::type>;
408 };
409 
410 /////////////////////////////
411 //  HasDynamicYStepTypeConcept
412 /////////////////////////////
413 
414 template <typename L>
415 struct dynamic_y_step_type<image_view<L>>
416 {
417     using type = image_view<typename gil::dynamic_y_step_type<L>::type>;
418 };
419 
420 /////////////////////////////
421 //  HasTransposedTypeConcept
422 /////////////////////////////
423 
424 template <typename L>
425 struct transposed_type<image_view<L>>
426 {
427     using type = image_view<typename transposed_type<L>::type>;
428 };
429 
430 }}  // namespace boost::gil
431 
432 #endif
433