Smart rendering of adjacent, anti-aliased patches is a question which has
come up a couple of times in various guises in the past.
It is my understanding that the lack of this functionality led us to
disable anti-aliasing for contouring and is the reason the following image
has a white stripe around the circle where there should be just a nice
blend of the two colors:


import matplotlib.pyplot as plt
import numpy as np
import matplotlib.patches as mpatches
import matplotlib.path as mpath
import matplotlib.collections as mcol


# create two paths. One a circle, the other
# a square with the same circle cut out.
x = np.linspace(0, np.pi * 2, 1000)

circle_coords = np.array(zip(*[np.sin(x) * 0.8, np.cos(x) * 0.8]))
pth_circle = mpath.Path(circle_coords)

sqr_codes = np.repeat(mpath.Path.MOVETO, len(circle_coords) + 5)
sqr_codes[1:5] = mpath.Path.LINETO
sqr_codes[6:] = mpath.Path.LINETO
sqr_coords = np.concatenate([[[-1, -1], [-1, 1], [1, 1], [1, -1], [-1,
-1]],
                            circle_coords[::-1]], axis=0)
sqr_path = mpath.Path(sqr_coords, sqr_codes)


ax = plt.axes()
patches = [mpatches.PathPatch(pth_circle), mpatches.PathPatch(sqr_path)]
col = mcol.PatchCollection(patches,
                           antialiaseds=True,
                           edgecolors='none',
                           facecolors=[(0, 0.0, 0.0, 0.9), (0.1, 0.1, 0.02,
0.9)])
ax.add_collection(col)
ax.set_xlim([-1, 1])
ax.set_ylim([-1, 1])
plt.show()



I know of lots of the workarounds for this (turn off AA, turn on lines,
extend the path slightly, set a dark background color) all of which have
down-sides, so I'm keen to find a final solution to the problem.

When the two patches marry up perfectly with full anti-aliasing, the
antigrain (AGG) community call this "flash" or compound rendering, and this
capability was added to Agg 2.4 (which we already ship with mpl).

In order to make full use of the compound rendering technique I believe the
drawing pipeline in "_backend_agg.cpp" would need to change, which could be
problematic. A less wide-impacting alternative would be to draw all
"patches" of a single Collection in the same rasterization step (i.e. just
change _draw_path_collection_generic), though this does mean that, as it
stands, the result of plt.contourf would not be able to make use of this
new functionality - a MEP which changes the return type of plt.contourf to
a single Collection might be able to fix that.

I've put together a simple example similar to this in C++ using agg (no mpl
changes yet), showing the differences in the code needed between the old
technique vs the "new" compound renderer (attached).


Ok, so the question to those that have knowledge of the _backend_agg.cpp
code (Mike, Eric, JJ + others?):


   - Have you already looked at doing this and determined that this is a
   non-starter?
   - Do you support adding the ability for the agg backend to draw compound
   artists (i.e. Collections) in this way rather than treating them as
   individual primitives in the canvas?
   - Since many of the other backends can't do flash rendering, would we
   even want to make this change?
      - SVG in Firefox 10.0.2 has the same problem, it is discussed
      slightly more in
      http://www.svgopen.org/2002/papers/sorotokin__svg_secrets/
      - Acroread has the same problem with PDFs, only to a much lesser
      extent than in the PNG attached


Thoughts?

<<figure_1.png>>

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <math.h>

#include "agg_rendering_buffer.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_pixfmt_rgb.h"
#include "agg_scanline_p.h"
#include "agg_renderer_scanline.h"
#include "agg_color_rgba.h"
#include "agg_array.h"
#include "util/agg_color_conv_rgb8.h"

#include "agg_path_storage.h"


#define PI 3.14159265


