• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     static_assert(
45         std::is_same<C1, C2>::value,
46         "default_color_converter_impl not specialized for given color spaces");
47 };
48 
49 /// \ingroup ColorConvert
50 /// \brief When the color space is the same, color convertion performs channel depth conversion
51 template <typename C>
52 struct default_color_converter_impl<C,C> {
53     template <typename P1, typename P2>
operator ()boost::gil::default_color_converter_impl54     void operator()(const P1& src, P2& dst) const {
55         static_for_each(src,dst,default_channel_converter());
56     }
57 };
58 
59 namespace detail {
60 
61 /// red * .3 + green * .59 + blue * .11 + .5
62 
63 // The default implementation of to_luminance uses float0..1 as the intermediate channel type
64 template <typename RedChannel, typename GreenChannel, typename BlueChannel, typename GrayChannelValue>
65 struct rgb_to_luminance_fn {
operator ()boost::gil::detail::rgb_to_luminance_fn66     GrayChannelValue operator()(const RedChannel& red, const GreenChannel& green, const BlueChannel& blue) const {
67         return channel_convert<GrayChannelValue>(float32_t(
68             channel_convert<float32_t>(red  )*0.30f +
69             channel_convert<float32_t>(green)*0.59f +
70             channel_convert<float32_t>(blue )*0.11f) );
71     }
72 };
73 
74 // performance specialization for unsigned char
75 template <typename GrayChannelValue>
76 struct rgb_to_luminance_fn<uint8_t,uint8_t,uint8_t, GrayChannelValue> {
operator ()boost::gil::detail::rgb_to_luminance_fn77     GrayChannelValue operator()(uint8_t red, uint8_t green, uint8_t blue) const {
78         return channel_convert<GrayChannelValue>(uint8_t(
79             ((uint32_t(red  )*4915 + uint32_t(green)*9667 + uint32_t(blue )*1802) + 8192) >> 14));
80     }
81 };
82 
83 template <typename GrayChannel, typename RedChannel, typename GreenChannel, typename BlueChannel>
rgb_to_luminance(const RedChannel & red,const GreenChannel & green,const BlueChannel & blue)84 typename channel_traits<GrayChannel>::value_type rgb_to_luminance(const RedChannel& red, const GreenChannel& green, const BlueChannel& blue) {
85     return rgb_to_luminance_fn<RedChannel,GreenChannel,BlueChannel,
86                                typename channel_traits<GrayChannel>::value_type>()(red,green,blue);
87 }
88 
89 }   // namespace detail
90 
91 /// \ingroup ColorConvert
92 /// \brief Gray to RGB
93 template <>
94 struct default_color_converter_impl<gray_t,rgb_t> {
95     template <typename P1, typename P2>
operator ()boost::gil::default_color_converter_impl96     void operator()(const P1& src, P2& dst) const {
97         get_color(dst,red_t())  =
98             channel_convert<typename color_element_type<P2, red_t  >::type>(get_color(src,gray_color_t()));
99         get_color(dst,green_t())=
100             channel_convert<typename color_element_type<P2, green_t>::type>(get_color(src,gray_color_t()));
101         get_color(dst,blue_t()) =
102             channel_convert<typename color_element_type<P2, blue_t >::type>(get_color(src,gray_color_t()));
103     }
104 };
105 
106 /// \ingroup ColorConvert
107 /// \brief Gray to CMYK
108 /// \todo FIXME: Where does this calculation come from? Shouldn't gray be inverted?
109 ///              Currently, white becomes black and black becomes white.
110 template <>
111 struct default_color_converter_impl<gray_t,cmyk_t> {
112     template <typename P1, typename P2>
operator ()boost::gil::default_color_converter_impl113     void operator()(const P1& src, P2& dst) const {
114         get_color(dst,cyan_t())=
115             channel_traits<typename color_element_type<P2, cyan_t   >::type>::min_value();
116         get_color(dst,magenta_t())=
117             channel_traits<typename color_element_type<P2, magenta_t>::type>::min_value();
118         get_color(dst,yellow_t())=
119             channel_traits<typename color_element_type<P2, yellow_t >::type>::min_value();
120         get_color(dst,black_t())=
121             channel_convert<typename color_element_type<P2, black_t >::type>(get_color(src,gray_color_t()));
122     }
123 };
124 
125 /// \ingroup ColorConvert
126 /// \brief RGB to Gray
127 template <>
128 struct default_color_converter_impl<rgb_t,gray_t> {
129     template <typename P1, typename P2>
operator ()boost::gil::default_color_converter_impl130     void operator()(const P1& src, P2& dst) const {
131         get_color(dst,gray_color_t()) =
132             detail::rgb_to_luminance<typename color_element_type<P2,gray_color_t>::type>(
133                 get_color(src,red_t()), get_color(src,green_t()), get_color(src,blue_t())
134             );
135     }
136 };
137 
138 
139 /// \ingroup ColorConvert
140 /// \brief RGB to CMYK (not the fastest code in the world)
141 ///
142 /// k = min(1 - r, 1 - g, 1 - b)
143 /// c = (1 - r - k) / (1 - k)
144 /// m = (1 - g - k) / (1 - k)
145 /// y = (1 - b - k) / (1 - k)
146 /// where `1` denotes max value of channel type of destination pixel.
147 ///
148 /// The conversion from RGB to CMYK is based on CMY->CMYK (Version 2)
149 /// from the Principles of Digital Image Processing - Fundamental Techniques
150 /// by Burger, Wilhelm, Burge, Mark J.
151 /// and it is a gross approximation not precise enough for professional work.
152 ///
153 /// \todo FIXME: The original implementation did not handle properly signed CMYK pixels as destination
154 ///
155 template <>
156 struct default_color_converter_impl<rgb_t, cmyk_t>
157 {
158     template <typename SrcPixel, typename DstPixel>
operator ()boost::gil::default_color_converter_impl159     void operator()(SrcPixel const& src, DstPixel& dst) const
160     {
161         using src_t = typename channel_type<SrcPixel>::type;
162         src_t const r = get_color(src, red_t());
163         src_t const g = get_color(src, green_t());
164         src_t const b = get_color(src, blue_t());
165 
166         using dst_t   = typename channel_type<DstPixel>::type;
167         dst_t const c = channel_invert(channel_convert<dst_t>(r)); // c = 1 - r
168         dst_t const m = channel_invert(channel_convert<dst_t>(g)); // m = 1 - g
169         dst_t const y = channel_invert(channel_convert<dst_t>(b)); // y = 1 - b
170         dst_t const k = (std::min)(c, (std::min)(m, y));           // k = minimum(c, m, y)
171 
172         // Apply color correction, strengthening, reducing non-zero components by
173         // s = 1 / (1 - k) for k < 1, where 1 denotes dst_t max, otherwise s = 1 (literal).
174         dst_t const dst_max = channel_traits<dst_t>::max_value();
175         dst_t const s_div   = dst_max - k;
176         if (s_div != 0)
177         {
178             double const s              = dst_max / static_cast<double>(s_div);
179             get_color(dst, cyan_t())    = static_cast<dst_t>((c - k) * s);
180             get_color(dst, magenta_t()) = static_cast<dst_t>((m - k) * s);
181             get_color(dst, yellow_t())  = static_cast<dst_t>((y - k) * s);
182         }
183         else
184         {
185             // Black only for k = 1 (max of dst_t)
186             get_color(dst, cyan_t())    = channel_traits<dst_t>::min_value();
187             get_color(dst, magenta_t()) = channel_traits<dst_t>::min_value();
188             get_color(dst, yellow_t())  = channel_traits<dst_t>::min_value();
189         }
190         get_color(dst, black_t()) = k;
191     }
192 };
193 
194 /// \ingroup ColorConvert
195 /// \brief CMYK to RGB (not the fastest code in the world)
196 ///
197 /// r = 1 - min(1, c*(1-k)+k)
198 /// g = 1 - min(1, m*(1-k)+k)
199 /// b = 1 - min(1, y*(1-k)+k)
200 template <>
201 struct default_color_converter_impl<cmyk_t,rgb_t> {
202     template <typename P1, typename P2>
operator ()boost::gil::default_color_converter_impl203     void operator()(const P1& src, P2& dst) const {
204         using T1 = typename channel_type<P1>::type;
205         get_color(dst,red_t())  =
206             channel_convert<typename color_element_type<P2,red_t>::type>(
207                 channel_invert<T1>(
208                     (std::min)(channel_traits<T1>::max_value(),
209                              T1(channel_multiply(get_color(src,cyan_t()),channel_invert(get_color(src,black_t())))+get_color(src,black_t())))));
210         get_color(dst,green_t())=
211             channel_convert<typename color_element_type<P2,green_t>::type>(
212                 channel_invert<T1>(
213                     (std::min)(channel_traits<T1>::max_value(),
214                              T1(channel_multiply(get_color(src,magenta_t()),channel_invert(get_color(src,black_t())))+get_color(src,black_t())))));
215         get_color(dst,blue_t()) =
216             channel_convert<typename color_element_type<P2,blue_t>::type>(
217                 channel_invert<T1>(
218                     (std::min)(channel_traits<T1>::max_value(),
219                              T1(channel_multiply(get_color(src,yellow_t()),channel_invert(get_color(src,black_t())))+get_color(src,black_t())))));
220     }
221 };
222 
223 
224 /// \ingroup ColorConvert
225 /// \brief CMYK to Gray
226 ///
227 /// gray = (1 - 0.212c - 0.715m - 0.0722y) * (1 - k)
228 template <>
229 struct default_color_converter_impl<cmyk_t,gray_t> {
230     template <typename P1, typename P2>
operator ()boost::gil::default_color_converter_impl231     void operator()(const P1& src, P2& dst) const  {
232         get_color(dst,gray_color_t())=
233             channel_convert<typename color_element_type<P2,gray_color_t>::type>(
234                 channel_multiply(
235                     channel_invert(
236                        detail::rgb_to_luminance<typename color_element_type<P1,black_t>::type>(
237                             get_color(src,cyan_t()),
238                             get_color(src,magenta_t()),
239                             get_color(src,yellow_t())
240                        )
241                     ),
242                     channel_invert(get_color(src,black_t()))));
243     }
244 };
245 
246 namespace detail {
247 
248 template <typename Pixel>
alpha_or_max_impl(Pixel const & p,std::true_type)249 auto alpha_or_max_impl(Pixel const& p, std::true_type) -> typename channel_type<Pixel>::type
250 {
251     return get_color(p,alpha_t());
252 }
253 template <typename Pixel>
alpha_or_max_impl(Pixel const &,std::false_type)254 auto alpha_or_max_impl(Pixel const&, std::false_type) -> typename channel_type<Pixel>::type
255 {
256     return channel_traits<typename channel_type<Pixel>::type>::max_value();
257 }
258 
259 } // namespace detail
260 
261 // Returns max_value if the pixel has no alpha channel. Otherwise returns the alpha.
262 template <typename Pixel>
alpha_or_max(Pixel const & p)263 auto alpha_or_max(Pixel const& p) -> typename channel_type<Pixel>::type
264 {
265     return detail::alpha_or_max_impl(
266         p,
267         mp11::mp_contains<typename color_space_type<Pixel>::type, alpha_t>());
268 }
269 
270 
271 /// \ingroup ColorConvert
272 /// \brief Converting any pixel type to RGBA. Note: Supports homogeneous pixels only.
273 template <typename C1>
274 struct default_color_converter_impl<C1,rgba_t> {
275     template <typename P1, typename P2>
operator ()boost::gil::default_color_converter_impl276     void operator()(const P1& src, P2& dst) const {
277         using T2 = typename channel_type<P2>::type;
278         pixel<T2,rgb_layout_t> tmp;
279         default_color_converter_impl<C1,rgb_t>()(src,tmp);
280         get_color(dst,red_t())  =get_color(tmp,red_t());
281         get_color(dst,green_t())=get_color(tmp,green_t());
282         get_color(dst,blue_t()) =get_color(tmp,blue_t());
283         get_color(dst,alpha_t())=channel_convert<T2>(alpha_or_max(src));
284     }
285 };
286 
287 /// \ingroup ColorConvert
288 ///  \brief Converting RGBA to any pixel type. Note: Supports homogeneous pixels only.
289 ///
290 /// Done by multiplying the alpha to get to RGB, then converting the RGB to the target pixel type
291 /// Note: This may be slower if the compiler doesn't optimize out constructing/destructing a temporary RGB pixel.
292 ///       Consider rewriting if performance is an issue
293 template <typename C2>
294 struct default_color_converter_impl<rgba_t,C2> {
295     template <typename P1, typename P2>
operator ()boost::gil::default_color_converter_impl296     void operator()(const P1& src, P2& dst) const {
297         using T1 = typename channel_type<P1>::type;
298         default_color_converter_impl<rgb_t,C2>()(
299             pixel<T1,rgb_layout_t>(channel_multiply(get_color(src,red_t()),  get_color(src,alpha_t())),
300                                    channel_multiply(get_color(src,green_t()),get_color(src,alpha_t())),
301                                    channel_multiply(get_color(src,blue_t()), get_color(src,alpha_t())))
302             ,dst);
303     }
304 };
305 
306 /// \ingroup ColorConvert
307 /// \brief Unfortunately RGBA to RGBA must be explicitly provided - otherwise we get ambiguous specialization error.
308 template <>
309 struct default_color_converter_impl<rgba_t,rgba_t> {
310     template <typename P1, typename P2>
operator ()boost::gil::default_color_converter_impl311     void operator()(const P1& src, P2& dst) const {
312         static_for_each(src,dst,default_channel_converter());
313     }
314 };
315 
316 /// @defgroup ColorConvert Color Space Converion
317 /// \ingroup ColorSpaces
318 /// \brief Support for conversion between pixels of different color spaces and channel depths
319 
320 /// \ingroup PixelAlgorithm ColorConvert
321 /// \brief class for color-converting one pixel to another
322 struct default_color_converter {
323     template <typename SrcP, typename DstP>
operator ()boost::gil::default_color_converter324     void operator()(const SrcP& src,DstP& dst) const {
325         using SrcColorSpace = typename color_space_type<SrcP>::type;
326         using DstColorSpace = typename color_space_type<DstP>::type;
327         default_color_converter_impl<SrcColorSpace,DstColorSpace>()(src,dst);
328     }
329 };
330 
331 /// \ingroup PixelAlgorithm
332 /// \brief helper function for converting one pixel to another using GIL default color-converters
333 ///     where ScrP models HomogeneousPixelConcept
334 ///           DstP models HomogeneousPixelValueConcept
335 template <typename SrcP, typename DstP>
color_convert(const SrcP & src,DstP & dst)336 inline void color_convert(const SrcP& src, DstP& dst) {
337     default_color_converter()(src,dst);
338 }
339 
340 } }  // namespace boost::gil
341 
342 #endif
343