Upload Tizen2.0 source
[framework/graphics/cairo.git] / test / get-path-extents.c
1 /*
2  * Copyright © 2006 Novell, 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  * Novell, Inc. not be used in advertising or publicity pertaining to
10  * distribution of the software without specific, written prior
11  * permission. Novell, 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  * NOVELL, 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: Robert O'Callahan <rocallahan@novell.com>
24  */
25
26 #include "cairo-test.h"
27 #include <stddef.h>
28 #include <math.h>
29
30 enum ExtentsType { FILL, STROKE, PATH };
31
32 enum Relation { EQUALS, APPROX_EQUALS, CONTAINS };
33
34 static cairo_bool_t
35 check_extents (const cairo_test_context_t *ctx,
36                const char *message, cairo_t *cr, enum ExtentsType type,
37                enum Relation relation,
38                double x, double y, double width, double height)
39 {
40     double ext_x1, ext_y1, ext_x2, ext_y2;
41     const char *type_string;
42     const char *relation_string;
43
44     switch (type) {
45     default:
46     case FILL:
47         type_string = "fill";
48         cairo_fill_extents (cr, &ext_x1, &ext_y1, &ext_x2, &ext_y2);
49         break;
50     case STROKE:
51         type_string = "stroke";
52         cairo_stroke_extents (cr, &ext_x1, &ext_y1, &ext_x2, &ext_y2);
53         break;
54     case PATH:
55         type_string = "path";
56         cairo_path_extents (cr, &ext_x1, &ext_y1, &ext_x2, &ext_y2);
57         break;
58     }
59
60     /* ignore results after an error occurs */
61     if (cairo_status (cr))
62         return 1;
63
64     switch (relation) {
65     default:
66     case EQUALS:
67         relation_string = "equal";
68         if (ext_x1 == x && ext_y1 == y && ext_x2 == x + width && ext_y2 == y + height)
69             return 1;
70         break;
71     case APPROX_EQUALS:
72         relation_string = "approx. equal";
73         if (fabs (ext_x1 - x) < 1. &&
74             fabs (ext_y1 - y) < 1. &&
75             fabs (ext_x2 - (x + width))  < 1. &&
76             fabs (ext_y2 - (y + height)) < 1.)
77         {
78             return 1;
79         }
80         break;
81     case CONTAINS:
82         relation_string = "contain";
83         if (width == 0 || height == 0) {
84             /* odd test that doesn't really test anything... */
85             return 1;
86         }
87         if (ext_x1 <= x && ext_y1 <= y && ext_x2 >= x + width && ext_y2 >= y + height)
88             return 1;
89         break;
90     }
91
92     cairo_test_log (ctx, "Error: %s; %s extents (%g, %g) x (%g, %g) should %s (%g, %g) x (%g, %g)\n",
93                     message, type_string,
94                     ext_x1, ext_y1, ext_x2 - ext_x1, ext_y2 - ext_y1,
95                     relation_string,
96                     x, y, width, height);
97     return 0;
98 }
99
100 static cairo_test_status_t
101 draw (cairo_t *cr, int width, int height)
102 {
103     const cairo_test_context_t *ctx = cairo_test_get_context (cr);
104     cairo_surface_t *surface;
105     cairo_t         *cr2;
106     const char      *phase;
107     const char       string[] = "The quick brown fox jumps over the lazy dog.";
108     cairo_text_extents_t extents, scaled_font_extents;
109     cairo_status_t   status;
110     int              errors = 0;
111
112     surface = cairo_surface_create_similar (cairo_get_group_target (cr),
113                                             CAIRO_CONTENT_COLOR, 100, 100);
114     /* don't use cr accidentally */
115     cr = NULL;
116     cr2 = cairo_create (surface);
117     cairo_surface_destroy (surface);
118
119     cairo_set_line_width (cr2, 10);
120     cairo_set_line_join (cr2, CAIRO_LINE_JOIN_MITER);
121     cairo_set_miter_limit (cr2, 100);
122
123     phase = "No path";
124     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
125     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 0, 0, 0, 0);
126     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 0, 0, 0, 0);
127
128     cairo_save (cr2);
129
130     cairo_new_path (cr2);
131     cairo_move_to (cr2, 200, 400);
132     cairo_rel_line_to (cr2, 0., 0.);
133     phase = "Degenerate line";
134     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
135     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 0, 0, 0, 0);
136     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 200, 400, 0, 0);
137
138     cairo_new_path (cr2);
139     cairo_move_to (cr2, 200, 400);
140     cairo_rel_curve_to (cr2, 0., 0., 0., 0., 0., 0.);
141     phase = "Degenerate curve";
142     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
143     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 0, 0, 0, 0);
144     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 200, 400, 0, 0);
145
146     cairo_new_path (cr2);
147     cairo_arc (cr2, 200, 400, 0., 0, 2 * M_PI);
148     phase = "Degenerate arc (R=0)";
149     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
150     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 0, 0, 0, 0);
151     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 200, 400, 0, 0);
152
153     cairo_new_path (cr2);
154     cairo_arc_negative (cr2, 200, 400, 0., 0, 2 * M_PI);
155     phase = "Degenerate negative arc (R=0)";
156     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
157     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 0, 0, 0, 0);
158     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 200, 400, 0, 0);
159
160     cairo_new_path (cr2);
161     cairo_arc (cr2, 200, 400, 10., 0, 0);
162     phase = "Degenerate arc (Θ=0)";
163     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
164     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 0, 0, 0, 0);
165     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 210, 400, 0, 0);
166
167     cairo_new_path (cr2);
168     cairo_arc_negative (cr2, 200, 400, 10., 0, 0);
169     phase = "Degenerate negative arc (Θ=0)";
170     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
171     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 0, 0, 0, 0);
172     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 210, 400, 0, 0);
173
174     cairo_new_path (cr2);
175     cairo_restore (cr2);
176
177     /* Test that with CAIRO_LINE_CAP_ROUND, we get "dots" from
178      * cairo_move_to; cairo_rel_line_to(0,0) */
179     cairo_save (cr2);
180
181     cairo_set_line_cap (cr2, CAIRO_LINE_CAP_ROUND);
182     cairo_set_line_width (cr2, 20);
183
184     cairo_move_to (cr2, 200, 400);
185     cairo_rel_line_to (cr2, 0, 0);
186     phase = "Single 'dot'";
187     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
188     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 190, 390, 20, 20);
189     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 200, 400, 0, 0);
190
191     /* Add another dot without starting a new path */
192     cairo_move_to (cr2, 100, 500);
193     cairo_rel_line_to (cr2, 0, 0);
194     phase = "Multiple 'dots'";
195     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
196     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 90, 390, 120, 120);
197     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 100, 400, 100, 100);
198
199     cairo_new_path (cr2);
200
201     cairo_restore (cr2);
202
203     /* http://bugs.freedesktop.org/show_bug.cgi?id=7965 */
204     phase = "A horizontal, open path";
205     cairo_save (cr2);
206     cairo_set_line_cap (cr2, CAIRO_LINE_CAP_ROUND);
207     cairo_set_line_join (cr2, CAIRO_LINE_JOIN_ROUND);
208     cairo_move_to (cr2, 0, 180);
209     cairo_line_to (cr2, 750, 180);
210     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
211     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, -5, 175, 760, 10);
212     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 0, 180, 750, 0);
213     cairo_new_path (cr2);
214     cairo_restore (cr2);
215
216     phase = "A vertical, open path";
217     cairo_save (cr2);
218     cairo_set_line_cap (cr2, CAIRO_LINE_CAP_ROUND);
219     cairo_set_line_join (cr2, CAIRO_LINE_JOIN_ROUND);
220     cairo_new_path (cr2);
221     cairo_move_to (cr2, 180, 0);
222     cairo_line_to (cr2, 180, 750);
223     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
224     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 175, -5, 10, 760);
225     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 180, 0, 0, 750);
226     cairo_new_path (cr2);
227     cairo_restore (cr2);
228
229     phase = "A degenerate open path";
230     cairo_save (cr2);
231     cairo_set_line_cap (cr2, CAIRO_LINE_CAP_ROUND);
232     cairo_set_line_join (cr2, CAIRO_LINE_JOIN_ROUND);
233     cairo_new_path (cr2);
234     cairo_move_to (cr2, 180, 0);
235     cairo_line_to (cr2, 180, 0);
236     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
237     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 175, -5, 10, 10);
238     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 180, 0, 0, 0);
239     cairo_new_path (cr2);
240     cairo_restore (cr2);
241
242     phase = "Simple rect";
243     cairo_save (cr2);
244     cairo_rectangle (cr2, 10, 10, 80, 80);
245     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 10, 10, 80, 80);
246     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 5, 5, 90, 90);
247     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 10, 10, 80, 80);
248     cairo_new_path (cr2);
249     cairo_restore (cr2);
250
251     phase = "Two rects";
252     cairo_save (cr2);
253     cairo_rectangle (cr2, 10, 10, 10, 10);
254     cairo_rectangle (cr2, 20, 20, 10, 10);
255     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 10, 10, 20, 20);
256     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 5, 5, 30, 30);
257     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 10, 10, 20, 20);
258     cairo_new_path (cr2);
259     cairo_restore (cr2);
260
261     phase = "Triangle";
262     cairo_save (cr2);
263     cairo_move_to (cr2, 10, 10);
264     cairo_line_to (cr2, 90, 90);
265     cairo_line_to (cr2, 90, 10);
266     cairo_close_path (cr2);
267     /* miter joins protrude 5*(1+sqrt(2)) above the top-left corner and to
268        the right of the bottom-right corner */
269     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 10, 10, 80, 80);
270     errors += !check_extents (ctx, phase, cr2, STROKE, CONTAINS, 0, 5, 95, 95);
271     errors += !check_extents (ctx, phase, cr2, PATH, CONTAINS, 10, 10, 80, 80);
272     cairo_new_path (cr2);
273     cairo_restore (cr2);
274
275     cairo_save (cr2);
276
277     cairo_set_line_width (cr2, 4);
278
279     cairo_rectangle (cr2, 10, 10, 30, 30);
280     cairo_rectangle (cr2, 25, 10, 15, 30);
281
282     cairo_set_fill_rule (cr2, CAIRO_FILL_RULE_EVEN_ODD);
283     phase = "EVEN_ODD overlapping rectangles";
284     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 10, 10, 15, 30);
285     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 8, 8, 34, 34);
286     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 10, 10, 30, 30);
287
288     /* Test other fill rule with the same path. */
289
290     cairo_set_fill_rule (cr2, CAIRO_FILL_RULE_WINDING);
291     phase = "WINDING overlapping rectangles";
292     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 10, 10, 30, 30);
293     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 8, 8, 34, 34);
294     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 10, 10, 30, 30);
295
296     /* Now, change the direction of the second rectangle and test both
297      * fill rules again. */
298     cairo_new_path (cr2);
299     cairo_rectangle (cr2, 10, 10, 30, 30);
300     cairo_rectangle (cr2, 25, 40, 15, -30);
301
302     cairo_set_fill_rule (cr2, CAIRO_FILL_RULE_EVEN_ODD);
303     phase = "EVEN_ODD overlapping rectangles";
304     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 10, 10, 15, 30);
305     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 8, 8, 34, 34);
306     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 10, 10, 30, 30);
307
308     /* Test other fill rule with the same path. */
309
310     cairo_set_fill_rule (cr2, CAIRO_FILL_RULE_WINDING);
311     phase = "WINDING overlapping rectangles";
312     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 10, 10, 15, 30);
313     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 8, 8, 34, 34);
314     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 10, 10, 30, 30);
315
316     cairo_new_path (cr2);
317
318     cairo_restore (cr2);
319
320     /* http://bugs.freedesktop.org/show_bug.cgi?id=7245 */
321     phase = "Arc";
322     cairo_save (cr2);
323     cairo_arc (cr2, 250.0, 250.0, 157.0, 5.147, 3.432);
324     cairo_set_line_width (cr2, 154.0);
325     errors += !check_extents (ctx, phase, cr2, STROKE, APPROX_EQUALS, 16, 38, 468, 446);
326     cairo_new_path (cr2);
327     cairo_restore (cr2);
328
329     phase = "Text";
330     cairo_save (cr2);
331     cairo_select_font_face (cr2, CAIRO_TEST_FONT_FAMILY " Sans",
332                             CAIRO_FONT_SLANT_NORMAL,
333                             CAIRO_FONT_WEIGHT_NORMAL);
334     cairo_set_font_size (cr2, 12);
335     cairo_text_extents (cr2, string, &extents);
336     /* double check that the two methods of measuring the text agree... */
337     cairo_scaled_font_text_extents (cairo_get_scaled_font (cr2),
338                                     string,
339                                     &scaled_font_extents);
340     if (memcmp (&extents, &scaled_font_extents, sizeof (extents))) {
341         cairo_test_log (ctx, "Error: cairo_text_extents() does not match cairo_scaled_font_text_extents() - font extents (%f, %f) x (%f, %f) should be (%f, %f) x (%f, %f)\n",
342                         scaled_font_extents.x_bearing,
343                         scaled_font_extents.y_bearing,
344                         scaled_font_extents.width,
345                         scaled_font_extents.height,
346                         extents.x_bearing,
347                         extents.y_bearing,
348                         extents.width,
349                         extents.height);
350         errors++;
351     }
352
353     cairo_move_to (cr2, -extents.x_bearing, -extents.y_bearing);
354     cairo_text_path (cr2, string);
355     cairo_set_line_width (cr2, 2.0);
356     /* XXX: We'd like to be able to use EQUALS here, but currently
357      * when hinting is enabled freetype returns integer extents. See
358      * http://cairographics.org/todo */
359     errors += !check_extents (ctx, phase, cr2, FILL, APPROX_EQUALS,
360                               0, 0, extents.width, extents.height);
361     errors += !check_extents (ctx, phase, cr2, STROKE, APPROX_EQUALS,
362                               -1, -1, extents.width+2, extents.height+2);
363     errors += !check_extents (ctx, phase, cr2, PATH, APPROX_EQUALS,
364                               0, 0, extents.width, extents.height);
365     cairo_new_path (cr2);
366     cairo_restore (cr2);
367
368     phase = "User space, simple scale, getting extents with same transform";
369     cairo_save (cr2);
370     cairo_scale (cr2, 2, 2);
371     cairo_rectangle (cr2, 5, 5, 40, 40);
372     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 5, 5, 40, 40);
373     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 0, 0, 50, 50);
374     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 5, 5, 40, 40);
375     cairo_new_path (cr2);
376     cairo_restore (cr2);
377
378     phase = "User space, simple scale, getting extents with no transform";
379     cairo_save (cr2);
380     cairo_save (cr2);
381     cairo_scale (cr2, 2, 2);
382     cairo_rectangle (cr2, 5, 5, 40, 40);
383     cairo_restore (cr2);
384     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 10, 10, 80, 80);
385     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 5, 5, 90, 90);
386     errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 10, 10, 80, 80);
387     cairo_new_path (cr2);
388     cairo_restore (cr2);
389
390     phase = "User space, rotation, getting extents with transform";
391     cairo_save (cr2);
392     cairo_rectangle (cr2, -50, -50, 50, 50);
393     cairo_rotate (cr2, -M_PI/4);
394     /* the path in user space is now (nearly) the square rotated by
395        45 degrees about the origin. Thus its x1 and x2 are both nearly 0.
396        This should show any bugs where we just transform device-space
397        x1,y1 and x2,y2 to get the extents. */
398     /* The largest axis-aligned square inside the rotated path has
399        side lengths 50*sqrt(2), so a bit over 35 on either side of
400        the axes. With the stroke width added to the rotated path,
401        the largest axis-aligned square is a bit over 38 on either side of
402        the axes. */
403     errors += !check_extents (ctx, phase, cr2, FILL, CONTAINS, -35, -35, 35, 35);
404     errors += !check_extents (ctx, phase, cr2, STROKE, CONTAINS, -38, -38, 38, 38);
405     errors += !check_extents (ctx, phase, cr2, PATH, CONTAINS, -35, -35, 35, 35);
406     cairo_new_path (cr2);
407     cairo_restore (cr2);
408
409     status = cairo_status (cr2);
410     cairo_destroy (cr2);
411
412     if (status)
413         return cairo_test_status_from_status (ctx, status);
414
415     return errors == 0 ? CAIRO_TEST_SUCCESS : CAIRO_TEST_FAILURE;
416 }
417
418 CAIRO_TEST (get_path_extents,
419             "Test cairo_fill_extents and cairo_stroke_extents",
420             "extents, path", /* keywords */
421             NULL, /* requirements */
422             0, 0,
423             NULL, draw)