bool save_image_file (agg::rendering_buffer& rbuf, const char *fn)
{
  FILE* fd = fopen(fn, "wb");
  if(fd == 0) return false;
            
  unsigned w = rbuf.width();
  unsigned h = rbuf.height();
            
  fprintf(fd, "P6\n%d %d\n255\n", w, h);
                
  unsigned y; 
  agg::pod_array<unsigned char> row_buf(w * 3);
  unsigned char *tmp_buf = row_buf.data();

  for(y = 0; y < rbuf.height(); y++)
    {
     const unsigned char* src = rbuf.row_ptr(h - 1 - y);
      agg::color_conv_row(tmp_buf, src, w, agg::color_conv_bgr24_to_rgb24());
      fwrite(tmp_buf, 1, w * 3, fd);
    }

  fclose(fd);
  return true;
}


int main()
{
  typedef agg::pixfmt_bgr24 pixel_type;
  
  const unsigned w = 500, h = 300;
  int i;
  int x_radius = 240, y_radius = 280, x_offset = 250, y_offset = 5;

  unsigned row_size = pixel_type::pix_width * w;
  unsigned buf_size = row_size * h;
  agg::pod_array<unsigned char> img_buf(buf_size);
  
  agg::rendering_buffer rendering_buffer(img_buf.data(), w, h, -row_size);
  pixel_type pixf(rendering_buffer);
  
  typedef agg::renderer_base<pixel_type> renderer_base_type;
  typedef agg::renderer_scanline_aa_solid<renderer_base_type> renderer_solid_type;
  renderer_base_type renderer_base(pixf);
  renderer_solid_type renderer_solid(renderer_base);
  
  agg::rasterizer_scanline_aa<> rasterizer;
  agg::scanline_p8 scanline;
  
  // Colors
  agg::rgba8 white(255, 255, 255);
  agg::rgba8 red(100, 0, 0, 200);
  agg:: rgba8 black(0, 0, 0, 200);

  // Background
  renderer_base.clear(white);


  // Shape 1
  agg::path_storage shape1;
  shape1.move_to(x_offset, y_offset);
  for(i= 0; i < 181; i++)
  {
      shape1.line_to((cos(i * PI / 180) * x_radius) + x_offset, (sin(i * PI / 180) * y_radius) + y_offset);
  }
  shape1.close_polygon();

  rasterizer.add_path(shape1);
  renderer_solid.color(red);
  agg::render_scanlines(rasterizer, scanline, renderer_solid);


  // Shape 2
  agg::path_storage shape2;
  shape2.move_to(x_offset - x_radius, y_radius + y_offset + 10);
  shape2.line_to(x_offset + x_radius, y_radius + y_offset + 10);
  shape2.line_to(x_offset + x_radius, y_offset);
  for(i= 0; i < 181; i++)
  {
      shape2.line_to((cos(i * PI / 180) * x_radius) + x_offset, (sin(i * PI / 180) * y_radius) + y_offset);
  }
  shape2.line_to(x_offset - x_radius, y_offset);
  shape2.close_polygon();

  rasterizer.add_path(shape2);
  renderer_solid.color(black);
  agg::render_scanlines(rasterizer, scanline, renderer_solid);

  
  save_image_file(rendering_buffer, "output.ppm");
  return 0;
}

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <math.h>

#include "agg_rendering_buffer.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_pixfmt_rgb.h"
#include "agg_renderer_scanline.h"
#include "agg_color_rgba.h"
#include "agg_array.h"
#include "agg_path_storage.h"
#include "agg_conv_transform.h"
#include "agg_conv_curve.h"
#include "agg_conv_stroke.h"
#include "agg_gsv_text.h"
#include "agg_scanline_u.h"
#include "agg_scanline_bin.h"
#include "agg_rasterizer_compound_aa.h"
#include "agg_span_allocator.h"

#include "platform/agg_platform_support.h"
#include "util/agg_color_conv_rgb8.h"

#define AGG_BGRA32
#include "pixel_formats.h"



#define PI 3.14159265


bool save_image_file (agg::rendering_buffer& rbuf, const char *fn)
{
  FILE* fd = fopen(fn, "wb");
  if(fd == 0) return false;
            
  unsigned w = rbuf.width();
  unsigned h = rbuf.height();
            
  fprintf(fd, "P6\n%d %d\n255\n", w, h);
                
  unsigned y; 
  agg::pod_array<unsigned char> row_buf(w * 3);
  unsigned char *tmp_buf = row_buf.data();

  for(y = 0; y < rbuf.height(); y++)
    {
      const unsigned char* src = rbuf.row_ptr(h - 1 - y);
      agg::color_conv_row(tmp_buf, src, w, agg::color_conv_bgr24_to_rgb24());
      fwrite(tmp_buf, 1, w * 3, fd);
    }

  fclose(fd);
  return true;
}




