Ellipses With Uniform Stroke Width

Cairo has built-in calls for drawing arcs of circles. The following discusses how to use them to draw ellipses.

## Common Setup

The following examples will be drawing an ellipse within an image area defined as follows:

``````const int img_width = 256;
const int img_height = 256;
cairo_surface_t * const pix = cairo_image_surface_create
(
/*format =*/ CAIRO_FORMAT_RGB24,
/*width =*/ img_width,
/*height =*/ img_height
);
cairo_t * const cr = cairo_create(pix);
``````

The ellipse will be drawn as a circle centred within the drawing area. Before the circle is drawn, the following scaling is applied:

``````cairo_translate(cr, img_width / 2.0, img_height / 2.0);
cairo_scale(cr, 0.5, 1);
cairo_translate(cr, - img_width / 2.0, - img_height / 2.0);
``````

This shrinks subsequent drawing operations (including path construction) horizontally by a factor of 0.5 about the image centre, to turn the circle into an ellipse. The ellipse itself is then constructed very simply:

``````cairo_arc
(
/*cr =*/ cr,
/*xc =*/ img_width / 2.0,
/*yc =*/ img_height / 2.0,
/*angle1 =*/ 0,
/*angle2 =*/ 2 * M_PI
);
``````

The following examples use a thick line width (20 units) to make the problem (and its solution) clearer.

## The Wrong Way

The obvious way to turn a circle into an ellipse is to apply non-uniform scaling to it. This works, but is prone to an interesting side-effect:

``````cairo_set_source_rgb(cr, 1, 1, 1);
cairo_paint(cr);
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_translate(cr, img_width / 2.0, img_height / 2.0);
cairo_scale(cr, 0.5, 1);
cairo_translate(cr, - img_width / 2.0, - img_height / 2.0);
cairo_new_path(cr);
cairo_arc
(
/*cr =*/ cr,
/*xc =*/ img_width / 2.0,
/*yc =*/ img_height / 2.0,
/*angle1 =*/ 0,
/*angle2 =*/ 2 * M_PI
);
cairo_set_line_width(cr, 20.0);
cairo_stroke(cr);
``````

Example output:

The problem is that the CTM also affects the `cairo_stroke` call. Thus, where the curve is narrow, the stroke gets narrow as well. This is probably not what you want.

## The Right Way

The answer is to apply the non-uniform scaling only during the path construction, and remove it before calling `cairo_stroke`. Here the CTM is saved before path construction, and restored after it:

``````cairo_set_source_rgb(cr, 1, 1, 1);
cairo_paint(cr);
cairo_set_source_rgb(cr, 0, 0, 0);
{
cairo_matrix_t save_matrix;
cairo_get_matrix(cr, &save_matrix);
cairo_translate(cr, img_width / 2.0, img_height / 2.0);
cairo_scale(cr, 0.5, 1);
cairo_translate(cr, - img_width / 2.0, - img_height / 2.0);
cairo_new_path(cr);
cairo_arc
(
/*cr =*/ cr,
/*xc =*/ img_width / 2.0,
/*yc =*/ img_height / 2.0,
/*angle1 =*/ 0,
/*angle2 =*/ 2 * M_PI
);
cairo_set_matrix(cr, &save_matrix);
}
cairo_set_line_width(cr, 20.0);
cairo_stroke(cr);
``````

Example output:

Much better, don’t you think?

Alternatively you may call `cairo_save` to save the entire graphics state before the path construction, and `cairo_restore` afterwards, before the `cairo_stroke` call. Cairo’s saved graphics state includes the CTM, but not the current path itself.