]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/gil/color_convert.hpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / 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 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>
54 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 {
66 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> {
77 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>
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>
96 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>
113 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>
130 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>
159 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>
203 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>
231 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>
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>
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>
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>
276 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>
296 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>
311 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>
324 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>
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