| 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 | |
| 27 | //////////////////////////////////////////////////////////////////////////////////////// |
| 28 | /// \ingroup ImageModel PixelBasedModel |
| 29 | /// \brief container interface over image view. Models ImageConcept, PixelBasedConcept |
| 30 | /// |
| 31 | /// A 2D container whose elements are pixels. It is templated over the pixel type, a boolean |
| 32 | /// indicating whether it should be planar, and an optional allocator. |
| 33 | /// |
| 34 | /// Note that its element type does not have to be a pixel. \p image can be instantiated with any Regular element, |
| 35 | /// in which case it models the weaker RandomAccess2DImageConcept and does not model PixelBasedConcept |
| 36 | /// |
| 37 | /// When recreating an image of the same or smaller size the memory will be reused if possible. |
| 38 | /// |
| 39 | //////////////////////////////////////////////////////////////////////////////////////// |
| 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(dimensions: 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(dimensions: 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(img&: 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(img&: 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(t&: rhs._memory, u: nullptr); |
| 178 | lhs._align_in_bytes = boost::exchange(t&: rhs._align_in_bytes, u: 0); |
| 179 | lhs._allocated_bytes = boost::exchange(t&: rhs._allocated_bytes, u: 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(lhs&: *this, rhs&: 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(lhs&: *this, rhs&: 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 | |
| 252 | ///////////////////// |
| 253 | // recreate |
| 254 | ///////////////////// |
| 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(dimensions: dims)) |
| 265 | { |
| 266 | destruct_pixels(_view); |
| 267 | create_view(dims, std::integral_constant<bool, IsPlanar>()); |
| 268 | default_construct_pixels(_view); |
| 269 | } |
| 270 | else |
| 271 | { |
| 272 | image tmp(dims, alignment); |
| 273 | swap(img&: 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(dimensions: 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(img&: 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(dimensions: dims)) |
| 316 | { |
| 317 | destruct_pixels(_view); |
| 318 | create_view(dims, std::integral_constant<bool, IsPlanar>()); |
| 319 | default_construct_pixels(_view); |
| 320 | } |
| 321 | else |
| 322 | { |
| 323 | image tmp(dims, alignment, alloc_in); |
| 324 | swap(img&: 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(dimensions: 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(img&: 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>()); |
| 374 | default_construct_pixels(_view); |
| 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, |
| 431 | num_channels<view_t>, |
| 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(width: 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 ) |
| 442 | / byte_to_memunit<x_iterator>::value |
| 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(val: size_in_memunits, alignment: 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(val: (std::size_t)_memory,alignment: _align_in_bytes) : _memory; |
| 467 | _view=view_t(dimensions,typename view_t::locator(typename view_t::x_iterator(tmp), get_row_size_in_memunits(width: 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(width: 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(val: (std::size_t)_memory,alignment: _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(width: dims.x); |
| 503 | std::size_t plane_size=row_size*dims.y; |
| 504 | |
| 505 | unsigned char* tmp = ( _align_in_bytes > 0 ) ? (unsigned char*) align( val: (std::size_t) _memory |
| 506 | ,alignment: _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( val: (std::size_t) _memory |
| 526 | , alignment: _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( width: 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> |
| 542 | void swap(image<Pixel, IsPlanar, Alloc>& im1,image<Pixel, IsPlanar, Alloc>& im2) |
| 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 | |
| 557 | ///@{ |
| 558 | /// \name view, const_view |
| 559 | /// \brief Get an image view from an image |
| 560 | |
| 561 | /// \ingroup ImageModel |
| 562 | |
| 563 | /// \brief Returns the non-constant-pixel view of an image |
| 564 | template <typename Pixel, bool IsPlanar, typename Alloc> |
| 565 | inline auto view(image<Pixel,IsPlanar,Alloc>& img) |
| 566 | -> typename image<Pixel,IsPlanar,Alloc>::view_t const& |
| 567 | { |
| 568 | return img._view; |
| 569 | } |
| 570 | |
| 571 | /// \brief Returns the constant-pixel view of an image |
| 572 | template <typename Pixel, bool IsPlanar, typename Alloc> |
| 573 | inline auto const_view(const image<Pixel,IsPlanar,Alloc>& img) |
| 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 | } |
| 578 | ///@} |
| 579 | |
| 580 | ///////////////////////////// |
| 581 | // PixelBasedConcept |
| 582 | ///////////////////////////// |
| 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 |
| 599 | |