Clipping Outside A Region

Cairo lets you clip drawing to arbitrary regions defined by paths. The basic technique is:

Note that the intersection is with the interior of your path—that is, the area that would be filled if you did a cairo_fill() operation on your path. But supposing you have a path, and you want drawing to be clipped to the exterior of that path?

The way to reverse the definitions of “interior” and “exterior” is simple: simply add another subpath to your path, surrounding the entire drawing area. If you use CAIRO_FILL_RULE_WINDING for your fill rule (the default), then make sure the enclosing subpath is defined in the opposite direction to the inner one. Now the interior of the path becomes the area between the subpaths, rather than inside the inner subpath.

Alternatively, you can use CAIRO_FILL_RULE_EVEN_ODD for your fill rule, then the subpath directions don’t matter.

const int img_size = 300;
cairo_surface_t * const pix = cairo_image_surface_create
  (
    /*format =*/ CAIRO_FORMAT_RGB24,
    /*width =*/ img_size,
    /*height =*/ img_size
  );
cairo_t * const cr = cairo_create(pix);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
  /* use overwriting operator to make clear that overwriting is blocked by clipping */
cairo_set_source_rgb(cr, 1, 1, 1); /* background */
cairo_paint(cr);
for (bool outside = false;;)
  {
  /* Do two drawing passes, once clipped inside the region, then clipped outside. */
  /* Because of the clipping, the second pass does not overwrite the first. */
    cairo_reset_clip(cr);
    cairo_new_path(cr);
    cairo_arc /* example inner region--note subpath is defined clockwise */
      (
        /*cr =*/ cr,
        /*xc =*/ img_size / 2.0,
        /*yc =*/ img_size / 2.0,
        /*radius =*/ img_size / 4.0,
        /*angle1 =*/ 0,
        /*angle2 =*/ 2 * M_PI
      );
    if (outside)
      {
      /* add outer subpath in anticlockwise direction */
        cairo_new_sub_path(cr);
        cairo_move_to(cr, img_size, 0);
        cairo_line_to(cr, 0, 0);
        cairo_line_to(cr, 0, img_size);
        cairo_line_to(cr, img_size, img_size);
        cairo_close_path(cr);
      } /*if*/
    cairo_clip(cr);
    if (outside) /* use different colours to distinguish cases */
      {
        cairo_set_source_rgb(cr, 1.0, 0.91, 0.83);
      }
    else
      {
        cairo_set_source_rgb(cr, 0.1, 0.7, 0.67);
      } /*if*/
    cairo_rectangle
      /* example shape that extends both inside and outside inner clipping region */
      (
        /*cr =*/ cr,
        /*x =*/ 0,
        /*y =*/ img_size / 4.0,
        /*width =*/ img_size,
        /*height =*/ img_size / 2.0
      );
    cairo_fill(cr);
    if (outside)
        break;
    outside = true;
  } /*for*/

Sample output:

This technique also extends to more than one inner subpath, defining multiple discontiguous inner regions to be clipped out of the drawing area.