Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

agg compositing: change src_over alpha to avoid pixel artifacts by re…

…ordering computations and add basic tests comparing src_over composting to normal agg alpha blending - closes #1452 - refs #1313, #1454, #1369
  • Loading branch information...
commit 5e84ce0dfcacefe7ea432a8b4baa179f642999a1 1 parent ac320b2
@springmeyer springmeyer authored
View
2  deps/agg/include/agg_pixfmt_rgba.h
@@ -329,7 +329,7 @@ namespace agg
p[Order::R] = (value_type)(sr + ((p[Order::R] * s1a + base_mask) >> base_shift));
p[Order::G] = (value_type)(sg + ((p[Order::G] * s1a + base_mask) >> base_shift));
p[Order::B] = (value_type)(sb + ((p[Order::B] * s1a + base_mask) >> base_shift));
- p[Order::A] = (value_type)(sa + p[Order::A] - ((sa * p[Order::A] + base_mask) >> base_shift));
+ p[Order::A] = (value_type)(sa + ((p[Order::A] * s1a + base_mask) >> base_shift));
}
};
View
202 tests/cpp_tests/agg_blend_src_over_test.cpp
@@ -0,0 +1,202 @@
+#include <boost/version.hpp>
+#include <boost/detail/lightweight_test.hpp>
+#include <iostream>
+#include <cstdio>
+#include <sstream>
+#include <string>
+#include "agg_color_rgba.h"
+#include "agg_pixfmt_rgba.h"
+#include "agg_rendering_buffer.h"
+#include "agg_renderer_base.h"
+
+typedef agg::rgba8 color;
+typedef agg::order_rgba order;
+
+std::string to_string(color const& c)
+{
+ std::ostringstream s;
+ s << "rgba(" << (unsigned)c.r << "," << (unsigned)c.g << "," << (unsigned)c.b << "," << (unsigned)c.a << ")";
+ return s.str();
+}
+
+template<typename blender>
+color blend(color const& source, color const& dest, unsigned cover=255)
+{
+ unsigned stride = 4;
+ unsigned size = 1;
+
+ color source_pre = source;
+ source_pre.premultiply();
+ color dest_pre = dest;
+ dest_pre.premultiply();
+
+ unsigned char* buffer = new unsigned char[size*size*stride];
+ memset(buffer, 0, size*size*stride);
+ buffer[0] = dest_pre.r;
+ buffer[1] = dest_pre.g;
+ buffer[2] = dest_pre.b;
+ buffer[3] = dest_pre.a;
+ // http://www.antigrain.com/doc/basic_renderers/basic_renderers.agdoc.html
+ agg::rendering_buffer rbuf(buffer,
+ size,
+ size,
+ size * stride);
+ color::value_type* psource = (color::value_type*)rbuf.row_ptr(0,0,1);
+ blender::blend_pix(psource,source_pre.r,source_pre.g,source_pre.b,source_pre.a,cover);
+ color color_result(psource[0],psource[1],psource[2],psource[3]);
+ color_result.demultiply();
+ delete [] buffer;
+ return color_result;
+}
+
+// agg::pixfmt_alpha_blend_rgba
+color normal_blend(color const& source, color const& dest, unsigned cover=255)
+{
+ typedef agg::renderer_base<agg::pixfmt_rgba32_pre> renderer_type;
+ unsigned stride = 4;
+ unsigned size = 1;
+ color source_pre = source;
+ source_pre.premultiply();
+ color dest_pre = dest;
+ dest_pre.premultiply();
+ // source buffer
+ unsigned char* source_buffer = new unsigned char[size*size*stride];
+ memset(source_buffer, 0, size*size*stride);
+ source_buffer[0] = source_pre.r;
+ source_buffer[1] = source_pre.g;
+ source_buffer[2] = source_pre.b;
+ source_buffer[3] = source_pre.a;
+ agg::rendering_buffer source_rbuffer(source_buffer,size,size,size * 4);
+ agg::pixfmt_rgba32_pre pixf_source(source_rbuffer);
+
+ // destination buffer
+ unsigned char* dest_buffer = new unsigned char[size*size*stride];
+ memset(dest_buffer, 0, size*size*stride);
+ dest_buffer[0] = dest_pre.r;
+ dest_buffer[1] = dest_pre.g;
+ dest_buffer[2] = dest_pre.b;
+ dest_buffer[3] = dest_pre.a;
+ agg::rendering_buffer dest_rbuffer(dest_buffer,size,size,size * 4);
+ agg::pixfmt_rgba32_pre pixf_dest(dest_rbuffer);
+
+ // renderer: blends source into destination
+ renderer_type ren(pixf_dest);
+ ren.blend_from(pixf_source,0,0,0,cover);
+ color color_result(dest_buffer[0],dest_buffer[1],dest_buffer[2],dest_buffer[3]);
+ color_result.demultiply();
+ delete [] source_buffer;
+ delete [] dest_buffer;
+ return color_result;
+}
+
+
+
+namespace agg {
+
+// the original agg template code for src_over
+// before we changed A as per https://github.com/mapnik/mapnik/issues/1452
+template<class ColorT, class Order> struct comp_op_rgba_src_over2
+{
+ typedef ColorT color_type;
+ typedef Order order_type;
+ typedef typename color_type::value_type value_type;
+ typedef typename color_type::calc_type calc_type;
+ enum base_scale_e
+ {
+ base_shift = color_type::base_shift,
+ base_mask = color_type::base_mask
+ };
+
+ // Dca' = Sca + Dca.(1 - Sa)
+ // Da' = Sa + Da - Sa.Da
+ static void blend_pix(value_type* p,
+ unsigned sr, unsigned sg, unsigned sb,
+ unsigned sa, unsigned cover)
+ {
+ if(cover < 255)
+ {
+ sr = (sr * cover + 255) >> 8;
+ sg = (sg * cover + 255) >> 8;
+ sb = (sb * cover + 255) >> 8;
+ sa = (sa * cover + 255) >> 8;
+ }
+ calc_type s1a = base_mask - sa;
+ p[Order::R] = (value_type)(sr + ((p[Order::R] * s1a + base_mask) >> base_shift));
+ p[Order::G] = (value_type)(sg + ((p[Order::G] * s1a + base_mask) >> base_shift));
+ p[Order::B] = (value_type)(sb + ((p[Order::B] * s1a + base_mask) >> base_shift));
+ p[Order::A] = (value_type)(sa + p[Order::A] - ((sa * p[Order::A] + base_mask) >> base_shift));
+ }
+};
+
+}
+
+int main( int, char*[] )
+{
+ typedef agg::comp_op_rgba_src_over2<color, agg::order_rgba> source_over_old_agg;
+ typedef agg::comp_op_rgba_src_over<color, agg::order_rgba> source_over;
+
+ color white(255,255,255,255);
+ color black(0,0,0,255);
+
+ BOOST_TEST_EQ( to_string(blend<source_over>(white,white)), to_string(white) );
+ BOOST_TEST_EQ( to_string(blend<source_over>(white,black)), to_string(white) );
+ BOOST_TEST_EQ( to_string(blend<source_over>(black,white)), to_string(black) );
+
+ // https://github.com/mapnik/mapnik/issues/1452#issuecomment-8154646
+ color near_white(254,254,254,254); // Source
+ color near_trans(1,1,1,1); // Dest
+ color expected(252,252,252,255); // expected result
+ BOOST_TEST_EQ( to_string(blend<source_over_old_agg>(near_white,near_trans)), to_string(color(252,252,252,254)) );
+ BOOST_TEST_EQ( to_string(blend<source_over>(near_white,near_trans)), to_string(expected) );
+ BOOST_TEST_EQ( to_string(normal_blend(near_white,near_trans)), to_string(expected) );
+
+ // using normal_blend as expected, compare a variety of other colors
+
+ {
+ color source(128,128,128,255);
+ color dest(128,128,128,255);
+ unsigned cover = 128;
+ std::string expected = to_string(normal_blend(source,dest,cover));
+ BOOST_TEST_EQ( to_string(blend<source_over>(source,dest,cover)), expected );
+ BOOST_TEST_EQ( to_string(blend<source_over_old_agg>(source,dest,cover)), expected );
+ }
+
+ {
+ color source(128,128,128,255);
+ color dest(128,128,128,255);
+ unsigned cover = 245;
+ std::string expected = to_string(normal_blend(source,dest,cover));
+ BOOST_TEST_EQ( to_string(blend<source_over>(source,dest,cover)), expected );
+ BOOST_TEST_EQ( to_string(blend<source_over_old_agg>(source,dest,cover)), expected );
+ }
+
+ {
+ // fails, why?
+ color source(127,127,127,127);
+ color dest(127,127,127,127);
+ unsigned cover = 255;
+ std::string expected = to_string(normal_blend(source,dest,cover));
+ BOOST_TEST_EQ( to_string(blend<source_over>(source,dest,cover)), expected );
+ BOOST_TEST_EQ( to_string(blend<source_over_old_agg>(source,dest,cover)), expected );
+ }
+
+ {
+ // fails, why?
+ color source(128,128,128,128);
+ color dest(128,128,128,128);
+ unsigned cover = 128;
+ std::string expected = to_string(normal_blend(source,dest,cover));
+ BOOST_TEST_EQ( to_string(blend<source_over>(source,dest,cover)), expected );
+ BOOST_TEST_EQ( to_string(blend<source_over_old_agg>(source,dest,cover)), expected );
+ }
+
+ if (!::boost::detail::test_errors()) {
+ std::clog << "C++ AGG blending: \x1b[1;32m✓ \x1b[0m\n";
+#if BOOST_VERSION >= 104600
+ ::boost::detail::report_errors_remind().called_report_errors_function = true;
+#endif
+ } else {
+ std::clog << "C++ AGG blending: ";
+ return ::boost::report_errors();
+ }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.