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  point_t const& 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(point_t const& 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(point_t const& 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 #ifdef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE
242  swap(_alloc, img._alloc);
243 #else
244  if constexpr (std::allocator_traits<Alloc>::propagate_on_container_swap::value)
245  swap(_alloc, img._alloc);
246  else
247  BOOST_ASSERT(_alloc == img._alloc);
248 #endif
249  swap(_allocated_bytes, img._allocated_bytes );
250  }
251 
253  // recreate
255 
256  // without Allocator
257  void recreate(point_t const& dims, std::size_t alignment = 0)
258  {
259  if (dims == _view.dimensions() && _align_in_bytes == alignment)
260  return;
261 
262  _align_in_bytes = alignment;
263 
264  if (_allocated_bytes >= total_allocated_size_in_bytes(dims))
265  {
266  destruct_pixels(_view);
267  create_view(dims, std::integral_constant<bool, IsPlanar>());
269  }
270  else
271  {
272  image tmp(dims, alignment);
273  swap(tmp);
274  }
275  }
276 
277  void recreate(x_coord_t width, y_coord_t height, std::size_t alignment = 0)
278  {
279  recreate(point_t(width, height), alignment);
280  }
281 
282  void recreate(point_t const& dims, const Pixel& p_in, std::size_t alignment = 0)
283  {
284  if (dims == _view.dimensions() && _align_in_bytes == alignment)
285  return;
286 
287  _align_in_bytes = alignment;
288 
289  if (_allocated_bytes >= total_allocated_size_in_bytes(dims))
290  {
291  destruct_pixels(_view);
292  create_view(dims, typename std::integral_constant<bool, IsPlanar>());
293  uninitialized_fill_pixels(_view, p_in);
294  }
295  else
296  {
297  image tmp(dims, p_in, alignment);
298  swap(tmp);
299  }
300  }
301 
302  void recreate( x_coord_t width, y_coord_t height, const Pixel& p_in, std::size_t alignment = 0 )
303  {
304  recreate( point_t( width, height ), p_in, alignment );
305  }
306 
307  // with Allocator
308  void recreate(point_t const& dims, std::size_t alignment, const Alloc alloc_in)
309  {
310  if (dims == _view.dimensions() && _align_in_bytes == alignment && alloc_in == _alloc)
311  return;
312 
313  _align_in_bytes = alignment;
314 
315  if (_allocated_bytes >= total_allocated_size_in_bytes(dims))
316  {
317  destruct_pixels(_view);
318  create_view(dims, std::integral_constant<bool, IsPlanar>());
320  }
321  else
322  {
323  image tmp(dims, alignment, alloc_in);
324  swap(tmp);
325  }
326  }
327 
328  void recreate(x_coord_t width, y_coord_t height, std::size_t alignment, const Alloc alloc_in)
329  {
330  recreate(point_t(width, height), alignment, alloc_in);
331  }
332 
333  void recreate(point_t const& dims, const Pixel& p_in, std::size_t alignment, const Alloc alloc_in)
334  {
335  if (dims == _view.dimensions() && _align_in_bytes == alignment && alloc_in == _alloc)
336  return;
337 
338  _align_in_bytes = alignment;
339 
340  if (_allocated_bytes >= total_allocated_size_in_bytes(dims))
341  {
342  destruct_pixels(_view);
343  create_view(dims, std::integral_constant<bool, IsPlanar>());
344  uninitialized_fill_pixels(_view, p_in);
345  }
346  else
347  {
348  image tmp(dims, p_in, alignment, alloc_in);
349  swap(tmp);
350  }
351  }
352 
353  void recreate(x_coord_t width, y_coord_t height, const Pixel& p_in, std::size_t alignment, const Alloc alloc_in )
354  {
355  recreate(point_t(width, height), p_in, alignment, alloc_in);
356  }
357 
358  view_t _view; // contains pointer to the pixels, the image size and ways to navigate pixels
359 
360  // for construction from other type
361  template <typename P2, bool IP2, typename Alloc2> friend class image;
362 private:
363  unsigned char* _memory;
364  std::size_t _align_in_bytes;
365  allocator_type _alloc;
366 
367  std::size_t _allocated_bytes;
368 
369  void allocate_and_default_construct(point_t const& dimensions)
370  {
371  try
372  {
373  allocate_(dimensions, std::integral_constant<bool, IsPlanar>());
375  }
376  catch (...) { deallocate(); throw; }
377  }
378 
379  void allocate_and_fill(point_t const& dimensions, Pixel const& p_in)
380  {
381  try
382  {
383  allocate_(dimensions, std::integral_constant<bool, IsPlanar>());
384  uninitialized_fill_pixels(_view, p_in);
385  }
386  catch(...) { deallocate(); throw; }
387  }
388 
389  template <typename View>
390  void allocate_and_copy(point_t const& dimensions, View const& v)
391  {
392  try
393  {
394  allocate_(dimensions, std::integral_constant<bool, IsPlanar>());
395  uninitialized_copy_pixels(v, _view);
396  }
397  catch(...) { deallocate(); throw; }
398  }
399 
400  void deallocate()
401  {
402  if (_memory && _allocated_bytes > 0)
403  _alloc.deallocate(_memory, _allocated_bytes);
404  }
405 
406  std::size_t is_planar_impl(
407  std::size_t const size_in_units,
408  std::size_t const channels_in_image,
409  std::true_type) const
410  {
411  return size_in_units * channels_in_image;
412  }
413 
414  std::size_t is_planar_impl(
415  std::size_t const size_in_units,
416  std::size_t const,
417  std::false_type) const
418  {
419  return size_in_units;
420  }
421 
422  std::size_t total_allocated_size_in_bytes(point_t const& dimensions) const
423  {
424  using x_iterator = typename view_t::x_iterator;
425 
426  // when value_type is a non-pixel, like int or float, num_channels< ... > doesn't work.
427  constexpr std::size_t _channels_in_image =
428  std::conditional
429  <
430  is_pixel<value_type>::value,
432  std::integral_constant<std::size_t, 1>
433  >::type::value;
434 
435  std::size_t size_in_units = is_planar_impl(
436  get_row_size_in_memunits(dimensions.x) * dimensions.y,
437  _channels_in_image,
438  std::integral_constant<bool, IsPlanar>());
439 
440  // return the size rounded up to the nearest byte
441  return ( size_in_units + byte_to_memunit< x_iterator >::value - 1 )
443  + ( _align_in_bytes > 0 ? _align_in_bytes - 1 : 0 ); // add extra padding in case we need to align the first image pixel
444  }
445 
446  std::size_t get_row_size_in_memunits(x_coord_t width) const { // number of units per row
447  std::size_t size_in_memunits = width*memunit_step(typename view_t::x_iterator());
448  if (_align_in_bytes>0) {
449  std::size_t alignment_in_memunits=_align_in_bytes*byte_to_memunit<typename view_t::x_iterator>::value;
450  return align(size_in_memunits, alignment_in_memunits);
451  }
452  return size_in_memunits;
453  }
454 
455  void allocate_(point_t const& dimensions, std::false_type)
456  {
457  // if it throws and _memory!=0 the client must deallocate _memory
458  _allocated_bytes = total_allocated_size_in_bytes(dimensions);
459  if (_allocated_bytes == 0)
460  {
461  return;
462  }
463 
464  _memory=_alloc.allocate( _allocated_bytes );
465 
466  unsigned char* tmp=(_align_in_bytes>0) ? (unsigned char*)align((std::size_t)_memory,_align_in_bytes) : _memory;
467  _view=view_t(dimensions,typename view_t::locator(typename view_t::x_iterator(tmp), get_row_size_in_memunits(dimensions.x)));
468 
469  BOOST_ASSERT(_view.width() == dimensions.x);
470  BOOST_ASSERT(_view.height() == dimensions.y);
471  }
472 
473  void allocate_(point_t const& dimensions, std::true_type)
474  {
475  // if it throws and _memory!=0 the client must deallocate _memory
476  std::size_t row_size=get_row_size_in_memunits(dimensions.x);
477  std::size_t plane_size=row_size*dimensions.y;
478 
479  _allocated_bytes = total_allocated_size_in_bytes( dimensions );
480  if (_allocated_bytes == 0)
481  {
482  return;
483  }
484 
485  _memory = _alloc.allocate( _allocated_bytes );
486 
487  unsigned char* tmp=(_align_in_bytes>0) ? (unsigned char*)align((std::size_t)_memory,_align_in_bytes) : _memory;
488  typename view_t::x_iterator first;
489  for (std::size_t i = 0; i < num_channels<view_t>::value; ++i)
490  {
491  dynamic_at_c(first, i) = (typename channel_type<view_t>::type*)tmp;
492  memunit_advance(dynamic_at_c(first, i), static_cast<std::ptrdiff_t>(plane_size * i));
493  }
494  _view=view_t(dimensions, typename view_t::locator(first, row_size));
495 
496  BOOST_ASSERT(_view.width() == dimensions.x);
497  BOOST_ASSERT(_view.height() == dimensions.y);
498  }
499 
500  void create_view(point_t const& dims, std::true_type) // is planar
501  {
502  std::size_t row_size=get_row_size_in_memunits(dims.x);
503  std::size_t plane_size=row_size*dims.y;
504 
505  unsigned char* tmp = ( _align_in_bytes > 0 ) ? (unsigned char*) align( (std::size_t) _memory
506  ,_align_in_bytes
507  )
508  : _memory;
509  typename view_t::x_iterator first;
510 
511  for (std::size_t i = 0; i < num_channels<view_t>::value; ++i)
512  {
513  dynamic_at_c(first, i) = (typename channel_type<view_t>::type*)tmp;
514  memunit_advance(dynamic_at_c(first, i), static_cast<std::ptrdiff_t>(plane_size * i));
515  }
516 
517  _view = view_t(dims, typename view_t::locator(first, row_size));
518 
519  BOOST_ASSERT(_view.width() == dims.x);
520  BOOST_ASSERT(_view.height() == dims.y);
521  }
522 
523  void create_view(point_t const& dims, std::false_type) // is planar
524  {
525  unsigned char* tmp = ( _align_in_bytes > 0 ) ? ( unsigned char* ) align( (std::size_t) _memory
526  , _align_in_bytes
527  )
528  : _memory;
529 
530  _view = view_t( dims
531  , typename view_t::locator( typename view_t::x_iterator( tmp )
532  , get_row_size_in_memunits( dims.x )
533  )
534  );
535 
536  BOOST_ASSERT(_view.width() == dims.x);
537  BOOST_ASSERT(_view.height() == dims.y);
538  }
539 };
540 
541 template <typename Pixel, bool IsPlanar, typename Alloc>
543 {
544  im1.swap(im2);
545 }
546 
547 template <typename Pixel1, bool IsPlanar1, typename Alloc1, typename Pixel2, bool IsPlanar2, typename Alloc2>
548 bool operator==(const image<Pixel1,IsPlanar1,Alloc1>& im1,const image<Pixel2,IsPlanar2,Alloc2>& im2)
549 {
550  if ((void*)(&im1)==(void*)(&im2)) return true;
551  if (const_view(im1).dimensions()!=const_view(im2).dimensions()) return false;
552  return equal_pixels(const_view(im1),const_view(im2));
553 }
554 template <typename Pixel1, bool IsPlanar1, typename Alloc1, typename Pixel2, bool IsPlanar2, typename Alloc2>
555 bool operator!=(const image<Pixel1,IsPlanar1,Alloc1>& im1,const image<Pixel2,IsPlanar2,Alloc2>& im2) {return !(im1==im2);}
556 
560 
562 
564 template <typename Pixel, bool IsPlanar, typename Alloc>
566  -> typename image<Pixel,IsPlanar,Alloc>::view_t const&
567 {
568  return img._view;
569 }
570 
572 template <typename Pixel, bool IsPlanar, typename Alloc>
574  -> typename image<Pixel,IsPlanar,Alloc>::const_view_t const
575 {
576  return static_cast<const typename image<Pixel,IsPlanar,Alloc>::const_view_t>(img._view);
577 }
579 
581 // PixelBasedConcept
583 
584 template <typename Pixel, bool IsPlanar, typename Alloc>
585 struct channel_type<image<Pixel, IsPlanar, Alloc>> : channel_type<Pixel> {};
586 
587 template <typename Pixel, bool IsPlanar, typename Alloc>
588 struct color_space_type<image<Pixel, IsPlanar, Alloc>> : color_space_type<Pixel> {};
589 
590 template <typename Pixel, bool IsPlanar, typename Alloc>
591 struct channel_mapping_type<image<Pixel, IsPlanar, Alloc>> : channel_mapping_type<Pixel> {};
592 
593 template <typename Pixel, bool IsPlanar, typename Alloc>
594 struct is_planar<image<Pixel, IsPlanar, Alloc>> : std::integral_constant<bool, IsPlanar> {};
595 
596 }} // namespace boost::gil
597 
598 #endif
A lightweight object that interprets memory as a 2D array of pixels. Models ImageViewConcept,...
Definition: image_view.hpp:54
container interface over image view. Models ImageConcept, PixelBasedConcept
Definition: image.hpp:43
auto const_view(const image< Pixel, IsPlanar, Alloc > &img) -> typename image< Pixel, IsPlanar, Alloc >::const_view_t const
Returns the constant-pixel view of an image.
Definition: image.hpp:573
auto view(image< Pixel, IsPlanar, Alloc > &img) -> typename image< Pixel, IsPlanar, Alloc >::view_t const &
Returns the non-constant-pixel view of an image.
Definition: image.hpp:565
BOOST_FORCEINLINE void copy_pixels(const View1 &src, const View2 &dst)
std::copy for image views
Definition: algorithm.hpp:292
void default_construct_pixels(View const &view)
Invokes the in-place default constructor on every pixel of the (uninitialized) view....
Definition: algorithm.hpp:724
BOOST_FORCEINLINE void destruct_pixels(View const &view)
Invokes the in-place destructor on every pixel of the view.
Definition: algorithm.hpp:518
BOOST_FORCEINLINE bool equal_pixels(const View1 &v1, const View2 &v2)
std::equal for image views
Definition: algorithm.hpp:1109
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:823
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:587
void swap(boost::gil::packed_channel_reference< BF, FB, NB, M > const x, R &y)
swap for packed_channel_reference
Definition: channel.hpp:583
defined(BOOST_NO_CXX17_HDR_MEMORY_RESOURCE)
Definition: algorithm.hpp:36
Definition: pixel_iterator.hpp:124
Definition: color_convert.hpp:31
Returns the number of channels of a pixel-based GIL construct.
Definition: pixel.hpp:54
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