diff --git a/include/mapnik/image_filter.hpp b/include/mapnik/image_filter.hpp index 8301adefd..f888f0480 100644 --- a/include/mapnik/image_filter.hpp +++ b/include/mapnik/image_filter.hpp @@ -674,6 +674,81 @@ void apply_filter(Src & src, scale_hsla const& transform) } } +template +void color_blind_filter(Src & src, ColorBlindFilter const& op) +{ + using namespace boost::gil; + rgba8_view_t src_view = rgba8_view(src); + + for (std::ptrdiff_t y = 0; y < src_view.height(); ++y) + { + rgba8_view_t::x_iterator src_it = src_view.row_begin(static_cast(y)); + for (std::ptrdiff_t x = 0; x < src_view.width(); ++x) + { + // formula taken from boost/gil/color_convert.hpp:rgb_to_luminance + uint8_t & r = get_color(src_it[x], red_t()); + uint8_t & g = get_color(src_it[x], green_t()); + uint8_t & b = get_color(src_it[x], blue_t()); + double dr = static_cast(r); + double dg = static_cast(g); + double db = static_cast(b); + // RGB to LMS matrix conversion + double L = (17.8824 * dr) + (43.5161 * dg) + (4.11935 * db); + double M = (3.45565 * dr) + (27.1554 * dg) + (3.86714 * db); + double S = (0.0299566 * dr) + (0.184309 * dg) + (1.46709 * db); + // Simulate color blindness + double l = (op.f0 * L) + (op.f1 * M) + (op.f2 * S); + double m = (op.f3 * L) + (op.f4 * M) + (op.f5 * S); + double s = (op.f6 * L) + (op.f7 * M) + (op.f8 * S); + // LMS to RGB matrix conversion + double R = (0.0809444479 * l) + (-0.130504409 * m) + (0.116721066 * s); + double G = (-0.0102485335 * l) + (0.0540193266 * m) + (-0.113614708 * s); + double B = (-0.000365296938 * l) + (-0.00412161469 * m) + (0.693511405 * s); + // Isolate invisible colors to color vision deficiency (calculate error matrix) + R = dr - R; + G = dg - G; + B = db - B; + // Shift colors towards visible spectrum (apply error modifications) + double RR = (0.0 * R) + (0.0 * G) + (0.0 * B); + double GG = (0.7 * R) + (1.0 * G) + (0.0 * B); + double BB = (0.7 * R) + (0.0 * G) + (1.0 * B); + // Add compensation to original values + R = RR + dr; + G = GG + dg; + B = BB + db; + // Clamp values + if(R < 0.0) R = 0.0; + if(R > 255.0) R = 255.0; + if(G < 0.0) G = 0.0; + if(G > 255.0) G = 255.0; + if(B < 0.0) B = 0.0; + if(B > 255.0) B = 255.0; + r = static_cast(R); + g = static_cast(G); + b = static_cast(B); + } + } + +} + +template +void apply_filter(Src & src, color_blind_protanope const& op) +{ + color_blind_filter(src, op); +} + +template +void apply_filter(Src & src, color_blind_deuteranope const& op) +{ + color_blind_filter(src, op); +} + +template +void apply_filter(Src & src, color_blind_tritanope const& op) +{ + color_blind_filter(src, op); +} + template void apply_filter(Src & src, gray const& /*op*/) { diff --git a/include/mapnik/image_filter_grammar_impl.hpp b/include/mapnik/image_filter_grammar_impl.hpp index a9f11d823..e41f63ede 100644 --- a/include/mapnik/image_filter_grammar_impl.hpp +++ b/include/mapnik/image_filter_grammar_impl.hpp @@ -80,6 +80,12 @@ image_filter_grammar::image_filter_grammar() | lit("invert") >> no_args [push_back(_val,construct())] | + lit("color-blind-protanope") >> no_args [push_back(_val,construct())] + | + lit("color-blind-deuteranope") >> no_args [push_back(_val,construct())] + | + lit("color-blind-tritanope") >> no_args [push_back(_val,construct())] + | agg_blur_filter(_val) | scale_hsla_filter(_val) diff --git a/include/mapnik/image_filter_types.hpp b/include/mapnik/image_filter_types.hpp index 5d61d1d3f..00b508de1 100644 --- a/include/mapnik/image_filter_types.hpp +++ b/include/mapnik/image_filter_types.hpp @@ -55,6 +55,47 @@ struct x_gradient : image_filter_base {}; struct y_gradient : image_filter_base {}; struct invert : image_filter_base {}; + +// http://vision.psychol.cam.ac.uk/jdmollon/papers/colourmaps.pdf +struct color_blind_protanope : image_filter_base +{ + const double f0 = 0.0; + const double f1 = 2.02344; + const double f2 = -2.52581; + const double f3 = 0.0; + const double f4 = 1.0; + const double f5 = 0.0; + const double f6 = 0.0; + const double f7 = 0.0; + const double f8 = 1.0; +}; + +struct color_blind_deuteranope : image_filter_base +{ + const double f0 = 1.0; + const double f1 = 0.0; + const double f2 = 0.0; + const double f3 = 0.494207; + const double f4 = 0.0; + const double f5 = 1.24827; + const double f6 = 0.0; + const double f7 = 0.0; + const double f8 = 1.0; +}; + +struct color_blind_tritanope : image_filter_base +{ + const double f0 = 1.0; + const double f1 = 0.0; + const double f2 = 0.0; + const double f3 = 0.0; + const double f4 = 1.0; + const double f5 = 0.0; + const double f6 = -0.395913; + const double f7 = 0.801109; + const double f8 = 0.0; +}; + struct agg_stack_blur : image_filter_base { agg_stack_blur(unsigned rx_, unsigned ry_) @@ -169,7 +210,10 @@ using filter_type = util::variant; + filter::color_to_alpha, + filter::color_blind_protanope, + filter::color_blind_deuteranope, + filter::color_blind_tritanope>; inline std::ostream& operator<< (std::ostream& os, blur) { @@ -247,6 +291,24 @@ inline std::ostream& operator<< (std::ostream& os, invert) return os; } +inline std::ostream& operator<< (std::ostream& os, color_blind_protanope) +{ + os << "color-blind-protanope"; + return os; +} + +inline std::ostream& operator<< (std::ostream& os, color_blind_deuteranope) +{ + os << "color-blind-deuteranope"; + return os; +} + +inline std::ostream& operator<< (std::ostream& os, color_blind_tritanope) +{ + os << "color-blind-tritanope"; + return os; +} + inline std::ostream& operator<< (std::ostream& os, colorize_alpha const& filter) { os << "colorize-alpha("; diff --git a/test/data-visual b/test/data-visual index e3d79ed49..22d448042 160000 --- a/test/data-visual +++ b/test/data-visual @@ -1 +1 @@ -Subproject commit e3d79ed493485afbb5c79cd90913c8db488561b6 +Subproject commit 22d44804277eec9e62964b4059c71d9f001747c7