diff --git a/include/mapnik/image_filter.hpp b/include/mapnik/image_filter.hpp index d2fabc9f2..4f09f2c2e 100644 --- a/include/mapnik/image_filter.hpp +++ b/include/mapnik/image_filter.hpp @@ -26,6 +26,7 @@ //mapnik #include +#include // boost #include @@ -403,6 +404,69 @@ void apply_filter(Src & src, agg_stack_blur const& op) agg::stack_blur_rgba32(pixf,op.rx,op.ry); } +template +void apply_filter(Src & src, hsla const& op) +{ + using namespace boost::gil; + Tinter tint; + tint.h0 = .1; + tint.s0 = .3; + tint.l1 = .9; + bool tinting = !tint.is_identity(); + bool set_alpha = !tint.is_alpha_identity(); + // todo - should filters be able to report if they should be run? + if (tinting || set_alpha) + { + rgba8_view_t src_view = rgba8_view(src); + for (int y=0; y 1) a2 = 1; + if (a2 < 0) a2 = 0; + a = static_cast(std::floor(a2 * 255.0)); + } + if (a > 1 && tinting) + { + double h; + double s; + double l; + 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()); + // demultiply + r /= a_original; + g /= a_original; + b /= a_original; + rgb2hsl(r,g,b,h,s,l); + double h2 = tint.h0 + (h * (tint.h1 - tint.h0)); + double s2 = tint.s0 + (s * (tint.s1 - tint.s0)); + double l2 = tint.l0 + (l * (tint.l1 - tint.l0)); + if (h2 > 1) h2 = 1; + if (h2 < 0) h2 = 0; + if (s2 > 1) s2 = 1; + if (s2 < 0) s2 = 0; + if (l2 > 1) l2 = 1; + if (l2 < 0) l2 = 0; + hsl2rgb(h2,s2,l2,r,g,b); + // premultiply + // we only work with premultiplied source, + // thus all color values must be <= alpha + r *= a; + g *= a; + b *= a; + } + } + } + } +} + template void apply_filter(Src & src, gray const& op) { diff --git a/include/mapnik/image_filter_types.hpp b/include/mapnik/image_filter_types.hpp index d651e9c68..9504d441d 100644 --- a/include/mapnik/image_filter_types.hpp +++ b/include/mapnik/image_filter_types.hpp @@ -55,6 +55,13 @@ struct agg_stack_blur unsigned ry; }; +struct hsla +{ + hsla(std::string const& tinter) + : tinter_(tinter) {} + std::string tinter_; +}; + typedef boost::variant + +namespace mapnik { + +struct Tinter { + double h0; + double h1; + double s0; + double s1; + double l0; + double l1; + double a0; + double a1; + + Tinter() : + h0(0), + h1(1), + s0(0), + s1(1), + l0(0), + l1(1), + a0(0), + a1(1) { } + + bool is_identity() { + return (h0 == 0 && + h1 == 1 && + s0 == 0 && + s1 == 1 && + l0 == 0 && + l1 == 1); + } + + bool is_alpha_identity() { + return (a0 == 0 && + a1 == 1); + } +}; + +static inline void rgb2hsl(unsigned char red, unsigned char green, unsigned char blue, + double & h, double & s, double & l) { + double r = red/255.0; + double g = green/255.0; + double b = blue/255.0; + double max = std::max(r,std::max(g,b)); + double min = std::min(r,std::min(g,b)); + double delta = max - min; + double gamma = max + min; + h = s = l = gamma / 2.0; + if (delta > 0.0) { + s = l > 0.5 ? delta / (2.0 - gamma) : delta / gamma; + if (max == r && max != g) h = (g - b) / delta + (g < b ? 6.0 : 0.0); + else if (max == g && max != b) h = (b - r) / delta + 2.0; + else if (max == b && max != r) h = (r - g) / delta + 4.0; + h /= 6.0; + } +} + +static inline double hueToRGB(double m1, double m2, double h) { + // poor mans fmod + if(h < 0) h += 1; + if(h > 1) h -= 1; + if (h * 6 < 1) return m1 + (m2 - m1) * h * 6; + if (h * 2 < 1) return m2; + if (h * 3 < 2) return m1 + (m2 - m1) * (0.66666 - h) * 6; + return m1; +} + +static inline void hsl2rgb(double h, double s, double l, + unsigned char & r, unsigned char & g, unsigned char & b) { + if (!s) { + r = g = b = static_cast(l * 255); + } + double m2 = (l <= 0.5) ? l * (s + 1) : l + s - l * s; + double m1 = l * 2 - m2; + r = static_cast(hueToRGB(m1, m2, h + 0.33333) * 255); + g = static_cast(hueToRGB(m1, m2, h) * 255); + b = static_cast(hueToRGB(m1, m2, h - 0.33333) * 255); +} + +} + +#endif // end MAPNIK_HSL_HPP \ No newline at end of file