color_convert.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  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. #include <boost/gil/channel_algorithm.hpp>
  11. #include <boost/gil/cmyk.hpp>
  12. #include <boost/gil/color_base_algorithm.hpp>
  13. #include <boost/gil/gray.hpp>
  14. #include <boost/gil/metafunctions.hpp>
  15. #include <boost/gil/pixel.hpp>
  16. #include <boost/gil/rgb.hpp>
  17. #include <boost/gil/rgba.hpp>
  18. #include <boost/gil/utilities.hpp>
  19. #include <algorithm>
  20. #include <functional>
  21. #include <type_traits>
  22. namespace boost { namespace gil {
  23. /// Support for fast and simple color conversion.
  24. /// Accurate color conversion using color profiles can be supplied separately in a dedicated module.
  25. // Forward-declare
  26. template <typename P> struct channel_type;
  27. ////////////////////////////////////////////////////////////////////////////////////////
  28. ///
  29. /// COLOR SPACE CONVERSION
  30. ///
  31. ////////////////////////////////////////////////////////////////////////////////////////
  32. /// \ingroup ColorConvert
  33. /// \brief Color Convertion function object. To be specialized for every src/dst color space
  34. template <typename C1, typename C2>
  35. struct default_color_converter_impl
  36. {
  37. static_assert(
  38. std::is_same<C1, C2>::value,
  39. "default_color_converter_impl not specialized for given color spaces");
  40. };
  41. /// \ingroup ColorConvert
  42. /// \brief When the color space is the same, color convertion performs channel depth conversion
  43. template <typename C>
  44. struct default_color_converter_impl<C,C> {
  45. template <typename P1, typename P2>
  46. void operator()(const P1& src, P2& dst) const {
  47. static_for_each(src,dst,default_channel_converter());
  48. }
  49. };
  50. namespace detail {
  51. /// red * .3 + green * .59 + blue * .11 + .5
  52. // The default implementation of to_luminance uses float0..1 as the intermediate channel type
  53. template <typename RedChannel, typename GreenChannel, typename BlueChannel, typename GrayChannelValue>
  54. struct rgb_to_luminance_fn {
  55. auto operator()(const RedChannel& red, const GreenChannel& green, const BlueChannel& blue) const -> GrayChannelValue
  56. {
  57. return channel_convert<GrayChannelValue>(float32_t(
  58. channel_convert<float32_t>(red )*0.30f +
  59. channel_convert<float32_t>(green)*0.59f +
  60. channel_convert<float32_t>(blue )*0.11f) );
  61. }
  62. };
  63. // performance specialization for unsigned char
  64. template <typename GrayChannelValue>
  65. struct rgb_to_luminance_fn<uint8_t,uint8_t,uint8_t, GrayChannelValue> {
  66. auto operator()(uint8_t red, uint8_t green, uint8_t blue) const -> GrayChannelValue
  67. {
  68. return channel_convert<GrayChannelValue>(uint8_t(
  69. ((uint32_t(red )*4915 + uint32_t(green)*9667 + uint32_t(blue )*1802) + 8192) >> 14));
  70. }
  71. };
  72. template <typename GrayChannel, typename RedChannel, typename GreenChannel, typename BlueChannel>
  73. auto rgb_to_luminance(const RedChannel& red, const GreenChannel& green, const BlueChannel& blue) -> typename channel_traits<GrayChannel>::value_type
  74. {
  75. return rgb_to_luminance_fn<RedChannel,GreenChannel,BlueChannel,
  76. typename channel_traits<GrayChannel>::value_type>()(red,green,blue);
  77. }
  78. } // namespace detail
  79. /// \ingroup ColorConvert
  80. /// \brief Gray to RGB
  81. template <>
  82. struct default_color_converter_impl<gray_t,rgb_t> {
  83. template <typename P1, typename P2>
  84. void operator()(const P1& src, P2& dst) const {
  85. get_color(dst,red_t()) =
  86. channel_convert<typename color_element_type<P2, red_t >::type>(get_color(src,gray_color_t()));
  87. get_color(dst,green_t())=
  88. channel_convert<typename color_element_type<P2, green_t>::type>(get_color(src,gray_color_t()));
  89. get_color(dst,blue_t()) =
  90. channel_convert<typename color_element_type<P2, blue_t >::type>(get_color(src,gray_color_t()));
  91. }
  92. };
  93. /// \ingroup ColorConvert
  94. /// \brief Gray to CMYK
  95. /// \todo FIXME: Where does this calculation come from? Shouldn't gray be inverted?
  96. /// Currently, white becomes black and black becomes white.
  97. template <>
  98. struct default_color_converter_impl<gray_t,cmyk_t> {
  99. template <typename P1, typename P2>
  100. void operator()(const P1& src, P2& dst) const {
  101. get_color(dst,cyan_t())=
  102. channel_traits<typename color_element_type<P2, cyan_t >::type>::min_value();
  103. get_color(dst,magenta_t())=
  104. channel_traits<typename color_element_type<P2, magenta_t>::type>::min_value();
  105. get_color(dst,yellow_t())=
  106. channel_traits<typename color_element_type<P2, yellow_t >::type>::min_value();
  107. get_color(dst,black_t())=
  108. channel_convert<typename color_element_type<P2, black_t >::type>(get_color(src,gray_color_t()));
  109. }
  110. };
  111. /// \ingroup ColorConvert
  112. /// \brief RGB to Gray
  113. template <>
  114. struct default_color_converter_impl<rgb_t,gray_t> {
  115. template <typename P1, typename P2>
  116. void operator()(const P1& src, P2& dst) const {
  117. get_color(dst,gray_color_t()) =
  118. detail::rgb_to_luminance<typename color_element_type<P2,gray_color_t>::type>(
  119. get_color(src,red_t()), get_color(src,green_t()), get_color(src,blue_t())
  120. );
  121. }
  122. };
  123. /// \ingroup ColorConvert
  124. /// \brief RGB to CMYK (not the fastest code in the world)
  125. ///
  126. /// k = min(1 - r, 1 - g, 1 - b)
  127. /// c = (1 - r - k) / (1 - k)
  128. /// m = (1 - g - k) / (1 - k)
  129. /// y = (1 - b - k) / (1 - k)
  130. /// where `1` denotes max value of channel type of destination pixel.
  131. ///
  132. /// The conversion from RGB to CMYK is based on CMY->CMYK (Version 2)
  133. /// from the Principles of Digital Image Processing - Fundamental Techniques
  134. /// by Burger, Wilhelm, Burge, Mark J.
  135. /// and it is a gross approximation not precise enough for professional work.
  136. ///
  137. template <>
  138. struct default_color_converter_impl<rgb_t, cmyk_t>
  139. {
  140. template <typename SrcPixel, typename DstPixel>
  141. void operator()(SrcPixel const& src, DstPixel& dst) const
  142. {
  143. using src_t = typename channel_type<SrcPixel>::type;
  144. src_t const r = get_color(src, red_t());
  145. src_t const g = get_color(src, green_t());
  146. src_t const b = get_color(src, blue_t());
  147. using uint_t = typename channel_type<cmyk8_pixel_t>::type;
  148. uint_t c = channel_invert(channel_convert<uint_t>(r)); // c = 1 - r
  149. uint_t m = channel_invert(channel_convert<uint_t>(g)); // m = 1 - g
  150. uint_t y = channel_invert(channel_convert<uint_t>(b)); // y = 1 - b
  151. uint_t k = (std::min)(c,(std::min)(m,y)); // k = minimum(c, m, y)
  152. // Apply color correction, strengthening, reducing non-zero components by
  153. // s = 1 / (1 - k) for k < 1, where 1 denotes dst_t max, otherwise s = 1 (literal).
  154. uint_t const dst_max = channel_traits<uint_t>::max_value();
  155. uint_t const s_div = static_cast<uint_t>(dst_max - k);
  156. if (s_div != 0)
  157. {
  158. double const s = dst_max / static_cast<double>(s_div);
  159. c = static_cast<uint_t>((c - k) * s);
  160. m = static_cast<uint_t>((m - k) * s);
  161. y = static_cast<uint_t>((y - k) * s);
  162. }
  163. else
  164. {
  165. // Black only for k = 1 (max of dst_t)
  166. c = channel_traits<uint_t>::min_value();
  167. m = channel_traits<uint_t>::min_value();
  168. y = channel_traits<uint_t>::min_value();
  169. }
  170. using dst_t = typename channel_type<DstPixel>::type;
  171. get_color(dst, cyan_t()) = channel_convert<dst_t>(c);
  172. get_color(dst, magenta_t()) = channel_convert<dst_t>(m);
  173. get_color(dst, yellow_t()) = channel_convert<dst_t>(y);
  174. get_color(dst, black_t()) = channel_convert<dst_t>(k);
  175. }
  176. };
  177. /// \ingroup ColorConvert
  178. /// \brief CMYK to RGB (not the fastest code in the world)
  179. ///
  180. /// r = 1 - min(1, c*(1-k)+k)
  181. /// g = 1 - min(1, m*(1-k)+k)
  182. /// b = 1 - min(1, y*(1-k)+k)
  183. template <>
  184. struct default_color_converter_impl<cmyk_t,rgb_t> {
  185. template <typename P1, typename P2>
  186. void operator()(const P1& src, P2& dst) const {
  187. using T1 = typename channel_type<P1>::type;
  188. get_color(dst,red_t()) =
  189. channel_convert<typename color_element_type<P2,red_t>::type>(
  190. channel_invert<T1>(
  191. (std::min)(channel_traits<T1>::max_value(),
  192. T1(channel_multiply(get_color(src,cyan_t()),channel_invert(get_color(src,black_t())))+get_color(src,black_t())))));
  193. get_color(dst,green_t())=
  194. channel_convert<typename color_element_type<P2,green_t>::type>(
  195. channel_invert<T1>(
  196. (std::min)(channel_traits<T1>::max_value(),
  197. T1(channel_multiply(get_color(src,magenta_t()),channel_invert(get_color(src,black_t())))+get_color(src,black_t())))));
  198. get_color(dst,blue_t()) =
  199. channel_convert<typename color_element_type<P2,blue_t>::type>(
  200. channel_invert<T1>(
  201. (std::min)(channel_traits<T1>::max_value(),
  202. T1(channel_multiply(get_color(src,yellow_t()),channel_invert(get_color(src,black_t())))+get_color(src,black_t())))));
  203. }
  204. };
  205. /// \ingroup ColorConvert
  206. /// \brief CMYK to Gray
  207. ///
  208. /// gray = (1 - 0.212c - 0.715m - 0.0722y) * (1 - k)
  209. template <>
  210. struct default_color_converter_impl<cmyk_t,gray_t> {
  211. template <typename P1, typename P2>
  212. void operator()(const P1& src, P2& dst) const {
  213. get_color(dst,gray_color_t())=
  214. channel_convert<typename color_element_type<P2,gray_color_t>::type>(
  215. channel_multiply(
  216. channel_invert(
  217. detail::rgb_to_luminance<typename color_element_type<P1,black_t>::type>(
  218. get_color(src,cyan_t()),
  219. get_color(src,magenta_t()),
  220. get_color(src,yellow_t())
  221. )
  222. ),
  223. channel_invert(get_color(src,black_t()))));
  224. }
  225. };
  226. namespace detail {
  227. template <typename Pixel>
  228. auto alpha_or_max_impl(Pixel const& p, std::true_type) -> typename channel_type<Pixel>::type
  229. {
  230. return get_color(p,alpha_t());
  231. }
  232. template <typename Pixel>
  233. auto alpha_or_max_impl(Pixel const&, std::false_type) -> typename channel_type<Pixel>::type
  234. {
  235. return channel_traits<typename channel_type<Pixel>::type>::max_value();
  236. }
  237. } // namespace detail
  238. // Returns max_value if the pixel has no alpha channel. Otherwise returns the alpha.
  239. template <typename Pixel>
  240. auto alpha_or_max(Pixel const& p) -> typename channel_type<Pixel>::type
  241. {
  242. return detail::alpha_or_max_impl(
  243. p,
  244. mp11::mp_contains<typename color_space_type<Pixel>::type, alpha_t>());
  245. }
  246. /// \ingroup ColorConvert
  247. /// \brief Converting any pixel type to RGBA. Note: Supports homogeneous pixels only.
  248. template <typename C1>
  249. struct default_color_converter_impl<C1,rgba_t> {
  250. template <typename P1, typename P2>
  251. void operator()(const P1& src, P2& dst) const {
  252. using T2 = typename channel_type<P2>::type;
  253. pixel<T2,rgb_layout_t> tmp;
  254. default_color_converter_impl<C1,rgb_t>()(src,tmp);
  255. get_color(dst,red_t()) =get_color(tmp,red_t());
  256. get_color(dst,green_t())=get_color(tmp,green_t());
  257. get_color(dst,blue_t()) =get_color(tmp,blue_t());
  258. get_color(dst,alpha_t())=channel_convert<T2>(alpha_or_max(src));
  259. }
  260. };
  261. /// \ingroup ColorConvert
  262. /// \brief Converting RGBA to any pixel type. Note: Supports homogeneous pixels only.
  263. ///
  264. /// Done by multiplying the alpha to get to RGB, then converting the RGB to the target pixel type
  265. /// Note: This may be slower if the compiler doesn't optimize out constructing/destructing a temporary RGB pixel.
  266. /// Consider rewriting if performance is an issue
  267. template <typename C2>
  268. struct default_color_converter_impl<rgba_t,C2> {
  269. template <typename P1, typename P2>
  270. void operator()(const P1& src, P2& dst) const {
  271. using T1 = typename channel_type<P1>::type;
  272. default_color_converter_impl<rgb_t,C2>()(
  273. pixel<T1,rgb_layout_t>(channel_multiply(get_color(src,red_t()), get_color(src,alpha_t())),
  274. channel_multiply(get_color(src,green_t()),get_color(src,alpha_t())),
  275. channel_multiply(get_color(src,blue_t()), get_color(src,alpha_t())))
  276. ,dst);
  277. }
  278. };
  279. /// \ingroup ColorConvert
  280. /// \brief Unfortunately RGBA to RGBA must be explicitly provided - otherwise we get ambiguous specialization error.
  281. template <>
  282. struct default_color_converter_impl<rgba_t,rgba_t> {
  283. template <typename P1, typename P2>
  284. void operator()(const P1& src, P2& dst) const {
  285. static_for_each(src,dst,default_channel_converter());
  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. /// \ingroup PixelAlgorithm ColorConvert
  292. /// \brief class for color-converting one pixel to another
  293. struct default_color_converter {
  294. template <typename SrcP, typename DstP>
  295. void operator()(const SrcP& src,DstP& dst) const {
  296. using SrcColorSpace = typename color_space_type<SrcP>::type;
  297. using DstColorSpace = typename color_space_type<DstP>::type;
  298. default_color_converter_impl<SrcColorSpace,DstColorSpace>()(src,dst);
  299. }
  300. };
  301. /// \ingroup PixelAlgorithm
  302. /// \brief helper function for converting one pixel to another using GIL default color-converters
  303. /// where ScrP models HomogeneousPixelConcept
  304. /// DstP models HomogeneousPixelValueConcept
  305. template <typename SrcP, typename DstP>
  306. inline void color_convert(const SrcP& src, DstP& dst) {
  307. default_color_converter()(src,dst);
  308. }
  309. } } // namespace boost::gil
  310. #endif