locator.hpp 18 KB

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