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_LOCATOR_HPP
9 #define BOOST_GIL_LOCATOR_HPP
10 
11 #include <boost/gil/dynamic_step.hpp>
12 #include <boost/gil/pixel_iterator.hpp>
13 #include <boost/gil/point.hpp>
14 
15 #include <boost/assert.hpp>
16 
17 #include <cstddef>
18 
19 namespace boost { namespace gil {
20 
21 /// Pixel 2D locator
22 
23 //forward declarations
24 template <typename P> std::ptrdiff_t memunit_step(const P*);
25 template <typename P> P* memunit_advanced(const P* p, std::ptrdiff_t diff);
26 template <typename P> P& memunit_advanced_ref(P* p, std::ptrdiff_t diff);
27 template <typename Iterator, typename D> struct iterator_add_deref;
28 template <typename T> class point;
29 namespace detail {
30     // helper class specialized for each axis of pixel_2d_locator
31     template <std::size_t D, typename Loc>  class locator_axis;
32 }
33 
34 template <typename T> struct channel_type;
35 template <typename T> struct color_space_type;
36 template <typename T> struct channel_mapping_type;
37 template <typename T> struct is_planar;
38 template <typename T> struct num_channels;
39 
40 /// Base template for types that model HasTransposedTypeConcept.
41 /// The type of a locator or a view that has X and Y swapped.
42 /// By default it is the same.
43 template <typename LocatorOrView>
44 struct transposed_type
45 {
46     using type = LocatorOrView;
47 };
48 
49 /// \class pixel_2d_locator_base
50 /// \brief base class for models of PixelLocatorConcept
51 /// \ingroup PixelLocatorModel PixelBasedModel
52 ///
53 /// Pixel locator is similar to a pixel iterator, but allows for 2D navigation of pixels within an image view.
54 /// It has a 2D difference_type and supports random access operations like:
55 /// \code
56 ///     difference_type offset2(2,3);
57 ///     locator+=offset2;
58 ///     locator[offset2]=my_pixel;
59 /// \endcode
60 ///
61 /// In addition, each coordinate acts as a random-access iterator that can be modified separately:
62 /// "++locator.x()" or "locator.y()+=10" thereby moving the locator horizontally or vertically.
63 ///
64 /// It is called a locator because it doesn't implement the complete interface of a random access iterator.
65 /// For example, increment and decrement operations don't make sense (no way to specify dimension).
66 /// Also 2D difference between two locators cannot be computed without knowledge of the X position within the image.
67 ///
68 /// This base class provides most of the methods and type aliases needed to create a model of a locator. GIL provides two
69 /// locator models as subclasses of \p pixel_2d_locator_base. A memory-based locator, \p memory_based_2d_locator and a virtual
70 /// locator, \p virtual_2d_locator.
71 /// The minimum functionality a subclass must provide is this:
72 /// \code
73 /// class my_locator : public pixel_2d_locator_base<my_locator, ..., ...> {  // supply the types for x-iterator and y-iterator
74 ///        using const_t = ...;                      // read-only locator
75 ///
76 ///        template <typename Deref> struct add_deref {
77 ///            using type = ...;                     // locator that invokes the Deref dereference object upon pixel access
78 ///            static type make(const my_locator& loc, const Deref& d);
79 ///        };
80 ///
81 ///        my_locator();
82 ///        my_locator(const my_locator& pl);
83 ///
84 ///        // constructors with dynamic step in y (and x). Only valid for locators with dynamic steps
85 ///        my_locator(const my_locator& loc, coord_t y_step);
86 ///        my_locator(const my_locator& loc, coord_t x_step, coord_t y_step, bool transpose);
87 ///
88 ///        bool              operator==(const my_locator& p) const;
89 ///
90 ///        // return _references_ to horizontal/vertical iterators. Advancing them moves this locator
91 ///        x_iterator&       x();
92 ///        y_iterator&       y();
93 ///        x_iterator const& x() const;
94 ///        y_iterator const& y() const;
95 ///
96 ///        // return the vertical distance to another locator. Some models need the horizontal distance to compute it
97 ///        y_coord_t         y_distance_to(const my_locator& loc2, x_coord_t xDiff) const;
98 ///
99 ///        // return true iff incrementing an x-iterator located at the last column will position it at the first
100 ///        // column of the next row. Some models need the image width to determine that.
101 ///        bool              is_1d_traversable(x_coord_t width) const;
102 /// };
103 /// \endcode
104 ///
105 /// Models may choose to override some of the functions in the base class with more efficient versions.
106 ///
107 
108 template <typename Loc, typename XIterator, typename YIterator>    // The concrete subclass, the X-iterator and the Y-iterator
109 class pixel_2d_locator_base
110 {
111 public:
112     using x_iterator = XIterator;
113     using y_iterator = YIterator;
114 
115     // aliasesrequired by ConstRandomAccessNDLocatorConcept
116     static const std::size_t num_dimensions=2;
117     using value_type = typename std::iterator_traits<x_iterator>::value_type;
118     using reference = typename std::iterator_traits<x_iterator>::reference;    // result of dereferencing
119     using coord_t = typename std::iterator_traits<x_iterator>::difference_type;      // 1D difference type (same for all dimensions)
120     using difference_type = point<coord_t>; // result of operator-(locator,locator)
121     using point_t = difference_type;
122     template <std::size_t D> struct axis
123     {
124         using coord_t = typename detail::locator_axis<D,Loc>::coord_t;
125         using iterator = typename detail::locator_axis<D,Loc>::iterator;
126     };
127 
128 // aliases required by ConstRandomAccess2DLocatorConcept
129     using x_coord_t = typename point_t::template axis<0>::coord_t;
130     using y_coord_t = typename point_t::template axis<1>::coord_t;
131 
operator !=(const Loc & p) const132     bool              operator!=(const Loc& p)          const { return !(concrete()==p); }
133 
x_at(x_coord_t dx,y_coord_t dy) const134     x_iterator        x_at(x_coord_t dx, y_coord_t dy)  const { Loc tmp=concrete(); tmp+=point_t(dx,dy); return tmp.x(); }
x_at(const difference_type & d) const135     x_iterator        x_at(const difference_type& d)    const { Loc tmp=concrete(); tmp+=d;              return tmp.x(); }
y_at(x_coord_t dx,y_coord_t dy) const136     y_iterator        y_at(x_coord_t dx, y_coord_t dy)  const { Loc tmp=concrete(); tmp+=point_t(dx,dy); return tmp.y(); }
y_at(const difference_type & d) const137     y_iterator        y_at(const difference_type& d)    const { Loc tmp=concrete(); tmp+=d;              return tmp.y(); }
xy_at(x_coord_t dx,y_coord_t dy) const138     Loc               xy_at(x_coord_t dx, y_coord_t dy) const { Loc tmp=concrete(); tmp+=point_t(dx,dy); return tmp; }
xy_at(const difference_type & d) const139     Loc               xy_at(const difference_type& d)   const { Loc tmp=concrete(); tmp+=d;              return tmp; }
140 
axis_iterator()141     template <std::size_t D> typename axis<D>::iterator&       axis_iterator()                       { return detail::locator_axis<D,Loc>()(concrete()); }
axis_iterator() const142     template <std::size_t D> typename axis<D>::iterator const& axis_iterator()                 const { return detail::locator_axis<D,Loc>()(concrete()); }
axis_iterator(const point_t & p) const143     template <std::size_t D> typename axis<D>::iterator        axis_iterator(const point_t& p) const { return detail::locator_axis<D,Loc>()(concrete(),p); }
144 
operator ()(x_coord_t dx,y_coord_t dy) const145     reference         operator()(x_coord_t dx, y_coord_t dy) const { return *x_at(dx,dy); }
operator [](const difference_type & d) const146     reference         operator[](const difference_type& d)   const { return *x_at(d.x,d.y); }
147 
operator *() const148     reference         operator*()                            const { return *concrete().x(); }
149 
operator +=(const difference_type & d)150     Loc&              operator+=(const difference_type& d)         { concrete().x()+=d.x; concrete().y()+=d.y; return concrete(); }
operator -=(const difference_type & d)151     Loc&              operator-=(const difference_type& d)         { concrete().x()-=d.x; concrete().y()-=d.y; return concrete(); }
152 
operator +(const difference_type & d) const153     Loc               operator+(const difference_type& d)    const { return xy_at(d); }
operator -(const difference_type & d) const154     Loc               operator-(const difference_type& d)    const { return xy_at(-d); }
155 
156     // Some locators can cache 2D coordinates for faster subsequent access. By default there is no caching
157     using cached_location_t = difference_type;
cache_location(const difference_type & d) const158     cached_location_t cache_location(const difference_type& d)  const { return d; }
cache_location(x_coord_t dx,y_coord_t dy) const159     cached_location_t cache_location(x_coord_t dx, y_coord_t dy)const { return difference_type(dx,dy); }
160 
161 private:
concrete()162     Loc&              concrete()       { return (Loc&)*this; }
concrete() const163     const Loc&        concrete() const { return (const Loc&)*this; }
164 
165     template <typename X> friend class pixel_2d_locator;
166 };
167 
168 // helper classes for each axis of pixel_2d_locator_base
169 namespace detail {
170     template <typename Loc>
171     class locator_axis<0,Loc> {
172         using point_t = typename Loc::point_t;
173     public:
174         using coord_t = typename point_t::template axis<0>::coord_t;
175         using iterator = typename Loc::x_iterator;
176 
operator ()(Loc & loc) const177         inline iterator&        operator()(      Loc& loc)                   const { return loc.x(); }
operator ()(const Loc & loc) const178         inline iterator  const& operator()(const Loc& loc)                   const { return loc.x(); }
operator ()(Loc & loc,const point_t & d) const179         inline iterator         operator()(      Loc& loc, const point_t& d) const { return loc.x_at(d); }
operator ()(const Loc & loc,const point_t & d) const180         inline iterator         operator()(const Loc& loc, const point_t& d) const { return loc.x_at(d); }
181     };
182 
183     template <typename Loc>
184     class locator_axis<1,Loc> {
185         using point_t = typename Loc::point_t;
186     public:
187         using coord_t = typename point_t::template axis<1>::coord_t;
188         using iterator = typename Loc::y_iterator;
189 
operator ()(Loc & loc) const190         inline iterator&        operator()(      Loc& loc)               const { return loc.y(); }
operator ()(const Loc & loc) const191         inline iterator const&  operator()(const Loc& loc)               const { return loc.y(); }
operator ()(Loc & loc,const point_t & d) const192         inline iterator     operator()(      Loc& loc, const point_t& d) const { return loc.y_at(d); }
operator ()(const Loc & loc,const point_t & d) const193         inline iterator     operator()(const Loc& loc, const point_t& d) const { return loc.y_at(d); }
194     };
195 }
196 
197 template <typename Loc, typename XIt, typename YIt>
198 struct channel_type<pixel_2d_locator_base<Loc,XIt,YIt> > : public channel_type<XIt> {};
199 
200 template <typename Loc, typename XIt, typename YIt>
201 struct color_space_type<pixel_2d_locator_base<Loc,XIt,YIt> > : public color_space_type<XIt> {};
202 
203 template <typename Loc, typename XIt, typename YIt>
204 struct channel_mapping_type<pixel_2d_locator_base<Loc,XIt,YIt> > : public channel_mapping_type<XIt> {};
205 
206 template <typename Loc, typename XIt, typename YIt>
207 struct is_planar<pixel_2d_locator_base<Loc,XIt,YIt> > : public is_planar<XIt> {};
208 
209 /// \class memory_based_2d_locator
210 /// \brief Memory-based pixel locator. Models: PixelLocatorConcept,HasDynamicXStepTypeConcept,HasDynamicYStepTypeConcept,HasTransposedTypeConcept
211 /// \ingroup PixelLocatorModel PixelBasedModel
212 ///
213 /// The class takes a step iterator as a parameter. The step iterator provides navigation along the vertical axis
214 /// while its base iterator provides horizontal navigation.
215 ///
216 /// Each instantiation is optimal in terms of size and efficiency.
217 /// For example, xy locator over interleaved rgb image results in a step iterator consisting of
218 /// one std::ptrdiff_t for the row size and one native pointer (8 bytes total). ++locator.x() resolves to pointer
219 /// increment. At the other extreme, a 2D navigation of the even pixels of a planar CMYK image results in a step
220 /// iterator consisting of one std::ptrdiff_t for the doubled row size, and one step iterator consisting of
221 /// one std::ptrdiff_t for the horizontal step of two and a CMYK planar_pixel_iterator consisting of 4 pointers (24 bytes).
222 /// In this case ++locator.x() results in four native pointer additions.
223 ///
224 /// Note also that \p memory_based_2d_locator does not require that its element type be a pixel. It could be
225 /// instantiated with an iterator whose \p value_type models only \p Regular. In this case the locator
226 /// models the weaker RandomAccess2DLocatorConcept, and does not model PixelBasedConcept.
227 /// Many generic algorithms don't require the elements to be pixels.
228 ////////////////////////////////////////////////////////////////////////////////////////
229 
230 template <typename StepIterator>
231 class memory_based_2d_locator : public pixel_2d_locator_base<memory_based_2d_locator<StepIterator>, typename iterator_adaptor_get_base<StepIterator>::type, StepIterator> {
232     using this_t = memory_based_2d_locator<StepIterator>;
233     BOOST_GIL_CLASS_REQUIRE(StepIterator, boost::gil, StepIteratorConcept)
234 public:
235     using parent_t = pixel_2d_locator_base<memory_based_2d_locator<StepIterator>, typename iterator_adaptor_get_base<StepIterator>::type, StepIterator>;
236     using const_t = memory_based_2d_locator<typename const_iterator_type<StepIterator>::type>; // same as this type, but over const values
237 
238     using coord_t = typename parent_t::coord_t;
239     using x_coord_t = typename parent_t::x_coord_t;
240     using y_coord_t = typename parent_t::y_coord_t;
241     using x_iterator = typename parent_t::x_iterator;
242     using y_iterator = typename parent_t::y_iterator;
243     using difference_type = typename parent_t::difference_type;
244     using reference = typename parent_t::reference;
245 
246     template <typename Deref> struct add_deref
247     {
248         using type = memory_based_2d_locator<typename iterator_add_deref<StepIterator,Deref>::type>;
makeboost::gil::memory_based_2d_locator::add_deref249         static type make(const memory_based_2d_locator<StepIterator>& loc, const Deref& nderef) {
250             return type(iterator_add_deref<StepIterator,Deref>::make(loc.y(),nderef));
251         }
252     };
253 
memory_based_2d_locator()254     memory_based_2d_locator() {}
memory_based_2d_locator(const StepIterator & yit)255     memory_based_2d_locator(const StepIterator& yit) : _p(yit) {}
memory_based_2d_locator(const memory_based_2d_locator<SI> & loc,coord_t y_step)256     template <typename SI> memory_based_2d_locator(const memory_based_2d_locator<SI>& loc, coord_t y_step) : _p(loc.x(), loc.row_size()*y_step) {}
memory_based_2d_locator(const memory_based_2d_locator<SI> & loc,coord_t x_step,coord_t y_step,bool transpose=false)257     template <typename SI> memory_based_2d_locator(const memory_based_2d_locator<SI>& loc, coord_t x_step, coord_t y_step, bool transpose=false)
258         : _p(make_step_iterator(loc.x(),(transpose ? loc.row_size() : loc.pixel_size())*x_step),
259                                         (transpose ? loc.pixel_size() : loc.row_size())*y_step ) {}
260 
memory_based_2d_locator(x_iterator xit,std::ptrdiff_t row_bytes)261     memory_based_2d_locator(x_iterator xit, std::ptrdiff_t row_bytes) : _p(xit,row_bytes) {}
memory_based_2d_locator(const memory_based_2d_locator<X> & pl)262     template <typename X> memory_based_2d_locator(const memory_based_2d_locator<X>& pl) : _p(pl._p) {}
memory_based_2d_locator(const memory_based_2d_locator & pl)263     memory_based_2d_locator(const memory_based_2d_locator& pl) : _p(pl._p) {}
264     memory_based_2d_locator& operator=(memory_based_2d_locator const& other) = default;
265 
operator ==(const this_t & p) const266     bool                  operator==(const this_t& p)  const { return _p==p._p; }
267 
x() const268     x_iterator const&     x()                          const { return _p.base(); }
y() const269     y_iterator const&     y()                          const { return _p; }
x()270     x_iterator&           x()                                { return _p.base(); }
y()271     y_iterator&           y()                                { return _p; }
272 
273     // These are faster versions of functions already provided in the superclass
x_at(x_coord_t dx,y_coord_t dy) const274     x_iterator x_at      (x_coord_t dx, y_coord_t dy)  const { return memunit_advanced(x(), offset(dx,dy)); }
x_at(const difference_type & d) const275     x_iterator x_at      (const difference_type& d)    const { return memunit_advanced(x(), offset(d.x,d.y)); }
xy_at(x_coord_t dx,y_coord_t dy) const276     this_t     xy_at     (x_coord_t dx, y_coord_t dy)  const { return this_t(x_at( dx , dy ), row_size()); }
xy_at(const difference_type & d) const277     this_t     xy_at     (const difference_type& d)    const { return this_t(x_at( d.x, d.y), row_size()); }
operator ()(x_coord_t dx,y_coord_t dy) const278     reference  operator()(x_coord_t dx, y_coord_t dy)  const { return memunit_advanced_ref(x(),offset(dx,dy)); }
operator [](const difference_type & d) const279     reference  operator[](const difference_type& d)    const { return memunit_advanced_ref(x(),offset(d.x,d.y)); }
operator +=(const difference_type & d)280     this_t&    operator+=(const difference_type& d)          { memunit_advance(x(),offset(d.x,d.y)); return *this; }
operator -=(const difference_type & d)281     this_t&    operator-=(const difference_type& d)          { memunit_advance(x(),offset(-d.x,-d.y)); return *this; }
282 
283     // Memory-based locators can have 1D caching of 2D relative coordinates
284     using cached_location_t = std::ptrdiff_t; // type used to store relative location (to allow for more efficient repeated access)
cache_location(const difference_type & d) const285     cached_location_t cache_location(const difference_type& d)  const { return offset(d.x,d.y); }
cache_location(x_coord_t dx,y_coord_t dy) const286     cached_location_t cache_location(x_coord_t dx, y_coord_t dy)const { return offset(dx,dy); }
operator [](const cached_location_t & loc) const287     reference         operator[](const cached_location_t& loc)  const { return memunit_advanced_ref(x(),loc); }
288 
289     // Only make sense for memory-based locators
row_size() const290     std::ptrdiff_t         row_size()                           const { return memunit_step(y()); }    // distance in mem units (bytes or bits) between adjacent rows
pixel_size() const291     std::ptrdiff_t         pixel_size()                         const { return memunit_step(x()); }    // distance in mem units (bytes or bits) between adjacent pixels on the same row
292 
is_1d_traversable(x_coord_t width) const293     bool                   is_1d_traversable(x_coord_t width)   const { return row_size()-pixel_size()*width==0; }   // is there no gap at the end of each row?
294 
295     // Returns the vertical distance (it2.y-it1.y) between two x_iterators given the difference of their x positions
y_distance_to(this_t const & p2,x_coord_t xDiff) const296     std::ptrdiff_t y_distance_to(this_t const& p2, x_coord_t xDiff) const
297     {
298         std::ptrdiff_t rowDiff = memunit_distance(x(), p2.x()) - pixel_size() * xDiff;
299         BOOST_ASSERT((rowDiff % row_size()) == 0);
300         return rowDiff / row_size();
301     }
302 
303 private:
304     template <typename X> friend class memory_based_2d_locator;
offset(x_coord_t x,y_coord_t y) const305     std::ptrdiff_t offset(x_coord_t x, y_coord_t y)        const { return y*row_size() + x*pixel_size(); }
306     StepIterator _p;
307 };
308 
309 /////////////////////////////
310 //  PixelBasedConcept
311 /////////////////////////////
312 
313 template <typename SI>
314 struct color_space_type<memory_based_2d_locator<SI> > : public color_space_type<typename memory_based_2d_locator<SI>::parent_t> {
315 };
316 
317 template <typename SI>
318 struct channel_mapping_type<memory_based_2d_locator<SI> > : public channel_mapping_type<typename memory_based_2d_locator<SI>::parent_t> {
319 };
320 
321 template <typename SI>
322 struct is_planar<memory_based_2d_locator<SI> > : public is_planar<typename memory_based_2d_locator<SI>::parent_t> {
323 };
324 
325 template <typename SI>
326 struct channel_type<memory_based_2d_locator<SI> > : public channel_type<typename memory_based_2d_locator<SI>::parent_t> {
327 };
328 
329 /////////////////////////////
330 //  HasDynamicXStepTypeConcept
331 /////////////////////////////
332 
333 // Take the base iterator of SI (which is typically a step iterator) and change it to have a step in x
334 template <typename SI>
335 struct dynamic_x_step_type<memory_based_2d_locator<SI> > {
336 private:
337     using base_iterator_t = typename iterator_adaptor_get_base<SI>::type;
338     using base_iterator_step_t = typename dynamic_x_step_type<base_iterator_t>::type;
339     using dynamic_step_base_t = typename iterator_adaptor_rebind<SI, base_iterator_step_t>::type;
340 public:
341     using type = memory_based_2d_locator<dynamic_step_base_t>;
342 };
343 
344 /////////////////////////////
345 //  HasDynamicYStepTypeConcept
346 /////////////////////////////
347 
348 template <typename SI>
349 struct dynamic_y_step_type<memory_based_2d_locator<SI> > {
350     using type = memory_based_2d_locator<SI>;
351 };
352 } }  // namespace boost::gil
353 
354 #endif
355