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
25namespace boost { namespace gil {
26
40
41template< typename Pixel, bool IsPlanar, typename Alloc>
42class image
43{
44public:
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>
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;
362private:
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>());
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
541template <typename Pixel, bool IsPlanar, typename Alloc>
543{
544 im1.swap(im2);
545}
546
547template <typename Pixel1, bool IsPlanar1, typename Alloc1, typename Pixel2, bool IsPlanar2, typename Alloc2>
548bool 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}
554template <typename Pixel1, bool IsPlanar1, typename Alloc1, typename Pixel2, bool IsPlanar2, typename Alloc2>
555bool operator!=(const image<Pixel1,IsPlanar1,Alloc1>& im1,const image<Pixel2,IsPlanar2,Alloc2>& im2) {return !(im1==im2);}
556
560
562
564template <typename Pixel, bool IsPlanar, typename Alloc>
566 -> typename image<Pixel,IsPlanar,Alloc>::view_t const&
567{
568 return img._view;
569}
570
572template <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
584template <typename Pixel, bool IsPlanar, typename Alloc>
585struct channel_type<image<Pixel, IsPlanar, Alloc>> : channel_type<Pixel> {};
586
587template <typename Pixel, bool IsPlanar, typename Alloc>
588struct color_space_type<image<Pixel, IsPlanar, Alloc>> : color_space_type<Pixel> {};
589
590template <typename Pixel, bool IsPlanar, typename Alloc>
591struct channel_mapping_type<image<Pixel, IsPlanar, Alloc>> : channel_mapping_type<Pixel> {};
592
593template <typename Pixel, bool IsPlanar, typename Alloc>
594struct 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 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
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
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
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