| /* |
| Copyright 2005-2007 Adobe Systems Incorporated |
| |
| Use, modification and distribution are subject to 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). |
| |
| See http://opensource.adobe.com/gil for most recent version including documentation. |
| */ |
| /*************************************************************************************************/ |
| |
| #ifndef GIL_COLOR_CONVERT_HPP |
| #define GIL_COLOR_CONVERT_HPP |
| |
| //////////////////////////////////////////////////////////////////////////////////////// |
| /// \file |
| /// \brief GIL default color space conversions |
| /// \author Lubomir Bourdev and Hailin Jin \n |
| /// Adobe Systems Incorporated |
| /// \date 2005-2007 \n Last updated on January 30, 2007 |
| /// |
| /// Support for fast and simple color conversion. Accurate color conversion using color |
| /// profiles can be supplied separately in a dedicated module |
| /// |
| //////////////////////////////////////////////////////////////////////////////////////// |
| |
| #include <functional> |
| #include "gil_config.hpp" |
| #include "channel_algorithm.hpp" |
| #include "pixel.hpp" |
| #include "gray.hpp" |
| #include "rgb.hpp" |
| #include "rgba.hpp" |
| #include "cmyk.hpp" |
| #include "metafunctions.hpp" |
| #include "utilities.hpp" |
| #include "color_base_algorithm.hpp" |
| |
| namespace boost { namespace gil { |
| |
| // Forward-declare |
| template <typename P> struct channel_type; |
| |
| //////////////////////////////////////////////////////////////////////////////////////// |
| /// |
| /// COLOR SPACE CONVERSION |
| /// |
| //////////////////////////////////////////////////////////////////////////////////////// |
| |
| /// \ingroup ColorConvert |
| /// \brief Color Convertion function object. To be specialized for every src/dst color space |
| template <typename C1, typename C2> |
| struct default_color_converter_impl {}; |
| |
| /// \ingroup ColorConvert |
| /// \brief When the color space is the same, color convertion performs channel depth conversion |
| template <typename C> |
| struct default_color_converter_impl<C,C> { |
| template <typename P1, typename P2> |
| void operator()(const P1& src, P2& dst) const { |
| static_for_each(src,dst,default_channel_converter()); |
| } |
| }; |
| |
| namespace detail { |
| |
| /// red * .3 + green * .59 + blue * .11 + .5 |
| |
| // The default implementation of to_luminance uses float0..1 as the intermediate channel type |
| template <typename RedChannel, typename GreenChannel, typename BlueChannel, typename GrayChannelValue> |
| struct rgb_to_luminance_fn { |
| GrayChannelValue operator()(const RedChannel& red, const GreenChannel& green, const BlueChannel& blue) const { |
| return channel_convert<GrayChannelValue>( bits32f( |
| channel_convert<bits32f>(red )*0.30f + |
| channel_convert<bits32f>(green)*0.59f + |
| channel_convert<bits32f>(blue )*0.11f) ); |
| } |
| }; |
| |
| // performance specialization for unsigned char |
| template <typename GrayChannelValue> |
| struct rgb_to_luminance_fn<uint8_t,uint8_t,uint8_t, GrayChannelValue> { |
| GrayChannelValue operator()(uint8_t red, uint8_t green, uint8_t blue) const { |
| return channel_convert<GrayChannelValue>(uint8_t( |
| ((uint32_t(red )*4915 + uint32_t(green)*9667 + uint32_t(blue )*1802) + 8192) >> 14)); |
| } |
| }; |
| |
| template <typename GrayChannel, typename RedChannel, typename GreenChannel, typename BlueChannel> |
| typename channel_traits<GrayChannel>::value_type rgb_to_luminance(const RedChannel& red, const GreenChannel& green, const BlueChannel& blue) { |
| return rgb_to_luminance_fn<RedChannel,GreenChannel,BlueChannel, |
| typename channel_traits<GrayChannel>::value_type>()(red,green,blue); |
| } |
| |
| } // namespace detail |
| |
| /// \ingroup ColorConvert |
| /// \brief Gray to RGB |
| template <> |
| struct default_color_converter_impl<gray_t,rgb_t> { |
| template <typename P1, typename P2> |
| void operator()(const P1& src, P2& dst) const { |
| get_color(dst,red_t()) = |
| channel_convert<typename color_element_type<P2, red_t >::type>(get_color(src,gray_color_t())); |
| get_color(dst,green_t())= |
| channel_convert<typename color_element_type<P2, green_t>::type>(get_color(src,gray_color_t())); |
| get_color(dst,blue_t()) = |
| channel_convert<typename color_element_type<P2, blue_t >::type>(get_color(src,gray_color_t())); |
| } |
| }; |
| |
| /// \ingroup ColorConvert |
| /// \brief Gray to CMYK |
| template <> |
| struct default_color_converter_impl<gray_t,cmyk_t> { |
| template <typename P1, typename P2> |
| void operator()(const P1& src, P2& dst) const { |
| get_color(dst,cyan_t())= |
| channel_traits<typename color_element_type<P2, cyan_t >::type>::min_value(); |
| get_color(dst,magenta_t())= |
| channel_traits<typename color_element_type<P2, magenta_t>::type>::min_value(); |
| get_color(dst,yellow_t())= |
| channel_traits<typename color_element_type<P2, yellow_t >::type>::min_value(); |
| get_color(dst,black_t())= |
| channel_convert<typename color_element_type<P2, black_t >::type>(get_color(src,gray_color_t())); |
| } |
| }; |
| |
| /// \ingroup ColorConvert |
| /// \brief RGB to Gray |
| template <> |
| struct default_color_converter_impl<rgb_t,gray_t> { |
| template <typename P1, typename P2> |
| void operator()(const P1& src, P2& dst) const { |
| get_color(dst,gray_color_t()) = |
| detail::rgb_to_luminance<typename color_element_type<P2,gray_color_t>::type>( |
| get_color(src,red_t()), get_color(src,green_t()), get_color(src,blue_t()) |
| ); |
| } |
| }; |
| |
| |
| /// \ingroup ColorConvert |
| /// \brief RGB to CMYK (not the fastest code in the world) |
| /// |
| /// k = min(1 - r, 1 - g, 1 - b) |
| /// c = (1 - r - k) / (1 - k) |
| /// m = (1 - g - k) / (1 - k) |
| /// y = (1 - b - k) / (1 - k) |
| template <> |
| struct default_color_converter_impl<rgb_t,cmyk_t> { |
| template <typename P1, typename P2> |
| void operator()(const P1& src, P2& dst) const { |
| typedef typename channel_type<P2>::type T2; |
| get_color(dst,cyan_t()) = channel_invert(channel_convert<T2>(get_color(src,red_t()))); // c = 1 - r |
| get_color(dst,magenta_t()) = channel_invert(channel_convert<T2>(get_color(src,green_t()))); // m = 1 - g |
| get_color(dst,yellow_t()) = channel_invert(channel_convert<T2>(get_color(src,blue_t()))); // y = 1 - b |
| get_color(dst,black_t()) = (std::min)(get_color(dst,cyan_t()), |
| (std::min)(get_color(dst,magenta_t()), |
| get_color(dst,yellow_t()))); // k = minimum(c, m, y) |
| T2 x = channel_traits<T2>::max_value()-get_color(dst,black_t()); // x = 1 - k |
| if (x>0.0001f) { |
| float x1 = channel_traits<T2>::max_value()/float(x); |
| get_color(dst,cyan_t()) = (T2)((get_color(dst,cyan_t()) - get_color(dst,black_t()))*x1); // c = (c - k) / x |
| get_color(dst,magenta_t()) = (T2)((get_color(dst,magenta_t()) - get_color(dst,black_t()))*x1); // m = (m - k) / x |
| get_color(dst,yellow_t()) = (T2)((get_color(dst,yellow_t()) - get_color(dst,black_t()))*x1); // y = (y - k) / x |
| } else { |
| get_color(dst,cyan_t())=get_color(dst,magenta_t())=get_color(dst,yellow_t())=0; |
| } |
| } |
| }; |
| |
| /// \ingroup ColorConvert |
| /// \brief CMYK to RGB (not the fastest code in the world) |
| /// |
| /// r = 1 - min(1, c*(1-k)+k) |
| /// g = 1 - min(1, m*(1-k)+k) |
| /// b = 1 - min(1, y*(1-k)+k) |
| template <> |
| struct default_color_converter_impl<cmyk_t,rgb_t> { |
| template <typename P1, typename P2> |
| void operator()(const P1& src, P2& dst) const { |
| typedef typename channel_type<P1>::type T1; |
| get_color(dst,red_t()) = |
| channel_convert<typename color_element_type<P2,red_t>::type>( |
| channel_invert<T1>( |
| (std::min)(channel_traits<T1>::max_value(), |
| T1(get_color(src,cyan_t())*channel_invert(get_color(src,black_t()))+get_color(src,black_t()))))); |
| get_color(dst,green_t())= |
| channel_convert<typename color_element_type<P2,green_t>::type>( |
| channel_invert<T1>( |
| (std::min)(channel_traits<T1>::max_value(), |
| T1(get_color(src,magenta_t())*channel_invert(get_color(src,black_t()))+get_color(src,black_t()))))); |
| get_color(dst,blue_t()) = |
| channel_convert<typename color_element_type<P2,blue_t>::type>( |
| channel_invert<T1>( |
| (std::min)(channel_traits<T1>::max_value(), |
| T1(get_color(src,yellow_t())*channel_invert(get_color(src,black_t()))+get_color(src,black_t()))))); |
| } |
| }; |
| |
| |
| /// \ingroup ColorConvert |
| /// \brief CMYK to Gray |
| /// |
| /// gray = (1 - 0.212c - 0.715m - 0.0722y) * (1 - k) |
| template <> |
| struct default_color_converter_impl<cmyk_t,gray_t> { |
| template <typename P1, typename P2> |
| void operator()(const P1& src, P2& dst) const { |
| get_color(dst,gray_color_t())= |
| channel_convert<typename color_element_type<P2,gray_t>::type>( |
| channel_multiply( |
| channel_invert( |
| detail::rgb_to_luminance<typename color_element_type<P1,black_t>::type>( |
| get_color(src,cyan_t()), |
| get_color(src,magenta_t()), |
| get_color(src,yellow_t()) |
| ) |
| ), |
| channel_invert(get_color(src,black_t())))); |
| } |
| }; |
| |
| namespace detail { |
| template <typename Pixel> |
| typename channel_type<Pixel>::type alpha_or_max_impl(const Pixel& p, mpl::true_) { |
| return get_color(p,alpha_t()); |
| } |
| template <typename Pixel> |
| typename channel_type<Pixel>::type alpha_or_max_impl(const Pixel& , mpl::false_) { |
| return channel_traits<typename channel_type<Pixel>::type>::max_value(); |
| } |
| } // namespace detail |
| |
| // Returns max_value if the pixel has no alpha channel. Otherwise returns the alpha. |
| template <typename Pixel> |
| typename channel_type<Pixel>::type alpha_or_max(const Pixel& p) { |
| return detail::alpha_or_max_impl(p, mpl::contains<typename color_space_type<Pixel>::type,alpha_t>()); |
| } |
| |
| |
| /// \ingroup ColorConvert |
| /// \brief Converting any pixel type to RGBA. Note: Supports homogeneous pixels only. |
| template <typename C1> |
| struct default_color_converter_impl<C1,rgba_t> { |
| template <typename P1, typename P2> |
| void operator()(const P1& src, P2& dst) const { |
| typedef typename channel_type<P2>::type T2; |
| pixel<T2,rgb_layout_t> tmp; |
| default_color_converter_impl<C1,rgb_t>()(src,tmp); |
| get_color(dst,red_t()) =get_color(tmp,red_t()); |
| get_color(dst,green_t())=get_color(tmp,green_t()); |
| get_color(dst,blue_t()) =get_color(tmp,blue_t()); |
| get_color(dst,alpha_t())=channel_convert<T2>(alpha_or_max(src)); |
| } |
| }; |
| |
| /// \ingroup ColorConvert |
| /// \brief Converting RGBA to any pixel type. Note: Supports homogeneous pixels only. |
| /// |
| /// Done by multiplying the alpha to get to RGB, then converting the RGB to the target pixel type |
| /// Note: This may be slower if the compiler doesn't optimize out constructing/destructing a temporary RGB pixel. |
| /// Consider rewriting if performance is an issue |
| template <typename C2> |
| struct default_color_converter_impl<rgba_t,C2> { |
| template <typename P1, typename P2> |
| void operator()(const P1& src, P2& dst) const { |
| typedef typename channel_type<P1>::type T1; |
| default_color_converter_impl<rgb_t,C2>()( |
| pixel<T1,rgb_layout_t>(channel_multiply(get_color(src,red_t()), get_color(src,alpha_t())), |
| channel_multiply(get_color(src,green_t()),get_color(src,alpha_t())), |
| channel_multiply(get_color(src,blue_t()), get_color(src,alpha_t()))) |
| ,dst); |
| } |
| }; |
| |
| /// \ingroup ColorConvert |
| /// \brief Unfortunately RGBA to RGBA must be explicitly provided - otherwise we get ambiguous specialization error. |
| template <> |
| struct default_color_converter_impl<rgba_t,rgba_t> { |
| template <typename P1, typename P2> |
| void operator()(const P1& src, P2& dst) const { |
| static_for_each(src,dst,default_channel_converter()); |
| } |
| }; |
| |
| /// @defgroup ColorConvert Color Space Converion |
| /// \ingroup ColorSpaces |
| /// \brief Support for conversion between pixels of different color spaces and channel depths |
| |
| /// \ingroup PixelAlgorithm ColorConvert |
| /// \brief class for color-converting one pixel to another |
| struct default_color_converter { |
| template <typename SrcP, typename DstP> |
| void operator()(const SrcP& src,DstP& dst) const { |
| typedef typename color_space_type<SrcP>::type SrcColorSpace; |
| typedef typename color_space_type<DstP>::type DstColorSpace; |
| default_color_converter_impl<SrcColorSpace,DstColorSpace>()(src,dst); |
| } |
| }; |
| |
| /// \ingroup PixelAlgorithm |
| /// \brief helper function for converting one pixel to another using GIL default color-converters |
| /// where ScrP models HomogeneousPixelConcept |
| /// DstP models HomogeneousPixelValueConcept |
| template <typename SrcP, typename DstP> |
| inline void color_convert(const SrcP& src, DstP& dst) { |
| default_color_converter()(src,dst); |
| } |
| |
| } } // namespace boost::gil |
| |
| #endif |