037b887fdddf447f30bbf96ca430a47b6bbaedd2
[framework/graphics/cairo.git] / test / line-width-scale.c
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 spline_path (cairo_t *cr)
57 {
58     cairo_save (cr);
59     {
60         cairo_move_to (cr,
61                        - SPLINE, 0);
62         cairo_curve_to (cr,
63                         - SPLINE / 4, - SPLINE,
64                           SPLINE / 4,   SPLINE,
65                           SPLINE, 0);
66     }
67     cairo_restore (cr);
68 }
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 scale_then_set_line_width_and_stroke (cairo_t *cr)
74 {
75     cairo_scale (cr, XSCALE, YSCALE);
76     cairo_set_line_width (cr, LINE_WIDTH);
77     spline_path (cr);
78     cairo_stroke (cr);
79 }
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 scale_path_and_line_width (cairo_t *cr)
89 {
90     cairo_save (cr);
91     {
92         cairo_scale (cr, XSCALE, YSCALE);
93         spline_path (cr);
94     }
95     cairo_restore (cr);
96
97     cairo_save (cr);
98     {
99         cairo_scale (cr, XSCALE, YSCALE);
100         cairo_set_line_width (cr, LINE_WIDTH);
101         cairo_stroke (cr);
102     }
103     cairo_restore (cr);
104 }
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 set_line_width_then_scale_and_stroke (cairo_t *cr)
115 {
116     cairo_set_line_width (cr, LINE_WIDTH);
117     cairo_scale (cr, XSCALE, YSCALE);
118     spline_path (cr);
119     cairo_stroke (cr);
120 }
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 scale_path_not_line_width (cairo_t *cr)
130 {
131     cairo_save (cr);
132     {
133         cairo_scale (cr, XSCALE, YSCALE);
134         spline_path (cr);
135     }
136     cairo_restore (cr);
137
138     cairo_save (cr);
139     {
140         cairo_set_line_width (cr, LINE_WIDTH);
141         cairo_stroke (cr);
142     }
143     cairo_restore (cr);
144 }
145
146 static cairo_test_status_t
147 draw (cairo_t *cr, int width, int height)
148 {
149     int i;
150     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     cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
158     cairo_paint (cr);
159     cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
160
161     for (i = 0; i < 4; i++) {
162         cairo_save (cr);
163         cairo_translate (cr,
164                          WIDTH/4  + (i % 2) * WIDTH/2,
165                          HEIGHT/4 + (i / 2) * HEIGHT/2);
166         (figures[i]) (cr);
167         cairo_restore (cr);
168     }
169
170     return CAIRO_TEST_SUCCESS;
171 }
172
173 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)