class style_handler
{
public:
    style_handler(const color_type* styles, unsigned count) :
        m_transparent(0, 0, 0, 0),
        m_styles(styles),
        m_count(count)
    {}

    bool is_solid(unsigned style) const { return true; }

    const color_type& color(unsigned style) const
    {
        if (style < m_count)
            return m_styles[style];

        return m_transparent;
    }

    void generate_span(color_type* span, int x, int y, unsigned len, unsigned style)
    {
    }

private:
    color_type          m_transparent;
    const color_type*   m_styles;
    unsigned            m_count;
};



int main()
{
  typedef agg::pixfmt_bgr24 pixel_type;
  
  const unsigned w = 500, h = 300;
  int i;
  int x_radius = 240, y_radius = 280, x_offset = 250, y_offset = 5;

  unsigned row_size = pixel_type::pix_width * w;
  unsigned buf_size = row_size * h;
  agg::pod_array<unsigned char> img_buf(buf_size);
  
  agg::rendering_buffer rendering_buffer(img_buf.data(), w, h, -row_size);
  pixel_type pixf(rendering_buffer);
  
  typedef agg::renderer_base<pixel_type> renderer_base_type;
  typedef agg::renderer_scanline_aa_solid<renderer_base_type> renderer_solid_type;
  renderer_base_type renderer_base(pixf);
  renderer_solid_type renderer_solid(renderer_base);
  
  agg::rasterizer_compound_aa<agg::rasterizer_sl_clip_dbl> rasterizer;
  agg::scanline_u8 scanline;
  
  // Colors
  agg::rgba8 white(255, 255, 255);
  agg::rgba8 red(100, 0, 0, 200);
  agg:: rgba8 black(0, 0, 0, 200);

  color_type styles[2];
  styles[1] = black;
  styles[0] = red;

  style_handler sh(styles, 2);

  // Background
  renderer_base.clear(white);

  // Shape 1
  agg::path_storage shape1;
  shape1.move_to(x_offset, y_offset);
  for(i= 0; i < 181; i++)
  {
      shape1.line_to((cos(i * PI / 180) * x_radius) + x_offset, (sin(i * PI / 180) * y_radius) + y_offset);
  }
  shape1.close_polygon();

  rasterizer.styles(0, -1); // Fill with style 0
  rasterizer.add_path(shape1);

  // Shape 2
  agg::path_storage shape2;
  shape2.move_to(x_offset - x_radius, y_radius + y_offset + 10);
  shape2.line_to(x_offset + x_radius, y_radius + y_offset + 10);
  shape2.line_to(x_offset + x_radius, y_offset);
  for(i= 0; i < 181; i++)
  {
      shape2.line_to((cos(i * PI / 180) * x_radius) + x_offset, (sin(i * PI / 180) * y_radius) + y_offset);
  }
  shape2.line_to(x_offset - x_radius, y_offset);
  shape2.close_polygon();

  rasterizer.styles(1, -1); // Fill with style 1
  rasterizer.add_path(shape2);
  
  agg::scanline_bin sl_bin;
  
  agg::span_allocator<color_type> alloc;
  agg::render_scanlines_compound(rasterizer, scanline, sl_bin, renderer_base, alloc, sh);
  
  save_image_file(rendering_buffer, "output.ppm");
  return 0;
}

------------------------------------------------------------------------------
Symantec Endpoint Protection 12 positioned as A LEADER in The Forrester  
Wave(TM): Endpoint Security, Q1 2013 and "remains a good choice" in the  
endpoint security space. For insight on selecting the right partner to 
tackle endpoint security challenges, access the full report. 
http://p.sf.net/sfu/symantec-dev2dev
_______________________________________________
Matplotlib-devel mailing list
Matplotlib-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-devel

Reply via email to