Boost GIL


image.hpp
1 //
2 // Copyright 2005-2007 Adobe Systems Incorporated
3 // Copyright 2021 Pranam Lashkari <plashkari628@gmail.com>
4 //
5 // Distributed under the Boost Software License, Version 1.0
6 // See accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt
8 //
9 #ifndef BOOST_GIL_IMAGE_HPP
10 #define BOOST_GIL_IMAGE_HPP
11 
12 #include <boost/gil/algorithm.hpp>
13 #include <boost/gil/image_view.hpp>
14 #include <boost/gil/metafunctions.hpp>
15 #include <boost/gil/detail/mp11.hpp>
16 
17 #include <boost/assert.hpp>
18 #include <boost/core/exchange.hpp>
19 
20 #include <cstddef>
21 #include <memory>
22 #include <utility>
23 #include <type_traits>
24 
25 namespace boost { namespace gil {
26 
40 
41 template< typename Pixel, bool IsPlanar, typename Alloc>
42 class image
43 {
44 public:
45 #if defined(BOOST_NO_CXX11_ALLOCATOR)
46  using allocator_type = typename Alloc::template rebind<unsigned char>::other;
47 #else
48  using allocator_type = typename std::allocator_traits<Alloc>::template rebind_alloc<unsigned char>;
49 #endif
50  using view_t = typename view_type_from_pixel<Pixel, IsPlanar>::type;
51  using const_view_t = typename view_t::const_t;
52  using point_t = typename view_t::point_t;
53  using coord_t = typename view_t::coord_t;
54  using value_type = typename view_t::value_type;
55  using x_coord_t = coord_t;
56  using y_coord_t = coord_t;
57 
58  const point_t& dimensions() const { return _view.dimensions(); }
59  x_coord_t width() const { return _view.width(); }
60  y_coord_t height() const { return _view.height(); }
61 
62  explicit image(std::size_t alignment=0,
63  const Alloc alloc_in = Alloc()) :
64  _memory(nullptr), _align_in_bytes(alignment), _alloc(alloc_in), _allocated_bytes( 0 ) {}
65 
66  // Create with dimensions and optional initial value and alignment
67  image(const point_t& dimensions,
68  std::size_t alignment=0,
69  const Alloc alloc_in = Alloc()) : _memory(nullptr), _align_in_bytes(alignment), _alloc(alloc_in)
70  , _allocated_bytes( 0 )
71  {
72  allocate_and_default_construct(dimensions);
73  }
74 
75  image(x_coord_t width, y_coord_t height,
76  std::size_t alignment=0,
77  const Alloc alloc_in = Alloc()) : _memory(nullptr), _align_in_bytes(alignment), _alloc(alloc_in)
78  , _allocated_bytes( 0 )
79  {
80  allocate_and_default_construct(point_t(width,height));
81  }
82 
83  image(const point_t& dimensions,
84  const Pixel& p_in,
85  std::size_t alignment = 0,
86  const Alloc alloc_in = Alloc()) : _memory(nullptr), _align_in_bytes(alignment), _alloc(alloc_in)
87  , _allocated_bytes( 0 )
88  {
89  allocate_and_fill(dimensions, p_in);
90  }
91 
92  image(x_coord_t width, y_coord_t height,
93  const Pixel& p_in,
94  std::size_t alignment = 0,
95  const Alloc alloc_in = Alloc()) : _memory(nullptr), _align_in_bytes(alignment), _alloc(alloc_in)
96  , _allocated_bytes ( 0 )
97  {
98  allocate_and_fill(point_t(width,height),p_in);
99  }
100 
101  image(const image& img) : _memory(nullptr), _align_in_bytes(img._align_in_bytes), _alloc(img._alloc)
102  , _allocated_bytes( img._allocated_bytes )
103  {
104  allocate_and_copy(img.dimensions(),img._view);
105  }
106 
107  template <typename P2, bool IP2, typename Alloc2>
108  image(const image<P2,IP2,Alloc2>& img) : _memory(nullptr), _align_in_bytes(img._align_in_bytes), _alloc(img._alloc)
109  , _allocated_bytes( img._allocated_bytes )
110  {
111  allocate_and_copy(img.dimensions(),img._view);
112  }
113 
114  template <typename Loc,
115  typename std::enable_if<pixels_are_compatible<typename Loc::value_type, Pixel>::value, int>::type = 0>
116  image(const image_view<Loc>& view,
117  std::size_t alignment = 0,
118  const Alloc alloc_in = Alloc()) : _memory(nullptr), _align_in_bytes(alignment), _alloc(alloc_in)
119  , _allocated_bytes( 0 )
120  {
121  allocate_and_copy(view.dimensions(),view);
122  }
123 
124  // TODO Optimization: use noexcept (requires _view to be nothrow copy constructible)
125  image(image&& img) :
126  _view(img._view),
127  _memory(img._memory),
128  _align_in_bytes(img._align_in_bytes),
129  _alloc(std::move(img._alloc)),
130  _allocated_bytes(img._allocated_bytes)
131  {
132  img._view = view_t();
133  img._memory = nullptr;
134  img._align_in_bytes = 0;
135  img._allocated_bytes = 0;
136  }
137 
138  image& operator=(const image& img)
139  {
140  if (dimensions() == img.dimensions())
141  copy_pixels(img._view,_view);
142  else
143  {
144  image tmp(img);
145  swap(tmp);
146  }
147  return *this;
148  }
149 
150  template <typename Img>
151  image& operator=(const Img& img)
152  {
153  if (dimensions() == img.dimensions())
154  copy_pixels(img._view,_view);
155  else
156  {
157  image tmp(img);
158  swap(tmp);
159  }
160  return *this;
161  }
162 
163  private:
164  using propagate_allocators = std::true_type;
165  using no_propagate_allocators = std::false_type;
166 
167  template <class Alloc2>
168  using choose_pocma = typename std::conditional<
169  // TODO: Use std::allocator_traits<Allocator>::is_always_equal if available
170  std::is_empty<Alloc2>::value,
171  std::true_type,
172  typename std::allocator_traits<Alloc2>::propagate_on_container_move_assignment::type
173  >::type;
174 
175  static void exchange_memory(image& lhs, image& rhs)
176  {
177  lhs._memory = boost::exchange(rhs._memory, nullptr);
178  lhs._align_in_bytes = boost::exchange(rhs._align_in_bytes, 0);
179  lhs._allocated_bytes = boost::exchange(rhs._allocated_bytes, 0);
180  lhs._view = boost::exchange(rhs._view, image::view_t{});
181  };
182 
183  void move_assign(image& img, propagate_allocators) noexcept {
184  // non-sticky allocator, can adopt the memory, fast
185  destruct_pixels(_view);
186  this->deallocate();
187  this->_alloc = img._alloc;
188  exchange_memory(*this, img);
189  }
190 
191  void move_assign(image& img, no_propagate_allocators) {
192  if (_alloc == img._alloc) {
193  // allocator stuck to the rhs, but it's equivalent of ours, we can still adopt the memory
194  destruct_pixels(_view);
195  this->deallocate();
196  exchange_memory(*this, img);
197  } else {
198  // cannot propagate the allocator and cannot adopt the memory
199  if (img._memory)
200  {
201  allocate_and_copy(img.dimensions(), img._view);
202  destruct_pixels(img._view);
203  img.deallocate();
204  img._view = image::view_t{};
205  }
206  else
207  {
208  destruct_pixels(this->_view);
209  this->deallocate();
210  this->_view = view_t{};
211  }
212  }
213  }
214 
215  public:
216  // TODO: Use noexcept(noexcept(move_assign(img, choose_pocma<allocator_type>{})))
217  // But https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52869 prevents it (fixed in GCC > 9)
218  image& operator=(image&& img) {
219  if (this != std::addressof(img))
220  // Use rebinded alloc to choose pocma
221  move_assign(img, choose_pocma<allocator_type>{});
222 
223  return *this;
224  }
225 
226  ~image()
227  {
228  destruct_pixels(_view);
229  deallocate();
230  }
231 
232  Alloc& allocator() { return _alloc; }
233  Alloc const& allocator() const { return _alloc; }
234 
235  void swap(image& img) // required by MutableContainerConcept
236  {
237  using std::swap;
238  swap(_align_in_bytes, img._align_in_bytes);
239  swap(_memory, img._memory);
240  swap(_view, img._view);
241  swap(_alloc, img._alloc);
242  swap(_allocated_bytes, img._allocated_bytes );
243  }
244 
246  // recreate
248 
249  // without Allocator
250  void recreate(const point_t& dims, std::size_t alignment = 0)
251  {
252  if (dims == _view.dimensions() && _align_in_bytes == alignment)
253  return;
254 
255  _align_in_bytes = alignment;
256 
257  if (_allocated_bytes >= total_allocated_size_in_bytes(dims))
258  {
259  destruct_pixels(_view);
260  create_view(dims, std::integral_constant<bool, IsPlanar>());
262  }
263  else
264  {
265  image tmp(dims, alignment);
266  swap(tmp);
267  }
268  }
269 
270  void recreate(x_coord_t width, y_coord_t height, std::size_t alignment = 0)
271  {
272  recreate(point_t(width, height), alignment);
273  }
274 
275  void recreate(const point_t& dims, const Pixel& p_in, std::size_t alignment = 0)
276  {
277  if (dims == _view.dimensions() && _align_in_bytes == alignment)
278  return;
279 
280  _align_in_bytes = alignment;
281 
282  if (_allocated_bytes >= total_allocated_size_in_bytes(dims))
283  {
284  destruct_pixels(_view);
285  create_view(dims, typename std::integral_constant<bool, IsPlanar>());
286  uninitialized_fill_pixels(_view, p_in);
287  }
288  else
289  {
290  image tmp(dims, p_in, alignment);
291  swap(tmp);
292  }
293  }
294 
295  void recreate( x_coord_t width, y_coord_t height, const Pixel& p_in, std::size_t alignment = 0 )
296  {
297  recreate( point_t( width, height ), p_in, alignment );
298  }
299 
300  // with Allocator
301  void recreate(const point_t& dims, std::size_t alignment, const Alloc alloc_in)
302  {
303  if (dims == _view.dimensions() && _align_in_bytes == alignment && alloc_in == _alloc)
304  return;
305 
306  _align_in_bytes = alignment;
307 
308  if (_allocated_bytes >= total_allocated_size_in_bytes(dims))
309  {
310  destruct_pixels(_view);
311  create_view(dims, std::integral_constant<bool, IsPlanar>());
313  }
314  else
315  {
316  image tmp(dims, alignment, alloc_in);
317  swap(tmp);
318  }
319  }
320 
321  void recreate(x_coord_t width, y_coord_t height, std::size_t alignment, const Alloc alloc_in)
322  {
323  recreate(point_t(width, height), alignment, alloc_in);
324  }
325 
326  void recreate(const point_t& dims, const Pixel& p_in, std::size_t alignment, const Alloc alloc_in)
327  {
328  if (dims == _view.dimensions() && _align_in_bytes == alignment && alloc_in == _alloc)
329  return;
330 
331  _align_in_bytes = alignment;
332 
333  if (_allocated_bytes >= total_allocated_size_in_bytes(dims))
334  {
335  destruct_pixels(_view);
336  create_view(dims, std::integral_constant<bool, IsPlanar>());
337  uninitialized_fill_pixels(_view, p_in);
338  }
339  else
340  {
341  image tmp(dims, p_in, alignment, alloc_in);
342  swap(tmp);
343  }
344  }
345 
346  void recreate(x_coord_t width, y_coord_t height, const Pixel& p_in, std::size_t alignment, const Alloc alloc_in )
347  {
348  recreate(point_t(width, height), p_in, alignment, alloc_in);
349  }
350 
351  view_t _view; // contains pointer to the pixels, the image size and ways to navigate pixels
352 
353  // for construction from other type
354  template <typename P2, bool IP2, typename Alloc2> friend class image;
355 private:
356  unsigned char* _memory;
357  std::size_t _align_in_bytes;
358  allocator_type _alloc;
359 
360  std::size_t _allocated_bytes;
361 
362  void allocate_and_default_construct(point_t const& dimensions)
363  {
364  try
365  {
366  allocate_(dimensions, std::integral_constant<bool, IsPlanar>());
368  }
369  catch (...) { deallocate(); throw; }
370  }
371 
372  void allocate_and_fill(const point_t& dimensions, Pixel const& p_in)
373  {
374  try
375  {
376  allocate_(dimensions, std::integral_constant<bool, IsPlanar>());
377  uninitialized_fill_pixels(_view, p_in);
378  }
379  catch(...) { deallocate(); throw; }
380  }
381 
382  template <typename View>
383  void allocate_and_copy(const point_t& dimensions, View const& v)
384  {
385  try
386  {
387  allocate_(dimensions, std::integral_constant<bool, IsPlanar>());
388  uninitialized_copy_pixels(v, _view);
389  }
390  catch(...) { deallocate(); throw; }
391  }
392 
393  void deallocate()
394  {
395  if (_memory && _allocated_bytes > 0)
396  _alloc.deallocate(_memory, _allocated_bytes);
397  }
398 
399  std::size_t is_planar_impl(
400  std::size_t const size_in_units,
401  std::size_t const channels_in_image,
402  std::true_type) const
403  {
404  return size_in_units * channels_in_image;
405  }
406 
407  std::size_t is_planar_impl(
408  std::size_t const size_in_units,
409  std::size_t const,
410  std::false_type) const
411  {
412  return size_in_units;
413  }
414 
415  std::size_t total_allocated_size_in_bytes(point_t const& dimensions) const
416  {
417  using x_iterator = typename view_t::x_iterator;
418 
419  // when value_type is a non-pixel, like int or float, num_channels< ... > doesn't work.
420  constexpr std::size_t _channels_in_image =
421  std::conditional
422  <
423  is_pixel<value_type>::value,
425  std::integral_constant<std::size_t, 1>
426  >::type::value;
427 
428  std::size_t size_in_units = is_planar_impl(
429  get_row_size_in_memunits(dimensions.x) * dimensions.y,
430  _channels_in_image,
431  std::integral_constant<bool, IsPlanar>());
432 
433  // return the size rounded up to the nearest byte
434  return ( size_in_units + byte_to_memunit< x_iterator >::value - 1 )
436  + ( _align_in_bytes > 0 ? _align_in_bytes - 1 : 0 ); // add extra padding in case we need to align the first image pixel
437  }
438 
439  std::size_t get_row_size_in_memunits(x_coord_t width) const { // number of units per row
440  std::size_t size_in_memunits = width*memunit_step(typename view_t::x_iterator());
441  if (_align_in_bytes>0) {
442  std::size_t alignment_in_memunits=_align_in_bytes*byte_to_memunit<typename view_t::x_iterator>::value;
443  return align(size_in_memunits, alignment_in_memunits);
444  }
445  return size_in_memunits;
446  }
447 
448  void allocate_(point_t const& dimensions, std::false_type)
449  {
450  // if it throws and _memory!=0 the client must deallocate _memory
451  _allocated_bytes = total_allocated_size_in_bytes(dimensions);
452  _memory=_alloc.allocate( _allocated_bytes );
453 
454  unsigned char* tmp=(_align_in_bytes>0) ? (unsigned char*)align((std::size_t)_memory,_align_in_bytes) : _memory;
455  _view=view_t(dimensions,typename view_t::locator(typename view_t::x_iterator(tmp), get_row_size_in_memunits(dimensions.x)));
456 
457  BOOST_ASSERT(_view.width() == dimensions.x);
458  BOOST_ASSERT(_view.height() == dimensions.y);
459  }
460 
461  void allocate_(point_t const& dimensions, std::true_type)
462  {
463  // if it throws and _memory!=0 the client must deallocate _memory
464  std::size_t row_size=get_row_size_in_memunits(dimensions.x);
465  std::size_t plane_size=row_size*dimensions.y;
466 
467  _allocated_bytes = total_allocated_size_in_bytes( dimensions );
468 
469  _memory = _alloc.allocate( _allocated_bytes );
470 
471  unsigned char* tmp=(_align_in_bytes>0) ? (unsigned char*)align((std::size_t)_memory,_align_in_bytes) : _memory;
472  typename view_t::x_iterator first;
473  for (std::size_t i = 0; i < num_channels<view_t>::value; ++i)
474  {
475  dynamic_at_c(first, i) = (typename channel_type<view_t>::type*)tmp;
476  memunit_advance(dynamic_at_c(first, i), static_cast<std::ptrdiff_t>(plane_size * i));
477  }
478  _view=view_t(dimensions, typename view_t::locator(first, row_size));
479 
480  BOOST_ASSERT(_view.width() == dimensions.x);
481  BOOST_ASSERT(_view.height() == dimensions.y);
482  }
483 
484  void create_view(point_t const& dims, std::true_type) // is planar
485  {
486  std::size_t row_size=get_row_size_in_memunits(dims.x);
487  std::size_t plane_size=row_size*dims.y;
488 
489  unsigned char* tmp = ( _align_in_bytes > 0 ) ? (unsigned char*) align( (std::size_t) _memory
490  ,_align_in_bytes
491  )
492  : _memory;
493  typename view_t::x_iterator first;
494 
495  for (std::size_t i = 0; i < num_channels<view_t>::value; ++i)
496  {
497  dynamic_at_c(first, i) = (typename channel_type<view_t>::type*)tmp;
498  memunit_advance(dynamic_at_c(first, i), static_cast<std::ptrdiff_t>(plane_size * i));
499  }
500 
501  _view = view_t(dims, typename view_t::locator(first, row_size));
502 
503  BOOST_ASSERT(_view.width() == dims.x);
504  BOOST_ASSERT(_view.height() == dims.y);
505  }
506 
507  void create_view(point_t const& dims, std::false_type) // is planar
508  {
509  unsigned char* tmp = ( _align_in_bytes > 0 ) ? ( unsigned char* ) align( (std::size_t) _memory
510  , _align_in_bytes
511  )
512  : _memory;
513 
514  _view = view_t( dims
515  , typename view_t::locator( typename view_t::x_iterator( tmp )
516  , get_row_size_in_memunits( dims.x )
517  )
518  );
519 
520  BOOST_ASSERT(_view.width() == dims.x);
521  BOOST_ASSERT(_view.height() == dims.y);
522  }
523 };
524 
525 template <typename Pixel, bool IsPlanar, typename Alloc>
527 {
528  im1.swap(im2);
529 }
530 
531 template <typename Pixel1, bool IsPlanar1, typename Alloc1, typename Pixel2, bool IsPlanar2, typename Alloc2>
532 bool operator==(const image<Pixel1,IsPlanar1,Alloc1>& im1,const image<Pixel2,IsPlanar2,Alloc2>& im2)
533 {
534  if ((void*)(&im1)==(void*)(&im2)) return true;
535  if (const_view(im1).dimensions()!=const_view(im2).dimensions()) return false;
536  return equal_pixels(const_view(im1),const_view(im2));
537 }
538 template <typename Pixel1, bool IsPlanar1, typename Alloc1, typename Pixel2, bool IsPlanar2, typename Alloc2>
539 bool operator!=(const image<Pixel1,IsPlanar1,Alloc1>& im1,const image<Pixel2,IsPlanar2,Alloc2>& im2) {return !(im1==im2);}
540 
544 
546 
548 template <typename Pixel, bool IsPlanar, typename Alloc> inline
549 const typename image<Pixel,IsPlanar,Alloc>::view_t& view(image<Pixel,IsPlanar,Alloc>& img) { return img._view; }
550 
552 template <typename Pixel, bool IsPlanar, typename Alloc> inline
553 const typename image<Pixel,IsPlanar,Alloc>::const_view_t const_view(const image<Pixel,IsPlanar,Alloc>& img)
554 {
555  return static_cast<const typename image<Pixel,IsPlanar,Alloc>::const_view_t>(img._view);
556 }
558 
560 // PixelBasedConcept
562 
563 template <typename Pixel, bool IsPlanar, typename Alloc>
564 struct channel_type<image<Pixel, IsPlanar, Alloc>> : channel_type<Pixel> {};
565 
566 template <typename Pixel, bool IsPlanar, typename Alloc>
567 struct color_space_type<image<Pixel, IsPlanar, Alloc>> : color_space_type<Pixel> {};
568 
569 template <typename Pixel, bool IsPlanar, typename Alloc>
570 struct channel_mapping_type<image<Pixel, IsPlanar, Alloc>> : channel_mapping_type<Pixel> {};
571 
572 template <typename Pixel, bool IsPlanar, typename Alloc>
573 struct is_planar<image<Pixel, IsPlanar, Alloc>> : std::integral_constant<bool, IsPlanar> {};
574 
575 }} // namespace boost::gil
576 
577 #endif
boost::gil::view_type_from_pixel
Returns the type of a view the pixel type, whether it operates on planar data and whether it has a st...
Definition: metafunctions.hpp:557
boost::gil::byte_to_memunit
Definition: pixel_iterator.hpp:124
boost::gil::image
container interface over image view. Models ImageConcept, PixelBasedConcept
Definition: image.hpp:42
std::swap
void swap(boost::gil::packed_channel_reference< BF, FB, NB, M > const x, R &y)
swap for packed_channel_reference
Definition: channel.hpp:529
boost::gil::equal_pixels
BOOST_FORCEINLINE bool equal_pixels(const View1 &v1, const View2 &v2)
std::equal for image views
Definition: algorithm.hpp:1105
boost::gil::uninitialized_copy_pixels
void uninitialized_copy_pixels(View1 const &view1, View2 const &view2)
std::uninitialized_copy for image views. Does not support planar heterogeneous views....
Definition: algorithm.hpp:819
boost::gil::image_view
A lightweight object that interprets memory as a 2D array of pixels. Models ImageViewConcept,...
Definition: image_view.hpp:53
boost::gil::operator!=
BOOST_FORCEINLINE bool operator!=(const point< T > &p1, const point< T > &p2)
Definition: point.hpp:137
boost::gil::copy_pixels
BOOST_FORCEINLINE void copy_pixels(const View1 &src, const View2 &dst)
std::copy for image views
Definition: algorithm.hpp:288
boost::gil::view
const image< Pixel, IsPlanar, Alloc >::view_t & view(image< Pixel, IsPlanar, Alloc > &img)
Returns the non-constant-pixel view of an image.
Definition: image.hpp:549
boost::gil::destruct_pixels
BOOST_FORCEINLINE void destruct_pixels(View const &view)
Invokes the in-place destructor on every pixel of the view.
Definition: algorithm.hpp:514
boost::gil::uninitialized_fill_pixels
void uninitialized_fill_pixels(const View &view, const Value &val)
std::uninitialized_fill for image views. Does not support planar heterogeneous views....
Definition: algorithm.hpp:583
boost::gil::default_construct_pixels
void default_construct_pixels(View const &view)
Invokes the in-place default constructor on every pixel of the (uninitialized) view....
Definition: algorithm.hpp:720
boost::gil::const_view
const image< Pixel, IsPlanar, Alloc >::const_view_t const_view(const image< Pixel, IsPlanar, Alloc > &img)
Returns the constant-pixel view of an image.
Definition: image.hpp:553
boost::gil::channel_type
Definition: color_convert.hpp:31
boost::gil::num_channels
Returns the number of channels of a pixel-based GIL construct.
Definition: locator.hpp:38
boost::gil::operator==
BOOST_FORCEINLINE bool operator==(const point< T > &p1, const point< T > &p2)
Definition: point.hpp:129