Boost GIL


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