Imported Upstream version 1.72.0
[platform/upstream/boost.git] / boost / gil / color_convert.hpp
1 //
2 // Copyright 2005-2007 Adobe Systems Incorporated
3 //
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
7 //
8 #ifndef BOOST_GIL_COLOR_CONVERT_HPP
9 #define BOOST_GIL_COLOR_CONVERT_HPP
10
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>
20
21 #include <algorithm>
22 #include <functional>
23 #include <type_traits>
24
25 namespace boost { namespace gil {
26
27 /// Support for fast and simple color conversion.
28 /// Accurate color conversion using color profiles can be supplied separately in a dedicated module.
29
30 // Forward-declare
31 template <typename P> struct channel_type;
32
33 ////////////////////////////////////////////////////////////////////////////////////////
34 ///
35 ///                 COLOR SPACE CONVERSION
36 ///
37 ////////////////////////////////////////////////////////////////////////////////////////
38
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 {};
43
44 /// \ingroup ColorConvert
45 /// \brief When the color space is the same, color convertion performs channel depth conversion
46 template <typename C>
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());
51     }
52 };
53
54 namespace detail {
55
56 /// red * .3 + green * .59 + blue * .11 + .5
57
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) );
66     }
67 };
68
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));
75     }
76 };
77
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);
82 }
83
84 }   // namespace detail
85
86 /// \ingroup ColorConvert
87 /// \brief Gray to RGB
88 template <>
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()));
98     }
99 };
100
101 /// \ingroup ColorConvert
102 /// \brief Gray to CMYK
103 template <>
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()));
115     }
116 };
117
118 /// \ingroup ColorConvert
119 /// \brief RGB to Gray
120 template <>
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())
127             );
128     }
129 };
130
131
132 /// \ingroup ColorConvert
133 /// \brief RGB to CMYK (not the fastest code in the world)
134 ///
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)
139 template <>
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
151         if (x>0.0001f) {
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
156         } else {
157             get_color(dst,cyan_t())=get_color(dst,magenta_t())=get_color(dst,yellow_t())=0;
158         }
159     }
160 };
161
162 /// \ingroup ColorConvert
163 /// \brief CMYK to RGB (not the fastest code in the world)
164 ///
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)
168 template <>
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>(
175                 channel_invert<T1>(
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>(
180                 channel_invert<T1>(
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>(
185                 channel_invert<T1>(
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())))));
188     }
189 };
190
191
192 /// \ingroup ColorConvert
193 /// \brief CMYK to Gray
194 ///
195 /// gray = (1 - 0.212c - 0.715m - 0.0722y) * (1 - k)
196 template <>
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>(
202                 channel_multiply(
203                     channel_invert(
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())
208                        )
209                     ),
210                     channel_invert(get_color(src,black_t()))));
211     }
212 };
213
214 namespace detail {
215
216 template <typename Pixel>
217 auto alpha_or_max_impl(Pixel const& p, std::true_type) -> typename channel_type<Pixel>::type
218 {
219     return get_color(p,alpha_t());
220 }
221 template <typename Pixel>
222 auto alpha_or_max_impl(Pixel const&, std::false_type) -> typename channel_type<Pixel>::type
223 {
224     return channel_traits<typename channel_type<Pixel>::type>::max_value();
225 }
226
227 } // namespace detail
228
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
232 {
233     return detail::alpha_or_max_impl(
234         p,
235         mp11::mp_contains<typename color_space_type<Pixel>::type, alpha_t>());
236 }
237
238
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));
252     }
253 };
254
255 /// \ingroup ColorConvert
256 ///  \brief Converting RGBA to any pixel type. Note: Supports homogeneous pixels only.
257 ///
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())))
270             ,dst);
271     }
272 };
273
274 /// \ingroup ColorConvert
275 /// \brief Unfortunately RGBA to RGBA must be explicitly provided - otherwise we get ambiguous specialization error.
276 template <>
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());
281     }
282 };
283
284 /// @defgroup ColorConvert Color Space Converion
285 /// \ingroup ColorSpaces
286 /// \brief Support for conversion between pixels of different color spaces and channel depths
287
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);
296     }
297 };
298
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);
306 }
307
308 } }  // namespace boost::gil
309
310 #endif