1
/*
2
 * Copyright © 2006 Red Hat, Inc.
3
 *
4
 * Permission to use, copy, modify, distribute, and sell this software
5
 * and its documentation for any purpose is hereby granted without
6
 * fee, provided that the above copyright notice appear in all copies
7
 * and that both that copyright notice and this permission notice
8
 * appear in supporting documentation, and that the name of
9
 * Red Hat, Inc. not be used in advertising or publicity pertaining to
10
 * distribution of the software without specific, written prior
11
 * permission. Red Hat, Inc. makes no representations about the
12
 * suitability of this software for any purpose.  It is provided "as
13
 * is" without express or implied warranty.
14
 *
15
 * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
16
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17
 * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
18
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
19
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
21
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22
 *
23
 * Author: Carl D. Worth <cworth@cworth.org>
24
 */
25

            
26
#include "cairo-test.h"
27

            
28
/* This test exercises the various interactions between
29
 * cairo_set_line_width and cairo_scale. Specifically it shows how
30
 * separate transformations can affect the pen for stroking compared
31
 * to the path itself.
32
 *
33
 * This was inspired by an image by Maxim Shemanarev demonstrating the
34
 * flexible-pipeline nature of his Antigrain Geometry project:
35
 *
36
 *	http://antigrain.com/tips/line_alignment/conv_order.gif
37
 *
38
 * It also uncovered some behavior in cairo that I found surprising.
39
 * Namely, cairo_set_line_width was not transforming the width
40
 * according the the current CTM, but instead delaying that
41
 * transformation until the time of cairo_stroke.
42
 *
43
 * This delayed behavior was released in cairo 1.0 so we're going to
44
 * document this as the way cairo_set_line_width works rather than
45
 * considering this a bug.
46
 */
47

            
48
#define LINE_WIDTH 13
49
#define SPLINE 50.0
50
#define XSCALE  0.5
51
#define YSCALE  2.0
52
#define WIDTH (XSCALE * SPLINE * 6.0)
53
#define HEIGHT (YSCALE * SPLINE * 2.0)
54

            
55
static void
56
12
spline_path (cairo_t *cr)
57
{
58
12
    cairo_save (cr);
59
    {
60
12
	cairo_move_to (cr,
61
		       - SPLINE, 0);
62
12
	cairo_curve_to (cr,
63
			- SPLINE / 4, - SPLINE,
64
			  SPLINE / 4,   SPLINE,
65
			  SPLINE, 0);
66
    }
67
12
    cairo_restore (cr);
68
12
}
69

            
70
/* If we scale before setting the line width or creating the path,
71
 * then obviously both will be scaled. */
72
static void
73
3
scale_then_set_line_width_and_stroke (cairo_t *cr)
74
{
75
3
    cairo_scale (cr, XSCALE, YSCALE);
76
3
    cairo_set_line_width (cr, LINE_WIDTH);
77
3
    spline_path (cr);
78
3
    cairo_stroke (cr);
79
3
}
80

            
81
/* This is used to verify the results of
82
 * scale_then_set_line_width_and_stroke.
83
 *
84
 * It uses save/restore pairs to isolate the scaling of the path and
85
 * line_width and ensures that both are scaled.
86
 */
87
static void
88
3
scale_path_and_line_width (cairo_t *cr)
89
{
90
3
    cairo_save (cr);
91
    {
92
3
	cairo_scale (cr, XSCALE, YSCALE);
93
3
	spline_path (cr);
94
    }
95
3
    cairo_restore (cr);
96

            
97
3
    cairo_save (cr);
98
    {
99
3
	cairo_scale (cr, XSCALE, YSCALE);
100
3
	cairo_set_line_width (cr, LINE_WIDTH);
101
3
	cairo_stroke (cr);
102
    }
103
3
    cairo_restore (cr);
104
3
}
105

            
106
/* This is the case that was surprising.
107
 *
108
 * Setting the line width before scaling doesn't change anything. The
109
 * line width will be interpreted under the CTM in effect at the time
110
 * of cairo_stroke, so the line width will be scaled as well as the
111
 * path here.
112
 */
113
static void
114
3
set_line_width_then_scale_and_stroke (cairo_t *cr)
115
{
116
3
    cairo_set_line_width (cr, LINE_WIDTH);
117
3
    cairo_scale (cr, XSCALE, YSCALE);
118
3
    spline_path (cr);
119
3
    cairo_stroke (cr);
120
3
}
121

            
122
/* Here then is the way to achieve the alternate result.
123
 *
124
 * This uses save/restore pairs to isolate the scaling of the path and
125
 * line_width and ensures that the path is scaled while the line width
126
 * is not.
127
 */
128
static void
129
3
scale_path_not_line_width (cairo_t *cr)
130
{
131
3
    cairo_save (cr);
132
    {
133
3
	cairo_scale (cr, XSCALE, YSCALE);
134
3
	spline_path (cr);
135
    }
136
3
    cairo_restore (cr);
137

            
138
3
    cairo_save (cr);
139
    {
140
3
	cairo_set_line_width (cr, LINE_WIDTH);
141
3
	cairo_stroke (cr);
142
    }
143
3
    cairo_restore (cr);
144
3
}
145

            
146
static cairo_test_status_t
147
3
draw (cairo_t *cr, int width, int height)
148
{
149
    int i;
150
3
    void (* const figures[4]) (cairo_t *cr) = {
151
	scale_then_set_line_width_and_stroke,
152
	scale_path_and_line_width,
153
	set_line_width_then_scale_and_stroke,
154
	scale_path_not_line_width
155
    };
156

            
157
3
    cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
158
3
    cairo_paint (cr);
159
3
    cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
160

            
161
15
    for (i = 0; i < 4; i++) {
162
12
	cairo_save (cr);
163
12
	cairo_translate (cr,
164
12
			 WIDTH/4  + (i % 2) * WIDTH/2,
165
12
			 HEIGHT/4 + (i / 2) * HEIGHT/2);
166
12
	(figures[i]) (cr);
167
12
	cairo_restore (cr);
168
    }
169

            
170
3
    return CAIRO_TEST_SUCCESS;
171
}
172

            
173
1
CAIRO_TEST (line_width_scale,
174
	    "Tests interaction of cairo_set_line_width with cairo_scale",
175
	    "stroke", /* keywords */
176
	    NULL, /* requirements */
177
	    WIDTH, HEIGHT,
178
	    NULL, draw)