2 // Copyright 2005-2007 Adobe Systems Incorporated
4 // Distributed under the Boost Software License, Version 1.0
5 // See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt
8 #ifndef BOOST_GIL_COLOR_CONVERT_HPP
9 #define BOOST_GIL_COLOR_CONVERT_HPP
11 #include <boost/gil/channel_algorithm.hpp>
12 #include <boost/gil/cmyk.hpp>
13 #include <boost/gil/color_base_algorithm.hpp>
14 #include <boost/gil/gray.hpp>
15 #include <boost/gil/metafunctions.hpp>
16 #include <boost/gil/pixel.hpp>
17 #include <boost/gil/rgb.hpp>
18 #include <boost/gil/rgba.hpp>
19 #include <boost/gil/utilities.hpp>
23 #include <type_traits>
25 namespace boost { namespace gil {
27 /// Support for fast and simple color conversion.
28 /// Accurate color conversion using color profiles can be supplied separately in a dedicated module.
31 template <typename P> struct channel_type;
33 ////////////////////////////////////////////////////////////////////////////////////////
35 /// COLOR SPACE CONVERSION
37 ////////////////////////////////////////////////////////////////////////////////////////
39 /// \ingroup ColorConvert
40 /// \brief Color Convertion function object. To be specialized for every src/dst color space
41 template <typename C1, typename C2>
42 struct default_color_converter_impl {};
44 /// \ingroup ColorConvert
45 /// \brief When the color space is the same, color convertion performs channel depth conversion
47 struct default_color_converter_impl<C,C> {
48 template <typename P1, typename P2>
49 void operator()(const P1& src, P2& dst) const {
50 static_for_each(src,dst,default_channel_converter());
56 /// red * .3 + green * .59 + blue * .11 + .5
58 // The default implementation of to_luminance uses float0..1 as the intermediate channel type
59 template <typename RedChannel, typename GreenChannel, typename BlueChannel, typename GrayChannelValue>
60 struct rgb_to_luminance_fn {
61 GrayChannelValue operator()(const RedChannel& red, const GreenChannel& green, const BlueChannel& blue) const {
62 return channel_convert<GrayChannelValue>(float32_t(
63 channel_convert<float32_t>(red )*0.30f +
64 channel_convert<float32_t>(green)*0.59f +
65 channel_convert<float32_t>(blue )*0.11f) );
69 // performance specialization for unsigned char
70 template <typename GrayChannelValue>
71 struct rgb_to_luminance_fn<uint8_t,uint8_t,uint8_t, GrayChannelValue> {
72 GrayChannelValue operator()(uint8_t red, uint8_t green, uint8_t blue) const {
73 return channel_convert<GrayChannelValue>(uint8_t(
74 ((uint32_t(red )*4915 + uint32_t(green)*9667 + uint32_t(blue )*1802) + 8192) >> 14));
78 template <typename GrayChannel, typename RedChannel, typename GreenChannel, typename BlueChannel>
79 typename channel_traits<GrayChannel>::value_type rgb_to_luminance(const RedChannel& red, const GreenChannel& green, const BlueChannel& blue) {
80 return rgb_to_luminance_fn<RedChannel,GreenChannel,BlueChannel,
81 typename channel_traits<GrayChannel>::value_type>()(red,green,blue);
86 /// \ingroup ColorConvert
87 /// \brief Gray to RGB
89 struct default_color_converter_impl<gray_t,rgb_t> {
90 template <typename P1, typename P2>
91 void operator()(const P1& src, P2& dst) const {
92 get_color(dst,red_t()) =
93 channel_convert<typename color_element_type<P2, red_t >::type>(get_color(src,gray_color_t()));
94 get_color(dst,green_t())=
95 channel_convert<typename color_element_type<P2, green_t>::type>(get_color(src,gray_color_t()));
96 get_color(dst,blue_t()) =
97 channel_convert<typename color_element_type<P2, blue_t >::type>(get_color(src,gray_color_t()));
101 /// \ingroup ColorConvert
102 /// \brief Gray to CMYK
104 struct default_color_converter_impl<gray_t,cmyk_t> {
105 template <typename P1, typename P2>
106 void operator()(const P1& src, P2& dst) const {
107 get_color(dst,cyan_t())=
108 channel_traits<typename color_element_type<P2, cyan_t >::type>::min_value();
109 get_color(dst,magenta_t())=
110 channel_traits<typename color_element_type<P2, magenta_t>::type>::min_value();
111 get_color(dst,yellow_t())=
112 channel_traits<typename color_element_type<P2, yellow_t >::type>::min_value();
113 get_color(dst,black_t())=
114 channel_convert<typename color_element_type<P2, black_t >::type>(get_color(src,gray_color_t()));
118 /// \ingroup ColorConvert
119 /// \brief RGB to Gray
121 struct default_color_converter_impl<rgb_t,gray_t> {
122 template <typename P1, typename P2>
123 void operator()(const P1& src, P2& dst) const {
124 get_color(dst,gray_color_t()) =
125 detail::rgb_to_luminance<typename color_element_type<P2,gray_color_t>::type>(
126 get_color(src,red_t()), get_color(src,green_t()), get_color(src,blue_t())
132 /// \ingroup ColorConvert
133 /// \brief RGB to CMYK (not the fastest code in the world)
135 /// k = min(1 - r, 1 - g, 1 - b)
136 /// c = (1 - r - k) / (1 - k)
137 /// m = (1 - g - k) / (1 - k)
138 /// y = (1 - b - k) / (1 - k)
140 struct default_color_converter_impl<rgb_t,cmyk_t> {
141 template <typename P1, typename P2>
142 void operator()(const P1& src, P2& dst) const {
143 using T2 = typename channel_type<P2>::type;
144 get_color(dst,cyan_t()) = channel_invert(channel_convert<T2>(get_color(src,red_t()))); // c = 1 - r
145 get_color(dst,magenta_t()) = channel_invert(channel_convert<T2>(get_color(src,green_t()))); // m = 1 - g
146 get_color(dst,yellow_t()) = channel_invert(channel_convert<T2>(get_color(src,blue_t()))); // y = 1 - b
147 get_color(dst,black_t()) = (std::min)(get_color(dst,cyan_t()),
148 (std::min)(get_color(dst,magenta_t()),
149 get_color(dst,yellow_t()))); // k = minimum(c, m, y)
150 T2 x = channel_traits<T2>::max_value()-get_color(dst,black_t()); // x = 1 - k
152 float x1 = channel_traits<T2>::max_value()/float(x);
153 get_color(dst,cyan_t()) = (T2)((get_color(dst,cyan_t()) - get_color(dst,black_t()))*x1); // c = (c - k) / x
154 get_color(dst,magenta_t()) = (T2)((get_color(dst,magenta_t()) - get_color(dst,black_t()))*x1); // m = (m - k) / x
155 get_color(dst,yellow_t()) = (T2)((get_color(dst,yellow_t()) - get_color(dst,black_t()))*x1); // y = (y - k) / x
157 get_color(dst,cyan_t())=get_color(dst,magenta_t())=get_color(dst,yellow_t())=0;
162 /// \ingroup ColorConvert
163 /// \brief CMYK to RGB (not the fastest code in the world)
165 /// r = 1 - min(1, c*(1-k)+k)
166 /// g = 1 - min(1, m*(1-k)+k)
167 /// b = 1 - min(1, y*(1-k)+k)
169 struct default_color_converter_impl<cmyk_t,rgb_t> {
170 template <typename P1, typename P2>
171 void operator()(const P1& src, P2& dst) const {
172 using T1 = typename channel_type<P1>::type;
173 get_color(dst,red_t()) =
174 channel_convert<typename color_element_type<P2,red_t>::type>(
176 (std::min)(channel_traits<T1>::max_value(),
177 T1(channel_multiply(get_color(src,cyan_t()),channel_invert(get_color(src,black_t())))+get_color(src,black_t())))));
178 get_color(dst,green_t())=
179 channel_convert<typename color_element_type<P2,green_t>::type>(
181 (std::min)(channel_traits<T1>::max_value(),
182 T1(channel_multiply(get_color(src,magenta_t()),channel_invert(get_color(src,black_t())))+get_color(src,black_t())))));
183 get_color(dst,blue_t()) =
184 channel_convert<typename color_element_type<P2,blue_t>::type>(
186 (std::min)(channel_traits<T1>::max_value(),
187 T1(channel_multiply(get_color(src,yellow_t()),channel_invert(get_color(src,black_t())))+get_color(src,black_t())))));
192 /// \ingroup ColorConvert
193 /// \brief CMYK to Gray
195 /// gray = (1 - 0.212c - 0.715m - 0.0722y) * (1 - k)
197 struct default_color_converter_impl<cmyk_t,gray_t> {
198 template <typename P1, typename P2>
199 void operator()(const P1& src, P2& dst) const {
200 get_color(dst,gray_color_t())=
201 channel_convert<typename color_element_type<P2,gray_color_t>::type>(
204 detail::rgb_to_luminance<typename color_element_type<P1,black_t>::type>(
205 get_color(src,cyan_t()),
206 get_color(src,magenta_t()),
207 get_color(src,yellow_t())
210 channel_invert(get_color(src,black_t()))));
216 template <typename Pixel>
217 auto alpha_or_max_impl(Pixel const& p, std::true_type) -> typename channel_type<Pixel>::type
219 return get_color(p,alpha_t());
221 template <typename Pixel>
222 auto alpha_or_max_impl(Pixel const&, std::false_type) -> typename channel_type<Pixel>::type
224 return channel_traits<typename channel_type<Pixel>::type>::max_value();
227 } // namespace detail
229 // Returns max_value if the pixel has no alpha channel. Otherwise returns the alpha.
230 template <typename Pixel>
231 auto alpha_or_max(Pixel const& p) -> typename channel_type<Pixel>::type
233 return detail::alpha_or_max_impl(
235 mp11::mp_contains<typename color_space_type<Pixel>::type, alpha_t>());
239 /// \ingroup ColorConvert
240 /// \brief Converting any pixel type to RGBA. Note: Supports homogeneous pixels only.
241 template <typename C1>
242 struct default_color_converter_impl<C1,rgba_t> {
243 template <typename P1, typename P2>
244 void operator()(const P1& src, P2& dst) const {
245 using T2 = typename channel_type<P2>::type;
246 pixel<T2,rgb_layout_t> tmp;
247 default_color_converter_impl<C1,rgb_t>()(src,tmp);
248 get_color(dst,red_t()) =get_color(tmp,red_t());
249 get_color(dst,green_t())=get_color(tmp,green_t());
250 get_color(dst,blue_t()) =get_color(tmp,blue_t());
251 get_color(dst,alpha_t())=channel_convert<T2>(alpha_or_max(src));
255 /// \ingroup ColorConvert
256 /// \brief Converting RGBA to any pixel type. Note: Supports homogeneous pixels only.
258 /// Done by multiplying the alpha to get to RGB, then converting the RGB to the target pixel type
259 /// Note: This may be slower if the compiler doesn't optimize out constructing/destructing a temporary RGB pixel.
260 /// Consider rewriting if performance is an issue
261 template <typename C2>
262 struct default_color_converter_impl<rgba_t,C2> {
263 template <typename P1, typename P2>
264 void operator()(const P1& src, P2& dst) const {
265 using T1 = typename channel_type<P1>::type;
266 default_color_converter_impl<rgb_t,C2>()(
267 pixel<T1,rgb_layout_t>(channel_multiply(get_color(src,red_t()), get_color(src,alpha_t())),
268 channel_multiply(get_color(src,green_t()),get_color(src,alpha_t())),
269 channel_multiply(get_color(src,blue_t()), get_color(src,alpha_t())))
274 /// \ingroup ColorConvert
275 /// \brief Unfortunately RGBA to RGBA must be explicitly provided - otherwise we get ambiguous specialization error.
277 struct default_color_converter_impl<rgba_t,rgba_t> {
278 template <typename P1, typename P2>
279 void operator()(const P1& src, P2& dst) const {
280 static_for_each(src,dst,default_channel_converter());
284 /// @defgroup ColorConvert Color Space Converion
285 /// \ingroup ColorSpaces
286 /// \brief Support for conversion between pixels of different color spaces and channel depths
288 /// \ingroup PixelAlgorithm ColorConvert
289 /// \brief class for color-converting one pixel to another
290 struct default_color_converter {
291 template <typename SrcP, typename DstP>
292 void operator()(const SrcP& src,DstP& dst) const {
293 using SrcColorSpace = typename color_space_type<SrcP>::type;
294 using DstColorSpace = typename color_space_type<DstP>::type;
295 default_color_converter_impl<SrcColorSpace,DstColorSpace>()(src,dst);
299 /// \ingroup PixelAlgorithm
300 /// \brief helper function for converting one pixel to another using GIL default color-converters
301 /// where ScrP models HomogeneousPixelConcept
302 /// DstP models HomogeneousPixelValueConcept
303 template <typename SrcP, typename DstP>
304 inline void color_convert(const SrcP& src, DstP& dst) {
305 default_color_converter()(src,dst);
308 } } // namespace boost::gil