diff --git a/RELEASES.md b/RELEASES.md index fc12f0c8d2..ca2574ae64 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [1.80.0] - 2022-08-10 +NOTICE: We are planning BREAKING switch to C++17 as minimum required C++ language version in one or two releases after Boost 1.80 ([Discussion #676](https://github.com/boostorg/gil/discussions/676)) + ### Added - Added `image` constructor from compatible view ([PR #520](https://github.com/boostorg/gil/pull/520)) - Added inverse function for affine `matrix3x2` ([PR #527](https://github.com/boostorg/gil/pull/527)) @@ -28,6 +30,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). support at least C++14 are considered unsupported as of now. - BREAKING: `any_color_converted_view()` is deprecated and will be removed in the next release. Use `color_converted_view()` instead, which provides the same feature. +- BREAKING: `apply_operation` for `any_image` is deprecated and will be removed in the next release. + Use `variant2::visit` instead, which provides the same feature. - documentation: Display that GIL is a header-only library - Moved numeric extension to core ([PR #573](https://github.com/boostorg/gil/pull/573)) - Added support for C++17's `` ([PR #636](https://github.com/boostorg/gil/pull/636)). @@ -327,10 +331,10 @@ linked PDF documents with detailed changes. ### Changed - Updated the design guide and tutorial, updated syntax of concepts to the latest concepts proposal. -- In `image`, `image_view`, `any_image`, `any_image_view`: +- In `image`, `image_view`, `any_image`, `any_image_view`: There are no longer global functions `get_width()`, `get_height()`, `get_dimensions()`, `num_channels()`. Use methods `width()`, `height()`, `dimensions()` instead. -- In models of pixel, pixel iterator, pixel locator, image view and image: +- In models of pixel, pixel iterator, pixel locator, image view and image: There used to be different ways of getting to a pixel, channel, color space, etc. of an image view, pixel, locator, iterator and image (e.g. traits, member typedefs). Now all pixel-based GIL constructs (pixels, pixel iterators, locators, image views and images) model @@ -338,7 +342,7 @@ linked PDF documents with detailed changes. and for homogeneous constructs we also have: `channel_type`. To get the pixel type or pixel reference/const reference type of an image, image view, locator and pixel, use member typedefs `value_type`, `reference` and `const_reference`. -- In `locator`, `image`, `image_view`, `any_image` and `any_image_view`: +- In `locator`, `image`, `image_view`, `any_image` and `any_image_view`: Removed `dynamic_x_step_t`, `dynamic_y_step_t`, `dynamic_xy_step_t` and `dynamic_xy_step_transposed_t` as member typedefs of locators and image views. Instead, there are separate concepts `HasDynamicXStepTypeConcept`, `HasDynamicYStepTypeConcept`, diff --git a/doc/design/dynamic_image.rst b/doc/design/dynamic_image.rst index f43c5eaffd..546bfa4d9f 100644 --- a/doc/design/dynamic_image.rst +++ b/doc/design/dynamic_image.rst @@ -103,7 +103,7 @@ GIL ``any_image_view`` and ``any_image`` are subclasses of ``variant``: y_coord_t height() const; }; -Operations are invoked on variants via ``apply_operation`` passing a +Operations are invoked on variants via ``variant2::visit`` passing a function object to perform the operation. The code for every allowed type in the variant is instantiated and the appropriate instantiation is selected via a switch statement. Since image view algorithms @@ -129,7 +129,7 @@ pixels. There is no "any_pixel" or "any_pixel_iterator" in GIL. Such constructs could be provided via the ``variant`` mechanism, but doing so would result in inefficient algorithms, since the type resolution would have to be performed per pixel. Image-level algorithms should be -implemented via ``apply_operation``. That said, many common operations +implemented via ``variant2::visit``. That said, many common operations are shared between the static and dynamic types. In addition, all of the image view transformations and many STL-like image view algorithms have overloads operating on ``any_image_view``, as illustrated with @@ -180,14 +180,13 @@ implemented: template typename dynamic_xy_step_type::type rotated180_view(const View& src) { ... } - namespace detail - { + namespace detail { // the function, wrapped inside a function object template struct rotated180_view_fn { typedef Result result_type; template result_type operator()(const View& src) const - { + { return result_type(rotated180_view(src)); } }; @@ -195,10 +194,11 @@ implemented: // overloading of the function using variant. Takes and returns run-time bound view. // The returned view has a dynamic step - template inline // Models MPL Random Access Container of models of ImageViewConcept - typename dynamic_xy_step_type >::type rotated180_view(const any_image_view& src) + template inline + typename dynamic_xy_step_type>::type rotated180_view(const any_image_view& src) { - return apply_operation(src,detail::rotated180_view_fn >::type>()); + using result_view_t = typename dynamic_xy_step_type>::type; + return variant2::visit(detail::rotated180_view_fn(), src); } Variants should be used with caution (especially algorithms that take diff --git a/doc/tutorial/gradient.rst b/doc/tutorial/gradient.rst index bbcb931e19..cc59d22e3e 100644 --- a/doc/tutorial/gradient.rst +++ b/doc/tutorial/gradient.rst @@ -870,22 +870,22 @@ source view: }; The second step is to provide an overload of ``x_luminosity_gradient`` that -takes image view variant and calls GIL's ``apply_operation`` passing it the +takes image view variant and calls ``variant2::visit`` passing it the function object: .. code-block:: cpp - template - void x_luminosity_gradient(const any_image_view& src, const DstView& dst) + template + void x_luminosity_gradient(const any_image_view& src, const DstView& dst) { - apply_operation(src, x_gradient_obj(dst)); + variant2::visit(x_gradient_obj(dst), src); } -``any_image_view`` is the image view variant. It is -templated over ``SrcViews``, an enumeration of all possible view types +``any_image_view`` is the image view variant. It is +templated over ``SrcViews...``, an enumeration of all possible view types the variant can take. ``src`` contains inside an index of the currently instantiated type, as well as a block of memory containing -the instance. ``apply_operation`` goes through a switch statement +the instance. ``variant2::visit`` goes through a switch statement over the index, each case of which casts the memory to the correct view type and invokes the function object with it. Invoking an algorithm on a variant has the overhead of one switch diff --git a/example/hough_transform_circle.cpp b/example/hough_transform_circle.cpp index 1e9d25798d..44a739d62d 100644 --- a/example/hough_transform_circle.cpp +++ b/example/hough_transform_circle.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -33,13 +34,8 @@ int main() const std::ptrdiff_t circle_radius = 16; const gil::point_t circle_center = {64, 64}; - const auto rasterizer = gil::midpoint_circle_rasterizer{}; - std::vector circle_points(rasterizer.point_count(circle_radius)); - rasterizer(circle_radius, circle_center, circle_points.begin()); - for (const auto& point : circle_points) - { - input(point) = std::numeric_limits::max(); - } + const auto rasterizer = gil::midpoint_circle_rasterizer{circle_center, circle_radius}; + gil::apply_rasterizer(input, rasterizer, gil::gray8_pixel_t{255}); const auto radius_parameter = gil::hough_parameter::from_step_count(circle_radius, 3, 3); diff --git a/example/rasterizer_circle.cpp b/example/rasterizer_circle.cpp index 2c56de3199..ae85e86447 100644 --- a/example/rasterizer_circle.cpp +++ b/example/rasterizer_circle.cpp @@ -8,15 +8,13 @@ #include #include -#include -#include -#include +#include namespace gil = boost::gil; // Demonstrates the use of a rasterizer to generate an image of a circle -// The various rasterizers available are defined in include/boost/gil/rasterization/circle.hpp, -// include/boost/gil/rasterization/ellipse.hpp and include/boost/gil/rasterization/line.hpp +// The various rasterizers available are defined in include/boost/gil/extension/rasterization/circle.hpp, +// include/boost/gil/extension/rasterization/ellipse.hpp and include/boost/gil/extension/rasterization/line.hpp // This example uses a trigonometric rasterizer; GIL also offers the rasterizer midpoint_circle_rasterizer, // which implements the Midpoint algorithm. // See also: @@ -29,14 +27,10 @@ int main() gil::gray8_image_t buffer_image(size, size); auto buffer = gil::view(buffer_image); + const gil::point_t center = {128, 128}; const std::ptrdiff_t radius = 64; - const auto rasterizer = gil::trigonometric_circle_rasterizer{}; - std::vector circle_points(rasterizer.point_count(radius)); - rasterizer(radius, {128, 128}, circle_points.begin()); - for (const auto& point : circle_points) - { - buffer(point) = std::numeric_limits::max(); - } + const auto rasterizer = gil::trigonometric_circle_rasterizer{center, radius}; + gil::apply_rasterizer(buffer, rasterizer, gil::gray8_pixel_t{255}); gil::write_view("circle.png", buffer, gil::png_tag{}); } diff --git a/example/rasterizer_ellipse.cpp b/example/rasterizer_ellipse.cpp index 295fe0533c..91ff538930 100644 --- a/example/rasterizer_ellipse.cpp +++ b/example/rasterizer_ellipse.cpp @@ -6,14 +6,14 @@ // http://www.boost.org/LICENSE_1_0.txt) // -#include #include +#include namespace gil = boost::gil; // Demonstrates the use of a rasterizer to generate an image of an ellipse -// The various rasterizers available are defined in include/boost/gil/rasterization/circle.hpp, -// include/boost/gil/rasterization/ellipse.hpp and include/boost/gil/rasterization/line.hpp +// The various rasterizers available are defined in include/boost/gil/extension/rasterization/circle.hpp, +// include/boost/gil/extension/rasterization/ellipse.hpp and include/boost/gil/extension/rasterization/line.hpp // The rasterizer used is a generalisation of the midpoint algorithm often used for drawing circle. // This examples also shows how to create images with various pixel depth, as well as the behaviour // in case of the rasterization of a curve that doesn't fit in a view. @@ -25,29 +25,28 @@ namespace gil = boost::gil; int main() { // Syntax for usage :- - // auto rasterizer = gil::midpoint_elliptical_rasterizer{}; - // rasterizer(img_view, colour, center, semi-axes_length); + // auto rasterizer = gil::midpoint_ellipse_rasterizer{}; + // rasterizer(img_view, pixel, center, semi-axes_length); // Where // img_view : gil view of the image on which ellipse is to be drawn. - // colour : Vector containing channel intensity values for img_view. Number of colours - // provided must be equal to the number of channels present in img_view. - // center : Array containing positive integer x co-ordinate and y co-ordinate of the center + // pixel : Pixel value for the elliptical curve to be drawn. Pixel's type must be compatible to the + // pixel type of the image view. + // center : Point containing positive integer x co-ordinate and y co-ordinate of the center // respectively. - // semi-axes_length : Array containing positive integer lengths of horizontal semi-axis + // semi-axes_length : Point containing positive integer lengths of horizontal semi-axis // and vertical semi-axis respectively. gil::gray8_image_t gray_buffer_image(256, 256); - auto gray_elliptical_rasterizer = gil::midpoint_elliptical_rasterizer{}; - gray_elliptical_rasterizer(view(gray_buffer_image), {128}, {128, 128}, {100, 50}); + auto gray_ellipse_rasterizer = gil::midpoint_ellipse_rasterizer{{128, 128}, {100, 50}}; + gil::apply_rasterizer(view(gray_buffer_image), gray_ellipse_rasterizer, gil::gray8_pixel_t{128}); gil::rgb8_image_t rgb_buffer_image(256, 256); - auto rgb_elliptical_rasterizer = gil::midpoint_elliptical_rasterizer{}; - rgb_elliptical_rasterizer(view(rgb_buffer_image), {0, 0, 255}, {128, 128}, {50, 100}); + auto rgb_ellipse_rasterizer = gil::midpoint_ellipse_rasterizer{{128, 128}, {50, 100}}; + gil::apply_rasterizer(view(rgb_buffer_image), rgb_ellipse_rasterizer, gil::rgb8_pixel_t{0, 0, 255}); gil::rgb8_image_t rgb_buffer_image_out_of_bound(256, 256); - auto rgb_elliptical_rasterizer_out_of_bound = gil::midpoint_elliptical_rasterizer{}; - rgb_elliptical_rasterizer_out_of_bound(view(rgb_buffer_image_out_of_bound), {255, 0, 0}, - {100, 100}, {160, 160}); + auto rgb_ellipse_rasterizer_out_of_bound = gil::midpoint_ellipse_rasterizer{{100, 100}, {160, 160}}; + apply_rasterizer(view(rgb_buffer_image_out_of_bound), rgb_ellipse_rasterizer_out_of_bound, gil::rgb8_pixel_t{255, 0, 0}); gil::write_view("rasterized_ellipse_gray.jpg", view(gray_buffer_image), gil::jpeg_tag{}); gil::write_view("rasterized_ellipse_rgb.jpg", view(rgb_buffer_image), gil::jpeg_tag{}); diff --git a/example/rasterizer_line.cpp b/example/rasterizer_line.cpp index 7b2aec35b1..6c217023b5 100644 --- a/example/rasterizer_line.cpp +++ b/example/rasterizer_line.cpp @@ -8,15 +8,13 @@ #include #include - -#include -#include +#include namespace gil = boost::gil; // Demonstrates the use of a rasterizer to generate an image of a line -// The various rasterizers available are defined in include/boost/gil/rasterization/circle.hpp, -// include/boost/gil/rasterization/ellipse.hpp and include/boost/gil/rasterization/line.hpp +// The various rasterizers available are defined in include/boost/gil/extension/rasterization/circle.hpp, +// include/boost/gil/extension/rasterization/ellipse.hpp and include/boost/gil/extension/rasterization/line.hpp // The rasterizer used implements the Bresenham's line algorithm. // Multiple images are created, all of the same size, but with areas of different sizes passed to the rasterizer, resulting in different lines. // See also: @@ -28,18 +26,11 @@ const std::ptrdiff_t size = 256; void line_bresenham(std::ptrdiff_t width, std::ptrdiff_t height, const std::string& output_name) { - const auto rasterizer = gil::bresenham_line_rasterizer{}; - std::vector line_points(rasterizer.point_count(width, height)); + const auto rasterizer = gil::bresenham_line_rasterizer{{0, 0}, {width - 1, height - 1}}; gil::gray8_image_t image(size, size); auto view = gil::view(image); - - rasterizer({0, 0}, {width - 1, height - 1}, line_points.begin()); - for (const auto& point : line_points) - { - view(point) = std::numeric_limits::max(); - } - + gil::apply_rasterizer(view, rasterizer, gil::gray8_pixel_t{255}); gil::write_view(output_name, view, gil::png_tag{}); } diff --git a/include/boost/gil/extension/dynamic_image/algorithm.hpp b/include/boost/gil/extension/dynamic_image/algorithm.hpp index 75a4aedb7f..174be8f31e 100644 --- a/include/boost/gil/extension/dynamic_image/algorithm.hpp +++ b/include/boost/gil/extension/dynamic_image/algorithm.hpp @@ -1,5 +1,6 @@ // // Copyright 2005-2007 Adobe Systems Incorporated +// Copyright 2022 Marco Langer // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at @@ -12,7 +13,10 @@ #include +#include + #include +#include //////////////////////////////////////////////////////////////////////////////////////// /// \file @@ -43,31 +47,31 @@ struct equal_pixels_fn : binary_operation_obj /// \tparam Types Model Boost.MP11-compatible list of models of ImageViewConcept /// \tparam View Model MutableImageViewConcept template -bool equal_pixels(any_image_view const& src, View const& dst) +auto equal_pixels(any_image_view const& src, View const& dst) -> bool { - return apply_operation( - src, - std::bind(detail::equal_pixels_fn(), std::placeholders::_1, dst)); + return variant2::visit( + std::bind(detail::equal_pixels_fn(), std::placeholders::_1, dst), + src); } /// \ingroup ImageViewSTLAlgorithmsEqualPixels /// \tparam View Model ImageViewConcept /// \tparam Types Model Boost.MP11-compatible list of models of MutableImageViewConcept template -bool equal_pixels(View const& src, any_image_view const& dst) +auto equal_pixels(View const& src, any_image_view const& dst) -> bool { - return apply_operation( - dst, - std::bind(detail::equal_pixels_fn(), src, std::placeholders::_1)); + return variant2::visit( + std::bind(detail::equal_pixels_fn(), src, std::placeholders::_1), + dst); } /// \ingroup ImageViewSTLAlgorithmsEqualPixels /// \tparam Types1 Model Boost.MP11-compatible list of models of ImageViewConcept /// \tparam Types2 Model Boost.MP11-compatible list of models of MutableImageViewConcept template -bool equal_pixels(any_image_view const& src, any_image_view const& dst) +auto equal_pixels(any_image_view const& src, any_image_view const& dst) -> bool { - return apply_operation(src, dst, detail::equal_pixels_fn()); + return variant2::visit(detail::equal_pixels_fn(), src, dst); } namespace detail { @@ -90,7 +94,7 @@ struct copy_pixels_fn : public binary_operation_obj template void copy_pixels(any_image_view const& src, View const& dst) { - apply_operation(src, std::bind(detail::copy_pixels_fn(), std::placeholders::_1, dst)); + variant2::visit(std::bind(detail::copy_pixels_fn(), std::placeholders::_1, dst), src); } /// \ingroup ImageViewSTLAlgorithmsCopyPixels @@ -99,7 +103,7 @@ void copy_pixels(any_image_view const& src, View const& dst) template void copy_pixels(View const& src, any_image_view const& dst) { - apply_operation(dst, std::bind(detail::copy_pixels_fn(), src, std::placeholders::_1)); + variant2::visit(std::bind(detail::copy_pixels_fn(), src, std::placeholders::_1), dst); } /// \ingroup ImageViewSTLAlgorithmsCopyPixels @@ -108,7 +112,7 @@ void copy_pixels(View const& src, any_image_view const& dst) template void copy_pixels(any_image_view const& src, any_image_view const& dst) { - apply_operation(src, dst, detail::copy_pixels_fn()); + variant2::visit(detail::copy_pixels_fn(), src, dst); } //forward declaration for default_color_converter (see full definition in color_convert.hpp) @@ -122,7 +126,7 @@ template void copy_and_convert_pixels(any_image_view const& src, View const& dst, CC cc) { using cc_fn = detail::copy_and_convert_pixels_fn; - apply_operation(src, std::bind(cc_fn{cc}, std::placeholders::_1, dst)); + variant2::visit(std::bind(cc_fn{cc}, std::placeholders::_1, dst), src); } /// \ingroup ImageViewSTLAlgorithmsCopyAndConvertPixels @@ -132,7 +136,7 @@ template void copy_and_convert_pixels(any_image_view const& src, View const& dst) { using cc_fn = detail::copy_and_convert_pixels_fn; - apply_operation(src, std::bind(cc_fn{}, std::placeholders::_1, dst)); + variant2::visit(std::bind(cc_fn{}, std::placeholders::_1, dst), src); } /// \ingroup ImageViewSTLAlgorithmsCopyAndConvertPixels @@ -143,7 +147,7 @@ template void copy_and_convert_pixels(View const& src, any_image_view const& dst, CC cc) { using cc_fn = detail::copy_and_convert_pixels_fn; - apply_operation(dst, std::bind(cc_fn{cc}, src, std::placeholders::_1)); + variant2::visit(std::bind(cc_fn{cc}, src, std::placeholders::_1), dst); } /// \ingroup ImageViewSTLAlgorithmsCopyAndConvertPixels @@ -153,7 +157,7 @@ template void copy_and_convert_pixels(View const& src, any_image_view const& dst) { using cc_fn = detail::copy_and_convert_pixels_fn; - apply_operation(dst, std::bind(cc_fn{}, src, std::placeholders::_1)); + variant2::visit(std::bind(cc_fn{}, src, std::placeholders::_1), dst); } /// \ingroup ImageViewSTLAlgorithmsCopyAndConvertPixels @@ -165,7 +169,7 @@ void copy_and_convert_pixels( any_image_view const& src, any_image_view const& dst, CC cc) { - apply_operation(src, dst, detail::copy_and_convert_pixels_fn(cc)); + variant2::visit(detail::copy_and_convert_pixels_fn(cc), src, dst); } /// \ingroup ImageViewSTLAlgorithmsCopyAndConvertPixels @@ -176,8 +180,8 @@ void copy_and_convert_pixels( any_image_view const& src, any_image_view const& dst) { - apply_operation(src, dst, - detail::copy_and_convert_pixels_fn()); + variant2::visit( + detail::copy_and_convert_pixels_fn(), src, dst); } namespace detail { @@ -186,7 +190,7 @@ template struct fill_pixels_fn1 { template - static void apply(V const &src, Value const &val) { fill_pixels(src, val); } + static void apply(V const& src, Value const& val) { fill_pixels(src, val); } }; // copy_pixels invoked on incompatible images @@ -194,7 +198,7 @@ template <> struct fill_pixels_fn1 { template - static void apply(V const &, Value const &) { throw std::bad_cast();} + static void apply(V const&, Value const&) { throw std::bad_cast();} }; template @@ -227,7 +231,7 @@ struct fill_pixels_fn template void fill_pixels(any_image_view const& view, Value const& val) { - apply_operation(view, detail::fill_pixels_fn(val)); + variant2::visit(detail::fill_pixels_fn(val), view); } namespace detail { @@ -236,7 +240,7 @@ template struct for_each_pixel_fn { for_each_pixel_fn(F&& fun) : fun_(std::move(fun)) {} - + template auto operator()(View const& view) -> F { diff --git a/include/boost/gil/extension/dynamic_image/any_image.hpp b/include/boost/gil/extension/dynamic_image/any_image.hpp index 457ba4ace9..590a193570 100644 --- a/include/boost/gil/extension/dynamic_image/any_image.hpp +++ b/include/boost/gil/extension/dynamic_image/any_image.hpp @@ -10,7 +10,6 @@ #define BOOST_GIL_EXTENSION_DYNAMIC_IMAGE_ANY_IMAGE_HPP #include -#include #include #include @@ -121,7 +120,7 @@ class any_image : public variant2::variant void recreate(const point_t& dims, unsigned alignment=1) { - apply_operation(*this, detail::recreate_image_fnobj(dims, alignment)); + variant2::visit(detail::recreate_image_fnobj(dims, alignment), *this); } void recreate(x_coord_t width, y_coord_t height, unsigned alignment=1) @@ -131,12 +130,12 @@ class any_image : public variant2::variant std::size_t num_channels() const { - return apply_operation(*this, detail::any_type_get_num_channels()); + return variant2::visit(detail::any_type_get_num_channels(), *this); } point_t dimensions() const { - return apply_operation(*this, detail::any_type_get_dimensions()); + return variant2::visit(detail::any_type_get_dimensions(), *this); } x_coord_t width() const { return dimensions().x; } @@ -156,7 +155,7 @@ BOOST_FORCEINLINE auto view(any_image& img) -> typename any_image::view_t { using view_t = typename any_image::view_t; - return apply_operation(img, detail::any_image_get_view()); + return variant2::visit(detail::any_image_get_view(), img); } /// \brief Returns the constant-pixel view of any image. The returned view is any view. @@ -166,7 +165,7 @@ BOOST_FORCEINLINE auto const_view(any_image const& img) -> typename any_image::const_view_t { using view_t = typename any_image::const_view_t; - return apply_operation(img, detail::any_image_get_const_view()); + return variant2::visit(detail::any_image_get_const_view(), img); } ///@} diff --git a/include/boost/gil/extension/dynamic_image/any_image_view.hpp b/include/boost/gil/extension/dynamic_image/any_image_view.hpp index 2a414e25ad..5b9c94fbe1 100644 --- a/include/boost/gil/extension/dynamic_image/any_image_view.hpp +++ b/include/boost/gil/extension/dynamic_image/any_image_view.hpp @@ -68,7 +68,7 @@ struct any_type_get_size /// Other requirements, such as access to the pixels, would be inefficient to provide. Thus \p any_image_view does not fully model ImageViewConcept. /// However, many algorithms provide overloads taking runtime specified views and thus in many cases \p any_image_view can be used in places taking a view. /// -/// To perform an algorithm on any_image_view, put the algorithm in a function object and invoke it by calling \p apply_operation(runtime_view, algorithm_fn); +/// To perform an algorithm on any_image_view, put the algorithm in a function object and invoke it by calling \p variant2::visit(algorithm_fn, runtime_view); //////////////////////////////////////////////////////////////////////////////////////// template @@ -105,9 +105,9 @@ class any_image_view : public variant2::variant return *this; } - std::size_t num_channels() const { return apply_operation(*this, detail::any_type_get_num_channels()); } - point_t dimensions() const { return apply_operation(*this, detail::any_type_get_dimensions()); } - size_type size() const { return apply_operation(*this, detail::any_type_get_size()); } + std::size_t num_channels() const { return variant2::visit(detail::any_type_get_num_channels(), *this); } + point_t dimensions() const { return variant2::visit(detail::any_type_get_dimensions(), *this); } + size_type size() const { return variant2::visit(detail::any_type_get_size(), *this); } x_coord_t width() const { return dimensions().x; } y_coord_t height() const { return dimensions().y; } }; diff --git a/include/boost/gil/extension/dynamic_image/apply_operation.hpp b/include/boost/gil/extension/dynamic_image/apply_operation.hpp index 2a5b542f5b..48ff1ac3b1 100644 --- a/include/boost/gil/extension/dynamic_image/apply_operation.hpp +++ b/include/boost/gil/extension/dynamic_image/apply_operation.hpp @@ -15,6 +15,7 @@ namespace boost { namespace gil { /// \ingroup Variant /// \brief Applies the visitor op to the variants template +[[deprecated("Use variant2::visit instead.")]] BOOST_FORCEINLINE auto apply_operation(Variant1&& arg1, Visitor&& op) #if defined(BOOST_NO_CXX14_DECLTYPE_AUTO) || defined(BOOST_NO_CXX11_DECLTYPE_N3276) @@ -27,6 +28,7 @@ auto apply_operation(Variant1&& arg1, Visitor&& op) /// \ingroup Variant /// \brief Applies the visitor op to the variants template +[[deprecated("Use variant2::visit instead.")]] BOOST_FORCEINLINE auto apply_operation(Variant1&& arg1, Variant2&& arg2, Visitor&& op) #if defined(BOOST_NO_CXX14_DECLTYPE_AUTO) || defined(BOOST_NO_CXX11_DECLTYPE_N3276) diff --git a/include/boost/gil/extension/dynamic_image/image_view_factory.hpp b/include/boost/gil/extension/dynamic_image/image_view_factory.hpp index 7afb6b12ec..6667a828fa 100644 --- a/include/boost/gil/extension/dynamic_image/image_view_factory.hpp +++ b/include/boost/gil/extension/dynamic_image/image_view_factory.hpp @@ -174,7 +174,7 @@ auto flipped_up_down_view(any_image_view const& src) -> typename dynamic_y_step_type>::type { using result_view_t = typename dynamic_y_step_type>::type; - return apply_operation(src, detail::flipped_up_down_view_fn()); + return variant2::visit(detail::flipped_up_down_view_fn(), src); } /// \ingroup ImageViewTransformationsFlipLR @@ -185,39 +185,40 @@ auto flipped_left_right_view(any_image_view const& src) -> typename dynamic_x_step_type>::type { using result_view_t = typename dynamic_x_step_type>::type; - return apply_operation(src, detail::flipped_left_right_view_fn()); + return variant2::visit(detail::flipped_left_right_view_fn(), src); } /// \ingroup ImageViewTransformationsTransposed /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline -auto transposed_view(const any_image_view& src) +auto transposed_view(any_image_view const& src) -> typename dynamic_xy_step_transposed_type>::type { using result_view_t = typename dynamic_xy_step_transposed_type>::type; - return apply_operation(src, detail::tranposed_view_fn()); + return variant2::visit(detail::tranposed_view_fn(), src); } /// \ingroup ImageViewTransformations90CW /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline -auto rotated90cw_view(const any_image_view& src) +auto rotated90cw_view(any_image_view const& src) -> typename dynamic_xy_step_transposed_type>::type { using result_view_t = typename dynamic_xy_step_transposed_type>::type; - return apply_operation(src,detail::rotated90cw_view_fn()); + return variant2::visit(detail::rotated90cw_view_fn(), src); } /// \ingroup ImageViewTransformations90CCW /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline -auto rotated90ccw_view(const any_image_view& src) +auto rotated90ccw_view(any_image_view const& src) -> typename dynamic_xy_step_transposed_type>::type { - return apply_operation(src,detail::rotated90ccw_view_fn>::type>()); + using result_view_t = typename dynamic_xy_step_transposed_type>::type; + return variant2::visit(detail::rotated90ccw_view_fn(), src); } /// \ingroup ImageViewTransformations180 @@ -227,8 +228,8 @@ inline auto rotated180_view(any_image_view const& src) -> typename dynamic_xy_step_type>::type { - using step_type = typename dynamic_xy_step_type>::type; - return apply_operation(src, detail::rotated180_view_fn()); + using result_view_t = typename dynamic_xy_step_type>::type; + return variant2::visit(detail::rotated180_view_fn(), src); } /// \ingroup ImageViewTransformationsSubimage @@ -242,7 +243,7 @@ auto subimage_view( -> any_image_view { using subimage_view_fn = detail::subimage_view_fn>; - return apply_operation(src, subimage_view_fn(topleft, dimensions)); + return variant2::visit(subimage_view_fn(topleft, dimensions), src); } /// \ingroup ImageViewTransformationsSubimage @@ -255,7 +256,7 @@ auto subimage_view( -> any_image_view { using subimage_view_fn = detail::subimage_view_fn>; - return apply_operation(src, subimage_view_fn(point_t(x_min, y_min),point_t(width, height))); + return variant2::visit(subimage_view_fn(point_t(x_min, y_min),point_t(width, height)), src); } /// \ingroup ImageViewTransformationsSubsampled @@ -267,7 +268,7 @@ auto subsampled_view(any_image_view const& src, point_t const& step) { using step_type = typename dynamic_xy_step_type>::type; using subsampled_view = detail::subsampled_view_fn; - return apply_operation(src, subsampled_view(step)); + return variant2::visit(subsampled_view(step), src); } /// \ingroup ImageViewTransformationsSubsampled @@ -279,7 +280,7 @@ auto subsampled_view(any_image_view const& src, std::ptrdiff_t x_step, { using step_type = typename dynamic_xy_step_type>::type; using subsampled_view_fn = detail::subsampled_view_fn; - return apply_operation(src, subsampled_view_fn(point_t(x_step, y_step))); + return variant2::visit(subsampled_view_fn(point_t(x_step, y_step)), src); } namespace detail { @@ -304,11 +305,11 @@ struct nth_channel_view_type> /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline -auto nth_channel_view(const any_image_view& src, int n) +auto nth_channel_view(any_image_view const& src, int n) -> typename nth_channel_view_type>::type { using result_view_t = typename nth_channel_view_type>::type; - return apply_operation(src,detail::nth_channel_view_fn(n)); + return variant2::visit(detail::nth_channel_view_fn(n), src); } namespace detail { @@ -347,11 +348,11 @@ struct color_converted_view_type,DstP,CC> /// \tparam Views Models Boost.MP11-compatible list of models of ImageViewConcept template inline -auto color_converted_view(const any_image_view& src, CC) +auto color_converted_view(any_image_view const& src, CC) -> typename color_converted_view_type, DstP, CC>::type { using cc_view_t = typename color_converted_view_type, DstP, CC>::type; - return apply_operation(src, detail::color_converted_view_fn()); + return variant2::visit(detail::color_converted_view_fn(), src); } /// \ingroup ImageViewTransformationsColorConvert @@ -371,7 +372,7 @@ auto color_converted_view(any_image_view const& src) -> typename color_converted_view_type, DstP>::type { using cc_view_t = typename color_converted_view_type, DstP>::type; - return apply_operation(src, detail::color_converted_view_fn()); + return variant2::visit(detail::color_converted_view_fn(), src); } /// \ingroup ImageViewTransformationsColorConvert diff --git a/include/boost/gil/extension/image_processing/hough_transform.hpp b/include/boost/gil/extension/image_processing/hough_transform.hpp index f0dada5fea..db63fbe837 100644 --- a/include/boost/gil/extension/image_processing/hough_transform.hpp +++ b/include/boost/gil/extension/image_processing/hough_transform.hpp @@ -86,14 +86,15 @@ void hough_circle_transform_brute(const ImageView& input, const hough_parameter radius_parameter, const hough_parameter x_parameter, const hough_parameter& y_parameter, - ForwardIterator d_first, Rasterizer rasterizer) + ForwardIterator d_first, Rasterizer) { for (std::size_t radius_index = 0; radius_index < radius_parameter.step_count; ++radius_index) { const auto radius = radius_parameter.start_point + radius_parameter.step_size * static_cast(radius_index); - std::vector circle_points(rasterizer.point_count(radius)); - rasterizer(radius, {0, 0}, circle_points.begin()); + Rasterizer rasterizer{point_t{}, radius}; + std::vector circle_points(rasterizer.point_count()); + rasterizer(circle_points.begin()); // sort by scanline to improve cache coherence for row major images std::sort(circle_points.begin(), circle_points.end(), [](const point_t& lhs, const point_t& rhs) { return lhs.y < rhs.y; }); diff --git a/include/boost/gil/extension/io/bmp/detail/read.hpp b/include/boost/gil/extension/io/bmp/detail/read.hpp index b898662f6e..fa6ab2db98 100644 --- a/include/boost/gil/extension/io/bmp/detail/read.hpp +++ b/include/boost/gil/extension/io/bmp/detail/read.hpp @@ -725,8 +725,8 @@ class dynamic_image_reader< Device , parent_t > op( this ); - apply_operation( view( images ) - , op + variant2::visit( op + ,view( images ) ); } } diff --git a/include/boost/gil/extension/io/bmp/detail/write.hpp b/include/boost/gil/extension/io/bmp/detail/write.hpp index d82e1f2844..d75a6bdbbe 100644 --- a/include/boost/gil/extension/io/bmp/detail/write.hpp +++ b/include/boost/gil/extension/io/bmp/detail/write.hpp @@ -201,8 +201,8 @@ class dynamic_image_writer< Device , parent_t > op( this ); - apply_operation( views - , op + variant2::visit( op + ,views ); } }; diff --git a/include/boost/gil/extension/io/jpeg/detail/read.hpp b/include/boost/gil/extension/io/jpeg/detail/read.hpp index 7ef0b5717b..57992c07e6 100644 --- a/include/boost/gil/extension/io/jpeg/detail/read.hpp +++ b/include/boost/gil/extension/io/jpeg/detail/read.hpp @@ -300,8 +300,8 @@ class dynamic_image_reader< Device , parent_t > op( this ); - apply_operation( view( images ) - , op + variant2::visit( op + , view( images ) ); } } diff --git a/include/boost/gil/extension/io/jpeg/detail/write.hpp b/include/boost/gil/extension/io/jpeg/detail/write.hpp index e3705616cf..e4534b3d58 100644 --- a/include/boost/gil/extension/io/jpeg/detail/write.hpp +++ b/include/boost/gil/extension/io/jpeg/detail/write.hpp @@ -167,7 +167,7 @@ class dynamic_image_writer< Device , parent_t > op( this ); - apply_operation( views, op ); + variant2::visit( op, views ); } }; diff --git a/include/boost/gil/extension/io/png/detail/read.hpp b/include/boost/gil/extension/io/png/detail/read.hpp index 62463d7557..089caf9fe6 100644 --- a/include/boost/gil/extension/io/png/detail/read.hpp +++ b/include/boost/gil/extension/io/png/detail/read.hpp @@ -432,8 +432,8 @@ class dynamic_image_reader< Device , parent_t > op( this ); - apply_operation( view( images ) - , op + variant2::visit( op + , view( images ) ); } } diff --git a/include/boost/gil/extension/io/png/detail/write.hpp b/include/boost/gil/extension/io/png/detail/write.hpp index f5cdf1cb67..5dcb6b698b 100644 --- a/include/boost/gil/extension/io/png/detail/write.hpp +++ b/include/boost/gil/extension/io/png/detail/write.hpp @@ -248,7 +248,7 @@ class dynamic_image_writer< Device , parent_t > op( this ); - apply_operation( views, op ); + variant2::visit( op, views ); } }; diff --git a/include/boost/gil/extension/io/pnm/detail/read.hpp b/include/boost/gil/extension/io/pnm/detail/read.hpp index 1ea53c7844..d25866bc86 100644 --- a/include/boost/gil/extension/io/pnm/detail/read.hpp +++ b/include/boost/gil/extension/io/pnm/detail/read.hpp @@ -437,8 +437,8 @@ class dynamic_image_reader< Device , parent_t > op( this ); - apply_operation( view( images ) - , op + variant2::visit( op + ,view( images ) ); } } diff --git a/include/boost/gil/extension/io/pnm/detail/write.hpp b/include/boost/gil/extension/io/pnm/detail/write.hpp index c6598ace45..c10d1f7955 100644 --- a/include/boost/gil/extension/io/pnm/detail/write.hpp +++ b/include/boost/gil/extension/io/pnm/detail/write.hpp @@ -233,7 +233,7 @@ class dynamic_image_writer< Device , parent_t > op( this ); - apply_operation( views, op ); + variant2::visit( op, views ); } }; diff --git a/include/boost/gil/extension/io/raw/detail/read.hpp b/include/boost/gil/extension/io/raw/detail/read.hpp index af2e7eea2f..c6fdb134f4 100644 --- a/include/boost/gil/extension/io/raw/detail/read.hpp +++ b/include/boost/gil/extension/io/raw/detail/read.hpp @@ -224,8 +224,8 @@ class dynamic_image_reader< Device , parent_t > op( this ); - apply_operation( view( images ) - , op + variant2::visit( op + , view( images ) ); } } diff --git a/include/boost/gil/extension/io/targa/detail/read.hpp b/include/boost/gil/extension/io/targa/detail/read.hpp index 8eec904db1..035d2d0b61 100644 --- a/include/boost/gil/extension/io/targa/detail/read.hpp +++ b/include/boost/gil/extension/io/targa/detail/read.hpp @@ -383,8 +383,8 @@ class dynamic_image_reader< Device , parent_t > op( this ); - apply_operation( view( images ) - , op + variant2::visit( op + ,view( images ) ); } } diff --git a/include/boost/gil/extension/io/targa/detail/write.hpp b/include/boost/gil/extension/io/targa/detail/write.hpp index 23d96bedad..cff5fb337e 100644 --- a/include/boost/gil/extension/io/targa/detail/write.hpp +++ b/include/boost/gil/extension/io/targa/detail/write.hpp @@ -170,7 +170,7 @@ class dynamic_image_writer< Device , parent_t > op( this ); - apply_operation( views, op ); + variant2::visit( op, views ); } }; diff --git a/include/boost/gil/extension/io/tiff/detail/device.hpp b/include/boost/gil/extension/io/tiff/detail/device.hpp index 26eb9cdbcf..a765a4ca60 100644 --- a/include/boost/gil/extension/io/tiff/detail/device.hpp +++ b/include/boost/gil/extension/io/tiff/detail/device.hpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -192,7 +193,7 @@ class tiff_device_base { io_error_if( TIFFReadScanline( _tiff_file.get() , reinterpret_cast< tdata_t >( &buffer.front() ) - , (uint32) row + , static_cast( row ) , plane ) == -1 , "Read error." ); @@ -205,7 +206,7 @@ class tiff_device_base { io_error_if( TIFFReadScanline( _tiff_file.get() , reinterpret_cast< tdata_t >( buffer ) - , (uint32) row + , static_cast( row ) , plane ) == -1 , "Read error." ); @@ -221,9 +222,9 @@ class tiff_device_base { if( TIFFReadTile( _tiff_file.get() , reinterpret_cast< tdata_t >( &buffer.front() ) - , (uint32) x - , (uint32) y - , (uint32) z + , static_cast< std::uint32_t >( x ) + , static_cast< std::uint32_t >( y ) + , static_cast< std::uint32_t >( z ) , plane ) == -1 ) { @@ -234,9 +235,9 @@ class tiff_device_base } template< typename Buffer > - void write_scaline( Buffer& buffer - , uint32 row - , tsample_t plane + void write_scaline( Buffer& buffer + , std::uint32_t row + , tsample_t plane ) { io_error_if( TIFFWriteScanline( _tiff_file.get() @@ -248,9 +249,9 @@ class tiff_device_base ); } - void write_scaline( byte_t* buffer - , uint32 row - , tsample_t plane + void write_scaline( byte_t* buffer + , std::uint32_t row + , tsample_t plane ) { io_error_if( TIFFWriteScanline( _tiff_file.get() @@ -263,11 +264,11 @@ class tiff_device_base } template< typename Buffer > - void write_tile( Buffer& buffer - , uint32 x - , uint32 y - , uint32 z - , tsample_t plane + void write_tile( Buffer& buffer + , std::uint32_t x + , std::uint32_t y + , std::uint32_t z + , tsample_t plane ) { if( TIFFWriteTile( _tiff_file.get() @@ -300,8 +301,8 @@ class tiff_device_base ) { bool result = true; - uint32 tw = static_cast< uint32 >( width ); - uint32 th = static_cast< uint32 >( height ); + std::uint32_t tw = static_cast< std::uint32_t >( width ); + std::uint32_t th = static_cast< std::uint32_t >( height ); TIFFDefaultTileSize( _tiff_file.get() , &tw diff --git a/include/boost/gil/extension/io/tiff/detail/read.hpp b/include/boost/gil/extension/io/tiff/detail/read.hpp index ccd695ff53..e0f40f412c 100644 --- a/include/boost/gil/extension/io/tiff/detail/read.hpp +++ b/include/boost/gil/extension/io/tiff/detail/read.hpp @@ -766,8 +766,8 @@ class dynamic_image_reader< Device , parent_t > op( this ); - apply_operation( view( images ) - , op + variant2::visit( op + , view( images ) ); } } diff --git a/include/boost/gil/extension/io/tiff/detail/write.hpp b/include/boost/gil/extension/io/tiff/detail/write.hpp index cd83a26ff6..cb10c1e9ee 100644 --- a/include/boost/gil/extension/io/tiff/detail/write.hpp +++ b/include/boost/gil/extension/io/tiff/detail/write.hpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -183,7 +184,7 @@ class writer< Device this->_io_dev.write_scaline( row - , (uint32) y + , static_cast( y ) , 0 ); @@ -211,7 +212,7 @@ class writer< Device this->_io_dev.write_scaline( row - , (uint32) y + , static_cast( y ) , 0 ); @@ -273,7 +274,7 @@ class writer< Device ); this->_io_dev.write_scaline( row_addr - , (uint32) y + , static_cast( y ) , 0 ); @@ -393,8 +394,8 @@ class writer< Device } this->_io_dev.write_tile( row - , static_cast< uint32 >( j ) - , static_cast< uint32 >( i ) + , static_cast< std::uint32_t >( j ) + , static_cast< std::uint32_t >( i ) , 0 , 0 ); @@ -437,7 +438,7 @@ class dynamic_image_writer< Device , parent_t > op( this ); - apply_operation( views, op ); + variant2::visit( op, views ); } }; diff --git a/include/boost/gil/extension/numeric/resample.hpp b/include/boost/gil/extension/numeric/resample.hpp index 16f859113f..aafcf2bd1d 100644 --- a/include/boost/gil/extension/numeric/resample.hpp +++ b/include/boost/gil/extension/numeric/resample.hpp @@ -73,10 +73,10 @@ namespace detail { template void resample_pixels(const any_image_view& src, const V2& dst, const MapFn& dst_to_src, Sampler sampler=Sampler()) { - apply_operation(src, std::bind( + variant2::visit(std::bind( detail::resample_pixels_fn(dst_to_src, sampler), std::placeholders::_1, - dst)); + dst), src); } /// \brief resample_pixels when the destination is run-time specified @@ -86,10 +86,10 @@ template void resample_pixels(const V1& src, const any_image_view& dst, const MapFn& dst_to_src, Sampler sampler=Sampler()) { using namespace std::placeholders; - apply_operation(dst, std::bind( + variant2::visit(std::bind( detail::resample_pixels_fn(dst_to_src, sampler), src, - std::placeholders::_1)); + std::placeholders::_1), dst); } /// \brief resample_pixels when both the source and the destination are run-time specified @@ -97,7 +97,7 @@ void resample_pixels(const V1& src, const any_image_view& dst, const /// \ingroup ImageAlgorithms template void resample_pixels(const any_image_view& src, const any_image_view& dst, const MapFn& dst_to_src, Sampler sampler=Sampler()) { - apply_operation(src,dst,detail::resample_pixels_fn(dst_to_src,sampler)); + variant2::visit(detail::resample_pixels_fn(dst_to_src,sampler), src, dst); } /////////////////////////////////////////////////////////////////////////// diff --git a/include/boost/gil/extension/rasterization/apply_rasterizer.hpp b/include/boost/gil/extension/rasterization/apply_rasterizer.hpp new file mode 100644 index 0000000000..4eaffc35c7 --- /dev/null +++ b/include/boost/gil/extension/rasterization/apply_rasterizer.hpp @@ -0,0 +1,28 @@ +#ifndef BOOST_GIL_EXTENSION_RASTERIZATION_APPLY_RASTERIZER +#define BOOST_GIL_EXTENSION_RASTERIZATION_APPLY_RASTERIZER + +namespace boost { namespace gil { + +namespace detail { + +template +struct apply_rasterizer_op +{ + void operator()( + View const& view, Rasterizer const& rasterizer, Pixel const& pixel); +}; + +} // namespace detail + +template +void apply_rasterizer( + View const& view, Rasterizer const& rasterizer, Pixel const& pixel) +{ + using tag_t = typename Rasterizer::type; + detail::apply_rasterizer_op{}( + view, rasterizer, pixel); +} + +}} // namespace boost::gil + +#endif diff --git a/include/boost/gil/extension/rasterization/circle.hpp b/include/boost/gil/extension/rasterization/circle.hpp index 52629741fa..2fe9a35c21 100644 --- a/include/boost/gil/extension/rasterization/circle.hpp +++ b/include/boost/gil/extension/rasterization/circle.hpp @@ -9,13 +9,17 @@ #define BOOST_GIL_EXTENSION_RASTERIZATION_CIRCLE_HPP #include +#include #include #include #include +#include namespace boost { namespace gil { +struct circle_rasterizer_t{}; + /// \defgroup CircleRasterization /// \ingroup Rasterization /// \brief Circle rasterization algorithms @@ -32,39 +36,49 @@ namespace boost { namespace gil { /// produces quite round like shapes. struct trigonometric_circle_rasterizer { + using type = circle_rasterizer_t; + + /// \brief Creates a trigonometric circle rasterizer + /// \param center_point - Point containing positive integer x co-ordinate and y co-ordinate of the + /// center respectively. + /// \param circle_radius - Radius of the circle + trigonometric_circle_rasterizer(point_t center_point, std::ptrdiff_t circle_radius) + : center(center_point), radius(circle_radius) + {} + /// \brief Calculates minimum angle step that is distinguishable when walking on circle /// /// It is important to not have disconnected circle and to not compute unnecessarily, /// thus the result of this function is used when rendering. - double minimum_angle_step(std::ptrdiff_t radius) const noexcept + double minimum_angle_step() const noexcept { const auto diameter = radius * 2 - 1; return std::atan2(1.0, diameter); } /// \brief Calculate the amount of points that rasterizer will output - std::ptrdiff_t point_count(std::ptrdiff_t radius) const noexcept + std::ptrdiff_t point_count() const noexcept { return 8 * static_cast( - std::round(detail::pi / 4 / minimum_angle_step(radius)) + 1); + std::round(detail::pi / 4 / minimum_angle_step()) + 1); } /// \brief perform rasterization and output into d_first - template - void operator()(std::ptrdiff_t radius, point_t offset, RandomAccessIterator d_first) const + template + void operator()(OutputIterator d_first) const { const double minimum_angle_step = std::atan2(1.0, radius); - auto translate_mirror_points = [&d_first, offset](point_t p) { - *d_first++ = point_t{offset.x + p.x, offset.y + p.y}; - *d_first++ = point_t{offset.x + p.x, offset.y - p.y}; - *d_first++ = point_t{offset.x - p.x, offset.y + p.y}; - *d_first++ = point_t{offset.x - p.x, offset.y - p.y}; - *d_first++ = point_t{offset.x + p.y, offset.y + p.x}; - *d_first++ = point_t{offset.x + p.y, offset.y - p.x}; - *d_first++ = point_t{offset.x - p.y, offset.y + p.x}; - *d_first++ = point_t{offset.x - p.y, offset.y - p.x}; + auto translate_mirror_points = [this, &d_first](point_t p) { + *d_first++ = point_t{center.x + p.x, center.y + p.y}; + *d_first++ = point_t{center.x + p.x, center.y - p.y}; + *d_first++ = point_t{center.x - p.x, center.y + p.y}; + *d_first++ = point_t{center.x - p.x, center.y - p.y}; + *d_first++ = point_t{center.x + p.y, center.y + p.x}; + *d_first++ = point_t{center.x + p.y, center.y - p.x}; + *d_first++ = point_t{center.x - p.y, center.y + p.x}; + *d_first++ = point_t{center.x - p.y, center.y - p.x}; }; - const std::ptrdiff_t iteration_count = point_count(radius) / 8; + const std::ptrdiff_t iteration_count = point_count() / 8; double angle = 0; // do note that + 1 was done inside count estimation, thus <= is not needed, only < for (std::ptrdiff_t i = 0; i < iteration_count; ++i, angle += minimum_angle_step) @@ -74,6 +88,9 @@ struct trigonometric_circle_rasterizer translate_mirror_points({x, y}); } } + + point_t center; + std::ptrdiff_t radius; }; /// \ingroup CircleRasterization @@ -84,8 +101,18 @@ struct trigonometric_circle_rasterizer /// https://en.wikipedia.org/wiki/Midpoint_circle_algorithm struct midpoint_circle_rasterizer { + using type = circle_rasterizer_t; + + /// \brief Creates a midpoint circle rasterizer + /// \param center_point - Point containing positive integer x co-ordinate and y co-ordinate of the + /// center respectively. + /// \param circle_radius - Radius of the circle + midpoint_circle_rasterizer(point_t center_point, std::ptrdiff_t circle_radius) + : center(center_point), radius(circle_radius) + {} + /// \brief Calculate the amount of points that rasterizer will output - std::ptrdiff_t point_count(std::ptrdiff_t radius) const noexcept + std::ptrdiff_t point_count() const noexcept { // the reason for pulling 8 out is so that when the expression radius * cos(45 degrees) // is used, it would yield the same result as here @@ -95,20 +122,20 @@ struct midpoint_circle_rasterizer } /// \brief perform rasterization and output into d_first - template - void operator()(std::ptrdiff_t radius, point_t offset, RAIterator d_first) const + template + void operator()(OutputIterator d_first) const { - auto translate_mirror_points = [&d_first, offset](point_t p) { - *d_first++ = point_t{offset.x + p.x, offset.y + p.y}; - *d_first++ = point_t{offset.x + p.x, offset.y - p.y}; - *d_first++ = point_t{offset.x - p.x, offset.y + p.y}; - *d_first++ = point_t{offset.x - p.x, offset.y - p.y}; - *d_first++ = point_t{offset.x + p.y, offset.y + p.x}; - *d_first++ = point_t{offset.x + p.y, offset.y - p.x}; - *d_first++ = point_t{offset.x - p.y, offset.y + p.x}; - *d_first++ = point_t{offset.x - p.y, offset.y - p.x}; + auto translate_mirror_points = [this, &d_first](point_t p) { + *d_first++ = point_t{center.x + p.x, center.y + p.y}; + *d_first++ = point_t{center.x + p.x, center.y - p.y}; + *d_first++ = point_t{center.x - p.x, center.y + p.y}; + *d_first++ = point_t{center.x - p.x, center.y - p.y}; + *d_first++ = point_t{center.x + p.y, center.y + p.x}; + *d_first++ = point_t{center.x + p.y, center.y - p.x}; + *d_first++ = point_t{center.x - p.y, center.y + p.x}; + *d_first++ = point_t{center.x - p.y, center.y - p.x}; }; - std::ptrdiff_t iteration_distance = point_count(radius) / 8; + std::ptrdiff_t iteration_distance = point_count() / 8; std::ptrdiff_t y_current = radius; std::ptrdiff_t r_squared = radius * radius; translate_mirror_points({0, y_current}); @@ -122,8 +149,31 @@ struct midpoint_circle_rasterizer translate_mirror_points({x, y_current}); } } + + point_t center; + std::ptrdiff_t radius; }; +namespace detail { + +template +struct apply_rasterizer_op +{ + void operator()( + View const& view, Rasterizer const& rasterizer, Pixel const& pixel) + { + std::vector trajectory(rasterizer.point_count()); + rasterizer(std::begin(trajectory)); + + for (auto const& point : trajectory) + { + view(point) = pixel; + } + } +}; + +} //namespace detail + }} // namespace boost::gil #endif diff --git a/include/boost/gil/extension/rasterization/ellipse.hpp b/include/boost/gil/extension/rasterization/ellipse.hpp index 6ea6ea46cb..091189c788 100644 --- a/include/boost/gil/extension/rasterization/ellipse.hpp +++ b/include/boost/gil/extension/rasterization/ellipse.hpp @@ -8,12 +8,18 @@ #ifndef BOOST_GIL_EXTENSION_RASTERIZATION_ELLIPSE_HPP #define BOOST_GIL_EXTENSION_RASTERIZATION_ELLIPSE_HPP +#include +#include +#include + #include +#include #include -#include namespace boost { namespace gil { +struct ellipse_rasterizer_t{}; + /// \defgroup EllipseRasterization /// \ingroup Rasterization /// \brief Ellipse rasterization algorithms. @@ -22,21 +28,32 @@ namespace boost { namespace gil { /// \brief Performs ellipse rasterization using midpoint algorithm. Initially, program considers /// origin as center of ellipse and obtains first quadrant trajectory points. After that, /// it shifts origin to provided co-ordinates of center and then draws the curve. -struct midpoint_elliptical_rasterizer +struct midpoint_ellipse_rasterizer { + using type = ellipse_rasterizer_t; + + /// \brief Creates a midpoint ellipse rasterizer + /// \param center - Point containing positive integer x co-ordinate and y co-ordinate of the + /// center respectively. + /// \param semi_axes - Point containing positive integer lengths of horizontal semi-axis + /// and vertical semi-axis respectively. + midpoint_ellipse_rasterizer(point center_point, + point semi_axes_values) + : center(center_point) + , semi_axes(semi_axes_values) + {} + /// \brief Returns a vector containing co-ordinates of first quadrant points which lie on /// rasterizer trajectory of the ellipse. - /// \param semi_axes - Array containing half of lengths of horizontal and vertical axis - /// respectively. - auto obtain_trajectory(std::array const semi_axes) - -> std::vector> + auto obtain_trajectory() const + -> std::vector { // Citation : J. Van Aken, "An Efficient Ellipse-Drawing Algorithm" in IEEE Computer // Graphics and Applications, vol. 4, no. 09, pp. 24-35, 1984. // doi: 10.1109/MCG.1984.275994 // keywords: {null} // url: https://doi.ieeecomputersociety.org/10.1109/MCG.1984.275994 - std::vector> trajectory_points; + std::vector trajectory_points; std::ptrdiff_t x = semi_axes[0], y = 0; // Variables declared on following lines are temporary variables used for improving @@ -96,58 +113,63 @@ struct midpoint_elliptical_rasterizer /// obtained from their reflection along major axis, minor axis and line passing through /// center with slope -1 using colours provided by user. /// \param view - Gil view of image on which the elliptical curve is to be drawn. - /// \param colour - Constant vector specifying colour intensity values for all channels present - /// in 'view'. - /// \param center - Constant array specifying co-ordinates of center of ellipse to be drawn. + /// \param pixel - Pixel value for the elliptical curve to be drawn. /// \param trajectory_points - Constant vector specifying pixel co-ordinates of points lying /// on rasterizer trajectory. /// \tparam View - Type of input image view. - template - void draw_curve(View view, std::vector const colour, - std::array const center, - std::vector> const trajectory_points) + /// \tparam Pixel - Type of pixel. Must be compatible to the pixel type of the image view + template + void draw_curve(View& view, Pixel const& pixel, + std::vector const& trajectory_points) const { - for (int i = 0, colour_index = 0; i < static_cast(view.num_channels()); - ++i, ++colour_index) + using pixel_t = typename View::value_type; + if (!pixels_are_compatible()) { - for (std::array point : trajectory_points) + throw std::runtime_error("Pixel type of the given image is not compatible to the " + "type of the provided pixel."); + } + + // mutable center copy + point center2(center); + --center2[0], --center2[1]; // For converting center co-ordinate values to zero based indexing. + for (point_t pnt : trajectory_points) + { + std::array co_ords = {center2[0] + pnt[0], + center2[0] - pnt[0], center2[1] + pnt[1], center2[1] - pnt[1] + }; + bool validity[4]{}; + if (co_ords[0] < view.width()) + { + validity[0] = true; + } + if (co_ords[1] >= 0 && co_ords[1] < view.width()) + { + validity[1] = true; + } + if (co_ords[2] < view.height()) + { + validity[2] = true; + } + if (co_ords[3] >= 0 && co_ords[3] < view.height()) + { + validity[3] = true; + } + + if (validity[0] && validity[2]) + { + view(co_ords[0], co_ords[2]) = pixel; + } + if (validity[1] && validity[2]) + { + view(co_ords[1], co_ords[2]) = pixel; + } + if (validity[1] && validity[3]) + { + view(co_ords[1], co_ords[3]) = pixel; + } + if (validity[0] && validity[3]) { - std::array co_ords = {center[0] + point[0], - center[0] - point[0], center[1] + point[1], center[1] - point[1] - }; - bool validity[4] = {0}; - if (co_ords[0] < view.width()) - { - validity[0] = 1; - } - if (co_ords[1] >= 0 && co_ords[1] < view.width()) - { - validity[1] = 1; - } - if (co_ords[2] < view.height()) - { - validity[2] = 1; - } - if (co_ords[3] >= 0 && co_ords[3] < view.height()) - { - validity[3] = 1; - } - if (validity[0] && validity[2]) - { - nth_channel_view(view, i)(co_ords[0], co_ords[2])[0] = colour[colour_index]; - } - if (validity[1] && validity[2]) - { - nth_channel_view(view, i)(co_ords[1], co_ords[2])[0] = colour[colour_index]; - } - if (validity[1] && validity[3]) - { - nth_channel_view(view, i)(co_ords[1], co_ords[3])[0] = colour[colour_index]; - } - if (validity[0] && validity[3]) - { - nth_channel_view(view, i)(co_ords[0], co_ords[3])[0] = colour[colour_index]; - } + view(co_ords[0], co_ords[3]) = pixel; } } } @@ -155,40 +177,33 @@ struct midpoint_elliptical_rasterizer /// \brief Calls the function 'obtain_trajectory' and then passes obtained trajectory points /// in the function 'draw_curve' for drawing the desired ellipse. /// \param view - Gil view of image on which the elliptical curve is to be drawn. - /// \param colour - Constant vector specifying colour intensity values for all channels present - /// in 'view'. - /// \param center - Array containing positive integer x co-ordinate and y co-ordinate of the - /// center respectively. - /// \param semi_axes - Array containing positive integer lengths of horizontal semi-axis - /// and vertical semi-axis respectively. + /// \param pixel - Pixel value for the elliptical curve to be drawn. /// \tparam View - Type of input image view. - template - void operator()(View view, std::vector const colour, - std::array center, std::array const semi_axes) + /// \tparam Pixel - Type of pixel. Must be compatible to the pixel type of the image view + template + void operator()(View& view, Pixel const& pixel) const { - --center[0], --center[1]; // For converting center co-ordinate values to zero based indexing. - if (colour.size() != view.num_channels()) - { - throw std::length_error("Number of channels in given image is not equal to the " - "number of colours provided."); - } - if (center[0] + semi_axes[0] >= view.width() || center[1] + semi_axes[1] >= view.height() - || static_cast(center[0] - semi_axes[0]) < 0 - || static_cast(center[0] - semi_axes[0]) >= view.width() - || static_cast(center[1] - semi_axes[1]) < 0 - || static_cast(center[1] - semi_axes[1]) >= view.height()) - { - std::cout << "Image can't contain whole curve.\n" - "However, it will contain those parts of curve which can fit inside it.\n" - "Note : Image width = " << view.width() << " and Image height = " << - view.height() << "\n"; - } - std::vector> trajectory_points = - obtain_trajectory(semi_axes); - draw_curve(view, colour, center, trajectory_points); + draw_curve(view, obtain_trajectory()); } + + point center; + point semi_axes; }; +namespace detail { + +template +struct apply_rasterizer_op +{ + void operator()( + View const& view, Rasterizer const& rasterizer, Pixel const& pixel) + { + rasterizer(view, pixel); + } +}; + +} //namespace detail + }} // namespace boost::gil #endif diff --git a/include/boost/gil/extension/rasterization/line.hpp b/include/boost/gil/extension/rasterization/line.hpp index 53dd842540..3014cabcc7 100644 --- a/include/boost/gil/extension/rasterization/line.hpp +++ b/include/boost/gil/extension/rasterization/line.hpp @@ -8,13 +8,18 @@ #ifndef BOOST_GIL_EXTENSION_RASTERIZATION_LINE_HPP #define BOOST_GIL_EXTENSION_RASTERIZATION_LINE_HPP +#include #include #include #include +#include +#include namespace boost { namespace gil { +struct line_rasterizer_t{}; + /// \defgroup Rasterization /// \brief A set of functions to rasterize shapes /// @@ -40,21 +45,26 @@ namespace boost { namespace gil { /// https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm#:~:text=Bresenham's%20line%20algorithm%20is%20a,straight%20line%20between%20two%20points. struct bresenham_line_rasterizer { - constexpr std::ptrdiff_t point_count(std::ptrdiff_t width, std::ptrdiff_t height) const noexcept - { - return width > height ? width : height; - } + using type = line_rasterizer_t; + + bresenham_line_rasterizer(point_t start, point_t end) + : start_point(start), end_point(end) + {} - std::ptrdiff_t point_count(point_t start, point_t end) const noexcept + std::ptrdiff_t point_count() const noexcept { - const auto abs_width = std::abs(end.x - start.x) + 1; - const auto abs_height = std::abs(end.y - start.y) + 1; - return point_count(abs_width, abs_height); + const auto abs_width = std::abs(end_point.x - start_point.x) + 1; + const auto abs_height = std::abs(end_point.y - start_point.y) + 1; + return abs_width > abs_height ? abs_width : abs_height; } - template - void operator()(point_t start, point_t end, RandomAccessIterator d_first) const + template + void operator()(OutputIterator d_first) const { + // mutable stack copies + point_t start = start_point; + point_t end = end_point; + if (start == end) { // put the point and immediately exit, as later on division by zero will @@ -92,8 +102,31 @@ struct bresenham_line_rasterizer } *d_first++ = needs_flip ? point_t{end.y, end.x} : end; } + + point_t start_point; + point_t end_point; +}; + +namespace detail { + +template +struct apply_rasterizer_op +{ + void operator()( + View const& view, Rasterizer const& rasterizer, Pixel const& pixel) + { + std::vector trajectory(rasterizer.point_count()); + rasterizer(std::begin(trajectory)); + + for (auto const& point : trajectory) + { + view(point) = pixel; + } + } }; +} //namespace detail + }} // namespace boost::gil #endif diff --git a/include/boost/gil/extension/toolbox/color_spaces/hsl.hpp b/include/boost/gil/extension/toolbox/color_spaces/hsl.hpp index 61e9b50052..c07ba19b8a 100644 --- a/include/boost/gil/extension/toolbox/color_spaces/hsl.hpp +++ b/include/boost/gil/extension/toolbox/color_spaces/hsl.hpp @@ -75,6 +75,7 @@ struct default_color_converter_impl< rgb_t, hsl_t > { float32_t diff = max_color - min_color; + float32_t sum = max_color + min_color; // lightness calculation @@ -84,13 +85,11 @@ struct default_color_converter_impl< rgb_t, hsl_t > if( lightness < 0.5f ) { - saturation = diff - / ( min_color + max_color ); + saturation = diff / ( sum ); } else { - saturation = ( max_color - min_color ) - / ( 2.f - diff ); + saturation = diff / ( 2.f - sum ); } @@ -115,7 +114,7 @@ struct default_color_converter_impl< rgb_t, hsl_t > { // max_color is blue hue = 4.f - + ( temp_red - temp_blue ) + + ( temp_red - temp_green ) / diff; } diff --git a/include/boost/gil/histogram.hpp b/include/boost/gil/histogram.hpp index fbf3020255..8d78caff1c 100644 --- a/include/boost/gil/histogram.hpp +++ b/include/boost/gil/histogram.hpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include diff --git a/include/boost/gil/io/detail/dynamic.hpp b/include/boost/gil/io/detail/dynamic.hpp index 4df5fef2e9..239f370068 100644 --- a/include/boost/gil/io/detail/dynamic.hpp +++ b/include/boost/gil/io/detail/dynamic.hpp @@ -41,7 +41,7 @@ struct construct_matched_t<0> static bool apply(any_image&, Pred) { return false; } }; -// A function object that can be passed to apply_operation. +// A function object that can be passed to variant2::visit. // Given a predicate IsSupported taking a view type and returning an boolean integral coonstant, // calls the apply method of OpClass with the view if the given view IsSupported, or throws an exception otherwise template diff --git a/include/boost/gil/io/detail/filesystem.hpp b/include/boost/gil/io/detail/filesystem.hpp index 2e4c702a75..7ad18cec30 100644 --- a/include/boost/gil/io/detail/filesystem.hpp +++ b/include/boost/gil/io/detail/filesystem.hpp @@ -10,8 +10,8 @@ #include -#if !defined(BOOST_GIL_IO_USE_BOOST_FILESYSTEM) -#if !defined(BOOST_NO_CXX17_HDR_FILESYSTEM) || defined(__cpp_lib_filesystem) +#if !defined(BOOST_GIL_IO_USE_BOOST_FILESYSTEM) && !defined(BOOST_NO_CXX17_HDR_FILESYSTEM) +#if defined(__cpp_lib_filesystem) #include #define BOOST_GIL_IO_USE_STD_FILESYSTEM #elif defined(__cpp_lib_experimental_filesystem) @@ -19,7 +19,7 @@ #define BOOST_GIL_IO_USE_STD_FILESYSTEM #define BOOST_GIL_IO_USE_STD_EXPERIMENTAL_FILESYSTEM #endif -#endif // !BOOST_GIL_IO_USE_BOOST_FILESYSTEM +#endif // !BOOST_GIL_IO_USE_BOOST_FILESYSTEM && !BOOST_NO_CXX17_HDR_FILESYSTEM #if !defined(BOOST_GIL_IO_USE_STD_FILESYSTEM) // Disable warning: conversion to 'std::atomic::__integral_type {aka int}' from 'long int' may alter its value diff --git a/test/Jamfile b/test/Jamfile index 9583ac6ff0..6acff3979c 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -26,10 +26,6 @@ local msvc-cxxs-with-experimental-fs = 11 14 ; project : requirements - . - # TODO: Enable concepts check for all, not just test/core - #BOOST_GIL_USE_CONCEPT_CHECK=1 - msvc,$(msvc-cxxs-with-experimental-fs):_SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING=1 [ requires cxx11_constexpr cxx11_defaulted_functions @@ -37,6 +33,11 @@ project cxx11_trailing_result_types # implies decltype and auto cxx11_variadic_templates ] + . + # TODO: Enable concepts check for all, not just test/core + #BOOST_GIL_USE_CONCEPT_CHECK=1 + msvc,$(msvc-cxxs-with-experimental-fs):_SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING=1 + msvc:/bigobj ; variant gil_ubsan_integer diff --git a/test/extension/dynamic_image/Jamfile b/test/extension/dynamic_image/Jamfile index 4d78b7554b..3f9e57bbbd 100644 --- a/test/extension/dynamic_image/Jamfile +++ b/test/extension/dynamic_image/Jamfile @@ -12,6 +12,7 @@ alias headers : [ generate_self_contained_headers extension/dynamic_image ] ; run any_image.cpp ; run any_image_view.cpp ; +run image_view_factory.cpp ; run subimage_view.cpp ; build-project algorithm ; diff --git a/test/extension/dynamic_image/algorithm/Jamfile b/test/extension/dynamic_image/algorithm/Jamfile index cdca8c65e8..071b29a56f 100644 --- a/test/extension/dynamic_image/algorithm/Jamfile +++ b/test/extension/dynamic_image/algorithm/Jamfile @@ -1,6 +1,6 @@ # Boost.GIL (Generic Image Library) - tests # -# Copyright (c) 2022 Marco Langer +# Copyright 2022 Marco Langer # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or @@ -8,4 +8,8 @@ import testing ; +run copy_and_convert_pixels.cpp ; +run copy_pixels.cpp ; +run equal_pixels.cpp ; +run fill_pixels.cpp ; run for_each_pixel.cpp ; diff --git a/test/extension/dynamic_image/algorithm/copy_and_convert_pixels.cpp b/test/extension/dynamic_image/algorithm/copy_and_convert_pixels.cpp new file mode 100644 index 0000000000..851b915551 --- /dev/null +++ b/test/extension/dynamic_image/algorithm/copy_and_convert_pixels.cpp @@ -0,0 +1,202 @@ +// +// Copyright 2022 Marco Langer +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +// +#include +#include + +#include + +#include + +#include "core/image/test_fixture.hpp" +#include "extension/dynamic_image/test_fixture.hpp" + +namespace gil = boost::gil; +namespace fixture = boost::gil::test::fixture; + +struct test_copy_and_convert_pixels +{ + template + void operator()(std::pair const&) + { + using image_lhs_t = ImageLhs; + using image_rhs_t = ImageRhs; + using image_view_lhs_t = typename image_lhs_t::view_t; + using image_view_rhs_t = typename image_rhs_t::view_t; + + gil::default_color_converter cc_default; + image_lhs_t image_lhs1(fixture::create_image(2, 2, 128)); + image_rhs_t image_rhs1(fixture::create_image(2, 2, 128)); + fixture::dynamic_image dyn_image_lhs1(image_lhs1); + fixture::dynamic_image dyn_image_rhs1(image_rhs1); + { + // dynamic_image -> image + image_lhs_t image_lhs2(2, 2); + image_lhs_t image_lhs3(2, 2); + image_rhs_t image_rhs2(2, 2); + image_rhs_t image_rhs3(2, 2); + + // lhs, lhs + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(image_lhs2))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(image_lhs2))); + + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(image_lhs3), cc_default)); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(image_lhs3))); + + + // lhs, rhs + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(image_rhs2))); + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(image_rhs3), cc_default)); + + if (gil::views_are_compatible::value) + { + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_rhs1), + gil::const_view(image_rhs2))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_rhs1), + gil::const_view(image_rhs3))); + } + } + { + // image -> dynamic_image + fixture::dynamic_image dyn_image_lhs2(image_lhs_t(2, 2)); + fixture::dynamic_image dyn_image_lhs3(image_lhs_t(2, 2)); + fixture::dynamic_image dyn_image_rhs2(image_rhs_t(2, 2)); + fixture::dynamic_image dyn_image_rhs3(image_rhs_t(2, 2)); + + // lhs, lhs + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(image_lhs1), + gil::view(dyn_image_lhs2))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(image_lhs1), + gil::const_view(dyn_image_lhs2))); + + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(image_lhs1), + gil::view(dyn_image_lhs3), cc_default)); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(image_lhs1), + gil::const_view(dyn_image_lhs3))); + + // lhs, rhs + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(image_lhs1), + gil::view(dyn_image_rhs2))); + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(image_lhs1), + gil::view(dyn_image_rhs3), cc_default)); + + if (gil::views_are_compatible::value) + { + BOOST_TEST( + gil::equal_pixels( + gil::const_view(image_lhs1), + gil::const_view(dyn_image_rhs2))); + + BOOST_TEST( + gil::equal_pixels( + gil::const_view(image_lhs1), + gil::const_view(dyn_image_rhs3))); + } + } + { + // dynamic_image -> dynamic_image + fixture::dynamic_image dyn_image_lhs2(image_lhs_t(2, 2)); + fixture::dynamic_image dyn_image_lhs3(image_lhs_t(2, 2)); + fixture::dynamic_image dyn_image_rhs2(image_rhs_t(2, 2)); + fixture::dynamic_image dyn_image_rhs3(image_rhs_t(2, 2)); + + // lhs, lhs + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(dyn_image_lhs2))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_lhs2))); + + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(dyn_image_lhs3), cc_default)); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_lhs3))); + + // lhs, rhs + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(dyn_image_rhs2))); + BOOST_TEST_NO_THROW( + gil::copy_and_convert_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(dyn_image_rhs3), cc_default)); + + if (gil::views_are_compatible::value) + { + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_rhs2))); + + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_rhs3))); + } + } + } + + static void run() + { + boost::mp11::mp_for_each + < + boost::mp11::mp_pairwise_fold + < + fixture::image_types, std::pair + > + >(test_copy_and_convert_pixels{}); + } +}; + +int main() +{ + test_copy_and_convert_pixels::run(); + + return ::boost::report_errors(); +} diff --git a/test/extension/dynamic_image/algorithm/copy_pixels.cpp b/test/extension/dynamic_image/algorithm/copy_pixels.cpp new file mode 100644 index 0000000000..40ad6eec59 --- /dev/null +++ b/test/extension/dynamic_image/algorithm/copy_pixels.cpp @@ -0,0 +1,163 @@ +// +// Copyright 2022 Marco Langer +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +// +#include +#include + +#include + +#include + +#include "core/image/test_fixture.hpp" +#include "extension/dynamic_image/test_fixture.hpp" + +namespace gil = boost::gil; +namespace fixture = boost::gil::test::fixture; + +struct test_copy_pixels +{ + template + void operator()(std::pair const&) + { + using image_lhs_t = ImageLhs; + using image_rhs_t = ImageRhs; + using image_view_lhs_t = typename image_lhs_t::view_t; + using image_view_rhs_t = typename image_rhs_t::view_t; + + { + // dynamic_image -> image + image_lhs_t image_lhs1(fixture::create_image(2, 2, 128)); + image_lhs_t image_lhs2(2, 2); + image_rhs_t image_rhs(2, 2); + fixture::dynamic_image dyn_image_lhs(image_lhs1); + + // lhs, lhs + BOOST_TEST_NO_THROW( + gil::copy_pixels( + gil::const_view(dyn_image_lhs), + gil::view(image_lhs2))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(image_lhs1), + gil::const_view(image_lhs2))); + + // lhs, rhs + if (gil::views_are_compatible::value) + { + BOOST_TEST_NO_THROW( + gil::copy_pixels( + gil::const_view(dyn_image_lhs), + gil::view(image_rhs))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(image_lhs1), + gil::const_view(image_rhs))); + } + else + { + BOOST_TEST_THROWS( + gil::copy_pixels( + gil::const_view(dyn_image_lhs), + gil::view(image_rhs)), + std::bad_cast); + } + } + { + // image -> dynamic_image + image_lhs_t image_lhs = fixture::create_image(2, 2, 128); + fixture::dynamic_image dyn_image_lhs1(image_lhs); + fixture::dynamic_image dyn_image_lhs2(image_lhs_t(2, 2)); + fixture::dynamic_image dyn_image_rhs(image_rhs_t(2, 2)); + + // lhs, lhs + BOOST_TEST_NO_THROW( + gil::copy_pixels( + gil::const_view(image_lhs), + gil::view(dyn_image_lhs2))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_lhs2))); + + // lhs, rhs + if (gil::views_are_compatible::value) + { + BOOST_TEST_NO_THROW( + gil::copy_pixels( + gil::const_view(image_lhs), + gil::view(dyn_image_rhs))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_rhs))); + } + else + { + BOOST_TEST_THROWS( + gil::copy_pixels( + gil::const_view(image_lhs), + gil::view(dyn_image_rhs)), + std::bad_cast); + } + } + { + // dynamic_image -> dynamic_image + fixture::dynamic_image dyn_image_lhs1(fixture::create_image(2, 2, 128)); + fixture::dynamic_image dyn_image_lhs2(image_lhs_t(2, 2)); + fixture::dynamic_image dyn_image_rhs(image_rhs_t(2, 2)); + + // lhs, lhs + BOOST_TEST_NO_THROW( + gil::copy_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(dyn_image_lhs2))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_lhs2))); + + // lhs, rhs + if (gil::views_are_compatible::value) + { + BOOST_TEST_NO_THROW( + gil::copy_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(dyn_image_rhs))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_rhs))); + } + else + { + BOOST_TEST_THROWS( + gil::copy_pixels( + gil::const_view(dyn_image_lhs1), + gil::view(dyn_image_rhs)), + std::bad_cast); + } + } + } + + static void run() + { + boost::mp11::mp_for_each + < + boost::mp11::mp_pairwise_fold + < + fixture::image_types, std::pair + > + >(test_copy_pixels{}); + } +}; + +int main() +{ + test_copy_pixels::run(); + + return ::boost::report_errors(); +} diff --git a/test/extension/dynamic_image/algorithm/equal_pixels.cpp b/test/extension/dynamic_image/algorithm/equal_pixels.cpp new file mode 100644 index 0000000000..eb9ef7440b --- /dev/null +++ b/test/extension/dynamic_image/algorithm/equal_pixels.cpp @@ -0,0 +1,104 @@ +// +// Copyright 2022 Marco Langer +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +// +#include +#include + +#include + +#include + +#include "core/image/test_fixture.hpp" +#include "extension/dynamic_image/test_fixture.hpp" + +namespace gil = boost::gil; +namespace fixture = boost::gil::test::fixture; + +struct test_equal_pixels +{ + template + void operator()(std::pair const&) + { + using image_lhs_t = ImageLhs; + using image_rhs_t = ImageRhs; + using image_view_lhs_t = typename image_lhs_t::view_t; + using image_view_rhs_t = typename image_rhs_t::view_t; + + image_lhs_t image_lhs = fixture::create_image(2, 2, 128); + image_rhs_t image_rhs = fixture::create_image(2, 2, 128); + + fixture::dynamic_image dyn_image_lhs(image_lhs); + fixture::dynamic_image dyn_image_rhs(image_rhs); + + // lhs, lhs + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs), + gil::const_view(image_lhs))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(image_lhs), + gil::const_view(dyn_image_lhs))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs), + gil::const_view(dyn_image_lhs))); + + // lhs, rhs + if (gil::views_are_compatible::value) + { + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs), + gil::const_view(image_rhs))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(image_lhs), + gil::const_view(dyn_image_rhs))); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs), + gil::const_view(dyn_image_rhs))); + } + else + { + BOOST_TEST_THROWS( + gil::equal_pixels( + gil::const_view(dyn_image_lhs), + gil::const_view(image_rhs)), + std::bad_cast); + BOOST_TEST_THROWS( + gil::equal_pixels( + gil::const_view(image_lhs), + gil::const_view(dyn_image_rhs)), + std::bad_cast); + BOOST_TEST_THROWS( + gil::equal_pixels( + gil::const_view(dyn_image_lhs), + gil::const_view(dyn_image_rhs)), + std::bad_cast); + } + } + + static void run() + { + boost::mp11::mp_for_each + < + boost::mp11::mp_pairwise_fold + < + fixture::image_types, std::pair + > + >(test_equal_pixels{}); + } +}; + +int main() +{ + test_equal_pixels::run(); + + return ::boost::report_errors(); +} diff --git a/test/extension/dynamic_image/algorithm/fill_pixels.cpp b/test/extension/dynamic_image/algorithm/fill_pixels.cpp new file mode 100644 index 0000000000..31832a63e0 --- /dev/null +++ b/test/extension/dynamic_image/algorithm/fill_pixels.cpp @@ -0,0 +1,86 @@ +// +// Copyright 2022 Marco Langer +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +// +#include +#include + +#include + +#include + +#include "core/image/test_fixture.hpp" +#include "extension/dynamic_image/test_fixture.hpp" + +namespace gil = boost::gil; +namespace fixture = boost::gil::test::fixture; + +struct test_fill_pixels +{ + template + void operator()(std::pair const&) + { + using image_lhs_t = ImageLhs; + using image_rhs_t = ImageRhs; + using image_view_lhs_t = typename image_lhs_t::view_t; + using image_view_rhs_t = typename image_rhs_t::view_t; + using value_lhs_t = typename image_lhs_t::value_type; + using value_rhs_t = typename image_rhs_t::value_type; + + image_lhs_t image_lhs = fixture::create_image(2, 2, 128); + fixture::dynamic_image dyn_image_lhs1(image_lhs); + fixture::dynamic_image dyn_image_lhs2(image_lhs_t(2, 2)); + fixture::dynamic_image dyn_image_lhs3(image_lhs_t(2, 2)); + value_lhs_t value_lhs(128); + value_rhs_t value_rhs(128); + + // lhs, lhs + BOOST_TEST_NO_THROW( + gil::fill_pixels( + gil::view(dyn_image_lhs2), value_lhs)); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_lhs2))); + + // lhs, rhs + if (gil::views_are_compatible::value) + { + BOOST_TEST_NO_THROW( + gil::fill_pixels( + gil::view(dyn_image_lhs3), value_rhs)); + BOOST_TEST( + gil::equal_pixels( + gil::const_view(dyn_image_lhs1), + gil::const_view(dyn_image_lhs3))); + } + else + { + BOOST_TEST_THROWS( + gil::fill_pixels( + gil::view(dyn_image_lhs3), value_rhs), + std::bad_cast); + } + } + + static void run() + { + boost::mp11::mp_for_each + < + boost::mp11::mp_pairwise_fold + < + fixture::image_types, std::pair + > + >(test_fill_pixels{}); + } +}; + +int main() +{ + test_fill_pixels::run(); + + return ::boost::report_errors(); +} diff --git a/test/extension/dynamic_image/algorithm/for_each_pixel.cpp b/test/extension/dynamic_image/algorithm/for_each_pixel.cpp index 67a947cac2..ad5620a5bf 100644 --- a/test/extension/dynamic_image/algorithm/for_each_pixel.cpp +++ b/test/extension/dynamic_image/algorithm/for_each_pixel.cpp @@ -1,17 +1,17 @@ // -// Copyright 2022 Marco Langer +// Copyright 2022 Marco Langer // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // -#include #include +#include #include -#include "../test_fixture.hpp" #include "core/image/test_fixture.hpp" +#include "extension/dynamic_image/test_fixture.hpp" namespace gil = boost::gil; namespace fixture = boost::gil::test::fixture; @@ -49,4 +49,4 @@ int main() test_for_each_pixel::run(); return ::boost::report_errors(); -} \ No newline at end of file +} diff --git a/test/extension/dynamic_image/any_image.cpp b/test/extension/dynamic_image/any_image.cpp index 68ead86bba..7dfe28e495 100644 --- a/test/extension/dynamic_image/any_image.cpp +++ b/test/extension/dynamic_image/any_image.cpp @@ -22,9 +22,12 @@ struct test_any_image_move_ctor void operator()(Image const&) { using image_t = Image; - fixture::dynamic_image i0(fixture::create_image(4, 4, 128)); + fixture::dynamic_image i0(fixture::create_image(4, 5, 128)); BOOST_TEST_EQ(i0.dimensions().x, 4); - BOOST_TEST_EQ(i0.dimensions().y, 4); + BOOST_TEST_EQ(i0.dimensions().y, 5); + BOOST_TEST_EQ(i0.width(), 4); + BOOST_TEST_EQ(i0.height(), 5); + BOOST_TEST_EQ(i0.num_channels(), gil::num_channels::value); fixture::dynamic_image i1 = fixture::create_image(4, 4, 128); BOOST_TEST_EQ(i1.dimensions().x, 4); @@ -36,10 +39,29 @@ struct test_any_image_move_ctor } }; +struct test_any_image_recreate +{ + template + void operator()(Image const&) + { + using image_t = Image; + using point_t = typename image_t::point_t; + fixture::dynamic_image image(image_t(4, 5, 128)); + point_t point(5, 6); + + BOOST_TEST_NO_THROW(image.recreate(point)); + } + + static void run() + { + boost::mp11::mp_for_each(test_any_image_recreate{}); + } +}; int main() { test_any_image_move_ctor::run(); + test_any_image_recreate::run(); return ::boost::report_errors(); } diff --git a/test/extension/dynamic_image/any_image_view.cpp b/test/extension/dynamic_image/any_image_view.cpp index 176e7f25b2..fc215e0379 100644 --- a/test/extension/dynamic_image/any_image_view.cpp +++ b/test/extension/dynamic_image/any_image_view.cpp @@ -34,18 +34,22 @@ struct test_any_image_view_init_ctor using view_t = typename Image::view_t; using any_view_t = gil::any_image_view; using any_const_view_t = typename any_view_t::const_t; - Image i0(fixture::create_image(4, 4, 128)); + Image i0(fixture::create_image(4, 5, 128)); view_t v0 = gil::view(i0); any_view_t v1 = v0; BOOST_TEST_EQ(v1.dimensions().x, 4); - BOOST_TEST_EQ(v1.dimensions().y, 4); + BOOST_TEST_EQ(v1.dimensions().y, 5); + BOOST_TEST_EQ(v1.width(), 4); + BOOST_TEST_EQ(v1.height(), 5); + BOOST_TEST_EQ(v1.size(), 4 * 5); + BOOST_TEST_EQ(v1.num_channels(), gil::num_channels::value); any_const_view_t v2 = v0; BOOST_TEST_EQ(v2.dimensions().x, 4); - BOOST_TEST_EQ(v2.dimensions().y, 4); + BOOST_TEST_EQ(v2.dimensions().y, 5); //any_const_view_t v3 = v1; } diff --git a/test/extension/dynamic_image/image_view_factory.cpp b/test/extension/dynamic_image/image_view_factory.cpp new file mode 100644 index 0000000000..ce0ad9ee28 --- /dev/null +++ b/test/extension/dynamic_image/image_view_factory.cpp @@ -0,0 +1,92 @@ +// +// Copyright 2022 Marco Langer +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +// +#include +#include + +#include + +#include + +#include "core/image/test_fixture.hpp" +#include "extension/dynamic_image/test_fixture.hpp" + +namespace gil = boost::gil; +namespace fixture = boost::gil::test::fixture; + +template +struct generator +{ + generator(int const* data) : data_(data) {} + + auto operator()() -> int + { + if(++i_ == Channels) { + i_ = 0; + return *data_++; + } + else + { + return *data_; + } + } + + int i_= 0; + int const* data_; +}; + +struct test_flipped_up_down_view +{ + template + void operator()(Image const&) + { + using image_t = Image; + using pixel_t = typename image_t::value_type; + static constexpr std::size_t num_channels = gil::num_channels::value; + + std::array pixel_data = + { + 0, 1, 2, + 3, 4, 5, + 6, 7, 8 + }; + std::array expected_pixel_data = + { + 6, 7, 8, + 3, 4, 5, + 0, 1, 2 + }; + + fixture::dynamic_image source_image = + fixture::generate_image( + 3, 3, generator{pixel_data.data()}); + + fixture::dynamic_image expected_image = + fixture::generate_image( + 3, 3, generator{expected_pixel_data.data()}); + + auto result_view = gil::flipped_up_down_view(gil::const_view(source_image)); + + BOOST_TEST( + gil::equal_pixels( + result_view, + gil::const_view(expected_image))); + } + + static void run() + { + boost::mp11::mp_for_each(test_flipped_up_down_view{}); + } +}; + + +int main() +{ + test_flipped_up_down_view::run(); + + return ::boost::report_errors(); +} diff --git a/test/extension/dynamic_image/subimage_view.cpp b/test/extension/dynamic_image/subimage_view.cpp index b1e22ae5f9..37cca2c6d7 100644 --- a/test/extension/dynamic_image/subimage_view.cpp +++ b/test/extension/dynamic_image/subimage_view.cpp @@ -15,6 +15,7 @@ #include "core/image/test_fixture.hpp" namespace gil = boost::gil; +namespace variant2 = boost::variant2; namespace fixture = boost::gil::test::fixture; struct test_subimage_equals_image @@ -61,16 +62,16 @@ struct test_subimage_equals_image_quadrants // create test image and set values of pixels in: // quadrant 1 auto const i1 = fixture::create_image(2, 2, 255); - gil::apply_operation(v0, fixture::fill_any_view({2, 3, 6, 7}, gil::const_view(i1)[0])); + variant2::visit(fixture::fill_any_view({2, 3, 6, 7}, gil::const_view(i1)[0]), v0); // quadrant 2 auto const i2 = fixture::create_image(2, 2, 128); - gil::apply_operation(v0, fixture::fill_any_view({0, 1, 4, 5}, gil::const_view(i2)[0])); + variant2::visit(fixture::fill_any_view({0, 1, 4, 5}, gil::const_view(i2)[0]), v0); // quadrant 3 auto const i3 = fixture::create_image(2, 2, 64); - gil::apply_operation(v0, fixture::fill_any_view({8, 9, 12, 13}, gil::const_view(i3)[0])); + variant2::visit(fixture::fill_any_view({8, 9, 12, 13}, gil::const_view(i3)[0]), v0); // quadrant 4 auto const i4 = fixture::create_image(2, 2, 32); - gil::apply_operation(v0, fixture::fill_any_view({10, 11, 14, 15}, gil::const_view(i4)[0])); + variant2::visit(fixture::fill_any_view({10, 11, 14, 15}, gil::const_view(i4)[0]), v0); auto v1 = gil::subimage_view(gil::view(i0), { 2, 0 }, i0.dimensions() / 2); BOOST_TEST(gil::equal_pixels(v1, gil::const_view(i1))); diff --git a/test/extension/image_processing/hough_circle_transform.cpp b/test/extension/image_processing/hough_circle_transform.cpp index 67156e51a1..8ec4eb9f31 100644 --- a/test/extension/image_processing/hough_circle_transform.cpp +++ b/test/extension/image_processing/hough_circle_transform.cpp @@ -23,8 +23,8 @@ namespace gil = boost::gil; template void exact_fit_test(std::ptrdiff_t radius, gil::point_t offset, Rasterizer rasterizer) { - std::vector circle_points(rasterizer.point_count(radius)); - rasterizer(radius, offset, circle_points.begin()); + std::vector circle_points(rasterizer.point_count()); + rasterizer(circle_points.begin()); // const std::ptrdiff_t diameter = radius * 2 - 1; const std::ptrdiff_t width = offset.x + radius + 1; const std::ptrdiff_t height = offset.y + radius + 1; @@ -54,12 +54,12 @@ void exact_fit_test(std::ptrdiff_t radius, gil::point_t offset, Rasterizer raste }); gil::hough_circle_transform_brute(input, radius_parameter, x_parameter, y_parameter, output_views.begin(), rasterizer); - if (output_views[0](0, 0) != rasterizer.point_count(radius)) + if (output_views[0](0, 0) != rasterizer.point_count()) { std::cout << "accumulated value: " << static_cast(output_views[0](0, 0)) - << " expected value: " << rasterizer.point_count(radius) << "\n\n"; + << " expected value: " << rasterizer.point_count() << "\n\n"; } - BOOST_TEST(output_views[0](0, 0) == rasterizer.point_count(radius)); + BOOST_TEST(output_views[0](0, 0) == rasterizer.point_count()); } int main() @@ -72,9 +72,10 @@ int main() for (std::ptrdiff_t y_offset = radius; y_offset < radius + test_dim_length; ++y_offset) { - exact_fit_test(radius, {x_offset, y_offset}, gil::midpoint_circle_rasterizer{}); exact_fit_test(radius, {x_offset, y_offset}, - gil::trigonometric_circle_rasterizer{}); + gil::midpoint_circle_rasterizer{{x_offset, y_offset}, radius}); + exact_fit_test(radius, {x_offset, y_offset}, + gil::trigonometric_circle_rasterizer{{x_offset, y_offset}, radius}); } } } diff --git a/test/extension/image_processing/hough_line_transform.cpp b/test/extension/image_processing/hough_line_transform.cpp index 3be4194261..6ff4059654 100644 --- a/test/extension/image_processing/hough_line_transform.cpp +++ b/test/extension/image_processing/hough_line_transform.cpp @@ -36,11 +36,12 @@ void translate(std::vector& points, std::ptrdiff_t intercept) void hough_line_test(std::ptrdiff_t height, std::ptrdiff_t intercept) { - const auto rasterizer = gil::bresenham_line_rasterizer{}; gil::gray8_image_t image(width, width, gil::gray8_pixel_t(0)); auto input = gil::view(image); - std::vector line_points(rasterizer.point_count(width, height)); - rasterizer({0, 0}, {width - 1, height - 1}, line_points.begin()); + + const auto rasterizer = gil::bresenham_line_rasterizer{{0, 0}, {width - 1, height - 1}}; + std::vector line_points(rasterizer.point_count()); + rasterizer(line_points.begin()); translate(line_points, intercept); for (const auto& p : line_points) { diff --git a/test/extension/io/simple_all_formats.cpp b/test/extension/io/simple_all_formats.cpp index b4bc87adca..1b110dc143 100644 --- a/test/extension/io/simple_all_formats.cpp +++ b/test/extension/io/simple_all_formats.cpp @@ -74,6 +74,18 @@ void test_tiff() gil::write_view(tiff_out + "simple_all_format.tif", gil::view(img), gil::tiff_tag()); } +void test_tiff_tiled() +{ + gil::rgba8_image_t img; + gil::read_image(tiff_filename, img, gil::tiff_tag()); + + fs::create_directories(fs::path(tiff_out)); + gil::image_write_info info; + info._is_tiled = true; + info._tile_width = info._tile_length = 64; // must be multiples of 16 + gil::write_view(tiff_out + "simple_all_format.tif", gil::view(img), info); +} + int main(int argc, char* argv[]) { try @@ -85,6 +97,7 @@ int main(int argc, char* argv[]) // TODO: test_raw() test_targa(); test_tiff(); + test_tiff_tiled(); } catch (std::exception const& e) { diff --git a/test/extension/numeric/matrix3x2.cpp b/test/extension/numeric/matrix3x2.cpp index 1f0ae8f67d..afc4048cc6 100644 --- a/test/extension/numeric/matrix3x2.cpp +++ b/test/extension/numeric/matrix3x2.cpp @@ -12,24 +12,12 @@ #include +#include "test_utility_with_tolerance.hpp" + #include namespace gil = boost::gil; -// Tolerance predicate for floating point comparison to use with BOOST_TEST_WITH -template -struct with_tolerance -{ - with_tolerance(T tolerance) : tolerance(tolerance) {} - bool operator()(T lhs, T rhs) - { - return (std::abs(lhs - rhs) <= tolerance); - } - -private: - T tolerance; -}; - namespace { constexpr double HALF_PI = 1.57079632679489661923; } @@ -134,10 +122,10 @@ void test_matrix3x2_vector_multiplication() void test_matrix3x2_get_rotate() { auto m1 = gil::matrix3x2::get_rotate(HALF_PI); - BOOST_TEST_WITH(m1.a, std::cos(HALF_PI), with_tolerance(0.03)); + BOOST_TEST_WITH(m1.a, std::cos(HALF_PI), gil::test::utility::with_tolerance(0.03)); BOOST_TEST_EQ(m1.b, 1); BOOST_TEST_EQ(m1.c, -1); - BOOST_TEST_WITH(m1.d, std::cos(HALF_PI), with_tolerance(0.03)); + BOOST_TEST_WITH(m1.d, std::cos(HALF_PI), gil::test::utility::with_tolerance(0.03)); BOOST_TEST_EQ(m1.e, 0); BOOST_TEST_EQ(m1.f, 0); } @@ -197,8 +185,8 @@ void test_matrix3x2_inverse() point_t q = gil::transform(inverse(m), p); point_t p2 = gil::transform(m, q); - BOOST_TEST_WITH(p.x, p2.x, with_tolerance(1e-9)); - BOOST_TEST_WITH(p.y, p2.y, with_tolerance(1e-9)); + BOOST_TEST_WITH(p.x, p2.x, gil::test::utility::with_tolerance(1e-9)); + BOOST_TEST_WITH(p.y, p2.y, gil::test::utility::with_tolerance(1e-9)); } void test_matrix3x2_center_rotate() @@ -208,12 +196,12 @@ void test_matrix3x2_center_rotate() m1 = gil::center_rotate(dimension, HALF_PI); - BOOST_TEST_WITH(m1.a , std::cos(HALF_PI) , with_tolerance(1e-9)); + BOOST_TEST_WITH(m1.a , std::cos(HALF_PI) , gil::test::utility::with_tolerance(1e-9)); BOOST_TEST_EQ (m1.b , 1); BOOST_TEST_EQ (m1.c , -1); - BOOST_TEST_WITH(m1.d , std::cos(HALF_PI) , with_tolerance(1e-9)); + BOOST_TEST_WITH(m1.d , std::cos(HALF_PI) , gil::test::utility::with_tolerance(1e-9)); BOOST_TEST_EQ (m1.e , 100); - BOOST_TEST_WITH(m1.f , std::cos(HALF_PI) , with_tolerance(1e-9)); + BOOST_TEST_WITH(m1.f , std::cos(HALF_PI) , gil::test::utility::with_tolerance(1e-9)); } int main() diff --git a/test/extension/rasterization/circle.cpp b/test/extension/rasterization/circle.cpp index 33af44c491..4df90adf4c 100644 --- a/test/extension/rasterization/circle.cpp +++ b/test/extension/rasterization/circle.cpp @@ -15,13 +15,13 @@ namespace gil = boost::gil; template -void test_rasterizer_follows_equation(std::ptrdiff_t radius, Rasterizer rasterizer) +void test_rasterizer_follows_equation(Rasterizer rasterizer) { - - std::vector circle_points(rasterizer.point_count(radius)); + const std::ptrdiff_t radius = rasterizer.radius; + std::vector circle_points(rasterizer.point_count()); std::ptrdiff_t const r_squared = radius * radius; - rasterizer(radius, {0, 0}, circle_points.begin()); - std::vector first_octant(rasterizer.point_count(radius) / 8); + rasterizer(circle_points.begin()); + std::vector first_octant(rasterizer.point_count() / 8); for (std::size_t i = 0, octant_index = 0; i < circle_points.size(); i += 8, ++octant_index) { @@ -38,10 +38,10 @@ void test_rasterizer_follows_equation(std::ptrdiff_t radius, Rasterizer rasteriz } template -void test_connectivity(std::ptrdiff_t radius, Rasterizer rasterizer) +void test_connectivity(Rasterizer rasterizer) { - std::vector circle_points(rasterizer.point_count(radius)); - rasterizer(radius, {radius, radius}, circle_points.begin()); + std::vector circle_points(rasterizer.point_count()); + rasterizer(circle_points.begin()); for (std::size_t i = 0; i < 8; ++i) { std::vector octant(circle_points.size() / 8); @@ -65,11 +65,11 @@ int main() { for (std::ptrdiff_t radius = 5; radius <= 512; ++radius) { - test_rasterizer_follows_equation(radius, gil::midpoint_circle_rasterizer{}); + test_rasterizer_follows_equation(gil::midpoint_circle_rasterizer{{0, 0}, radius}); // TODO: find out a new testing procedure for trigonometric rasterizer // test_equation_following(radius, gil::trigonometric_circle_rasterizer{}); - test_connectivity(radius, gil::midpoint_circle_rasterizer{}); - test_connectivity(radius, gil::trigonometric_circle_rasterizer{}); + test_connectivity(gil::midpoint_circle_rasterizer{{radius, radius}, radius}); + test_connectivity(gil::trigonometric_circle_rasterizer{{radius, radius}, radius}); } return boost::report_errors(); diff --git a/test/extension/rasterization/ellipse.cpp b/test/extension/rasterization/ellipse.cpp index 495e40400a..974ca7f87e 100644 --- a/test/extension/rasterization/ellipse.cpp +++ b/test/extension/rasterization/ellipse.cpp @@ -6,8 +6,8 @@ // http://www.boost.org/LICENSE_1_0.txt) // #include -#include "boost/gil.hpp" -#include +#include + #include #include @@ -17,7 +17,7 @@ namespace gil = boost::gil; // is equal to the length of major axis of the ellipse. // Parameters b and a represent half of lengths of vertical and horizontal axis respectively. void test_rasterizer_follows_equation( - std::vector> trajectory_points, float a, float b) + std::vector const& trajectory_points, float a, float b) { float focus_x, focus_y; if (a > b) // For horizontal ellipse @@ -34,7 +34,7 @@ void test_rasterizer_follows_equation( for (auto trajectory_point : trajectory_points) { // To suppress conversion warnings from compiler - std::array point { + gil::point point { static_cast(trajectory_point[0]), static_cast(trajectory_point[1])}; double dist_sum = std::sqrt(std::pow(focus_x - point[0], 2) + @@ -53,7 +53,7 @@ void test_rasterizer_follows_equation( // This function verifies that the difference between x co-ordinates and y co-ordinates for two // successive trajectory points is less than or equal to 1. This ensures that the curve is connected. -void test_connectivity(std::vector> points) +void test_connectivity(std::vector const& points) { for (std::size_t i = 1; i < points.size(); ++i) { @@ -72,9 +72,9 @@ int main() { for (float b = 1; b < 101; ++b) { - auto rasterizer = gil::midpoint_elliptical_rasterizer{}; - std::vector> points = rasterizer.obtain_trajectory( - {static_cast(a), static_cast(b)}); + auto rasterizer = gil::midpoint_ellipse_rasterizer{{}, + {static_cast(a), static_cast(b)}}; + std::vector points = rasterizer.obtain_trajectory(); test_rasterizer_follows_equation(points, a, b); test_connectivity(points); } diff --git a/test/extension/rasterization/line.cpp b/test/extension/rasterization/line.cpp index 92986edbf4..f68f904f9a 100644 --- a/test/extension/rasterization/line.cpp +++ b/test/extension/rasterization/line.cpp @@ -49,9 +49,9 @@ endpoints create_endpoints(std::mt19937& twister, line_type create_line(endpoints points) { - gil::bresenham_line_rasterizer rasterizer; - line_type forward_line(rasterizer.point_count(points.start, points.end)); - rasterizer(points.start, points.end, forward_line.begin()); + gil::bresenham_line_rasterizer rasterizer(points.start, points.end); + line_type forward_line(rasterizer.point_count()); + rasterizer(forward_line.begin()); return forward_line; } diff --git a/test/extension/toolbox/Jamfile b/test/extension/toolbox/Jamfile index a2a78f7bed..a2d4980c85 100644 --- a/test/extension/toolbox/Jamfile +++ b/test/extension/toolbox/Jamfile @@ -25,6 +25,7 @@ run color_convert_hsl.cpp ; run color_convert_hsv.cpp ; run color_convert_lab.cpp ; run color_convert_luminance.cpp ; +run color_convert_rgb.cpp ; run color_convert_xyz.cpp ; run indexed_image.cpp ; diff --git a/test/extension/toolbox/color_convert_hsl.cpp b/test/extension/toolbox/color_convert_hsl.cpp index 33c5c19629..299691842f 100644 --- a/test/extension/toolbox/color_convert_hsl.cpp +++ b/test/extension/toolbox/color_convert_hsl.cpp @@ -12,23 +12,15 @@ #include #include +#include +#include #include "test_utility_output_stream.hpp" +#include "test_utility_with_tolerance.hpp" namespace gil = boost::gil; -void test_rgb_to_hsl() -{ - gil::rgb8_pixel_t p{128, 0, 128}; - gil::hsl32f_pixel_t h; - gil::color_convert(p, h); - - BOOST_TEST_GT(gil::get_color(h, gil::hsl_color_space::hue_t()), 0.8); // 0.83333331 - BOOST_TEST_EQ(gil::get_color(h, gil::hsl_color_space::saturation_t()), 1.0); // 1.00000000 - BOOST_TEST_GT(gil::get_color(h, gil::hsl_color_space::lightness_t()), 0.25); // 0.25098040 -} - -void test_hsl_to_rgb() +void test_basic_hsl_to_rgb() { gil::rgb8_pixel_t p(64, 0, 64); gil::hsl32f_pixel_t h; @@ -39,6 +31,43 @@ void test_hsl_to_rgb() BOOST_TEST_EQ(b, p); } +void test_colors_hsl_to_rgb() +{ + using color_t = std::tuple; + std::vector colors = { + {0, 0, 0, 0, 0, 0}, // black + {0, 0, 1, 255, 255, 255}, // white + {0, 1, 0.5, 255, 0, 0}, // red + {2 / 6.f, 1, 0.5, 0, 255, 0}, // lime + {4 / 6.f, 1, 0.5, 0, 0, 255}, // blue + {5 / 6.f, 1, 0.25, 128, 0, 128}, // purple + {3 / 6.f, 1, 0.25, 0, 128, 128}, // teal + {4 / 6.f, 1, 0.25, 0, 0, 128}, // navy + }; + + for (auto&& color : colors) + { + float h{0}, s{0}, l{0}; + std::uint8_t r{0}, g{0}, b{0}; + std::tie(h, s, l, r, g, b) = color; + + gil::hsl32f_pixel_t hsl(h, s, l); + gil::rgb8_pixel_t rgb; + gil::color_convert(hsl, rgb); + + float const abs_error = 1; + BOOST_TEST_WITH( + r, gil::get_color(rgb, gil::red_t()), + gil::test::utility::with_tolerance(abs_error)); + BOOST_TEST_WITH( + g, gil::get_color(rgb, gil::green_t()), + gil::test::utility::with_tolerance(abs_error)); + BOOST_TEST_WITH( + b, gil::get_color(rgb, gil::blue_t()), + gil::test::utility::with_tolerance(abs_error)); + } +} + void test_image_assign_hsl() { std::ptrdiff_t const w = 320; @@ -48,7 +77,7 @@ void test_image_assign_hsl() for (std::ptrdiff_t y = 0; y < h; y++) { gil::hsl32f_view_t::x_iterator hsl_x_it = view(hsl_img).row_begin(y); - float v = static_cast(h - y) / h; + float v = static_cast(h - y) / h; for (std::ptrdiff_t x = 0; x < w; x++) { float const hue = (x + 1.f) / w; @@ -59,29 +88,11 @@ void test_image_assign_hsl() } } -void test_copy_pixels_rgb_to_hsl() -{ - gil::rgb8_image_t rgb_img(320, 240); - gil::rgb8_pixel_t rgb_pix(64, 32, 64); - gil::fill_pixels(view(rgb_img), rgb_pix); - gil::hsl32f_image_t hsl_img(view(rgb_img).dimensions()); - gil::copy_pixels(gil::color_converted_view(view(rgb_img)), view(hsl_img)); - - auto view = gil::view(hsl_img); - for (auto it = view.begin(), end = view.end(); it != end; ++it) - { - gil::rgb8_pixel_t p; - gil::color_convert(*it, p); - BOOST_TEST_EQ(p, rgb_pix); - } -} - int main() { - test_rgb_to_hsl(); - test_hsl_to_rgb(); + test_basic_hsl_to_rgb(); + test_colors_hsl_to_rgb(); test_image_assign_hsl(); - test_copy_pixels_rgb_to_hsl(); return ::boost::report_errors(); } diff --git a/test/extension/toolbox/color_convert_rgb.cpp b/test/extension/toolbox/color_convert_rgb.cpp new file mode 100644 index 0000000000..cb22727a1c --- /dev/null +++ b/test/extension/toolbox/color_convert_rgb.cpp @@ -0,0 +1,102 @@ +// +// Copyright 2013 Christian Henning +// Copyright 2020 Mateusz Loskot +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +// +#include +#include + +#include + +#include +#include +#include + +#include "test_utility_output_stream.hpp" +#include "test_utility_with_tolerance.hpp" + +namespace gil = boost::gil; + +void test_basic_rgb_to_hsl() +{ + gil::rgb8_pixel_t p{128, 0, 128}; + gil::hsl32f_pixel_t h; + gil::color_convert(p, h); + + BOOST_TEST_GT(gil::get_color(h, gil::hsl_color_space::hue_t()), 0.8); // 0.83333331 + BOOST_TEST_EQ(gil::get_color(h, gil::hsl_color_space::saturation_t()), 1.0); // 1.00000000 + BOOST_TEST_GT(gil::get_color(h, gil::hsl_color_space::lightness_t()), 0.25); // 0.25098040 +} + +void test_colors_rgb_to_hsl() +{ + using color_t = std::tuple; + std::vector colors = { + {0, 0, 0, 0, 0, 0}, // black + {255, 255, 255, 0, 0, 1}, // white + {255, 0, 0, 0, 1, 0.5}, // red + {0, 255, 0, 2 / 6.f, 1, 0.5}, // lime + {0, 0, 255, 4 / 6.f, 1, 0.5}, // blue + {255, 255, 0, 1 / 6.f, 1, 0.5}, // yellow + {0, 255, 255, 3 / 6.f, 1, 0.5}, // cyan + {255, 0, 255, 5 / 6.f, 1, 0.5}, // magenta + {191, 191, 191, 0, 0, 0.75}, // silver + {128, 128, 128, 0, 0, 0.5}, // gray + {128, 0, 0, 0, 1, 0.25}, // maroon + {128, 128, 0, 1 / 6.f, 1, 0.25}, // olive + {0, 128, 0, 2 / 6.f, 1, 0.25}, // green + {128, 0, 128, 5 / 6.f, 1, 0.25}, // purple + {0, 128, 128, 3 / 6.f, 1, 0.25}, // teal + {0, 0, 128, 4 / 6.f, 1, 0.25}, // navy + }; + + for (auto&& color : colors) + { + std::uint8_t r{0}, g{0}, b{0}; + float h{0}, s{0}, l{0}; + std::tie(r, g, b, h, s, l) = color; + gil::rgb8_pixel_t rgb(r, g, b); + gil::hsl32f_pixel_t hsl; + gil::color_convert(rgb, hsl); + + float const abs_error = 0.002; + BOOST_TEST_WITH( + h, get_color(hsl, gil::hsl_color_space::hue_t()), + gil::test::utility::with_tolerance(abs_error)); + BOOST_TEST_WITH( + s, get_color(hsl, gil::hsl_color_space::saturation_t()), + gil::test::utility::with_tolerance(abs_error)); + BOOST_TEST_WITH( + l, get_color(hsl, gil::hsl_color_space::lightness_t()), + gil::test::utility::with_tolerance(abs_error)); + } +} + +void test_copy_pixels_rgb_to_hsl() +{ + gil::rgb8_image_t rgb_img(320, 240); + gil::rgb8_pixel_t rgb_pix(64, 32, 64); + gil::fill_pixels(view(rgb_img), rgb_pix); + gil::hsl32f_image_t hsl_img(view(rgb_img).dimensions()); + gil::copy_pixels(gil::color_converted_view(view(rgb_img)), view(hsl_img)); + + auto view = gil::view(hsl_img); + for (auto it = view.begin(), end = view.end(); it != end; ++it) + { + gil::rgb8_pixel_t p; + gil::color_convert(*it, p); + BOOST_TEST_EQ(p, rgb_pix); + } +} + +int main() +{ + test_basic_rgb_to_hsl(); + test_colors_rgb_to_hsl(); + test_copy_pixels_rgb_to_hsl(); + + return ::boost::report_errors(); +} diff --git a/test/test_utility_output_stream.hpp b/test/test_utility_output_stream.hpp index ec6aff901c..44585332a6 100644 --- a/test/test_utility_output_stream.hpp +++ b/test/test_utility_output_stream.hpp @@ -5,8 +5,8 @@ // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // -#ifndef BOOST_GIL_TEST_TEST_UTILITY_HPP -#define BOOST_GIL_TEST_TEST_UTILITY_HPP +#ifndef BOOST_GIL_TEST_TEST_UTILITY_OUTPUT_STREAM_HPP +#define BOOST_GIL_TEST_TEST_UTILITY_OUTPUT_STREAM_HPP #include // static_for_each #include diff --git a/test/test_utility_with_tolerance.hpp b/test/test_utility_with_tolerance.hpp new file mode 100644 index 0000000000..af5c7816c5 --- /dev/null +++ b/test/test_utility_with_tolerance.hpp @@ -0,0 +1,41 @@ +// +// Copyright 2020 Samuel Debionne +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +// +#ifndef BOOST_GIL_TEST_TEST_UTILITY_WITH_TOLERANCE_HPP +#define BOOST_GIL_TEST_TEST_UTILITY_WITH_TOLERANCE_HPP + +#include +#include +#include + +namespace boost { namespace gil { + +namespace test { namespace utility { + +// Tolerance predicate for floating point comparison to use with BOOST_TEST_WITH. +// See https://github.com/boostorg/core/pull/77 for details. +template +struct with_tolerance +{ + with_tolerance(T tolerance) : tolerance(tolerance) + { + } + + bool operator()(T lhs, T rhs) + { + return (std::abs(lhs - rhs) <= tolerance); + } + +private: + T tolerance; +}; + +}} // namespace test::utility + +}} // namespace boost::gil + +#endif