Comment 10 for bug 1409520

Revision history for this message
Tavmjong Bah (tavmjong-free) wrote :

I've dumped out some of the Cairo calls to see how Inkscape is drawing the rectangle. It is using a series of lineto's. Converting the rectangle to a path gives exactly the same calls (as expected).

I then wrote a small C program that used the same Cairo calls. I see a change in behavior between a stroke width of 0.6 and 0.7. At 0.6, rounded corners are drawn as bevels while at 0.7 they are rendered rounded. Cairo may be attempting to optimize drawing the corners (if the two ends of the outer offset paths are less than a given distance apart, draw a bevel). This is confirmed by examining the source code (cairo-stroke-path.c). Changing the 'tolerance' from the default 0.1 to 0.05 (cairo_set_tolernace()) results in rounded corners when the stroke width is 0.5.

In Inkscape, converting the rectangle to a path and then dragging one of the adjacent corners (so that the enclosed angle changes) results in the join being drawn round when the angle is more acute... which makes the two ends of the offset paths be farther apart.

With the small C program all corners are effected, not just at the start of the path. I cannot reproduce the behavior in Inkscape where only the start/stop corner is incorrect. Using 'cairo_rectangle' instead of a path, the behavior is the same.

I've changed the default value of 'tolerance' inside Inkscape. If I increase the value to 0.5 or decrease the value to 0.05 the start/end corner is rendered correctly. The default value of 0.1 yields a bad corner. There is probably a subtle bug in Cairo.

I've attached a cleaned up test case.

Here is a snip-it of the Cairo code for the test image with scaling of 4000% (and with some debugging output):

  // Inkscape Path ----------------------------------------
  cairo_matrix_t cm;
  cm.xx = 40;
  cm.xy = 0;
  cm.x0 = 0;
  cm.yx = 0;
  cm.yy = 40;
  cm.y0 = -160;
  cairo_transform( c, &cm ); // ink_cairo_transform
  // calling prepareStroke()
  // called prepareStroke()
  // DrawingContext::path
  // feed_pathvector_to_cairo: entrance
  // feed_path_to_cairo: entrance
  // cairo_rectangle( c, 0.25, 0.25, 3, 2 );
  cairo_move_to( c, 0.25, 1.25);
  cairo_line_to( c, 3.75, 1.25); // Not optimized
  cairo_line_to( c, 3.75, 3.75); // Not optimized
  cairo_line_to( c, 0.25, 3.75); // Not optimized
  cairo_close_path( c );
  // has_stroke... calling applyStroke()
  cairo_set_line_width( c, 0.5);
  cairo_set_line_join( c, 1);
  // has_stroke... called applyStroke()
  cairo_stroke_preserve( c );
  cairo_new_path( c );