Bug 4441

Summary: inverse transformation of line width
Product: cairo Reporter: Attila Babo <ababo>
Component: generalAssignee: Carl Worth <cworth>
Status: RESOLVED WONTFIX QA Contact: cairo-bugs mailing list <cairo-bugs>
Severity: normal    
Priority: high CC: billy.biggs
Version: 1.0.0   
Hardware: x86 (IA32)   
OS: Windows (All)   
Whiteboard:
i915 platform: i915 features:

Description Attila Babo 2005-09-13 06:01:53 UTC
When applying a scaling transformation it would be better to do an inverse
transformation of the current line width to keep the effective line width the same.

It's a common practice to set the line width in pixel dimensions then apply a
scaling and translation to match the modeling transformation. Now the previous
line width remains the same number, i.e. this will scale like the model, which
is counterproductive. Try this snippet to get a better understanding of my problem:

  cairo_set_line_width(cr, 1);  // one pixel  wide line

  cairo_scale(cr, width, height); // window dimensions in pixel
	
  cairo_move_to(cr, 0.1, 0.1);  // a rectangle in relative dimensions
  cairo_rel_line_to(cr, 0.8, 0);
  cairo_rel_line_to(cr, 0, 0.8);
  cairo_rel_line_to(cr, -0.8, 0);
  cairo_close_path(cr);
Comment 1 Billy Biggs 2005-09-13 06:14:58 UTC
If you save and restore around the path creation, you will create a scaled path
but draw with the current line width.

cairo_set_line_width(cr, 1);  // one pixel  wide line

cairo_save(cr);
cairo_scale(cr, width, height); // window dimensions in pixel
cairo_move_to(cr, 0.1, 0.1);  // a rectangle in relative dimensions
cairo_rel_line_to(cr, 0.8, 0);
cairo_rel_line_to(cr, 0, 0.8);
cairo_rel_line_to(cr, -0.8, 0);
cairo_close_path(cr);
cairo_restore(cr);
Comment 2 Attila Babo 2005-09-13 06:40:13 UTC
Thank you for the workaround! My current solution is to use
cairo_device_to_user() to apply the inverse transformation like this:

double lw=cairo_get_line_width(cr);

cairo_scale(cr, ..., ...);

cairo_device_to_user(cr, &lw, &lw);
cairo_set_line_width(cr, lw);

This working fine, but it would be better to do it in the library level in
cairo_scale automatically.

Comment 3 Attila Babo 2005-09-13 06:44:03 UTC
Sorry, this is the correct code to keep the line width:

double lw=cairo_get_line_width(cr);
cairo_user_to_device_distance(cr, &lw, &lw);

cairo_scale(cr, ..., ...);

cairo_device_to_user(cr, &lw, &lw);
cairo_set_line_width(cr, lw);
Comment 4 Owen Taylor 2005-09-13 16:41:37 UTC
Whether or not this change would be useful, it isn't a compatible change,
and therefore we are unlikely ever to make it.

(Making cairo_scale() *not* scale all aspects uniformly might also be
suprising and hard to predict. Though I agree that wanting to scale
coordinates for convenience but preserve line width is common.)
Comment 5 Carl Worth 2005-09-13 16:51:47 UTC
WONTFIX due to incompatibility is the correct resolution.

As for "better" it really could be argued in either direction as Owen
suggests above. There are use cases for both scaling and not scaling the
line width.

Billy already volunteered one approach that allows for using transformations
while creating a path without affecting the line width.

Here's another alternative which you might find more suitable for your situation.

Here's a way to set the line width in pixel dimensions just prior to stroking,
regardless of what transformation has been in effect before:

    /* create path here using whatever scaling desired. */

    cairo_save (cr);
    cairo_identity_matrix (cr);
    cairo_set_line_width (cr, width_in_pixels);
    cairo_stroke (cr);
    cairo_restore (cr);

Good luck, and have fun!
Comment 6 Attila Babo 2005-09-14 14:05:17 UTC
Talking about compatibility, please try out this code and explain the output.

void lwt(HDC hdc, int width, int height) {
	cairo_surface_t *surface = cairo_win32_surface_create(hdc);
	cairo_t *cr = cairo_create(surface);

	cairo_rectangle(cr, width*0.1, height*0.1, width*0.8, height*0.8);
	cairo_stroke(cr);

	cairo_move_to(cr, width*0.2, height*0.2);
	cairo_scale(cr, width, height);

	cairo_rel_line_to(cr, 0.6, 0.0);
	cairo_rel_line_to(cr, 0.0, 0.6);
	cairo_rel_line_to(cr, -0.6, 0.0);
	cairo_rel_line_to(cr, 0.0, -0.6);

	cairo_rectangle(cr, 0.3, 0.3, 0.4, 0.4);
	cairo_stroke(cr);

	cairo_show_page(cr);

	cairo_destroy(cr);
	cairo_surface_destroy(surface);
}

Now add this line after the scale operation and try again.
	cairo_set_line_width(cr, 1.0/width);

The scale doesn't effect the drawing position what I set before the
transformation as expected, but changed the effective line width. Do you really
like the current behaviour?
Comment 7 Owen Taylor 2005-09-14 19:17:11 UTC
The path is not part of the graphics state. That's how it works.

Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.