/work/workdir/UnpackedTarball/cairo/src/cairo-path-stroke-boxes.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ |
2 | | /* cairo - a vector graphics library with display and print output |
3 | | * |
4 | | * Copyright © 2002 University of Southern California |
5 | | * |
6 | | * This library is free software; you can redistribute it and/or |
7 | | * modify it either under the terms of the GNU Lesser General Public |
8 | | * License version 2.1 as published by the Free Software Foundation |
9 | | * (the "LGPL") or, at your option, under the terms of the Mozilla |
10 | | * Public License Version 1.1 (the "MPL"). If you do not alter this |
11 | | * notice, a recipient may use your version of this file under either |
12 | | * the MPL or the LGPL. |
13 | | * |
14 | | * You should have received a copy of the LGPL along with this library |
15 | | * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
16 | | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
17 | | * You should have received a copy of the MPL along with this library |
18 | | * in the file COPYING-MPL-1.1 |
19 | | * |
20 | | * The contents of this file are subject to the Mozilla Public License |
21 | | * Version 1.1 (the "License"); you may not use this file except in |
22 | | * compliance with the License. You may obtain a copy of the License at |
23 | | * http://www.mozilla.org/MPL/ |
24 | | * |
25 | | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
26 | | * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
27 | | * the specific language governing rights and limitations. |
28 | | * |
29 | | * The Original Code is the cairo graphics library. |
30 | | * |
31 | | * The Initial Developer of the Original Code is University of Southern |
32 | | * California. |
33 | | * |
34 | | * Contributor(s): |
35 | | * Carl D. Worth <cworth@cworth.org> |
36 | | * Chris Wilson <chris@chris-wilson.co.uk> |
37 | | */ |
38 | | |
39 | | #define _DEFAULT_SOURCE /* for hypot() */ |
40 | | #include "cairoint.h" |
41 | | |
42 | | #include "cairo-box-inline.h" |
43 | | #include "cairo-boxes-private.h" |
44 | | #include "cairo-error-private.h" |
45 | | #include "cairo-path-fixed-private.h" |
46 | | #include "cairo-slope-private.h" |
47 | | #include "cairo-stroke-dash-private.h" |
48 | | |
49 | | typedef struct _segment_t { |
50 | | cairo_point_t p1, p2; |
51 | | unsigned flags; |
52 | 97.9k | #define HORIZONTAL 0x1 |
53 | 0 | #define FORWARDS 0x2 |
54 | 48.9k | #define JOIN 0x4 |
55 | | } segment_t; |
56 | | |
57 | | typedef struct _cairo_rectilinear_stroker { |
58 | | const cairo_stroke_style_t *stroke_style; |
59 | | const cairo_matrix_t *ctm; |
60 | | cairo_antialias_t antialias; |
61 | | |
62 | | cairo_fixed_t half_line_x, half_line_y; |
63 | | cairo_boxes_t *boxes; |
64 | | cairo_point_t current_point; |
65 | | cairo_point_t first_point; |
66 | | cairo_bool_t open_sub_path; |
67 | | |
68 | | cairo_stroker_dash_t dash; |
69 | | |
70 | | cairo_bool_t has_bounds; |
71 | | cairo_box_t bounds; |
72 | | |
73 | | int num_segments; |
74 | | int segments_size; |
75 | | segment_t *segments; |
76 | | segment_t segments_embedded[8]; /* common case is a single rectangle */ |
77 | | } cairo_rectilinear_stroker_t; |
78 | | |
79 | | static void |
80 | | _cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker, |
81 | | const cairo_box_t *boxes, |
82 | | int num_boxes) |
83 | 14.5k | { |
84 | 14.5k | stroker->has_bounds = TRUE; |
85 | 14.5k | _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds); |
86 | | |
87 | 14.5k | stroker->bounds.p1.x -= stroker->half_line_x; |
88 | 14.5k | stroker->bounds.p2.x += stroker->half_line_x; |
89 | | |
90 | 14.5k | stroker->bounds.p1.y -= stroker->half_line_y; |
91 | 14.5k | stroker->bounds.p2.y += stroker->half_line_y; |
92 | 14.5k | } |
93 | | |
94 | | static cairo_bool_t |
95 | | _cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker, |
96 | | const cairo_stroke_style_t *stroke_style, |
97 | | const cairo_matrix_t *ctm, |
98 | | cairo_antialias_t antialias, |
99 | | cairo_boxes_t *boxes) |
100 | 20.6k | { |
101 | | /* This special-case rectilinear stroker only supports |
102 | | * miter-joined lines (not curves) and a translation-only matrix |
103 | | * (though it could probably be extended to support a matrix with |
104 | | * uniform, integer scaling). |
105 | | * |
106 | | * It also only supports horizontal and vertical line_to |
107 | | * elements. But we don't catch that here, but instead return |
108 | | * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any |
109 | | * non-rectilinear line_to is encountered. |
110 | | */ |
111 | 20.6k | if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER) |
112 | 0 | return FALSE; |
113 | | |
114 | | /* If the miter limit turns right angles into bevels, then we |
115 | | * can't use this optimization. Remember, the ratio is |
116 | | * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2, |
117 | | * which we round for safety. */ |
118 | 20.6k | if (stroke_style->miter_limit < M_SQRT2) |
119 | 0 | return FALSE; |
120 | | |
121 | 20.6k | if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT || |
122 | 20.6k | stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE)) |
123 | 0 | { |
124 | 0 | return FALSE; |
125 | 0 | } |
126 | | |
127 | 20.6k | if (! _cairo_matrix_is_scale (ctm)) |
128 | 0 | return FALSE; |
129 | | |
130 | 20.6k | stroker->stroke_style = stroke_style; |
131 | 20.6k | stroker->ctm = ctm; |
132 | 20.6k | stroker->antialias = antialias; |
133 | | |
134 | 20.6k | stroker->half_line_x = |
135 | 20.6k | _cairo_fixed_from_double (fabs(ctm->xx) * stroke_style->line_width / 2.0); |
136 | 20.6k | stroker->half_line_y = |
137 | 20.6k | _cairo_fixed_from_double (fabs(ctm->yy) * stroke_style->line_width / 2.0); |
138 | | |
139 | 20.6k | stroker->open_sub_path = FALSE; |
140 | 20.6k | stroker->segments = stroker->segments_embedded; |
141 | 20.6k | stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded); |
142 | 20.6k | stroker->num_segments = 0; |
143 | | |
144 | 20.6k | _cairo_stroker_dash_init (&stroker->dash, stroke_style); |
145 | | |
146 | 20.6k | stroker->has_bounds = FALSE; |
147 | | |
148 | 20.6k | stroker->boxes = boxes; |
149 | | |
150 | 20.6k | return TRUE; |
151 | 20.6k | } |
152 | | |
153 | | static void |
154 | | _cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker) |
155 | 20.6k | { |
156 | 20.6k | if (stroker->segments != stroker->segments_embedded) |
157 | 297 | free (stroker->segments); |
158 | 20.6k | } |
159 | | |
160 | | static cairo_status_t |
161 | | _cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker, |
162 | | const cairo_point_t *p1, |
163 | | const cairo_point_t *p2, |
164 | | unsigned flags) |
165 | 48.9k | { |
166 | 48.9k | if (CAIRO_INJECT_FAULT ()) |
167 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
168 | | |
169 | 48.9k | if (stroker->num_segments == stroker->segments_size) { |
170 | 401 | int new_size = stroker->segments_size * 2; |
171 | 401 | segment_t *new_segments; |
172 | | |
173 | 401 | if (stroker->segments == stroker->segments_embedded) { |
174 | 297 | new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t)); |
175 | 297 | if (unlikely (new_segments == NULL)) |
176 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
177 | | |
178 | 297 | memcpy (new_segments, stroker->segments, |
179 | 297 | stroker->num_segments * sizeof (segment_t)); |
180 | 297 | } else { |
181 | 104 | new_segments = _cairo_realloc_ab (stroker->segments, |
182 | 104 | new_size, sizeof (segment_t)); |
183 | 104 | if (unlikely (new_segments == NULL)) |
184 | 0 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
185 | 104 | } |
186 | | |
187 | 401 | stroker->segments_size = new_size; |
188 | 401 | stroker->segments = new_segments; |
189 | 401 | } |
190 | | |
191 | 48.9k | stroker->segments[stroker->num_segments].p1 = *p1; |
192 | 48.9k | stroker->segments[stroker->num_segments].p2 = *p2; |
193 | 48.9k | stroker->segments[stroker->num_segments].flags = flags; |
194 | 48.9k | stroker->num_segments++; |
195 | | |
196 | 48.9k | return CAIRO_STATUS_SUCCESS; |
197 | 48.9k | } |
198 | | |
199 | | static cairo_status_t |
200 | | _cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker) |
201 | 67.2k | { |
202 | 67.2k | cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; |
203 | 67.2k | cairo_fixed_t half_line_x = stroker->half_line_x; |
204 | 67.2k | cairo_fixed_t half_line_y = stroker->half_line_y; |
205 | 67.2k | cairo_status_t status; |
206 | 67.2k | int i, j; |
207 | | |
208 | | /* For each segment we generate a single rectangle. |
209 | | * This rectangle is based on a perpendicular extension (by half the |
210 | | * line width) of the segment endpoints * after some adjustments of the |
211 | | * endpoints to account for caps and joins. |
212 | | */ |
213 | 116k | for (i = 0; i < stroker->num_segments; i++) { |
214 | 48.9k | cairo_bool_t lengthen_initial, lengthen_final; |
215 | 48.9k | cairo_point_t *a, *b; |
216 | 48.9k | cairo_box_t box; |
217 | | |
218 | 48.9k | a = &stroker->segments[i].p1; |
219 | 48.9k | b = &stroker->segments[i].p2; |
220 | | |
221 | | /* We adjust the initial point of the segment to extend the |
222 | | * rectangle to include the previous cap or join, (this |
223 | | * adjustment applies to all segments except for the first |
224 | | * segment of open, butt-capped paths). However, we must be |
225 | | * careful not to emit a miter join across a degenerate segment |
226 | | * which has been elided. |
227 | | * |
228 | | * Overlapping segments will be eliminated by the tessellation. |
229 | | * Ideally, we would not emit these self-intersections at all, |
230 | | * but that is tricky with segments shorter than half_line_width. |
231 | | */ |
232 | 48.9k | j = i == 0 ? stroker->num_segments - 1 : i-1; |
233 | 48.9k | lengthen_initial = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL; |
234 | 48.9k | j = i == stroker->num_segments - 1 ? 0 : i+1; |
235 | 48.9k | lengthen_final = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL; |
236 | 48.9k | if (stroker->open_sub_path) { |
237 | 32.0k | if (i == 0) |
238 | 16.2k | lengthen_initial = line_cap != CAIRO_LINE_CAP_BUTT; |
239 | | |
240 | 32.0k | if (i == stroker->num_segments - 1) |
241 | 16.2k | lengthen_final = line_cap != CAIRO_LINE_CAP_BUTT; |
242 | 32.0k | } |
243 | | |
244 | | /* Perform the adjustments of the endpoints. */ |
245 | 48.9k | if (lengthen_initial | lengthen_final) { |
246 | 5.96k | if (a->y == b->y) { |
247 | 3.17k | if (a->x < b->x) { |
248 | 1.58k | if (lengthen_initial) |
249 | 1.27k | a->x -= half_line_x; |
250 | 1.58k | if (lengthen_final) |
251 | 1.06k | b->x += half_line_x; |
252 | 1.59k | } else { |
253 | 1.59k | if (lengthen_initial) |
254 | 1.23k | a->x += half_line_x; |
255 | 1.59k | if (lengthen_final) |
256 | 1.03k | b->x -= half_line_x; |
257 | 1.59k | } |
258 | 3.17k | } else { |
259 | 2.79k | if (a->y < b->y) { |
260 | 1.47k | if (lengthen_initial) |
261 | 1.06k | a->y -= half_line_y; |
262 | 1.47k | if (lengthen_final) |
263 | 1.26k | b->y += half_line_y; |
264 | 1.47k | } else { |
265 | 1.32k | if (lengthen_initial) |
266 | 1.03k | a->y += half_line_y; |
267 | 1.32k | if (lengthen_final) |
268 | 1.24k | b->y -= half_line_y; |
269 | 1.32k | } |
270 | 2.79k | } |
271 | 5.96k | } |
272 | | |
273 | | /* Form the rectangle by expanding by half the line width in |
274 | | * either perpendicular direction. */ |
275 | 48.9k | if (a->y == b->y) { |
276 | 22.6k | a->y -= half_line_y; |
277 | 22.6k | b->y += half_line_y; |
278 | 26.3k | } else { |
279 | 26.3k | a->x -= half_line_x; |
280 | 26.3k | b->x += half_line_x; |
281 | 26.3k | } |
282 | | |
283 | 48.9k | if (a->x < b->x) { |
284 | 37.6k | box.p1.x = a->x; |
285 | 37.6k | box.p2.x = b->x; |
286 | 37.6k | } else { |
287 | 11.3k | box.p1.x = b->x; |
288 | 11.3k | box.p2.x = a->x; |
289 | 11.3k | } |
290 | 48.9k | if (a->y < b->y) { |
291 | 34.6k | box.p1.y = a->y; |
292 | 34.6k | box.p2.y = b->y; |
293 | 34.6k | } else { |
294 | 14.2k | box.p1.y = b->y; |
295 | 14.2k | box.p2.y = a->y; |
296 | 14.2k | } |
297 | | |
298 | 48.9k | status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box); |
299 | 48.9k | if (unlikely (status)) |
300 | 0 | return status; |
301 | 48.9k | } |
302 | | |
303 | 67.2k | stroker->num_segments = 0; |
304 | 67.2k | return CAIRO_STATUS_SUCCESS; |
305 | 67.2k | } |
306 | | |
307 | | static cairo_status_t |
308 | | _cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker) |
309 | 0 | { |
310 | 0 | cairo_status_t status; |
311 | 0 | cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; |
312 | 0 | cairo_fixed_t half_line_x = stroker->half_line_x; |
313 | 0 | cairo_fixed_t half_line_y = stroker->half_line_y; |
314 | 0 | int i; |
315 | |
|
316 | 0 | for (i = 0; i < stroker->num_segments; i++) { |
317 | 0 | cairo_point_t *a, *b; |
318 | 0 | cairo_bool_t is_horizontal; |
319 | 0 | cairo_box_t box; |
320 | |
|
321 | 0 | a = &stroker->segments[i].p1; |
322 | 0 | b = &stroker->segments[i].p2; |
323 | |
|
324 | 0 | is_horizontal = stroker->segments[i].flags & HORIZONTAL; |
325 | | |
326 | | /* Handle the joins for a potentially degenerate segment. */ |
327 | 0 | if (line_cap == CAIRO_LINE_CAP_BUTT && |
328 | 0 | stroker->segments[i].flags & JOIN && |
329 | 0 | (i != stroker->num_segments - 1 || |
330 | 0 | (! stroker->open_sub_path && stroker->dash.dash_starts_on))) |
331 | 0 | { |
332 | 0 | cairo_slope_t out_slope; |
333 | 0 | int j = (i + 1) % stroker->num_segments; |
334 | 0 | cairo_bool_t forwards = !!(stroker->segments[i].flags & FORWARDS); |
335 | |
|
336 | 0 | _cairo_slope_init (&out_slope, |
337 | 0 | &stroker->segments[j].p1, |
338 | 0 | &stroker->segments[j].p2); |
339 | 0 | box.p2 = box.p1 = stroker->segments[i].p2; |
340 | |
|
341 | 0 | if (is_horizontal) { |
342 | 0 | if (forwards) |
343 | 0 | box.p2.x += half_line_x; |
344 | 0 | else |
345 | 0 | box.p1.x -= half_line_x; |
346 | |
|
347 | 0 | if (out_slope.dy > 0) |
348 | 0 | box.p1.y -= half_line_y; |
349 | 0 | else |
350 | 0 | box.p2.y += half_line_y; |
351 | 0 | } else { |
352 | 0 | if (forwards) |
353 | 0 | box.p2.y += half_line_y; |
354 | 0 | else |
355 | 0 | box.p1.y -= half_line_y; |
356 | |
|
357 | 0 | if (out_slope.dx > 0) |
358 | 0 | box.p1.x -= half_line_x; |
359 | 0 | else |
360 | 0 | box.p2.x += half_line_x; |
361 | 0 | } |
362 | |
|
363 | 0 | status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box); |
364 | 0 | if (unlikely (status)) |
365 | 0 | return status; |
366 | 0 | } |
367 | | |
368 | | /* Perform the adjustments of the endpoints. */ |
369 | 0 | if (is_horizontal) { |
370 | 0 | if (line_cap == CAIRO_LINE_CAP_SQUARE) { |
371 | 0 | if (a->x <= b->x) { |
372 | 0 | a->x -= half_line_x; |
373 | 0 | b->x += half_line_x; |
374 | 0 | } else { |
375 | 0 | a->x += half_line_x; |
376 | 0 | b->x -= half_line_x; |
377 | 0 | } |
378 | 0 | } |
379 | |
|
380 | 0 | a->y += half_line_y; |
381 | 0 | b->y -= half_line_y; |
382 | 0 | } else { |
383 | 0 | if (line_cap == CAIRO_LINE_CAP_SQUARE) { |
384 | 0 | if (a->y <= b->y) { |
385 | 0 | a->y -= half_line_y; |
386 | 0 | b->y += half_line_y; |
387 | 0 | } else { |
388 | 0 | a->y += half_line_y; |
389 | 0 | b->y -= half_line_y; |
390 | 0 | } |
391 | 0 | } |
392 | |
|
393 | 0 | a->x += half_line_x; |
394 | 0 | b->x -= half_line_x; |
395 | 0 | } |
396 | |
|
397 | 0 | if (a->x == b->x && a->y == b->y) |
398 | 0 | continue; |
399 | | |
400 | 0 | if (a->x < b->x) { |
401 | 0 | box.p1.x = a->x; |
402 | 0 | box.p2.x = b->x; |
403 | 0 | } else { |
404 | 0 | box.p1.x = b->x; |
405 | 0 | box.p2.x = a->x; |
406 | 0 | } |
407 | 0 | if (a->y < b->y) { |
408 | 0 | box.p1.y = a->y; |
409 | 0 | box.p2.y = b->y; |
410 | 0 | } else { |
411 | 0 | box.p1.y = b->y; |
412 | 0 | box.p2.y = a->y; |
413 | 0 | } |
414 | |
|
415 | 0 | status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box); |
416 | 0 | if (unlikely (status)) |
417 | 0 | return status; |
418 | 0 | } |
419 | | |
420 | 0 | stroker->num_segments = 0; |
421 | |
|
422 | 0 | return CAIRO_STATUS_SUCCESS; |
423 | 0 | } |
424 | | |
425 | | static cairo_status_t |
426 | | _cairo_rectilinear_stroker_move_to (void *closure, |
427 | | const cairo_point_t *point) |
428 | 41.5k | { |
429 | 41.5k | cairo_rectilinear_stroker_t *stroker = closure; |
430 | 41.5k | cairo_status_t status; |
431 | | |
432 | 41.5k | if (stroker->dash.dashed) |
433 | 0 | status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker); |
434 | 41.5k | else |
435 | 41.5k | status = _cairo_rectilinear_stroker_emit_segments (stroker); |
436 | 41.5k | if (unlikely (status)) |
437 | 0 | return status; |
438 | | |
439 | | /* reset the dash pattern for new sub paths */ |
440 | 41.5k | _cairo_stroker_dash_start (&stroker->dash); |
441 | | |
442 | 41.5k | stroker->current_point = *point; |
443 | 41.5k | stroker->first_point = *point; |
444 | | |
445 | 41.5k | return CAIRO_STATUS_SUCCESS; |
446 | 41.5k | } |
447 | | |
448 | | static cairo_status_t |
449 | | _cairo_rectilinear_stroker_line_to (void *closure, |
450 | | const cairo_point_t *b) |
451 | 59.8k | { |
452 | 59.8k | cairo_rectilinear_stroker_t *stroker = closure; |
453 | 59.8k | cairo_point_t *a = &stroker->current_point; |
454 | 59.8k | cairo_status_t status; |
455 | | |
456 | | /* We only support horizontal or vertical elements. */ |
457 | 59.8k | assert (a->x == b->x || a->y == b->y); |
458 | | |
459 | | /* We don't draw anything for degenerate paths. */ |
460 | 59.8k | if (a->x == b->x && a->y == b->y) |
461 | 10.9k | return CAIRO_STATUS_SUCCESS; |
462 | | |
463 | 48.9k | status = _cairo_rectilinear_stroker_add_segment (stroker, a, b, |
464 | 48.9k | (a->y == b->y) | JOIN); |
465 | | |
466 | 48.9k | stroker->current_point = *b; |
467 | 48.9k | stroker->open_sub_path = TRUE; |
468 | | |
469 | 48.9k | return status; |
470 | 59.8k | } |
471 | | |
472 | | static cairo_status_t |
473 | | _cairo_rectilinear_stroker_line_to_dashed (void *closure, |
474 | | const cairo_point_t *point) |
475 | 0 | { |
476 | 0 | cairo_rectilinear_stroker_t *stroker = closure; |
477 | 0 | const cairo_point_t *a = &stroker->current_point; |
478 | 0 | const cairo_point_t *b = point; |
479 | 0 | cairo_bool_t fully_in_bounds; |
480 | 0 | double sf, sign, remain; |
481 | 0 | cairo_fixed_t mag; |
482 | 0 | cairo_status_t status; |
483 | 0 | cairo_line_t segment; |
484 | 0 | cairo_bool_t dash_on = FALSE; |
485 | 0 | unsigned is_horizontal; |
486 | | |
487 | | /* We don't draw anything for degenerate paths. */ |
488 | 0 | if (a->x == b->x && a->y == b->y) |
489 | 0 | return CAIRO_STATUS_SUCCESS; |
490 | | |
491 | | /* We only support horizontal or vertical elements. */ |
492 | 0 | assert (a->x == b->x || a->y == b->y); |
493 | | |
494 | 0 | fully_in_bounds = TRUE; |
495 | 0 | if (stroker->has_bounds && |
496 | 0 | (! _cairo_box_contains_point (&stroker->bounds, a) || |
497 | 0 | ! _cairo_box_contains_point (&stroker->bounds, b))) |
498 | 0 | { |
499 | 0 | fully_in_bounds = FALSE; |
500 | 0 | } |
501 | |
|
502 | 0 | is_horizontal = a->y == b->y; |
503 | 0 | if (is_horizontal) { |
504 | 0 | mag = b->x - a->x; |
505 | 0 | sf = fabs (stroker->ctm->xx); |
506 | 0 | } else { |
507 | 0 | mag = b->y - a->y; |
508 | 0 | sf = fabs (stroker->ctm->yy); |
509 | 0 | } |
510 | 0 | if (mag < 0) { |
511 | 0 | remain = _cairo_fixed_to_double (-mag); |
512 | 0 | sign = 1.; |
513 | 0 | } else { |
514 | 0 | remain = _cairo_fixed_to_double (mag); |
515 | 0 | is_horizontal |= FORWARDS; |
516 | 0 | sign = -1.; |
517 | 0 | } |
518 | |
|
519 | 0 | segment.p2 = segment.p1 = *a; |
520 | 0 | while (remain > 0.) { |
521 | 0 | double step_length; |
522 | |
|
523 | 0 | step_length = MIN (sf * stroker->dash.dash_remain, remain); |
524 | 0 | remain -= step_length; |
525 | |
|
526 | 0 | mag = _cairo_fixed_from_double (sign*remain); |
527 | 0 | if (is_horizontal & 0x1) |
528 | 0 | segment.p2.x = b->x + mag; |
529 | 0 | else |
530 | 0 | segment.p2.y = b->y + mag; |
531 | |
|
532 | 0 | if (stroker->dash.dash_on && |
533 | 0 | (fully_in_bounds || |
534 | 0 | _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) |
535 | 0 | { |
536 | 0 | status = _cairo_rectilinear_stroker_add_segment (stroker, |
537 | 0 | &segment.p1, |
538 | 0 | &segment.p2, |
539 | 0 | is_horizontal | (remain <= 0.) << 2); |
540 | 0 | if (unlikely (status)) |
541 | 0 | return status; |
542 | | |
543 | 0 | dash_on = TRUE; |
544 | 0 | } |
545 | 0 | else |
546 | 0 | { |
547 | 0 | dash_on = FALSE; |
548 | 0 | } |
549 | | |
550 | 0 | _cairo_stroker_dash_step (&stroker->dash, step_length / sf); |
551 | 0 | segment.p1 = segment.p2; |
552 | 0 | } |
553 | | |
554 | 0 | if (stroker->dash.dash_on && ! dash_on && |
555 | 0 | (fully_in_bounds || |
556 | 0 | _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) |
557 | 0 | { |
558 | | |
559 | | /* This segment ends on a transition to dash_on, compute a new face |
560 | | * and add cap for the beginning of the next dash_on step. |
561 | | */ |
562 | |
|
563 | 0 | status = _cairo_rectilinear_stroker_add_segment (stroker, |
564 | 0 | &segment.p1, |
565 | 0 | &segment.p1, |
566 | 0 | is_horizontal | JOIN); |
567 | 0 | if (unlikely (status)) |
568 | 0 | return status; |
569 | 0 | } |
570 | | |
571 | 0 | stroker->current_point = *point; |
572 | 0 | stroker->open_sub_path = TRUE; |
573 | |
|
574 | 0 | return CAIRO_STATUS_SUCCESS; |
575 | 0 | } |
576 | | |
577 | | static cairo_status_t |
578 | | _cairo_rectilinear_stroker_close_path (void *closure) |
579 | 9.22k | { |
580 | 9.22k | cairo_rectilinear_stroker_t *stroker = closure; |
581 | 9.22k | cairo_status_t status; |
582 | | |
583 | | /* We don't draw anything for degenerate paths. */ |
584 | 9.22k | if (! stroker->open_sub_path) |
585 | 3.54k | return CAIRO_STATUS_SUCCESS; |
586 | | |
587 | 5.67k | if (stroker->dash.dashed) { |
588 | 0 | status = _cairo_rectilinear_stroker_line_to_dashed (stroker, |
589 | 0 | &stroker->first_point); |
590 | 5.67k | } else { |
591 | 5.67k | status = _cairo_rectilinear_stroker_line_to (stroker, |
592 | 5.67k | &stroker->first_point); |
593 | 5.67k | } |
594 | 5.67k | if (unlikely (status)) |
595 | 0 | return status; |
596 | | |
597 | 5.67k | stroker->open_sub_path = FALSE; |
598 | | |
599 | 5.67k | if (stroker->dash.dashed) |
600 | 0 | status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker); |
601 | 5.67k | else |
602 | 5.67k | status = _cairo_rectilinear_stroker_emit_segments (stroker); |
603 | 5.67k | if (unlikely (status)) |
604 | 0 | return status; |
605 | | |
606 | 5.67k | return CAIRO_STATUS_SUCCESS; |
607 | 5.67k | } |
608 | | |
609 | | cairo_int_status_t |
610 | | _cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path, |
611 | | const cairo_stroke_style_t *stroke_style, |
612 | | const cairo_matrix_t *ctm, |
613 | | cairo_antialias_t antialias, |
614 | | cairo_boxes_t *boxes) |
615 | 20.6k | { |
616 | 20.6k | cairo_rectilinear_stroker_t rectilinear_stroker; |
617 | 20.6k | cairo_int_status_t status; |
618 | 20.6k | cairo_box_t box; |
619 | | |
620 | 20.6k | assert (_cairo_path_fixed_stroke_is_rectilinear (path)); |
621 | | |
622 | 20.6k | if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker, |
623 | 20.6k | stroke_style, ctm, antialias, |
624 | 20.6k | boxes)) |
625 | 0 | { |
626 | 0 | return CAIRO_INT_STATUS_UNSUPPORTED; |
627 | 0 | } |
628 | | |
629 | 20.6k | if (! rectilinear_stroker.dash.dashed && |
630 | 20.6k | _cairo_path_fixed_is_stroke_box (path, &box) && |
631 | | /* if the segments overlap we need to feed them into the tessellator */ |
632 | 20.6k | box.p2.x - box.p1.x > 2* rectilinear_stroker.half_line_x && |
633 | 20.6k | box.p2.y - box.p1.y > 2* rectilinear_stroker.half_line_y) |
634 | 620 | { |
635 | 620 | cairo_box_t b; |
636 | | |
637 | | /* top */ |
638 | 620 | b.p1.x = box.p1.x - rectilinear_stroker.half_line_x; |
639 | 620 | b.p2.x = box.p2.x + rectilinear_stroker.half_line_x; |
640 | 620 | b.p1.y = box.p1.y - rectilinear_stroker.half_line_y; |
641 | 620 | b.p2.y = box.p1.y + rectilinear_stroker.half_line_y; |
642 | 620 | status = _cairo_boxes_add (boxes, antialias, &b); |
643 | 620 | assert (status == CAIRO_INT_STATUS_SUCCESS); |
644 | | |
645 | | /* left (excluding top/bottom) */ |
646 | 620 | b.p1.x = box.p1.x - rectilinear_stroker.half_line_x; |
647 | 620 | b.p2.x = box.p1.x + rectilinear_stroker.half_line_x; |
648 | 620 | b.p1.y = box.p1.y + rectilinear_stroker.half_line_y; |
649 | 620 | b.p2.y = box.p2.y - rectilinear_stroker.half_line_y; |
650 | 620 | status = _cairo_boxes_add (boxes, antialias, &b); |
651 | 620 | assert (status == CAIRO_INT_STATUS_SUCCESS); |
652 | | |
653 | | /* right (excluding top/bottom) */ |
654 | 620 | b.p1.x = box.p2.x - rectilinear_stroker.half_line_x; |
655 | 620 | b.p2.x = box.p2.x + rectilinear_stroker.half_line_x; |
656 | 620 | b.p1.y = box.p1.y + rectilinear_stroker.half_line_y; |
657 | 620 | b.p2.y = box.p2.y - rectilinear_stroker.half_line_y; |
658 | 620 | status = _cairo_boxes_add (boxes, antialias, &b); |
659 | 620 | assert (status == CAIRO_INT_STATUS_SUCCESS); |
660 | | |
661 | | /* bottom */ |
662 | 620 | b.p1.x = box.p1.x - rectilinear_stroker.half_line_x; |
663 | 620 | b.p2.x = box.p2.x + rectilinear_stroker.half_line_x; |
664 | 620 | b.p1.y = box.p2.y - rectilinear_stroker.half_line_y; |
665 | 620 | b.p2.y = box.p2.y + rectilinear_stroker.half_line_y; |
666 | 620 | status = _cairo_boxes_add (boxes, antialias, &b); |
667 | 620 | assert (status == CAIRO_INT_STATUS_SUCCESS); |
668 | | |
669 | 620 | goto done; |
670 | 620 | } |
671 | | |
672 | 20.0k | if (boxes->num_limits) { |
673 | 14.5k | _cairo_rectilinear_stroker_limit (&rectilinear_stroker, |
674 | 14.5k | boxes->limits, |
675 | 14.5k | boxes->num_limits); |
676 | 14.5k | } |
677 | | |
678 | 20.0k | status = _cairo_path_fixed_interpret (path, |
679 | 20.0k | _cairo_rectilinear_stroker_move_to, |
680 | 20.0k | rectilinear_stroker.dash.dashed ? |
681 | 0 | _cairo_rectilinear_stroker_line_to_dashed : |
682 | 20.0k | _cairo_rectilinear_stroker_line_to, |
683 | 20.0k | NULL, |
684 | 20.0k | _cairo_rectilinear_stroker_close_path, |
685 | 20.0k | &rectilinear_stroker); |
686 | 20.0k | if (unlikely (status)) |
687 | 0 | goto BAIL; |
688 | | |
689 | 20.0k | if (rectilinear_stroker.dash.dashed) |
690 | 0 | status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker); |
691 | 20.0k | else |
692 | 20.0k | status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); |
693 | 20.0k | if (unlikely (status)) |
694 | 0 | goto BAIL; |
695 | | |
696 | | /* As we incrementally tessellate, we do not eliminate self-intersections */ |
697 | 20.0k | status = _cairo_bentley_ottmann_tessellate_boxes (boxes, |
698 | 20.0k | CAIRO_FILL_RULE_WINDING, |
699 | 20.0k | boxes); |
700 | 20.0k | if (unlikely (status)) |
701 | 0 | goto BAIL; |
702 | | |
703 | 20.6k | done: |
704 | 20.6k | _cairo_rectilinear_stroker_fini (&rectilinear_stroker); |
705 | 20.6k | return CAIRO_STATUS_SUCCESS; |
706 | | |
707 | 0 | BAIL: |
708 | 0 | _cairo_rectilinear_stroker_fini (&rectilinear_stroker); |
709 | 0 | _cairo_boxes_clear (boxes); |
710 | 0 | return status; |
711 | 20.0k | } |