From bfed3de0049ed1f0de2f9045ee5b1409666330dc Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Sun, 26 Jun 2022 13:21:18 +0200 Subject: [PATCH 1/2] refactor: Deprecate apply_operation in favor of variant2::visit for any_image (#656) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit build: Add /bigobj to MSVC flags Avoid test\extension\dynamic_image\algorithm\copy_and_convert_pixels.cpp compilation error: fatal error C1128: number of sections exceeded object file format limit: compile with /bigobj Co-authored-by: Mateusz Łoskot --- RELEASES.md | 8 +- doc/design/dynamic_image.rst | 16 +- doc/tutorial/gradient.rst | 14 +- .../gil/extension/dynamic_image/algorithm.hpp | 52 ++--- .../gil/extension/dynamic_image/any_image.hpp | 11 +- .../dynamic_image/any_image_view.hpp | 8 +- .../dynamic_image/apply_operation.hpp | 2 + .../dynamic_image/image_view_factory.hpp | 39 ++-- .../gil/extension/io/bmp/detail/read.hpp | 4 +- .../gil/extension/io/bmp/detail/write.hpp | 4 +- .../gil/extension/io/jpeg/detail/read.hpp | 4 +- .../gil/extension/io/jpeg/detail/write.hpp | 2 +- .../gil/extension/io/png/detail/read.hpp | 4 +- .../gil/extension/io/png/detail/write.hpp | 2 +- .../gil/extension/io/pnm/detail/read.hpp | 4 +- .../gil/extension/io/pnm/detail/write.hpp | 2 +- .../gil/extension/io/raw/detail/read.hpp | 4 +- .../gil/extension/io/targa/detail/read.hpp | 4 +- .../gil/extension/io/targa/detail/write.hpp | 2 +- .../gil/extension/io/tiff/detail/read.hpp | 4 +- .../gil/extension/io/tiff/detail/write.hpp | 2 +- .../boost/gil/extension/numeric/resample.hpp | 10 +- include/boost/gil/io/detail/dynamic.hpp | 2 +- test/Jamfile | 9 +- test/extension/dynamic_image/Jamfile | 1 + .../extension/dynamic_image/algorithm/Jamfile | 6 +- .../algorithm/copy_and_convert_pixels.cpp | 202 ++++++++++++++++++ .../dynamic_image/algorithm/copy_pixels.cpp | 163 ++++++++++++++ .../dynamic_image/algorithm/equal_pixels.cpp | 104 +++++++++ .../dynamic_image/algorithm/fill_pixels.cpp | 86 ++++++++ .../algorithm/for_each_pixel.cpp | 8 +- test/extension/dynamic_image/any_image.cpp | 26 ++- .../dynamic_image/any_image_view.cpp | 10 +- .../dynamic_image/image_view_factory.cpp | 92 ++++++++ .../extension/dynamic_image/subimage_view.cpp | 9 +- 35 files changed, 804 insertions(+), 116 deletions(-) create mode 100644 test/extension/dynamic_image/algorithm/copy_and_convert_pixels.cpp create mode 100644 test/extension/dynamic_image/algorithm/copy_pixels.cpp create mode 100644 test/extension/dynamic_image/algorithm/equal_pixels.cpp create mode 100644 test/extension/dynamic_image/algorithm/fill_pixels.cpp create mode 100644 test/extension/dynamic_image/image_view_factory.cpp diff --git a/RELEASES.md b/RELEASES.md index fc12f0c8d2..419ca6e145 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -28,6 +28,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 +329,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 +340,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/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/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/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 9bc7b7c17e..cb10c1e9ee 100644 --- a/include/boost/gil/extension/io/tiff/detail/write.hpp +++ b/include/boost/gil/extension/io/tiff/detail/write.hpp @@ -438,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/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/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))); From adddbec8961a152db5751802307cc4ee977fd15d Mon Sep 17 00:00:00 2001 From: Marco Langer Date: Sun, 26 Jun 2022 13:33:53 +0200 Subject: [PATCH 2/2] refactor: Ellipse rasterizer according to the comment at (#692) https://github.com/boostorg/gil/pull/585#issuecomment-1144412220 --- example/rasterizer_circle.cpp | 5 +- example/rasterizer_ellipse.cpp | 30 ++-- example/rasterizer_line.cpp | 4 +- .../gil/extension/rasterization/ellipse.hpp | 147 ++++++++---------- test/extension/rasterization/ellipse.cpp | 14 +- 5 files changed, 95 insertions(+), 105 deletions(-) diff --git a/example/rasterizer_circle.cpp b/example/rasterizer_circle.cpp index 2c56de3199..0ab36ddf0f 100644 --- a/example/rasterizer_circle.cpp +++ b/example/rasterizer_circle.cpp @@ -8,6 +8,7 @@ #include #include + #include #include #include @@ -15,8 +16,8 @@ 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: diff --git a/example/rasterizer_ellipse.cpp b/example/rasterizer_ellipse.cpp index 295fe0533c..d34ba53635 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,28 +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{}; + gray_ellipse_rasterizer(view(gray_buffer_image), gil::gray8_pixel_t{128}, {128, 128}, {100, 50}); 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{}; + rgb_ellipse_rasterizer(view(rgb_buffer_image), gil::rgb8_pixel_t{0, 0, 255}, {128, 128}, {50, 100}); 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}, + auto rgb_ellipse_rasterizer_out_of_bound = gil::midpoint_ellipse_rasterizer{}; + rgb_ellipse_rasterizer_out_of_bound(view(rgb_buffer_image_out_of_bound), gil::rgb8_pixel_t{255, 0, 0}, {100, 100}, {160, 160}); gil::write_view("rasterized_ellipse_gray.jpg", view(gray_buffer_image), gil::jpeg_tag{}); diff --git a/example/rasterizer_line.cpp b/example/rasterizer_line.cpp index 7b2aec35b1..026ad10d9a 100644 --- a/example/rasterizer_line.cpp +++ b/example/rasterizer_line.cpp @@ -15,8 +15,8 @@ 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: diff --git a/include/boost/gil/extension/rasterization/ellipse.hpp b/include/boost/gil/extension/rasterization/ellipse.hpp index 6ea6ea46cb..2f9aca9442 100644 --- a/include/boost/gil/extension/rasterization/ellipse.hpp +++ b/include/boost/gil/extension/rasterization/ellipse.hpp @@ -8,9 +8,12 @@ #ifndef BOOST_GIL_EXTENSION_RASTERIZATION_ELLIPSE_HPP #define BOOST_GIL_EXTENSION_RASTERIZATION_ELLIPSE_HPP +#include +#include + #include +#include #include -#include namespace boost { namespace gil { @@ -22,21 +25,21 @@ 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 { /// \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 + /// \param semi_axes - Point containing half of lengths of horizontal and vertical axis /// respectively. - auto obtain_trajectory(std::array const semi_axes) - -> std::vector> + auto obtain_trajectory(point semi_axes) + -> 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 +99,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 center - Point specifying co-ordinates of center of ellipse 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, + point center, + std::vector const& trajectory_points) { - 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."); + } + + --center[0], --center[1]; // For converting center co-ordinate values to zero based indexing. + for (point_t pnt : trajectory_points) + { + std::array co_ords = {center[0] + pnt[0], + center[0] - pnt[0], center[1] + pnt[1], center[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()) { - 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]; - } + 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]) + { + view(co_ords[0], co_ords[3]) = pixel; } } } @@ -155,37 +163,18 @@ 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 + /// \param pixel - Pixel value for the elliptical curve to be drawn. + /// \param center - Point 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 + /// \param semi_axes - Point containing positive integer lengths of horizontal semi-axis /// and vertical semi-axis respectively. /// \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, + point center, point semi_axes) { - --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, pixel, center, obtain_trajectory(semi_axes)); } }; diff --git a/test/extension/rasterization/ellipse.cpp b/test/extension/rasterization/ellipse.cpp index 495e40400a..1b44707917 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,8 +72,8 @@ int main() { for (float b = 1; b < 101; ++b) { - auto rasterizer = gil::midpoint_elliptical_rasterizer{}; - std::vector> points = rasterizer.obtain_trajectory( + auto rasterizer = gil::midpoint_ellipse_rasterizer{}; + std::vector points = rasterizer.obtain_trajectory( {static_cast(a), static_cast(b)}); test_rasterizer_follows_equation(points, a, b); test_connectivity(points);