algorithm.hpp 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033
  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_ALGORITHM_HPP
  9. #define BOOST_GIL_ALGORITHM_HPP
  10. #include <boost/gil/bit_aligned_pixel_iterator.hpp>
  11. #include <boost/gil/color_base_algorithm.hpp>
  12. #include <boost/gil/concepts.hpp>
  13. #include <boost/gil/image_view.hpp>
  14. #include <boost/gil/image_view_factory.hpp>
  15. #include <boost/assert.hpp>
  16. #include <boost/config.hpp>
  17. #include <boost/mpl/and.hpp>
  18. #include <boost/mpl/or.hpp>
  19. #include <algorithm>
  20. #include <cstddef>
  21. #include <cstring>
  22. #include <iterator>
  23. #include <memory>
  24. #include <type_traits>
  25. #include <typeinfo>
  26. namespace boost { namespace gil {
  27. //forward declarations
  28. template <typename ChannelPtr, typename ColorSpace>
  29. struct planar_pixel_iterator;
  30. template <typename Iterator>
  31. class memory_based_step_iterator;
  32. template <typename StepIterator>
  33. class memory_based_2d_locator;
  34. // a tag denoting incompatible arguments
  35. struct error_t {};
  36. /// \defgroup ImageViewSTLAlgorithms STL-like Algorithms
  37. /// \ingroup ImageViewAlgorithm
  38. /// \brief Image view-equivalents of STL algorithms
  39. ///
  40. /// Image views provide 1D iteration of their pixels via \p begin() and \p end() methods,
  41. /// which makes it possible to use STL algorithms with them. However, using nested loops
  42. /// over X and Y is in many cases more efficient. The algorithms in this section resemble
  43. /// STL algorithms, but they abstract away the nested loops and take views (as opposed to ranges) as input.
  44. ///
  45. /// Most algorithms check whether the image views are 1D-traversable. A 1D-traversable image view has no gaps
  46. /// at the end of the rows. In other words, if an x_iterator of that view is advanced past the last pixel in a row
  47. /// it will move to the first pixel of the next row. When image views are 1D-traversable, the algorithms use
  48. /// a single loop and run more efficiently. If one or more of the input views are not 1D-traversable, the algorithms
  49. /// fall-back to an X-loop nested inside a Y-loop.
  50. ///
  51. /// The algorithms typically delegate the work to their corresponding STL algorithms. For example, \p copy_pixels calls
  52. /// \p std::copy either for each row, or, when the images are 1D-traversable, once for all pixels.
  53. ///
  54. /// In addition, overloads are sometimes provided for the STL algorithms. For example, std::copy for planar iterators
  55. /// is overloaded to perform \p std::copy for each of the planes. \p std::copy over bitwise-copiable pixels results in
  56. /// std::copy over unsigned char, which STL typically implements via \p memmove.
  57. ///
  58. /// As a result \p copy_pixels may result in a single call to \p memmove for interleaved 1D-traversable views,
  59. /// or one per each plane of planar 1D-traversable views, or one per each row of interleaved non-1D-traversable images, etc.
  60. /// \defgroup STLOptimizations Performance overloads of STL algorithms
  61. /// \ingroup ImageViewAlgorithm
  62. /// \brief overloads of STL algorithms allowing more efficient implementation when used with GIL constructs
  63. /// \brief A generic binary operation on views
  64. /// \ingroup ImageViewSTLAlgorithms
  65. ///
  66. /// Use this class as a convenience superclass when defining an operation for any image views.
  67. /// Many operations have different behavior when the two views are compatible. This class checks
  68. /// for compatibility and invokes apply_compatible(V1,V2) or apply_incompatible(V1,V2) of the subclass.
  69. /// You must provide apply_compatible(V1,V2) method in your subclass, but apply_incompatible(V1,V2)
  70. /// is not required and the default throws std::bad_cast.
  71. template <typename Derived, typename Result=void>
  72. struct binary_operation_obj {
  73. using result_type = Result;
  74. template <typename V1, typename V2> BOOST_FORCEINLINE
  75. result_type operator()(const std::pair<const V1*,const V2*>& p) const {
  76. return apply(*p.first, *p.second, typename views_are_compatible<V1,V2>::type());
  77. }
  78. template <typename V1, typename V2> BOOST_FORCEINLINE
  79. result_type operator()(const V1& v1, const V2& v2) const {
  80. return apply(v1, v2, typename views_are_compatible<V1,V2>::type());
  81. }
  82. result_type operator()(const error_t&) const { throw std::bad_cast(); }
  83. private:
  84. // dispatch from apply overload to a function with distinct name
  85. template <typename V1, typename V2>
  86. BOOST_FORCEINLINE result_type apply(const V1& v1, const V2& v2, mpl::false_) const {
  87. return ((const Derived*)this)->apply_incompatible(v1,v2);
  88. }
  89. // dispatch from apply overload to a function with distinct name
  90. template <typename V1, typename V2>
  91. BOOST_FORCEINLINE result_type apply(const V1& v1, const V2& v2, mpl::true_) const {
  92. return ((const Derived*)this)->apply_compatible(v1,v2);
  93. }
  94. // function with distinct name - it can be overloaded by subclasses
  95. template <typename V1, typename V2>
  96. BOOST_FORCEINLINE result_type apply_incompatible(const V1&, const V2&) const {
  97. throw std::bad_cast();
  98. }
  99. };
  100. } } // namespace boost::gil
  101. //////////////////////////////////////////////////////////////////////////////////////
  102. ///
  103. /// std::copy and gil::copy_pixels
  104. ///
  105. //////////////////////////////////////////////////////////////////////////////////////
  106. /// \defgroup ImageViewSTLAlgorithmsCopyPixels copy_pixels
  107. /// \ingroup ImageViewSTLAlgorithms
  108. /// \brief std::copy for image views
  109. namespace std {
  110. /// \ingroup STLOptimizations
  111. /// \brief Copy when both src and dst are interleaved and of the same type can be just memmove
  112. template<typename T, typename Cs>
  113. BOOST_FORCEINLINE boost::gil::pixel<T,Cs>*
  114. copy(boost::gil::pixel<T,Cs>* first, boost::gil::pixel<T,Cs>* last,
  115. boost::gil::pixel<T,Cs>* dst) {
  116. return (boost::gil::pixel<T,Cs>*)std::copy((unsigned char*)first,(unsigned char*)last, (unsigned char*)dst);
  117. }
  118. /// \ingroup STLOptimizations
  119. /// \brief Copy when both src and dst are interleaved and of the same type can be just memmove
  120. template<typename T, typename Cs>
  121. BOOST_FORCEINLINE boost::gil::pixel<T,Cs>*
  122. copy(const boost::gil::pixel<T,Cs>* first, const boost::gil::pixel<T,Cs>* last,
  123. boost::gil::pixel<T,Cs>* dst) {
  124. return (boost::gil::pixel<T,Cs>*)std::copy((unsigned char*)first,(unsigned char*)last, (unsigned char*)dst);
  125. }
  126. } // namespace std
  127. namespace boost { namespace gil {
  128. namespace detail {
  129. template <typename I, typename O> struct copy_fn {
  130. BOOST_FORCEINLINE I operator()(I first, I last, O dst) const { return std::copy(first,last,dst); }
  131. };
  132. } // namespace detail
  133. } } // namespace boost::gil
  134. namespace std {
  135. /// \ingroup STLOptimizations
  136. /// \brief Copy when both src and dst are planar pointers is copy for each channel
  137. template<typename Cs, typename IC1, typename IC2> BOOST_FORCEINLINE
  138. boost::gil::planar_pixel_iterator<IC2,Cs> copy(boost::gil::planar_pixel_iterator<IC1,Cs> first, boost::gil::planar_pixel_iterator<IC1,Cs> last, boost::gil::planar_pixel_iterator<IC2,Cs> dst) {
  139. boost::gil::gil_function_requires<boost::gil::ChannelsCompatibleConcept<typename std::iterator_traits<IC1>::value_type,typename std::iterator_traits<IC2>::value_type> >();
  140. static_for_each(first,last,dst,boost::gil::detail::copy_fn<IC1,IC2>());
  141. return dst+(last-first);
  142. }
  143. } // namespace std
  144. namespace boost { namespace gil {
  145. namespace detail {
  146. /// Does a copy-n. If the inputs contain image iterators, performs a copy at each row using the row iterators
  147. /// \ingroup CopyPixels
  148. template <typename I, typename O>
  149. struct copier_n {
  150. BOOST_FORCEINLINE void operator()(I src, typename std::iterator_traits<I>::difference_type n, O dst) const { std::copy(src,src+n, dst); }
  151. };
  152. /// Source range is delimited by image iterators
  153. template <typename IL, typename O> // IL Models ConstPixelLocatorConcept, O Models PixelIteratorConcept
  154. struct copier_n<iterator_from_2d<IL>,O> {
  155. using diff_t = typename std::iterator_traits<iterator_from_2d<IL> >::difference_type;
  156. BOOST_FORCEINLINE void operator()(iterator_from_2d<IL> src, diff_t n, O dst) const {
  157. gil_function_requires<PixelLocatorConcept<IL> >();
  158. gil_function_requires<MutablePixelIteratorConcept<O> >();
  159. while (n>0) {
  160. diff_t l=src.width()-src.x_pos();
  161. diff_t numToCopy=(n<l ? n:l);
  162. detail::copy_n(src.x(), numToCopy, dst);
  163. dst+=numToCopy;
  164. src+=numToCopy;
  165. n-=numToCopy;
  166. }
  167. }
  168. };
  169. /// Destination range is delimited by image iterators
  170. template <typename I, typename OL> // I Models ConstPixelIteratorConcept, OL Models PixelLocatorConcept
  171. struct copier_n<I,iterator_from_2d<OL> > {
  172. using diff_t = typename std::iterator_traits<I>::difference_type;
  173. BOOST_FORCEINLINE void operator()(I src, diff_t n, iterator_from_2d<OL> dst) const {
  174. gil_function_requires<PixelIteratorConcept<I> >();
  175. gil_function_requires<MutablePixelLocatorConcept<OL> >();
  176. while (n>0) {
  177. diff_t l=dst.width()-dst.x_pos();
  178. diff_t numToCopy=(n<l ? n:l);
  179. detail::copy_n(src, numToCopy, dst.x());
  180. dst+=numToCopy;
  181. src+=numToCopy;
  182. n-=numToCopy;
  183. }
  184. }
  185. };
  186. /// Both source and destination ranges are delimited by image iterators
  187. template <typename IL, typename OL>
  188. struct copier_n<iterator_from_2d<IL>,iterator_from_2d<OL> > {
  189. using diff_t = typename iterator_from_2d<IL>::difference_type;
  190. BOOST_FORCEINLINE void operator()(iterator_from_2d<IL> src, diff_t n, iterator_from_2d<OL> dst) const {
  191. gil_function_requires<PixelLocatorConcept<IL> >();
  192. gil_function_requires<MutablePixelLocatorConcept<OL> >();
  193. if (src.x_pos()!=dst.x_pos() || src.width()!=dst.width()) {
  194. while(n-->0) {
  195. *dst++=*src++;
  196. }
  197. }
  198. while (n>0) {
  199. diff_t l=dst.width()-dst.x_pos();
  200. diff_t numToCopy=(n<l ? n : l);
  201. detail::copy_n(src.x(), numToCopy, dst.x());
  202. dst+=numToCopy;
  203. src+=numToCopy;
  204. n-=numToCopy;
  205. }
  206. }
  207. };
  208. template <typename SrcIterator, typename DstIterator>
  209. BOOST_FORCEINLINE DstIterator copy_with_2d_iterators(SrcIterator first, SrcIterator last, DstIterator dst) {
  210. using src_x_iterator = typename SrcIterator::x_iterator;
  211. using dst_x_iterator = typename DstIterator::x_iterator;
  212. typename SrcIterator::difference_type n = last - first;
  213. if (first.is_1d_traversable()) {
  214. if (dst.is_1d_traversable())
  215. copier_n<src_x_iterator,dst_x_iterator>()(first.x(),n, dst.x());
  216. else
  217. copier_n<src_x_iterator,DstIterator >()(first.x(),n, dst);
  218. } else {
  219. if (dst.is_1d_traversable())
  220. copier_n<SrcIterator,dst_x_iterator>()(first,n, dst.x());
  221. else
  222. copier_n<SrcIterator,DstIterator>()(first,n,dst);
  223. }
  224. return dst+n;
  225. }
  226. } // namespace detail
  227. } } // namespace boost::gil
  228. namespace std {
  229. /// \ingroup STLOptimizations
  230. /// \brief std::copy(I1,I1,I2) with I1 and I2 being a iterator_from_2d
  231. template <typename IL, typename OL>
  232. BOOST_FORCEINLINE boost::gil::iterator_from_2d<OL> copy1(boost::gil::iterator_from_2d<IL> first, boost::gil::iterator_from_2d<IL> last, boost::gil::iterator_from_2d<OL> dst) {
  233. return boost::gil::detail::copy_with_2d_iterators(first,last,dst);
  234. }
  235. } // namespace std
  236. namespace boost { namespace gil {
  237. /// \ingroup ImageViewSTLAlgorithmsCopyPixels
  238. /// \brief std::copy for image views
  239. template <typename View1, typename View2> BOOST_FORCEINLINE
  240. void copy_pixels(const View1& src, const View2& dst)
  241. {
  242. BOOST_ASSERT(src.dimensions() == dst.dimensions());
  243. detail::copy_with_2d_iterators(src.begin(),src.end(),dst.begin());
  244. }
  245. //////////////////////////////////////////////////////////////////////////////////////
  246. ///
  247. /// copy_and_convert_pixels
  248. ///
  249. //////////////////////////////////////////////////////////////////////////////////////
  250. /// \defgroup ImageViewSTLAlgorithmsCopyAndConvertPixels copy_and_convert_pixels
  251. /// \ingroup ImageViewSTLAlgorithms
  252. /// \brief copies src view into dst view, color converting if necessary.
  253. ///
  254. /// Versions taking static and runtime views are provided. Versions taking user-defined color convered are provided.
  255. namespace detail {
  256. template <typename CC>
  257. class copy_and_convert_pixels_fn : public binary_operation_obj<copy_and_convert_pixels_fn<CC> > {
  258. private:
  259. CC _cc;
  260. public:
  261. using result_type = typename binary_operation_obj<copy_and_convert_pixels_fn<default_color_converter> >::result_type;
  262. copy_and_convert_pixels_fn() {}
  263. copy_and_convert_pixels_fn(CC cc_in) : _cc(cc_in) {}
  264. // when the two color spaces are incompatible, a color conversion is performed
  265. template <typename V1, typename V2> BOOST_FORCEINLINE
  266. result_type apply_incompatible(const V1& src, const V2& dst) const {
  267. copy_pixels(color_converted_view<typename V2::value_type>(src,_cc),dst);
  268. }
  269. // If the two color spaces are compatible, copy_and_convert is just copy
  270. template <typename V1, typename V2> BOOST_FORCEINLINE
  271. result_type apply_compatible(const V1& src, const V2& dst) const {
  272. copy_pixels(src,dst);
  273. }
  274. };
  275. } // namespace detail
  276. /// \ingroup ImageViewSTLAlgorithmsCopyAndConvertPixels
  277. template <typename V1, typename V2,typename CC>
  278. BOOST_FORCEINLINE
  279. void copy_and_convert_pixels(const V1& src, const V2& dst,CC cc) {
  280. detail::copy_and_convert_pixels_fn<CC> ccp(cc);
  281. ccp(src,dst);
  282. }
  283. struct default_color_converter;
  284. /// \ingroup ImageViewSTLAlgorithmsCopyAndConvertPixels
  285. template <typename View1, typename View2>
  286. BOOST_FORCEINLINE
  287. void copy_and_convert_pixels(const View1& src, const View2& dst) {
  288. detail::copy_and_convert_pixels_fn<default_color_converter> ccp;
  289. ccp(src,dst);
  290. }
  291. } } // namespace boost::gil
  292. //////////////////////////////////////////////////////////////////////////////////////
  293. //
  294. // std::fill and gil::fill_pixels
  295. //
  296. //////////////////////////////////////////////////////////////////////////////////////
  297. /// \defgroup ImageViewSTLAlgorithmsFillPixels fill_pixels
  298. /// \ingroup ImageViewSTLAlgorithms
  299. /// \brief std::fill for image views
  300. namespace std {
  301. /// \ingroup STLOptimizations
  302. /// \brief std::fill(I,I,V) with I being a iterator_from_2d
  303. ///
  304. /// Invoked when one calls std::fill(I,I,V) with I being a iterator_from_2d (which is
  305. /// a 1D iterator over the pixels in an image). For contiguous images (i.e. images that have
  306. /// no alignment gap at the end of each row) it is more efficient to use the underlying
  307. /// pixel iterator that does not check for the end of rows. For non-contiguous images fill
  308. /// resolves to fill of each row using the underlying pixel iterator, which is still faster
  309. template <typename IL, typename V>
  310. void fill(boost::gil::iterator_from_2d<IL> first, boost::gil::iterator_from_2d<IL> last, const V& val) {
  311. boost::gil::gil_function_requires<boost::gil::MutablePixelLocatorConcept<IL> >();
  312. if (first.is_1d_traversable()) {
  313. std::fill(first.x(), last.x(), val);
  314. } else {
  315. // fill row by row
  316. std::ptrdiff_t n=last-first;
  317. while (n>0) {
  318. std::ptrdiff_t numToDo=std::min<const std::ptrdiff_t>(n,(std::ptrdiff_t)(first.width()-first.x_pos()));
  319. std::fill_n(first.x(), numToDo, val);
  320. first+=numToDo;
  321. n-=numToDo;
  322. }
  323. }
  324. }
  325. } // namespace std
  326. namespace boost { namespace gil {
  327. namespace detail {
  328. /// struct to do std::fill
  329. struct std_fill_t {
  330. template <typename It, typename P>
  331. void operator()(It first, It last, const P& p_in) {
  332. std::fill(first,last,p_in);
  333. }
  334. };
  335. /// std::fill for planar iterators
  336. template <typename It, typename P>
  337. BOOST_FORCEINLINE
  338. void fill_aux(It first, It last, const P& p, mpl::true_) {
  339. static_for_each(first,last,p,std_fill_t());
  340. }
  341. /// std::fill for interleaved iterators
  342. template <typename It, typename P>
  343. BOOST_FORCEINLINE
  344. void fill_aux(It first, It last, const P& p,mpl::false_) {
  345. std::fill(first,last,p);
  346. }
  347. } // namespace detail
  348. /// \ingroup ImageViewSTLAlgorithmsFillPixels
  349. /// \brief std::fill for image views
  350. template <typename View, typename Value> BOOST_FORCEINLINE
  351. void fill_pixels(const View& img_view, const Value& val) {
  352. if (img_view.is_1d_traversable())
  353. detail::fill_aux(img_view.begin().x(), img_view.end().x(),
  354. val,is_planar<View>());
  355. else
  356. for (std::ptrdiff_t y=0; y<img_view.height(); ++y)
  357. detail::fill_aux(img_view.row_begin(y),img_view.row_end(y),
  358. val,is_planar<View>());
  359. }
  360. //////////////////////////////////////////////////////////////////////////////////////
  361. ///
  362. /// destruct_pixels
  363. ///
  364. //////////////////////////////////////////////////////////////////////////////////////
  365. /// \defgroup ImageViewSTLAlgorithmsDestructPixels destruct_pixels
  366. /// \ingroup ImageViewSTLAlgorithms
  367. /// \brief invokes the destructor on every pixel of an image view
  368. namespace detail {
  369. template <typename Iterator>
  370. BOOST_FORCEINLINE
  371. void destruct_range_impl(Iterator first, Iterator last,
  372. typename std::enable_if
  373. <
  374. mpl::and_
  375. <
  376. is_pointer<Iterator>,
  377. mpl::not_<std::is_trivially_destructible<typename std::iterator_traits<Iterator>::value_type>>
  378. >::value
  379. >::type* /*ptr*/ = 0)
  380. {
  381. while (first != last)
  382. {
  383. first->~value_t();
  384. ++first;
  385. }
  386. }
  387. template <typename Iterator>
  388. BOOST_FORCEINLINE
  389. void destruct_range_impl(Iterator /*first*/, Iterator /*last*/,
  390. typename std::enable_if
  391. <
  392. mpl::or_
  393. <
  394. mpl::not_<is_pointer<Iterator>>,
  395. std::is_trivially_destructible<typename std::iterator_traits<Iterator>::value_type>
  396. >::value
  397. >::type* /* ptr */ = nullptr)
  398. {
  399. }
  400. template <typename Iterator>
  401. BOOST_FORCEINLINE
  402. void destruct_range(Iterator first, Iterator last)
  403. {
  404. destruct_range_impl(first, last);
  405. }
  406. struct std_destruct_t
  407. {
  408. template <typename Iterator>
  409. void operator()(Iterator first, Iterator last) const
  410. {
  411. destruct_range(first,last);
  412. }
  413. };
  414. /// destruct for planar iterators
  415. template <typename It>
  416. BOOST_FORCEINLINE
  417. void destruct_aux(It first, It last, mpl::true_) {
  418. static_for_each(first,last,std_destruct_t());
  419. }
  420. /// destruct for interleaved iterators
  421. template <typename It>
  422. BOOST_FORCEINLINE
  423. void destruct_aux(It first, It last, mpl::false_) {
  424. destruct_range(first,last);
  425. }
  426. } // namespace detail
  427. /// \ingroup ImageViewSTLAlgorithmsDestructPixels
  428. /// \brief Invokes the in-place destructor on every pixel of the view
  429. template <typename View> BOOST_FORCEINLINE
  430. void destruct_pixels(const View& img_view) {
  431. if (img_view.is_1d_traversable())
  432. detail::destruct_aux(img_view.begin().x(), img_view.end().x(),
  433. is_planar<View>());
  434. else
  435. for (std::ptrdiff_t y=0; y<img_view.height(); ++y)
  436. detail::destruct_aux(img_view.row_begin(y),img_view.row_end(y),
  437. is_planar<View>());
  438. }
  439. //////////////////////////////////////////////////////////////////////////////////////
  440. ///
  441. /// uninitialized_fill_pixels
  442. ///
  443. //////////////////////////////////////////////////////////////////////////////////////
  444. /// \defgroup ImageViewSTLAlgorithmsUninitializedFillPixels uninitialized_fill_pixels
  445. /// \ingroup ImageViewSTLAlgorithms
  446. /// \brief std::uninitialized_fill for image views
  447. namespace detail {
  448. /// std::uninitialized_fill for planar iterators
  449. /// If an exception is thrown destructs any in-place copy-constructed objects
  450. template <typename It, typename P>
  451. BOOST_FORCEINLINE
  452. void uninitialized_fill_aux(It first, It last,
  453. const P& p, mpl::true_) {
  454. int channel=0;
  455. try {
  456. using pixel_t = typename std::iterator_traits<It>::value_type;
  457. while (channel < num_channels<pixel_t>::value) {
  458. std::uninitialized_fill(dynamic_at_c(first,channel), dynamic_at_c(last,channel),
  459. dynamic_at_c(p,channel));
  460. ++channel;
  461. }
  462. } catch (...) {
  463. for (int c=0; c<channel; ++c)
  464. destruct_range(dynamic_at_c(first,c), dynamic_at_c(last,c));
  465. throw;
  466. }
  467. }
  468. /// std::uninitialized_fill for interleaved iterators
  469. /// If an exception is thrown destructs any in-place copy-constructed objects
  470. template <typename It, typename P>
  471. BOOST_FORCEINLINE
  472. void uninitialized_fill_aux(It first, It last,
  473. const P& p,mpl::false_) {
  474. std::uninitialized_fill(first,last,p);
  475. }
  476. } // namespace detail
  477. /// \ingroup ImageViewSTLAlgorithmsUninitializedFillPixels
  478. /// \brief std::uninitialized_fill for image views.
  479. /// Does not support planar heterogeneous views.
  480. /// If an exception is thrown destructs any in-place copy-constructed pixels
  481. template <typename View, typename Value>
  482. void uninitialized_fill_pixels(const View& img_view, const Value& val) {
  483. if (img_view.is_1d_traversable())
  484. detail::uninitialized_fill_aux(img_view.begin().x(), img_view.end().x(),
  485. val,is_planar<View>());
  486. else {
  487. typename View::y_coord_t y = 0;
  488. try {
  489. for (y=0; y<img_view.height(); ++y)
  490. detail::uninitialized_fill_aux(img_view.row_begin(y),img_view.row_end(y),
  491. val,is_planar<View>());
  492. } catch(...) {
  493. for (typename View::y_coord_t y0=0; y0<y; ++y0)
  494. detail::destruct_aux(img_view.row_begin(y0),img_view.row_end(y0), is_planar<View>());
  495. throw;
  496. }
  497. }
  498. }
  499. //////////////////////////////////////////////////////////////////////////////////////
  500. ///
  501. /// default_construct_pixels
  502. ///
  503. //////////////////////////////////////////////////////////////////////////////////////
  504. /// \defgroup ImageViewSTLAlgorithmsDefaultConstructPixels default_construct_pixels
  505. /// \ingroup ImageViewSTLAlgorithms
  506. /// \brief invokes the default constructor on every pixel of an image view
  507. namespace detail {
  508. template <typename It> BOOST_FORCEINLINE
  509. void default_construct_range_impl(It first, It last, mpl::true_) {
  510. using value_t = typename std::iterator_traits<It>::value_type;
  511. It first1=first;
  512. try {
  513. while (first!=last) {
  514. new (first) value_t();
  515. ++first;
  516. }
  517. } catch (...) {
  518. destruct_range(first1,first);
  519. throw;
  520. }
  521. }
  522. template <typename It> BOOST_FORCEINLINE
  523. void default_construct_range_impl(It, It, mpl::false_) {}
  524. template <typename It> BOOST_FORCEINLINE
  525. void default_construct_range(It first, It last) { default_construct_range_impl(first, last, typename is_pointer<It>::type()); }
  526. /// uninitialized_default_construct for planar iterators
  527. template <typename It>
  528. BOOST_FORCEINLINE
  529. void default_construct_aux(It first, It last, mpl::true_) {
  530. int channel=0;
  531. try {
  532. using pixel_t = typename std::iterator_traits<It>::value_type;
  533. while (channel < num_channels<pixel_t>::value) {
  534. default_construct_range(dynamic_at_c(first,channel), dynamic_at_c(last,channel));
  535. ++channel;
  536. }
  537. } catch (...) {
  538. for (int c=0; c<channel; ++c)
  539. destruct_range(dynamic_at_c(first,c), dynamic_at_c(last,c));
  540. throw;
  541. }
  542. }
  543. /// uninitialized_default_construct for interleaved iterators
  544. template <typename It>
  545. BOOST_FORCEINLINE
  546. void default_construct_aux(It first, It last, mpl::false_) {
  547. default_construct_range(first,last);
  548. }
  549. template <typename View, bool IsPlanar>
  550. struct has_trivial_pixel_constructor : public boost::has_trivial_constructor<typename View::value_type> {};
  551. template <typename View>
  552. struct has_trivial_pixel_constructor<View, true> : public boost::has_trivial_constructor<typename channel_type<View>::type> {};
  553. } // namespace detail
  554. namespace detail {
  555. template<typename View, bool IsTriviallyConstructible>
  556. BOOST_FORCEINLINE
  557. void default_construct_pixels_impl(
  558. View const& img_view,
  559. std::enable_if<!IsTriviallyConstructible>* /* ptr */ = nullptr)
  560. {
  561. if( img_view.is_1d_traversable() )
  562. {
  563. detail::default_construct_aux( img_view.begin().x()
  564. , img_view.end().x()
  565. , is_planar<View>()
  566. );
  567. }
  568. else
  569. {
  570. typename View::y_coord_t y = 0;
  571. try
  572. {
  573. for( y = 0; y < img_view.height(); ++y )
  574. {
  575. detail::default_construct_aux( img_view.row_begin( y )
  576. ,img_view.row_end( y )
  577. , is_planar<View>()
  578. );
  579. }
  580. } catch(...)
  581. {
  582. for (typename View::y_coord_t y0 = 0; y0 < y; ++y0 )
  583. {
  584. detail::destruct_aux( img_view.row_begin(y0)
  585. , img_view.row_end(y0)
  586. , is_planar<View>()
  587. );
  588. }
  589. throw;
  590. }
  591. }
  592. }
  593. } // namespace detail
  594. /// \ingroup ImageViewSTLAlgorithmsDefaultConstructPixels
  595. /// \brief Invokes the in-place default constructor on every pixel of the (uninitialized) view.
  596. /// Does not support planar heterogeneous views.
  597. /// If an exception is thrown destructs any in-place default-constructed pixels
  598. template <typename View>
  599. void default_construct_pixels(const View& img_view) {
  600. detail::default_construct_pixels_impl< View
  601. , detail::has_trivial_pixel_constructor< View
  602. , is_planar< View >::value
  603. >::value
  604. >( img_view );
  605. }
  606. //////////////////////////////////////////////////////////////////////////////////////
  607. ///
  608. /// uninitialized_copy_pixels
  609. ///
  610. //////////////////////////////////////////////////////////////////////////////////////
  611. /// \defgroup ImageViewSTLAlgorithmsUninitializedCopyPixels uninitialized_copy_pixels
  612. /// \ingroup ImageViewSTLAlgorithms
  613. /// \brief std::uninitialized_copy for image views
  614. namespace detail {
  615. /// std::uninitialized_copy for pairs of planar iterators
  616. template <typename It1, typename It2>
  617. BOOST_FORCEINLINE
  618. void uninitialized_copy_aux(It1 first1, It1 last1,
  619. It2 first2, mpl::true_) {
  620. int channel=0;
  621. try {
  622. using pixel_t = typename std::iterator_traits<It1>::value_type;
  623. while (channel < num_channels<pixel_t>::value) {
  624. std::uninitialized_copy(dynamic_at_c(first1,channel), dynamic_at_c(last1,channel), dynamic_at_c(first2,channel));
  625. ++channel;
  626. }
  627. } catch (...) {
  628. It2 last2=first2;
  629. std::advance(last2, std::distance(first1,last1));
  630. for (int c=0; c<channel; ++c)
  631. destruct_range(dynamic_at_c(first2,c), dynamic_at_c(last2,c));
  632. throw;
  633. }
  634. }
  635. /// std::uninitialized_copy for interleaved or mixed iterators
  636. template <typename It1, typename It2>
  637. BOOST_FORCEINLINE
  638. void uninitialized_copy_aux(It1 first1, It1 last1,
  639. It2 first2,mpl::false_) {
  640. std::uninitialized_copy(first1,last1,first2);
  641. }
  642. } // namespace detail
  643. /// \ingroup ImageViewSTLAlgorithmsUninitializedCopyPixels
  644. /// \brief std::uninitialized_copy for image views.
  645. /// Does not support planar heterogeneous views.
  646. /// If an exception is thrown destructs any in-place copy-constructed objects
  647. template <typename View1, typename View2>
  648. void uninitialized_copy_pixels(const View1& view1, const View2& view2) {
  649. using is_planar = mpl::bool_<is_planar<View1>::value && is_planar<View2>::value>;
  650. BOOST_ASSERT(view1.dimensions() == view2.dimensions());
  651. if (view1.is_1d_traversable() && view2.is_1d_traversable())
  652. detail::uninitialized_copy_aux(view1.begin().x(), view1.end().x(),
  653. view2.begin().x(),
  654. is_planar());
  655. else {
  656. typename View1::y_coord_t y = 0;
  657. try {
  658. for (y=0; y<view1.height(); ++y)
  659. detail::uninitialized_copy_aux(view1.row_begin(y), view1.row_end(y),
  660. view2.row_begin(y),
  661. is_planar());
  662. } catch(...) {
  663. for (typename View1::y_coord_t y0=0; y0<y; ++y0)
  664. detail::destruct_aux(view2.row_begin(y0),view2.row_end(y0), is_planar());
  665. throw;
  666. }
  667. }
  668. }
  669. //////////////////////////////////////////////////////////////////////////////////////
  670. ///
  671. /// for_each_pixel
  672. ///
  673. //////////////////////////////////////////////////////////////////////////////////////
  674. /// \defgroup ImageViewSTLAlgorithmsForEachPixel for_each_pixel
  675. /// \ingroup ImageViewSTLAlgorithms
  676. /// \brief std::for_each for image views
  677. ///
  678. /// For contiguous images (i.e. images that have no alignment gap at the end of each row) it is
  679. /// more efficient to use the underlying pixel iterator that does not check for the end of rows.
  680. /// For non-contiguous images for_each_pixel resolves to for_each of each row using the underlying
  681. /// pixel iterator, which is still faster
  682. /// \ingroup ImageViewSTLAlgorithmsForEachPixel
  683. template <typename V, typename F>
  684. F for_each_pixel(const V& img, F fun) {
  685. if (img.is_1d_traversable()) {
  686. return std::for_each(img.begin().x(), img.end().x(), fun);
  687. } else {
  688. for (std::ptrdiff_t y=0; y<img.height(); ++y)
  689. std::for_each(img.row_begin(y),img.row_end(y),fun);
  690. return fun;
  691. }
  692. }
  693. /// \defgroup ImageViewSTLAlgorithmsForEachPixelPosition for_each_pixel_position
  694. /// \ingroup ImageViewSTLAlgorithms
  695. /// \brief adobe::for_each_position for image views (passes locators, instead of pixel references, to the function object)
  696. /// \ingroup ImageViewSTLAlgorithmsForEachPixelPosition
  697. template <typename View, typename F>
  698. F for_each_pixel_position(const View& img, F fun) {
  699. typename View::xy_locator loc=img.xy_at(0,0);
  700. for (std::ptrdiff_t y=0; y<img.height(); ++y) {
  701. for (std::ptrdiff_t x=0; x<img.width(); ++x, ++loc.x())
  702. fun(loc);
  703. loc.x()-=img.width(); ++loc.y();
  704. }
  705. return fun;
  706. }
  707. //////////////////////////////////////////////////////////////////////////////////////
  708. ///
  709. /// generate_pixels
  710. ///
  711. //////////////////////////////////////////////////////////////////////////////////////
  712. /// \defgroup ImageViewSTLAlgorithmsGeneratePixels generate_pixels
  713. /// \ingroup ImageViewSTLAlgorithms
  714. /// \brief std::generate for image views
  715. /// \ingroup ImageViewSTLAlgorithmsGeneratePixels
  716. /// \brief std::generate for image views
  717. template <typename View, typename F>
  718. void generate_pixels(const View& v, F fun) {
  719. if (v.is_1d_traversable()) {
  720. std::generate(v.begin().x(), v.end().x(), fun);
  721. } else {
  722. for (std::ptrdiff_t y=0; y<v.height(); ++y)
  723. std::generate(v.row_begin(y),v.row_end(y),fun);
  724. }
  725. }
  726. //////////////////////////////////////////////////////////////////////////////////////
  727. ///
  728. /// std::equal and gil::equal_pixels for GIL constructs
  729. ///
  730. //////////////////////////////////////////////////////////////////////////////////////
  731. /// \defgroup ImageViewSTLAlgorithmsEqualPixels equal_pixels
  732. /// \ingroup ImageViewSTLAlgorithms
  733. /// \brief std::equal for image views
  734. template <typename I1, typename I2> BOOST_FORCEINLINE bool equal_n(I1 i1, std::ptrdiff_t n, I2 i2);
  735. namespace detail {
  736. template <typename I1, typename I2>
  737. struct equal_n_fn {
  738. BOOST_FORCEINLINE bool operator()(I1 i1, std::ptrdiff_t n, I2 i2) const { return std::equal(i1,i1+n, i2); }
  739. };
  740. /// Equal when both ranges are interleaved and of the same type.
  741. /// GIL pixels are bitwise comparable, so memcmp is used. User-defined pixels that are not bitwise comparable need to provide an overload
  742. template<typename T, typename Cs>
  743. struct equal_n_fn<const pixel<T,Cs>*, const pixel<T,Cs>*> {
  744. BOOST_FORCEINLINE bool operator()(const pixel<T,Cs>* i1, std::ptrdiff_t n, const pixel<T,Cs>* i2) const {
  745. return memcmp(i1, i2, n*sizeof(pixel<T,Cs>))==0;
  746. }
  747. };
  748. template<typename T, typename Cs>
  749. struct equal_n_fn<pixel<T,Cs>*, pixel<T,Cs>*> : equal_n_fn<const pixel<T,Cs>*, const pixel<T,Cs>*> {};
  750. /// EqualPixels
  751. /// Equal when both ranges are planar pointers of the same type. memcmp is invoked for each channel plane
  752. /// User-defined channels that are not bitwise comparable need to provide an overload
  753. template<typename IC, typename Cs>
  754. struct equal_n_fn<planar_pixel_iterator<IC,Cs>, planar_pixel_iterator<IC,Cs> > {
  755. BOOST_FORCEINLINE bool operator()(const planar_pixel_iterator<IC,Cs> i1, std::ptrdiff_t n, const planar_pixel_iterator<IC,Cs> i2) const {
  756. std::ptrdiff_t numBytes=n*sizeof(typename std::iterator_traits<IC>::value_type);
  757. for (std::ptrdiff_t i=0; i<mpl::size<Cs>::value; ++i)
  758. if (memcmp(dynamic_at_c(i1,i), dynamic_at_c(i2,i), numBytes)!=0)
  759. return false;
  760. return true;
  761. }
  762. };
  763. /// Source range is delimited by image iterators
  764. template <typename Loc, typename I2> // IL Models ConstPixelLocatorConcept, O Models PixelIteratorConcept
  765. struct equal_n_fn<boost::gil::iterator_from_2d<Loc>,I2> {
  766. BOOST_FORCEINLINE bool operator()(boost::gil::iterator_from_2d<Loc> i1, std::ptrdiff_t n, I2 i2) const {
  767. gil_function_requires<boost::gil::PixelLocatorConcept<Loc> >();
  768. gil_function_requires<boost::gil::PixelIteratorConcept<I2> >();
  769. while (n>0) {
  770. std::ptrdiff_t num=std::min<const std::ptrdiff_t>(n, i1.width()-i1.x_pos());
  771. if (!equal_n(i1.x(), num, i2))
  772. return false;
  773. i1+=num;
  774. i2+=num;
  775. n-=num;
  776. }
  777. return true;
  778. }
  779. };
  780. /// Destination range is delimited by image iterators
  781. template <typename I1, typename Loc> // I Models PixelIteratorConcept, OL Models PixelLocatorConcept
  782. struct equal_n_fn<I1,boost::gil::iterator_from_2d<Loc> > {
  783. BOOST_FORCEINLINE bool operator()(I1 i1, std::ptrdiff_t n, boost::gil::iterator_from_2d<Loc> i2) const {
  784. gil_function_requires<boost::gil::PixelIteratorConcept<I1> >();
  785. gil_function_requires<boost::gil::PixelLocatorConcept<Loc> >();
  786. while (n>0) {
  787. std::ptrdiff_t num=std::min<const std::ptrdiff_t>(n,i2.width()-i2.x_pos());
  788. if (!equal_n(i1, num, i2.x()))
  789. return false;
  790. i1+=num;
  791. i2+=num;
  792. n-=num;
  793. }
  794. return true;
  795. }
  796. };
  797. /// Both source and destination ranges are delimited by image iterators
  798. template <typename Loc1, typename Loc2>
  799. struct equal_n_fn<boost::gil::iterator_from_2d<Loc1>,boost::gil::iterator_from_2d<Loc2> > {
  800. BOOST_FORCEINLINE bool operator()(boost::gil::iterator_from_2d<Loc1> i1, std::ptrdiff_t n, boost::gil::iterator_from_2d<Loc2> i2) const {
  801. gil_function_requires<boost::gil::PixelLocatorConcept<Loc1> >();
  802. gil_function_requires<boost::gil::PixelLocatorConcept<Loc2> >();
  803. if (i1.x_pos()!=i2.x_pos() || i1.width()!=i2.width()) {
  804. while(n-->0) {
  805. if (*i1++!=*i2++) return false;
  806. }
  807. }
  808. while (n>0) {
  809. std::ptrdiff_t num=std::min<const std::ptrdiff_t>(n,i2.width()-i2.x_pos());
  810. if (!equal_n(i1.x(), num, i2.x()))
  811. return false;
  812. i1+=num;
  813. i2+=num;
  814. n-=num;
  815. }
  816. return true;
  817. }
  818. };
  819. } // namespace detail
  820. template <typename I1, typename I2> BOOST_FORCEINLINE
  821. bool equal_n(I1 i1, std::ptrdiff_t n, I2 i2) {
  822. return detail::equal_n_fn<I1,I2>()(i1,n,i2);
  823. }
  824. } } // namespace boost::gil
  825. namespace std {
  826. /// \ingroup STLOptimizations
  827. /// \brief std::equal(I1,I1,I2) with I1 and I2 being a iterator_from_2d
  828. ///
  829. /// Invoked when one calls std::equal(I1,I1,I2) with I1 and I2 being a iterator_from_2d (which is
  830. /// a 1D iterator over the pixels in an image). Attempts to demote the source and destination
  831. /// iterators to simpler/faster types if the corresponding range is contiguous.
  832. /// For contiguous images (i.e. images that have
  833. /// no alignment gap at the end of each row) it is more efficient to use the underlying
  834. /// pixel iterator that does not check for the end of rows. If the underlying pixel iterator
  835. /// happens to be a fundamental planar/interleaved pointer, the call may further resolve
  836. /// to memcmp. Otherwise it resolves to copying each row using the underlying pixel iterator
  837. template <typename Loc1, typename Loc2> BOOST_FORCEINLINE
  838. bool equal(boost::gil::iterator_from_2d<Loc1> first, boost::gil::iterator_from_2d<Loc1> last, boost::gil::iterator_from_2d<Loc2> first2) {
  839. boost::gil::gil_function_requires<boost::gil::PixelLocatorConcept<Loc1> >();
  840. boost::gil::gil_function_requires<boost::gil::PixelLocatorConcept<Loc2> >();
  841. std::ptrdiff_t n=last-first;
  842. if (first.is_1d_traversable()) {
  843. if (first2.is_1d_traversable())
  844. return boost::gil::detail::equal_n_fn<typename Loc1::x_iterator,typename Loc2::x_iterator>()(first.x(),n, first2.x());
  845. else
  846. return boost::gil::detail::equal_n_fn<typename Loc1::x_iterator,boost::gil::iterator_from_2d<Loc2> >()(first.x(),n, first2);
  847. } else {
  848. if (first2.is_1d_traversable())
  849. return boost::gil::detail::equal_n_fn<boost::gil::iterator_from_2d<Loc1>,typename Loc2::x_iterator>()(first,n, first2.x());
  850. else
  851. return boost::gil::detail::equal_n_fn<boost::gil::iterator_from_2d<Loc1>,boost::gil::iterator_from_2d<Loc2> >()(first,n,first2);
  852. }
  853. }
  854. } // namespace std
  855. namespace boost { namespace gil {
  856. /// \ingroup ImageViewSTLAlgorithmsEqualPixels
  857. /// \brief std::equal for image views
  858. template <typename View1, typename View2> BOOST_FORCEINLINE
  859. bool equal_pixels(const View1& v1, const View2& v2) {
  860. BOOST_ASSERT(v1.dimensions() == v2.dimensions());
  861. return std::equal(v1.begin(),v1.end(),v2.begin()); // std::equal has overloads with GIL iterators for optimal performance
  862. }
  863. //////////////////////////////////////////////////////////////////////////////////////
  864. ///
  865. /// transform_pixels
  866. ///
  867. //////////////////////////////////////////////////////////////////////////////////////
  868. /// \defgroup ImageViewSTLAlgorithmsTransformPixels transform_pixels
  869. /// \ingroup ImageViewSTLAlgorithms
  870. /// \brief std::transform for image views
  871. /// \ingroup ImageViewSTLAlgorithmsTransformPixels
  872. /// \brief std::transform for image views
  873. template <typename View1, typename View2, typename F> BOOST_FORCEINLINE
  874. F transform_pixels(const View1& src,const View2& dst, F fun) {
  875. BOOST_ASSERT(src.dimensions() == dst.dimensions());
  876. for (std::ptrdiff_t y=0; y<src.height(); ++y) {
  877. typename View1::x_iterator srcIt=src.row_begin(y);
  878. typename View2::x_iterator dstIt=dst.row_begin(y);
  879. for (std::ptrdiff_t x=0; x<src.width(); ++x)
  880. dstIt[x]=fun(srcIt[x]);
  881. }
  882. return fun;
  883. }
  884. /// \ingroup ImageViewSTLAlgorithmsTransformPixels
  885. /// \brief transform_pixels with two sources
  886. template <typename View1, typename View2, typename View3, typename F> BOOST_FORCEINLINE
  887. F transform_pixels(const View1& src1, const View2& src2,const View3& dst, F fun) {
  888. for (std::ptrdiff_t y=0; y<dst.height(); ++y) {
  889. typename View1::x_iterator srcIt1=src1.row_begin(y);
  890. typename View2::x_iterator srcIt2=src2.row_begin(y);
  891. typename View3::x_iterator dstIt=dst.row_begin(y);
  892. for (std::ptrdiff_t x=0; x<dst.width(); ++x)
  893. dstIt[x]=fun(srcIt1[x],srcIt2[x]);
  894. }
  895. return fun;
  896. }
  897. /// \defgroup ImageViewSTLAlgorithmsTransformPixelPositions transform_pixel_positions
  898. /// \ingroup ImageViewSTLAlgorithms
  899. /// \brief adobe::transform_positions for image views (passes locators, instead of pixel references, to the function object)
  900. /// \ingroup ImageViewSTLAlgorithmsTransformPixelPositions
  901. /// \brief Like transform_pixels but passes to the function object pixel locators instead of pixel references
  902. template <typename View1, typename View2, typename F> BOOST_FORCEINLINE
  903. F transform_pixel_positions(const View1& src,const View2& dst, F fun) {
  904. BOOST_ASSERT(src.dimensions() == dst.dimensions());
  905. typename View1::xy_locator loc=src.xy_at(0,0);
  906. for (std::ptrdiff_t y=0; y<src.height(); ++y) {
  907. typename View2::x_iterator dstIt=dst.row_begin(y);
  908. for (std::ptrdiff_t x=0; x<src.width(); ++x, ++loc.x())
  909. dstIt[x]=fun(loc);
  910. loc.x()-=src.width(); ++loc.y();
  911. }
  912. return fun;
  913. }
  914. /// \ingroup ImageViewSTLAlgorithmsTransformPixelPositions
  915. /// \brief transform_pixel_positions with two sources
  916. template <typename View1, typename View2, typename View3, typename F> BOOST_FORCEINLINE
  917. F transform_pixel_positions(const View1& src1,const View2& src2,const View3& dst, F fun) {
  918. BOOST_ASSERT(src1.dimensions() == dst.dimensions());
  919. BOOST_ASSERT(src2.dimensions() == dst.dimensions());
  920. typename View1::xy_locator loc1=src1.xy_at(0,0);
  921. typename View2::xy_locator loc2=src2.xy_at(0,0);
  922. for (std::ptrdiff_t y=0; y<src1.height(); ++y) {
  923. typename View3::x_iterator dstIt=dst.row_begin(y);
  924. for (std::ptrdiff_t x=0; x<src1.width(); ++x, ++loc1.x(), ++loc2.x())
  925. dstIt[x]=fun(loc1,loc2);
  926. loc1.x()-=src1.width(); ++loc1.y();
  927. loc2.x()-=src2.width(); ++loc2.y();
  928. }
  929. return fun;
  930. }
  931. } } // namespace boost::gil
  932. #endif