/* | |
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 |