1 | /* |
2 | Copyright 2005-2007 Adobe Systems Incorporated |
3 | |
4 | Use, modification and distribution are subject to the Boost Software License, |
5 | Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at |
6 | http://www.boost.org/LICENSE_1_0.txt). |
7 | |
8 | See http://opensource.adobe.com/gil for most recent version including documentation. |
9 | */ |
10 | /*************************************************************************************************/ |
11 | |
12 | #ifndef GIL_COLOR_CONVERT_HPP |
13 | #define GIL_COLOR_CONVERT_HPP |
14 | |
15 | //////////////////////////////////////////////////////////////////////////////////////// |
16 | /// \file |
17 | /// \brief GIL default color space conversions |
18 | /// \author Lubomir Bourdev and Hailin Jin \n |
19 | /// Adobe Systems Incorporated |
20 | /// \date 2005-2007 \n Last updated on January 30, 2007 |
21 | /// |
22 | /// Support for fast and simple color conversion. Accurate color conversion using color |
23 | /// profiles can be supplied separately in a dedicated module |
24 | /// |
25 | //////////////////////////////////////////////////////////////////////////////////////// |
26 | |
27 | #include <functional> |
28 | #include "gil_config.hpp" |
29 | #include "channel_algorithm.hpp" |
30 | #include "pixel.hpp" |
31 | #include "gray.hpp" |
32 | #include "rgb.hpp" |
33 | #include "rgba.hpp" |
34 | #include "cmyk.hpp" |
35 | #include "metafunctions.hpp" |
36 | #include "utilities.hpp" |
37 | #include "color_base_algorithm.hpp" |
38 | |
39 | namespace boost { namespace gil { |
40 | |
41 | // Forward-declare |
42 | template <typename P> struct channel_type; |
43 | |
44 | //////////////////////////////////////////////////////////////////////////////////////// |
45 | /// |
46 | /// COLOR SPACE CONVERSION |
47 | /// |
48 | //////////////////////////////////////////////////////////////////////////////////////// |
49 | |
50 | /// \ingroup ColorConvert |
51 | /// \brief Color Convertion function object. To be specialized for every src/dst color space |
52 | template <typename C1, typename C2> |
53 | struct default_color_converter_impl {}; |
54 | |
55 | /// \ingroup ColorConvert |
56 | /// \brief When the color space is the same, color convertion performs channel depth conversion |
57 | template <typename C> |
58 | struct default_color_converter_impl<C,C> { |
59 | template <typename P1, typename P2> |
60 | void operator()(const P1& src, P2& dst) const { |
61 | static_for_each(src,dst,default_channel_converter()); |
62 | } |
63 | }; |
64 | |
65 | namespace detail { |
66 | |
67 | /// red * .3 + green * .59 + blue * .11 + .5 |
68 | |
69 | // The default implementation of to_luminance uses float0..1 as the intermediate channel type |
70 | template <typename RedChannel, typename GreenChannel, typename BlueChannel, typename GrayChannelValue> |
71 | struct rgb_to_luminance_fn { |
72 | GrayChannelValue operator()(const RedChannel& red, const GreenChannel& green, const BlueChannel& blue) const { |
73 | return channel_convert<GrayChannelValue>( bits32f( |
74 | channel_convert<bits32f>(red )*0.30f + |
75 | channel_convert<bits32f>(green)*0.59f + |
76 | channel_convert<bits32f>(blue )*0.11f) ); |
77 | } |
78 | }; |
79 | |
80 | // performance specialization for unsigned char |
81 | template <typename GrayChannelValue> |
82 | struct rgb_to_luminance_fn<uint8_t,uint8_t,uint8_t, GrayChannelValue> { |
83 | GrayChannelValue operator()(uint8_t red, uint8_t green, uint8_t blue) const { |
84 | return channel_convert<GrayChannelValue>(uint8_t( |
85 | ((uint32_t(red )*4915 + uint32_t(green)*9667 + uint32_t(blue )*1802) + 8192) >> 14)); |
86 | } |
87 | }; |
88 | |
89 | template <typename GrayChannel, typename RedChannel, typename GreenChannel, typename BlueChannel> |
90 | typename channel_traits<GrayChannel>::value_type rgb_to_luminance(const RedChannel& red, const GreenChannel& green, const BlueChannel& blue) { |
91 | return rgb_to_luminance_fn<RedChannel,GreenChannel,BlueChannel, |
92 | typename channel_traits<GrayChannel>::value_type>()(red,green,blue); |
93 | } |
94 | |
95 | } // namespace detail |
96 | |
97 | /// \ingroup ColorConvert |
98 | /// \brief Gray to RGB |
99 | template <> |
100 | struct default_color_converter_impl<gray_t,rgb_t> { |
101 | template <typename P1, typename P2> |
102 | void operator()(const P1& src, P2& dst) const { |
103 | get_color(dst,red_t()) = |
104 | channel_convert<typename color_element_type<P2, red_t >::type>(get_color(src,gray_color_t())); |
105 | get_color(dst,green_t())= |
106 | channel_convert<typename color_element_type<P2, green_t>::type>(get_color(src,gray_color_t())); |
107 | get_color(dst,blue_t()) = |
108 | channel_convert<typename color_element_type<P2, blue_t >::type>(get_color(src,gray_color_t())); |
109 | } |
110 | }; |
111 | |
112 | /// \ingroup ColorConvert |
113 | /// \brief Gray to CMYK |
114 | template <> |
115 | struct default_color_converter_impl<gray_t,cmyk_t> { |
116 | template <typename P1, typename P2> |
117 | void operator()(const P1& src, P2& dst) const { |
118 | get_color(dst,cyan_t())= |
119 | channel_traits<typename color_element_type<P2, cyan_t >::type>::min_value(); |
120 | get_color(dst,magenta_t())= |
121 | channel_traits<typename color_element_type<P2, magenta_t>::type>::min_value(); |
122 | get_color(dst,yellow_t())= |
123 | channel_traits<typename color_element_type<P2, yellow_t >::type>::min_value(); |
124 | get_color(dst,black_t())= |
125 | channel_convert<typename color_element_type<P2, black_t >::type>(get_color(src,gray_color_t())); |
126 | } |
127 | }; |
128 | |
129 | /// \ingroup ColorConvert |
130 | /// \brief RGB to Gray |
131 | template <> |
132 | struct default_color_converter_impl<rgb_t,gray_t> { |
133 | template <typename P1, typename P2> |
134 | void operator()(const P1& src, P2& dst) const { |
135 | get_color(dst,gray_color_t()) = |
136 | detail::rgb_to_luminance<typename color_element_type<P2,gray_color_t>::type>( |
137 | get_color(src,red_t()), get_color(src,green_t()), get_color(src,blue_t()) |
138 | ); |
139 | } |
140 | }; |
141 | |
142 | |
143 | /// \ingroup ColorConvert |
144 | /// \brief RGB to CMYK (not the fastest code in the world) |
145 | /// |
146 | /// k = min(1 - r, 1 - g, 1 - b) |
147 | /// c = (1 - r - k) / (1 - k) |
148 | /// m = (1 - g - k) / (1 - k) |
149 | /// y = (1 - b - k) / (1 - k) |
150 | template <> |
151 | struct default_color_converter_impl<rgb_t,cmyk_t> { |
152 | template <typename P1, typename P2> |
153 | void operator()(const P1& src, P2& dst) const { |
154 | typedef typename channel_type<P2>::type T2; |
155 | get_color(dst,cyan_t()) = channel_invert(channel_convert<T2>(get_color(src,red_t()))); // c = 1 - r |
156 | get_color(dst,magenta_t()) = channel_invert(channel_convert<T2>(get_color(src,green_t()))); // m = 1 - g |
157 | get_color(dst,yellow_t()) = channel_invert(channel_convert<T2>(get_color(src,blue_t()))); // y = 1 - b |
158 | get_color(dst,black_t()) = (std::min)(get_color(dst,cyan_t()), |
159 | (std::min)(get_color(dst,magenta_t()), |
160 | get_color(dst,yellow_t()))); // k = minimum(c, m, y) |
161 | T2 x = channel_traits<T2>::max_value()-get_color(dst,black_t()); // x = 1 - k |
162 | if (x>0.0001f) { |
163 | float x1 = channel_traits<T2>::max_value()/float(x); |
164 | get_color(dst,cyan_t()) = (T2)((get_color(dst,cyan_t()) - get_color(dst,black_t()))*x1); // c = (c - k) / x |
165 | get_color(dst,magenta_t()) = (T2)((get_color(dst,magenta_t()) - get_color(dst,black_t()))*x1); // m = (m - k) / x |
166 | get_color(dst,yellow_t()) = (T2)((get_color(dst,yellow_t()) - get_color(dst,black_t()))*x1); // y = (y - k) / x |
167 | } else { |
168 | get_color(dst,cyan_t())=get_color(dst,magenta_t())=get_color(dst,yellow_t())=0; |
169 | } |
170 | } |
171 | }; |
172 | |
173 | /// \ingroup ColorConvert |
174 | /// \brief CMYK to RGB (not the fastest code in the world) |
175 | /// |
176 | /// r = 1 - min(1, c*(1-k)+k) |
177 | /// g = 1 - min(1, m*(1-k)+k) |
178 | /// b = 1 - min(1, y*(1-k)+k) |
179 | template <> |
180 | struct default_color_converter_impl<cmyk_t,rgb_t> { |
181 | template <typename P1, typename P2> |
182 | void operator()(const P1& src, P2& dst) const { |
183 | typedef typename channel_type<P1>::type T1; |
184 | get_color(dst,red_t()) = |
185 | channel_convert<typename color_element_type<P2,red_t>::type>( |
186 | channel_invert<T1>( |
187 | (std::min)(channel_traits<T1>::max_value(), |
188 | T1(get_color(src,cyan_t())*channel_invert(get_color(src,black_t()))+get_color(src,black_t()))))); |
189 | get_color(dst,green_t())= |
190 | channel_convert<typename color_element_type<P2,green_t>::type>( |
191 | channel_invert<T1>( |
192 | (std::min)(channel_traits<T1>::max_value(), |
193 | T1(get_color(src,magenta_t())*channel_invert(get_color(src,black_t()))+get_color(src,black_t()))))); |
194 | get_color(dst,blue_t()) = |
195 | channel_convert<typename color_element_type<P2,blue_t>::type>( |
196 | channel_invert<T1>( |
197 | (std::min)(channel_traits<T1>::max_value(), |
198 | T1(get_color(src,yellow_t())*channel_invert(get_color(src,black_t()))+get_color(src,black_t()))))); |
199 | } |
200 | }; |
201 | |
202 | |
203 | /// \ingroup ColorConvert |
204 | /// \brief CMYK to Gray |
205 | /// |
206 | /// gray = (1 - 0.212c - 0.715m - 0.0722y) * (1 - k) |
207 | template <> |
208 | struct default_color_converter_impl<cmyk_t,gray_t> { |
209 | template <typename P1, typename P2> |
210 | void operator()(const P1& src, P2& dst) const { |
211 | get_color(dst,gray_color_t())= |
212 | channel_convert<typename color_element_type<P2,gray_t>::type>( |
213 | channel_multiply( |
214 | channel_invert( |
215 | detail::rgb_to_luminance<typename color_element_type<P1,black_t>::type>( |
216 | get_color(src,cyan_t()), |
217 | get_color(src,magenta_t()), |
218 | get_color(src,yellow_t()) |
219 | ) |
220 | ), |
221 | channel_invert(get_color(src,black_t())))); |
222 | } |
223 | }; |
224 | |
225 | namespace detail { |
226 | template <typename Pixel> |
227 | typename channel_type<Pixel>::type alpha_or_max_impl(const Pixel& p, mpl::true_) { |
228 | return get_color(p,alpha_t()); |
229 | } |
230 | template <typename Pixel> |
231 | typename channel_type<Pixel>::type alpha_or_max_impl(const Pixel& , mpl::false_) { |
232 | return channel_traits<typename channel_type<Pixel>::type>::max_value(); |
233 | } |
234 | } // namespace detail |
235 | |
236 | // Returns max_value if the pixel has no alpha channel. Otherwise returns the alpha. |
237 | template <typename Pixel> |
238 | typename channel_type<Pixel>::type alpha_or_max(const Pixel& p) { |
239 | return detail::alpha_or_max_impl(p, mpl::contains<typename color_space_type<Pixel>::type,alpha_t>()); |
240 | } |
241 | |
242 | |
243 | /// \ingroup ColorConvert |
244 | /// \brief Converting any pixel type to RGBA. Note: Supports homogeneous pixels only. |
245 | template <typename C1> |
246 | struct default_color_converter_impl<C1,rgba_t> { |
247 | template <typename P1, typename P2> |
248 | void operator()(const P1& src, P2& dst) const { |
249 | typedef typename channel_type<P2>::type T2; |
250 | pixel<T2,rgb_layout_t> tmp; |
251 | default_color_converter_impl<C1,rgb_t>()(src,tmp); |
252 | get_color(dst,red_t()) =get_color(tmp,red_t()); |
253 | get_color(dst,green_t())=get_color(tmp,green_t()); |
254 | get_color(dst,blue_t()) =get_color(tmp,blue_t()); |
255 | get_color(dst,alpha_t())=channel_convert<T2>(alpha_or_max(src)); |
256 | } |
257 | }; |
258 | |
259 | /// \ingroup ColorConvert |
260 | /// \brief Converting RGBA to any pixel type. Note: Supports homogeneous pixels only. |
261 | /// |
262 | /// Done by multiplying the alpha to get to RGB, then converting the RGB to the target pixel type |
263 | /// Note: This may be slower if the compiler doesn't optimize out constructing/destructing a temporary RGB pixel. |
264 | /// Consider rewriting if performance is an issue |
265 | template <typename C2> |
266 | struct default_color_converter_impl<rgba_t,C2> { |
267 | template <typename P1, typename P2> |
268 | void operator()(const P1& src, P2& dst) const { |
269 | typedef typename channel_type<P1>::type T1; |
270 | default_color_converter_impl<rgb_t,C2>()( |
271 | pixel<T1,rgb_layout_t>(channel_multiply(get_color(src,red_t()), get_color(src,alpha_t())), |
272 | channel_multiply(get_color(src,green_t()),get_color(src,alpha_t())), |
273 | channel_multiply(get_color(src,blue_t()), get_color(src,alpha_t()))) |
274 | ,dst); |
275 | } |
276 | }; |
277 | |
278 | /// \ingroup ColorConvert |
279 | /// \brief Unfortunately RGBA to RGBA must be explicitly provided - otherwise we get ambiguous specialization error. |
280 | template <> |
281 | struct default_color_converter_impl<rgba_t,rgba_t> { |
282 | template <typename P1, typename P2> |
283 | void operator()(const P1& src, P2& dst) const { |
284 | static_for_each(src,dst,default_channel_converter()); |
285 | } |
286 | }; |
287 | |
288 | /// @defgroup ColorConvert Color Space Converion |
289 | /// \ingroup ColorSpaces |
290 | /// \brief Support for conversion between pixels of different color spaces and channel depths |
291 | |
292 | /// \ingroup PixelAlgorithm ColorConvert |
293 | /// \brief class for color-converting one pixel to another |
294 | struct default_color_converter { |
295 | template <typename SrcP, typename DstP> |
296 | void operator()(const SrcP& src,DstP& dst) const { |
297 | typedef typename color_space_type<SrcP>::type SrcColorSpace; |
298 | typedef typename color_space_type<DstP>::type DstColorSpace; |
299 | default_color_converter_impl<SrcColorSpace,DstColorSpace>()(src,dst); |
300 | } |
301 | }; |
302 | |
303 | /// \ingroup PixelAlgorithm |
304 | /// \brief helper function for converting one pixel to another using GIL default color-converters |
305 | /// where ScrP models HomogeneousPixelConcept |
306 | /// DstP models HomogeneousPixelValueConcept |
307 | template <typename SrcP, typename DstP> |
308 | inline void color_convert(const SrcP& src, DstP& dst) { |
309 | default_color_converter()(src,dst); |
310 | } |
311 | |
312 | } } // namespace boost::gil |
313 | |
314 | #endif |
315 | |