/* 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 #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 struct channel_type; //////////////////////////////////////////////////////////////////////////////////////// /// /// COLOR SPACE CONVERSION /// //////////////////////////////////////////////////////////////////////////////////////// /// \ingroup ColorConvert /// \brief Color Convertion function object. To be specialized for every src/dst color space template struct default_color_converter_impl {}; /// \ingroup ColorConvert /// \brief When the color space is the same, color convertion performs channel depth conversion template struct default_color_converter_impl { template 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 struct rgb_to_luminance_fn { GrayChannelValue operator()(const RedChannel& red, const GreenChannel& green, const BlueChannel& blue) const { return channel_convert( bits32f( channel_convert(red )*0.30f + channel_convert(green)*0.59f + channel_convert(blue )*0.11f) ); } }; // performance specialization for unsigned char template struct rgb_to_luminance_fn { GrayChannelValue operator()(uint8_t red, uint8_t green, uint8_t blue) const { return channel_convert(uint8_t( ((uint32_t(red )*4915 + uint32_t(green)*9667 + uint32_t(blue )*1802) + 8192) >> 14)); } }; template typename channel_traits::value_type rgb_to_luminance(const RedChannel& red, const GreenChannel& green, const BlueChannel& blue) { return rgb_to_luminance_fn::value_type>()(red,green,blue); } } // namespace detail /// \ingroup ColorConvert /// \brief Gray to RGB template <> struct default_color_converter_impl { template void operator()(const P1& src, P2& dst) const { get_color(dst,red_t()) = channel_convert::type>(get_color(src,gray_color_t())); get_color(dst,green_t())= channel_convert::type>(get_color(src,gray_color_t())); get_color(dst,blue_t()) = channel_convert::type>(get_color(src,gray_color_t())); } }; /// \ingroup ColorConvert /// \brief Gray to CMYK template <> struct default_color_converter_impl { template void operator()(const P1& src, P2& dst) const { get_color(dst,cyan_t())= channel_traits::type>::min_value(); get_color(dst,magenta_t())= channel_traits::type>::min_value(); get_color(dst,yellow_t())= channel_traits::type>::min_value(); get_color(dst,black_t())= channel_convert::type>(get_color(src,gray_color_t())); } }; /// \ingroup ColorConvert /// \brief RGB to Gray template <> struct default_color_converter_impl { template void operator()(const P1& src, P2& dst) const { get_color(dst,gray_color_t()) = detail::rgb_to_luminance::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 { template void operator()(const P1& src, P2& dst) const { typedef typename channel_type::type T2; get_color(dst,cyan_t()) = channel_invert(channel_convert(get_color(src,red_t()))); // c = 1 - r get_color(dst,magenta_t()) = channel_invert(channel_convert(get_color(src,green_t()))); // m = 1 - g get_color(dst,yellow_t()) = channel_invert(channel_convert(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::max_value()-get_color(dst,black_t()); // x = 1 - k if (x>0.0001f) { float x1 = channel_traits::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 { template void operator()(const P1& src, P2& dst) const { typedef typename channel_type::type T1; get_color(dst,red_t()) = channel_convert::type>( channel_invert( (std::min)(channel_traits::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::type>( channel_invert( (std::min)(channel_traits::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::type>( channel_invert( (std::min)(channel_traits::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 { template void operator()(const P1& src, P2& dst) const { get_color(dst,gray_color_t())= channel_convert::type>( channel_multiply( channel_invert( detail::rgb_to_luminance::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 channel_type::type alpha_or_max_impl(const Pixel& p, mpl::true_) { return get_color(p,alpha_t()); } template typename channel_type::type alpha_or_max_impl(const Pixel& , mpl::false_) { return channel_traits::type>::max_value(); } } // namespace detail // Returns max_value if the pixel has no alpha channel. Otherwise returns the alpha. template typename channel_type::type alpha_or_max(const Pixel& p) { return detail::alpha_or_max_impl(p, mpl::contains::type,alpha_t>()); } /// \ingroup ColorConvert /// \brief Converting any pixel type to RGBA. Note: Supports homogeneous pixels only. template struct default_color_converter_impl { template void operator()(const P1& src, P2& dst) const { typedef typename channel_type::type T2; pixel tmp; default_color_converter_impl()(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(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 struct default_color_converter_impl { template void operator()(const P1& src, P2& dst) const { typedef typename channel_type::type T1; default_color_converter_impl()( pixel(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 { template 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 void operator()(const SrcP& src,DstP& dst) const { typedef typename color_space_type::type SrcColorSpace; typedef typename color_space_type::type DstColorSpace; default_color_converter_impl()(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 inline void color_convert(const SrcP& src, DstP& dst) { default_color_converter()(src,dst); } } } // namespace boost::gil #endif