/// \ingroup ColorConvert
/// \brief Gray to CMYK
+/// \todo FIXME: Where does this calculation come from? Shouldn't gray be inverted?
+/// Currently, white becomes black and black becomes white.
template <>
struct default_color_converter_impl<gray_t,cmyk_t> {
template <typename P1, typename P2>
/// c = (1 - r - k) / (1 - k)
/// m = (1 - g - k) / (1 - k)
/// y = (1 - b - k) / (1 - k)
+/// where `1` denotes max value of channel type of destination pixel.
+///
+/// The conversion from RGB to CMYK is based on CMY->CMYK (Version 2)
+/// from the Principles of Digital Image Processing - Fundamental Techniques
+/// by Burger, Wilhelm, Burge, Mark J.
+/// and it is a gross approximation not precise enough for professional work.
+///
+/// \todo FIXME: The original implementation did not handle properly signed CMYK pixels as destination
+///
template <>
-struct default_color_converter_impl<rgb_t,cmyk_t> {
- template <typename P1, typename P2>
- void operator()(const P1& src, P2& dst) const {
- using T2 = typename channel_type<P2>::type;
- get_color(dst,cyan_t()) = channel_invert(channel_convert<T2>(get_color(src,red_t()))); // c = 1 - r
- get_color(dst,magenta_t()) = channel_invert(channel_convert<T2>(get_color(src,green_t()))); // m = 1 - g
- get_color(dst,yellow_t()) = channel_invert(channel_convert<T2>(get_color(src,blue_t()))); // y = 1 - b
- get_color(dst,black_t()) = (std::min)(get_color(dst,cyan_t()),
- (std::min)(get_color(dst,magenta_t()),
- get_color(dst,yellow_t()))); // k = minimum(c, m, y)
- T2 x = channel_traits<T2>::max_value()-get_color(dst,black_t()); // x = 1 - k
- if (x>0.0001f) {
- float x1 = channel_traits<T2>::max_value()/float(x);
- get_color(dst,cyan_t()) = (T2)((get_color(dst,cyan_t()) - get_color(dst,black_t()))*x1); // c = (c - k) / x
- get_color(dst,magenta_t()) = (T2)((get_color(dst,magenta_t()) - get_color(dst,black_t()))*x1); // m = (m - k) / x
- get_color(dst,yellow_t()) = (T2)((get_color(dst,yellow_t()) - get_color(dst,black_t()))*x1); // y = (y - k) / x
- } else {
- get_color(dst,cyan_t())=get_color(dst,magenta_t())=get_color(dst,yellow_t())=0;
+struct default_color_converter_impl<rgb_t, cmyk_t>
+{
+ template <typename SrcPixel, typename DstPixel>
+ void operator()(SrcPixel const& src, DstPixel& dst) const
+ {
+ using src_t = typename channel_type<SrcPixel>::type;
+ src_t const r = get_color(src, red_t());
+ src_t const g = get_color(src, green_t());
+ src_t const b = get_color(src, blue_t());
+
+ using dst_t = typename channel_type<DstPixel>::type;
+ dst_t const c = channel_invert(channel_convert<dst_t>(r)); // c = 1 - r
+ dst_t const m = channel_invert(channel_convert<dst_t>(g)); // m = 1 - g
+ dst_t const y = channel_invert(channel_convert<dst_t>(b)); // y = 1 - b
+ dst_t const k = (std::min)(c, (std::min)(m, y)); // k = minimum(c, m, y)
+
+ // Apply color correction, strengthening, reducing non-zero components by
+ // s = 1 / (1 - k) for k < 1, where 1 denotes dst_t max, otherwise s = 1 (literal).
+ dst_t const dst_max = channel_traits<dst_t>::max_value();
+ dst_t const s_div = dst_max - k;
+ if (s_div != 0)
+ {
+ double const s = dst_max / static_cast<double>(s_div);
+ get_color(dst, cyan_t()) = static_cast<dst_t>((c - k) * s);
+ get_color(dst, magenta_t()) = static_cast<dst_t>((m - k) * s);
+ get_color(dst, yellow_t()) = static_cast<dst_t>((y - k) * s);
+ }
+ else
+ {
+ // Black only for k = 1 (max of dst_t)
+ get_color(dst, cyan_t()) = channel_traits<dst_t>::min_value();
+ get_color(dst, magenta_t()) = channel_traits<dst_t>::min_value();
+ get_color(dst, yellow_t()) = channel_traits<dst_t>::min_value();
}
+ get_color(dst, black_t()) = k;
}
};