Line data Source code
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 _BSD_SOURCE /* for hypot() */
40 : #include "cairoint.h"
41 :
42 : #include "cairo-boxes-private.h"
43 : #include "cairo-error-private.h"
44 : #include "cairo-path-fixed-private.h"
45 : #include "cairo-slope-private.h"
46 :
47 : typedef struct _cairo_stroker_dash {
48 : cairo_bool_t dashed;
49 : unsigned int dash_index;
50 : cairo_bool_t dash_on;
51 : cairo_bool_t dash_starts_on;
52 : double dash_remain;
53 :
54 : double dash_offset;
55 : const double *dashes;
56 : unsigned int num_dashes;
57 : } cairo_stroker_dash_t;
58 :
59 : typedef struct cairo_stroker {
60 : cairo_stroke_style_t style;
61 :
62 : const cairo_matrix_t *ctm;
63 : const cairo_matrix_t *ctm_inverse;
64 : double tolerance;
65 : double ctm_determinant;
66 : cairo_bool_t ctm_det_positive;
67 :
68 : void *closure;
69 : cairo_status_t (*add_external_edge) (void *closure,
70 : const cairo_point_t *p1,
71 : const cairo_point_t *p2);
72 : cairo_status_t (*add_triangle) (void *closure,
73 : const cairo_point_t triangle[3]);
74 : cairo_status_t (*add_triangle_fan) (void *closure,
75 : const cairo_point_t *midpt,
76 : const cairo_point_t *points,
77 : int npoints);
78 : cairo_status_t (*add_convex_quad) (void *closure,
79 : const cairo_point_t quad[4]);
80 :
81 : cairo_pen_t pen;
82 :
83 : cairo_point_t current_point;
84 : cairo_point_t first_point;
85 :
86 : cairo_bool_t has_initial_sub_path;
87 :
88 : cairo_bool_t has_current_face;
89 : cairo_stroke_face_t current_face;
90 :
91 : cairo_bool_t has_first_face;
92 : cairo_stroke_face_t first_face;
93 :
94 : cairo_stroker_dash_t dash;
95 :
96 : cairo_bool_t has_bounds;
97 : cairo_box_t bounds;
98 : } cairo_stroker_t;
99 :
100 : static void
101 0 : _cairo_stroker_dash_start (cairo_stroker_dash_t *dash)
102 : {
103 : double offset;
104 0 : cairo_bool_t on = TRUE;
105 0 : unsigned int i = 0;
106 :
107 0 : if (! dash->dashed)
108 0 : return;
109 :
110 0 : offset = dash->dash_offset;
111 :
112 : /* We stop searching for a starting point as soon as the
113 : offset reaches zero. Otherwise when an initial dash
114 : segment shrinks to zero it will be skipped over. */
115 0 : while (offset > 0.0 && offset >= dash->dashes[i]) {
116 0 : offset -= dash->dashes[i];
117 0 : on = !on;
118 0 : if (++i == dash->num_dashes)
119 0 : i = 0;
120 : }
121 :
122 0 : dash->dash_index = i;
123 0 : dash->dash_on = dash->dash_starts_on = on;
124 0 : dash->dash_remain = dash->dashes[i] - offset;
125 : }
126 :
127 : static void
128 0 : _cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step)
129 : {
130 0 : dash->dash_remain -= step;
131 0 : if (dash->dash_remain <= 0.) {
132 0 : if (++dash->dash_index == dash->num_dashes)
133 0 : dash->dash_index = 0;
134 :
135 0 : dash->dash_on = ! dash->dash_on;
136 0 : dash->dash_remain = dash->dashes[dash->dash_index];
137 : }
138 0 : }
139 :
140 : static void
141 0 : _cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
142 : const cairo_stroke_style_t *style)
143 : {
144 0 : dash->dashed = style->dash != NULL;
145 0 : if (! dash->dashed)
146 0 : return;
147 :
148 0 : dash->dashes = style->dash;
149 0 : dash->num_dashes = style->num_dashes;
150 0 : dash->dash_offset = style->dash_offset;
151 :
152 0 : _cairo_stroker_dash_start (dash);
153 : }
154 :
155 : static cairo_status_t
156 0 : _cairo_stroker_init (cairo_stroker_t *stroker,
157 : const cairo_stroke_style_t *stroke_style,
158 : const cairo_matrix_t *ctm,
159 : const cairo_matrix_t *ctm_inverse,
160 : double tolerance)
161 : {
162 : cairo_status_t status;
163 :
164 0 : stroker->style = *stroke_style;
165 0 : stroker->ctm = ctm;
166 0 : stroker->ctm_inverse = ctm_inverse;
167 0 : stroker->tolerance = tolerance;
168 :
169 0 : stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm);
170 0 : stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0;
171 :
172 0 : status = _cairo_pen_init (&stroker->pen,
173 0 : stroke_style->line_width / 2.0,
174 : tolerance, ctm);
175 0 : if (unlikely (status))
176 0 : return status;
177 :
178 0 : stroker->has_bounds = FALSE;
179 :
180 0 : stroker->has_current_face = FALSE;
181 0 : stroker->has_first_face = FALSE;
182 0 : stroker->has_initial_sub_path = FALSE;
183 :
184 0 : _cairo_stroker_dash_init (&stroker->dash, stroke_style);
185 :
186 0 : stroker->add_external_edge = NULL;
187 :
188 0 : return CAIRO_STATUS_SUCCESS;
189 : }
190 :
191 : static void
192 0 : _cairo_stroker_limit (cairo_stroker_t *stroker,
193 : const cairo_box_t *boxes,
194 : int num_boxes)
195 : {
196 : double dx, dy;
197 : cairo_fixed_t fdx, fdy;
198 :
199 0 : stroker->has_bounds = TRUE;
200 0 : _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
201 :
202 : /* Extend the bounds in each direction to account for the maximum area
203 : * we might generate trapezoids, to capture line segments that are outside
204 : * of the bounds but which might generate rendering that's within bounds.
205 : */
206 :
207 0 : _cairo_stroke_style_max_distance_from_path (&stroker->style, stroker->ctm,
208 : &dx, &dy);
209 :
210 0 : fdx = _cairo_fixed_from_double (dx);
211 0 : fdy = _cairo_fixed_from_double (dy);
212 :
213 0 : stroker->bounds.p1.x -= fdx;
214 0 : stroker->bounds.p2.x += fdx;
215 :
216 0 : stroker->bounds.p1.y -= fdy;
217 0 : stroker->bounds.p2.y += fdy;
218 0 : }
219 :
220 : static void
221 0 : _cairo_stroker_fini (cairo_stroker_t *stroker)
222 : {
223 0 : _cairo_pen_fini (&stroker->pen);
224 0 : }
225 :
226 : static void
227 0 : _translate_point (cairo_point_t *point, const cairo_point_t *offset)
228 : {
229 0 : point->x += offset->x;
230 0 : point->y += offset->y;
231 0 : }
232 :
233 : static int
234 0 : _cairo_stroker_join_is_clockwise (const cairo_stroke_face_t *in,
235 : const cairo_stroke_face_t *out)
236 : {
237 : cairo_slope_t in_slope, out_slope;
238 :
239 0 : _cairo_slope_init (&in_slope, &in->point, &in->cw);
240 0 : _cairo_slope_init (&out_slope, &out->point, &out->cw);
241 :
242 0 : return _cairo_slope_compare (&in_slope, &out_slope) < 0;
243 : }
244 :
245 : /**
246 : * _cairo_slope_compare_sgn
247 : *
248 : * Return -1, 0 or 1 depending on the relative slopes of
249 : * two lines.
250 : */
251 : static int
252 0 : _cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
253 : {
254 0 : double c = (dx1 * dy2 - dx2 * dy1);
255 :
256 0 : if (c > 0) return 1;
257 0 : if (c < 0) return -1;
258 0 : return 0;
259 : }
260 :
261 : static inline int
262 0 : _range_step (int i, int step, int max)
263 : {
264 0 : i += step;
265 0 : if (i < 0)
266 0 : i = max - 1;
267 0 : if (i >= max)
268 0 : i = 0;
269 0 : return i;
270 : }
271 :
272 : /*
273 : * Construct a fan around the midpoint using the vertices from pen between
274 : * inpt and outpt.
275 : */
276 : static cairo_status_t
277 0 : _tessellate_fan (cairo_stroker_t *stroker,
278 : const cairo_slope_t *in_vector,
279 : const cairo_slope_t *out_vector,
280 : const cairo_point_t *midpt,
281 : const cairo_point_t *inpt,
282 : const cairo_point_t *outpt,
283 : cairo_bool_t clockwise)
284 : {
285 0 : cairo_point_t stack_points[64], *points = stack_points;
286 : int start, stop, step, i, npoints;
287 : cairo_status_t status;
288 :
289 0 : if (clockwise) {
290 0 : step = -1;
291 :
292 0 : start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
293 : in_vector);
294 0 : if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw,
295 : in_vector) < 0)
296 0 : start = _range_step (start, -1, stroker->pen.num_vertices);
297 :
298 0 : stop = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
299 : out_vector);
300 0 : if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
301 : out_vector) > 0)
302 : {
303 0 : stop = _range_step (stop, 1, stroker->pen.num_vertices);
304 0 : if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
305 : in_vector) < 0)
306 : {
307 0 : goto BEVEL;
308 : }
309 : }
310 :
311 0 : npoints = start - stop;
312 : } else {
313 0 : step = 1;
314 :
315 0 : start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
316 : in_vector);
317 0 : if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw,
318 : in_vector) < 0)
319 0 : start = _range_step (start, 1, stroker->pen.num_vertices);
320 :
321 0 : stop = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
322 : out_vector);
323 0 : if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
324 : out_vector) > 0)
325 : {
326 0 : stop = _range_step (stop, -1, stroker->pen.num_vertices);
327 0 : if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
328 : in_vector) < 0)
329 : {
330 0 : goto BEVEL;
331 : }
332 : }
333 :
334 0 : npoints = stop - start;
335 : }
336 0 : stop = _range_step (stop, step, stroker->pen.num_vertices);
337 :
338 0 : if (npoints < 0)
339 0 : npoints += stroker->pen.num_vertices;
340 0 : npoints += 3;
341 :
342 0 : if (npoints <= 1)
343 0 : goto BEVEL;
344 :
345 0 : if (npoints > ARRAY_LENGTH (stack_points)) {
346 0 : points = _cairo_malloc_ab (npoints, sizeof (cairo_point_t));
347 0 : if (unlikely (points == NULL))
348 0 : return _cairo_error (CAIRO_STATUS_NO_MEMORY);
349 : }
350 :
351 :
352 : /* Construct the fan. */
353 0 : npoints = 0;
354 0 : points[npoints++] = *inpt;
355 0 : for (i = start;
356 : i != stop;
357 0 : i = _range_step (i, step, stroker->pen.num_vertices))
358 : {
359 0 : points[npoints] = *midpt;
360 0 : _translate_point (&points[npoints], &stroker->pen.vertices[i].point);
361 0 : npoints++;
362 : }
363 0 : points[npoints++] = *outpt;
364 :
365 0 : if (stroker->add_external_edge != NULL) {
366 0 : for (i = 0; i < npoints - 1; i++) {
367 0 : if (clockwise) {
368 0 : status = stroker->add_external_edge (stroker->closure,
369 0 : &points[i], &points[i+1]);
370 : } else {
371 0 : status = stroker->add_external_edge (stroker->closure,
372 0 : &points[i+1], &points[i]);
373 : }
374 0 : if (unlikely (status))
375 0 : break;
376 : }
377 : } else {
378 0 : status = stroker->add_triangle_fan (stroker->closure,
379 : midpt, points, npoints);
380 : }
381 :
382 0 : if (points != stack_points)
383 0 : free (points);
384 :
385 0 : return status;
386 :
387 : BEVEL:
388 : /* Ensure a leak free connection... */
389 0 : if (stroker->add_external_edge != NULL) {
390 0 : if (clockwise)
391 0 : return stroker->add_external_edge (stroker->closure, inpt, outpt);
392 : else
393 0 : return stroker->add_external_edge (stroker->closure, outpt, inpt);
394 : } else {
395 0 : stack_points[0] = *midpt;
396 0 : stack_points[1] = *inpt;
397 0 : stack_points[2] = *outpt;
398 0 : return stroker->add_triangle (stroker->closure, stack_points);
399 : }
400 : }
401 :
402 : static cairo_status_t
403 0 : _cairo_stroker_join (cairo_stroker_t *stroker,
404 : const cairo_stroke_face_t *in,
405 : const cairo_stroke_face_t *out)
406 : {
407 0 : int clockwise = _cairo_stroker_join_is_clockwise (out, in);
408 : const cairo_point_t *inpt, *outpt;
409 : cairo_point_t points[4];
410 : cairo_status_t status;
411 :
412 0 : if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
413 0 : in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y)
414 : {
415 0 : return CAIRO_STATUS_SUCCESS;
416 : }
417 :
418 0 : if (clockwise) {
419 0 : if (stroker->add_external_edge != NULL) {
420 0 : status = stroker->add_external_edge (stroker->closure,
421 : &out->cw, &in->point);
422 0 : if (unlikely (status))
423 0 : return status;
424 :
425 0 : status = stroker->add_external_edge (stroker->closure,
426 : &in->point, &in->cw);
427 0 : if (unlikely (status))
428 0 : return status;
429 : }
430 :
431 0 : inpt = &in->ccw;
432 0 : outpt = &out->ccw;
433 : } else {
434 0 : if (stroker->add_external_edge != NULL) {
435 0 : status = stroker->add_external_edge (stroker->closure,
436 : &in->ccw, &in->point);
437 0 : if (unlikely (status))
438 0 : return status;
439 :
440 0 : status = stroker->add_external_edge (stroker->closure,
441 : &in->point, &out->ccw);
442 0 : if (unlikely (status))
443 0 : return status;
444 : }
445 :
446 0 : inpt = &in->cw;
447 0 : outpt = &out->cw;
448 : }
449 :
450 0 : switch (stroker->style.line_join) {
451 : case CAIRO_LINE_JOIN_ROUND:
452 : /* construct a fan around the common midpoint */
453 0 : return _tessellate_fan (stroker,
454 : &in->dev_vector,
455 : &out->dev_vector,
456 : &in->point, inpt, outpt,
457 : clockwise);
458 :
459 : case CAIRO_LINE_JOIN_MITER:
460 : default: {
461 : /* dot product of incoming slope vector with outgoing slope vector */
462 0 : double in_dot_out = -in->usr_vector.x * out->usr_vector.x +
463 0 : -in->usr_vector.y * out->usr_vector.y;
464 0 : double ml = stroker->style.miter_limit;
465 :
466 : /* Check the miter limit -- lines meeting at an acute angle
467 : * can generate long miters, the limit converts them to bevel
468 : *
469 : * Consider the miter join formed when two line segments
470 : * meet at an angle psi:
471 : *
472 : * /.\
473 : * /. .\
474 : * /./ \.\
475 : * /./psi\.\
476 : *
477 : * We can zoom in on the right half of that to see:
478 : *
479 : * |\
480 : * | \ psi/2
481 : * | \
482 : * | \
483 : * | \
484 : * | \
485 : * miter \
486 : * length \
487 : * | \
488 : * | .\
489 : * | . \
490 : * |. line \
491 : * \ width \
492 : * \ \
493 : *
494 : *
495 : * The right triangle in that figure, (the line-width side is
496 : * shown faintly with three '.' characters), gives us the
497 : * following expression relating miter length, angle and line
498 : * width:
499 : *
500 : * 1 /sin (psi/2) = miter_length / line_width
501 : *
502 : * The right-hand side of this relationship is the same ratio
503 : * in which the miter limit (ml) is expressed. We want to know
504 : * when the miter length is within the miter limit. That is
505 : * when the following condition holds:
506 : *
507 : * 1/sin(psi/2) <= ml
508 : * 1 <= ml sin(psi/2)
509 : * 1 <= ml² sin²(psi/2)
510 : * 2 <= ml² 2 sin²(psi/2)
511 : * 2·sin²(psi/2) = 1-cos(psi)
512 : * 2 <= ml² (1-cos(psi))
513 : *
514 : * in · out = |in| |out| cos (psi)
515 : *
516 : * in and out are both unit vectors, so:
517 : *
518 : * in · out = cos (psi)
519 : *
520 : * 2 <= ml² (1 - in · out)
521 : *
522 : */
523 0 : if (2 <= ml * ml * (1 - in_dot_out)) {
524 : double x1, y1, x2, y2;
525 : double mx, my;
526 : double dx1, dx2, dy1, dy2;
527 : double ix, iy;
528 : double fdx1, fdy1, fdx2, fdy2;
529 : double mdx, mdy;
530 :
531 : /*
532 : * we've got the points already transformed to device
533 : * space, but need to do some computation with them and
534 : * also need to transform the slope from user space to
535 : * device space
536 : */
537 : /* outer point of incoming line face */
538 0 : x1 = _cairo_fixed_to_double (inpt->x);
539 0 : y1 = _cairo_fixed_to_double (inpt->y);
540 0 : dx1 = in->usr_vector.x;
541 0 : dy1 = in->usr_vector.y;
542 0 : cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
543 :
544 : /* outer point of outgoing line face */
545 0 : x2 = _cairo_fixed_to_double (outpt->x);
546 0 : y2 = _cairo_fixed_to_double (outpt->y);
547 0 : dx2 = out->usr_vector.x;
548 0 : dy2 = out->usr_vector.y;
549 0 : cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
550 :
551 : /*
552 : * Compute the location of the outer corner of the miter.
553 : * That's pretty easy -- just the intersection of the two
554 : * outer edges. We've got slopes and points on each
555 : * of those edges. Compute my directly, then compute
556 : * mx by using the edge with the larger dy; that avoids
557 : * dividing by values close to zero.
558 : */
559 0 : my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
560 0 : (dx1 * dy2 - dx2 * dy1));
561 0 : if (fabs (dy1) >= fabs (dy2))
562 0 : mx = (my - y1) * dx1 / dy1 + x1;
563 : else
564 0 : mx = (my - y2) * dx2 / dy2 + x2;
565 :
566 : /*
567 : * When the two outer edges are nearly parallel, slight
568 : * perturbations in the position of the outer points of the lines
569 : * caused by representing them in fixed point form can cause the
570 : * intersection point of the miter to move a large amount. If
571 : * that moves the miter intersection from between the two faces,
572 : * then draw a bevel instead.
573 : */
574 :
575 0 : ix = _cairo_fixed_to_double (in->point.x);
576 0 : iy = _cairo_fixed_to_double (in->point.y);
577 :
578 : /* slope of one face */
579 0 : fdx1 = x1 - ix; fdy1 = y1 - iy;
580 :
581 : /* slope of the other face */
582 0 : fdx2 = x2 - ix; fdy2 = y2 - iy;
583 :
584 : /* slope from the intersection to the miter point */
585 0 : mdx = mx - ix; mdy = my - iy;
586 :
587 : /*
588 : * Make sure the miter point line lies between the two
589 : * faces by comparing the slopes
590 : */
591 0 : if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
592 0 : _cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy))
593 : {
594 0 : if (stroker->add_external_edge != NULL) {
595 0 : points[0].x = _cairo_fixed_from_double (mx);
596 0 : points[0].y = _cairo_fixed_from_double (my);
597 :
598 0 : if (clockwise) {
599 0 : status = stroker->add_external_edge (stroker->closure,
600 : inpt, &points[0]);
601 0 : if (unlikely (status))
602 0 : return status;
603 :
604 0 : status = stroker->add_external_edge (stroker->closure,
605 : &points[0], outpt);
606 0 : if (unlikely (status))
607 0 : return status;
608 : } else {
609 0 : status = stroker->add_external_edge (stroker->closure,
610 : outpt, &points[0]);
611 0 : if (unlikely (status))
612 0 : return status;
613 :
614 0 : status = stroker->add_external_edge (stroker->closure,
615 : &points[0], inpt);
616 0 : if (unlikely (status))
617 0 : return status;
618 : }
619 :
620 0 : return CAIRO_STATUS_SUCCESS;
621 : } else {
622 0 : points[0] = in->point;
623 0 : points[1] = *inpt;
624 0 : points[2].x = _cairo_fixed_from_double (mx);
625 0 : points[2].y = _cairo_fixed_from_double (my);
626 0 : points[3] = *outpt;
627 :
628 0 : return stroker->add_convex_quad (stroker->closure, points);
629 : }
630 : }
631 : }
632 : }
633 :
634 : /* fall through ... */
635 :
636 : case CAIRO_LINE_JOIN_BEVEL:
637 0 : if (stroker->add_external_edge != NULL) {
638 0 : if (clockwise) {
639 0 : return stroker->add_external_edge (stroker->closure,
640 : inpt, outpt);
641 : } else {
642 0 : return stroker->add_external_edge (stroker->closure,
643 : outpt, inpt);
644 : }
645 : } else {
646 0 : points[0] = in->point;
647 0 : points[1] = *inpt;
648 0 : points[2] = *outpt;
649 :
650 0 : return stroker->add_triangle (stroker->closure, points);
651 : }
652 : }
653 : }
654 :
655 : static cairo_status_t
656 0 : _cairo_stroker_add_cap (cairo_stroker_t *stroker,
657 : const cairo_stroke_face_t *f)
658 : {
659 0 : switch (stroker->style.line_cap) {
660 : case CAIRO_LINE_CAP_ROUND: {
661 : cairo_slope_t slope;
662 :
663 0 : slope.dx = -f->dev_vector.dx;
664 0 : slope.dy = -f->dev_vector.dy;
665 :
666 0 : return _tessellate_fan (stroker,
667 : &f->dev_vector,
668 : &slope,
669 : &f->point, &f->cw, &f->ccw,
670 : FALSE);
671 :
672 : }
673 :
674 : case CAIRO_LINE_CAP_SQUARE: {
675 : double dx, dy;
676 : cairo_slope_t fvector;
677 : cairo_point_t quad[4];
678 :
679 0 : dx = f->usr_vector.x;
680 0 : dy = f->usr_vector.y;
681 0 : dx *= stroker->style.line_width / 2.0;
682 0 : dy *= stroker->style.line_width / 2.0;
683 0 : cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
684 0 : fvector.dx = _cairo_fixed_from_double (dx);
685 0 : fvector.dy = _cairo_fixed_from_double (dy);
686 :
687 0 : quad[0] = f->ccw;
688 0 : quad[1].x = f->ccw.x + fvector.dx;
689 0 : quad[1].y = f->ccw.y + fvector.dy;
690 0 : quad[2].x = f->cw.x + fvector.dx;
691 0 : quad[2].y = f->cw.y + fvector.dy;
692 0 : quad[3] = f->cw;
693 :
694 0 : if (stroker->add_external_edge != NULL) {
695 : cairo_status_t status;
696 :
697 0 : status = stroker->add_external_edge (stroker->closure,
698 : &quad[0], &quad[1]);
699 0 : if (unlikely (status))
700 0 : return status;
701 :
702 0 : status = stroker->add_external_edge (stroker->closure,
703 : &quad[1], &quad[2]);
704 0 : if (unlikely (status))
705 0 : return status;
706 :
707 0 : status = stroker->add_external_edge (stroker->closure,
708 : &quad[2], &quad[3]);
709 0 : if (unlikely (status))
710 0 : return status;
711 :
712 0 : return CAIRO_STATUS_SUCCESS;
713 : } else {
714 0 : return stroker->add_convex_quad (stroker->closure, quad);
715 : }
716 : }
717 :
718 : case CAIRO_LINE_CAP_BUTT:
719 : default:
720 0 : if (stroker->add_external_edge != NULL) {
721 0 : return stroker->add_external_edge (stroker->closure,
722 : &f->ccw, &f->cw);
723 : } else {
724 0 : return CAIRO_STATUS_SUCCESS;
725 : }
726 : }
727 : }
728 :
729 : static cairo_status_t
730 0 : _cairo_stroker_add_leading_cap (cairo_stroker_t *stroker,
731 : const cairo_stroke_face_t *face)
732 : {
733 : cairo_stroke_face_t reversed;
734 : cairo_point_t t;
735 :
736 0 : reversed = *face;
737 :
738 : /* The initial cap needs an outward facing vector. Reverse everything */
739 0 : reversed.usr_vector.x = -reversed.usr_vector.x;
740 0 : reversed.usr_vector.y = -reversed.usr_vector.y;
741 0 : reversed.dev_vector.dx = -reversed.dev_vector.dx;
742 0 : reversed.dev_vector.dy = -reversed.dev_vector.dy;
743 0 : t = reversed.cw;
744 0 : reversed.cw = reversed.ccw;
745 0 : reversed.ccw = t;
746 :
747 0 : return _cairo_stroker_add_cap (stroker, &reversed);
748 : }
749 :
750 : static cairo_status_t
751 0 : _cairo_stroker_add_trailing_cap (cairo_stroker_t *stroker,
752 : const cairo_stroke_face_t *face)
753 : {
754 0 : return _cairo_stroker_add_cap (stroker, face);
755 : }
756 :
757 : static inline cairo_bool_t
758 0 : _compute_normalized_device_slope (double *dx, double *dy,
759 : const cairo_matrix_t *ctm_inverse,
760 : double *mag_out)
761 : {
762 0 : double dx0 = *dx, dy0 = *dy;
763 : double mag;
764 :
765 0 : cairo_matrix_transform_distance (ctm_inverse, &dx0, &dy0);
766 :
767 0 : if (dx0 == 0.0 && dy0 == 0.0) {
768 0 : if (mag_out)
769 0 : *mag_out = 0.0;
770 0 : return FALSE;
771 : }
772 :
773 0 : if (dx0 == 0.0) {
774 0 : *dx = 0.0;
775 0 : if (dy0 > 0.0) {
776 0 : mag = dy0;
777 0 : *dy = 1.0;
778 : } else {
779 0 : mag = -dy0;
780 0 : *dy = -1.0;
781 : }
782 0 : } else if (dy0 == 0.0) {
783 0 : *dy = 0.0;
784 0 : if (dx0 > 0.0) {
785 0 : mag = dx0;
786 0 : *dx = 1.0;
787 : } else {
788 0 : mag = -dx0;
789 0 : *dx = -1.0;
790 : }
791 : } else {
792 0 : mag = hypot (dx0, dy0);
793 0 : *dx = dx0 / mag;
794 0 : *dy = dy0 / mag;
795 : }
796 :
797 0 : if (mag_out)
798 0 : *mag_out = mag;
799 :
800 0 : return TRUE;
801 : }
802 :
803 : static void
804 0 : _compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope,
805 : double slope_dx, double slope_dy,
806 : cairo_stroker_t *stroker, cairo_stroke_face_t *face)
807 : {
808 : double face_dx, face_dy;
809 : cairo_point_t offset_ccw, offset_cw;
810 :
811 : /*
812 : * rotate to get a line_width/2 vector along the face, note that
813 : * the vector must be rotated the right direction in device space,
814 : * but by 90° in user space. So, the rotation depends on
815 : * whether the ctm reflects or not, and that can be determined
816 : * by looking at the determinant of the matrix.
817 : */
818 0 : if (stroker->ctm_det_positive)
819 : {
820 0 : face_dx = - slope_dy * (stroker->style.line_width / 2.0);
821 0 : face_dy = slope_dx * (stroker->style.line_width / 2.0);
822 : }
823 : else
824 : {
825 0 : face_dx = slope_dy * (stroker->style.line_width / 2.0);
826 0 : face_dy = - slope_dx * (stroker->style.line_width / 2.0);
827 : }
828 :
829 : /* back to device space */
830 0 : cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
831 :
832 0 : offset_ccw.x = _cairo_fixed_from_double (face_dx);
833 0 : offset_ccw.y = _cairo_fixed_from_double (face_dy);
834 0 : offset_cw.x = -offset_ccw.x;
835 0 : offset_cw.y = -offset_ccw.y;
836 :
837 0 : face->ccw = *point;
838 0 : _translate_point (&face->ccw, &offset_ccw);
839 :
840 0 : face->point = *point;
841 :
842 0 : face->cw = *point;
843 0 : _translate_point (&face->cw, &offset_cw);
844 :
845 0 : face->usr_vector.x = slope_dx;
846 0 : face->usr_vector.y = slope_dy;
847 :
848 0 : face->dev_vector = *dev_slope;
849 0 : }
850 :
851 : static cairo_status_t
852 0 : _cairo_stroker_add_caps (cairo_stroker_t *stroker)
853 : {
854 : cairo_status_t status;
855 :
856 : /* check for a degenerative sub_path */
857 0 : if (stroker->has_initial_sub_path
858 0 : && ! stroker->has_first_face
859 0 : && ! stroker->has_current_face
860 0 : && stroker->style.line_cap == CAIRO_LINE_JOIN_ROUND)
861 : {
862 : /* pick an arbitrary slope to use */
863 0 : double dx = 1.0, dy = 0.0;
864 0 : cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
865 : cairo_stroke_face_t face;
866 :
867 0 : _compute_normalized_device_slope (&dx, &dy,
868 : stroker->ctm_inverse, NULL);
869 :
870 : /* arbitrarily choose first_point
871 : * first_point and current_point should be the same */
872 0 : _compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face);
873 :
874 0 : status = _cairo_stroker_add_leading_cap (stroker, &face);
875 0 : if (unlikely (status))
876 0 : return status;
877 :
878 0 : status = _cairo_stroker_add_trailing_cap (stroker, &face);
879 0 : if (unlikely (status))
880 0 : return status;
881 : }
882 :
883 0 : if (stroker->has_first_face) {
884 0 : status = _cairo_stroker_add_leading_cap (stroker,
885 0 : &stroker->first_face);
886 0 : if (unlikely (status))
887 0 : return status;
888 : }
889 :
890 0 : if (stroker->has_current_face) {
891 0 : status = _cairo_stroker_add_trailing_cap (stroker,
892 0 : &stroker->current_face);
893 0 : if (unlikely (status))
894 0 : return status;
895 : }
896 :
897 0 : return CAIRO_STATUS_SUCCESS;
898 : }
899 :
900 : static cairo_status_t
901 0 : _cairo_stroker_add_sub_edge (cairo_stroker_t *stroker,
902 : const cairo_point_t *p1,
903 : const cairo_point_t *p2,
904 : cairo_slope_t *dev_slope,
905 : double slope_dx, double slope_dy,
906 : cairo_stroke_face_t *start,
907 : cairo_stroke_face_t *end)
908 : {
909 0 : _compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start);
910 0 : *end = *start;
911 :
912 0 : if (p1->x == p2->x && p1->y == p2->y)
913 0 : return CAIRO_STATUS_SUCCESS;
914 :
915 0 : end->point = *p2;
916 0 : end->ccw.x += p2->x - p1->x;
917 0 : end->ccw.y += p2->y - p1->y;
918 0 : end->cw.x += p2->x - p1->x;
919 0 : end->cw.y += p2->y - p1->y;
920 :
921 0 : if (stroker->add_external_edge != NULL) {
922 : cairo_status_t status;
923 :
924 0 : status = stroker->add_external_edge (stroker->closure,
925 0 : &end->cw, &start->cw);
926 0 : if (unlikely (status))
927 0 : return status;
928 :
929 0 : status = stroker->add_external_edge (stroker->closure,
930 0 : &start->ccw, &end->ccw);
931 0 : if (unlikely (status))
932 0 : return status;
933 :
934 0 : return CAIRO_STATUS_SUCCESS;
935 : } else {
936 : cairo_point_t quad[4];
937 :
938 0 : quad[0] = start->cw;
939 0 : quad[1] = end->cw;
940 0 : quad[2] = end->ccw;
941 0 : quad[3] = start->ccw;
942 :
943 0 : return stroker->add_convex_quad (stroker->closure, quad);
944 : }
945 : }
946 :
947 : static cairo_status_t
948 0 : _cairo_stroker_move_to (void *closure,
949 : const cairo_point_t *point)
950 : {
951 0 : cairo_stroker_t *stroker = closure;
952 : cairo_status_t status;
953 :
954 : /* reset the dash pattern for new sub paths */
955 0 : _cairo_stroker_dash_start (&stroker->dash);
956 :
957 : /* Cap the start and end of the previous sub path as needed */
958 0 : status = _cairo_stroker_add_caps (stroker);
959 0 : if (unlikely (status))
960 0 : return status;
961 :
962 0 : stroker->first_point = *point;
963 0 : stroker->current_point = *point;
964 :
965 0 : stroker->has_first_face = FALSE;
966 0 : stroker->has_current_face = FALSE;
967 0 : stroker->has_initial_sub_path = FALSE;
968 :
969 0 : return CAIRO_STATUS_SUCCESS;
970 : }
971 :
972 : static cairo_status_t
973 0 : _cairo_stroker_line_to (void *closure,
974 : const cairo_point_t *point)
975 : {
976 0 : cairo_stroker_t *stroker = closure;
977 : cairo_stroke_face_t start, end;
978 0 : cairo_point_t *p1 = &stroker->current_point;
979 : cairo_slope_t dev_slope;
980 : double slope_dx, slope_dy;
981 : cairo_status_t status;
982 :
983 0 : stroker->has_initial_sub_path = TRUE;
984 :
985 0 : if (p1->x == point->x && p1->y == point->y)
986 0 : return CAIRO_STATUS_SUCCESS;
987 :
988 0 : _cairo_slope_init (&dev_slope, p1, point);
989 0 : slope_dx = _cairo_fixed_to_double (point->x - p1->x);
990 0 : slope_dy = _cairo_fixed_to_double (point->y - p1->y);
991 0 : _compute_normalized_device_slope (&slope_dx, &slope_dy,
992 : stroker->ctm_inverse, NULL);
993 :
994 0 : status = _cairo_stroker_add_sub_edge (stroker,
995 : p1, point,
996 : &dev_slope,
997 : slope_dx, slope_dy,
998 : &start, &end);
999 0 : if (unlikely (status))
1000 0 : return status;
1001 :
1002 0 : if (stroker->has_current_face) {
1003 : /* Join with final face from previous segment */
1004 0 : status = _cairo_stroker_join (stroker,
1005 0 : &stroker->current_face,
1006 : &start);
1007 0 : if (unlikely (status))
1008 0 : return status;
1009 0 : } else if (! stroker->has_first_face) {
1010 : /* Save sub path's first face in case needed for closing join */
1011 0 : stroker->first_face = start;
1012 0 : stroker->has_first_face = TRUE;
1013 : }
1014 0 : stroker->current_face = end;
1015 0 : stroker->has_current_face = TRUE;
1016 :
1017 0 : stroker->current_point = *point;
1018 :
1019 0 : return CAIRO_STATUS_SUCCESS;
1020 : }
1021 :
1022 : /*
1023 : * Dashed lines. Cap each dash end, join around turns when on
1024 : */
1025 : static cairo_status_t
1026 0 : _cairo_stroker_line_to_dashed (void *closure,
1027 : const cairo_point_t *p2)
1028 : {
1029 0 : cairo_stroker_t *stroker = closure;
1030 0 : double mag, remain, step_length = 0;
1031 : double slope_dx, slope_dy;
1032 : double dx2, dy2;
1033 : cairo_stroke_face_t sub_start, sub_end;
1034 0 : cairo_point_t *p1 = &stroker->current_point;
1035 : cairo_slope_t dev_slope;
1036 : cairo_line_t segment;
1037 : cairo_bool_t fully_in_bounds;
1038 : cairo_status_t status;
1039 :
1040 0 : stroker->has_initial_sub_path = stroker->dash.dash_starts_on;
1041 :
1042 0 : if (p1->x == p2->x && p1->y == p2->y)
1043 0 : return CAIRO_STATUS_SUCCESS;
1044 :
1045 0 : fully_in_bounds = TRUE;
1046 0 : if (stroker->has_bounds &&
1047 0 : (! _cairo_box_contains_point (&stroker->bounds, p1) ||
1048 0 : ! _cairo_box_contains_point (&stroker->bounds, p2)))
1049 : {
1050 0 : fully_in_bounds = FALSE;
1051 : }
1052 :
1053 0 : _cairo_slope_init (&dev_slope, p1, p2);
1054 :
1055 0 : slope_dx = _cairo_fixed_to_double (p2->x - p1->x);
1056 0 : slope_dy = _cairo_fixed_to_double (p2->y - p1->y);
1057 :
1058 0 : if (! _compute_normalized_device_slope (&slope_dx, &slope_dy,
1059 : stroker->ctm_inverse, &mag))
1060 : {
1061 0 : return CAIRO_STATUS_SUCCESS;
1062 : }
1063 :
1064 0 : remain = mag;
1065 0 : segment.p1 = *p1;
1066 0 : while (remain) {
1067 0 : step_length = MIN (stroker->dash.dash_remain, remain);
1068 0 : remain -= step_length;
1069 0 : dx2 = slope_dx * (mag - remain);
1070 0 : dy2 = slope_dy * (mag - remain);
1071 0 : cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
1072 0 : segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x;
1073 0 : segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y;
1074 :
1075 0 : if (stroker->dash.dash_on &&
1076 0 : (fully_in_bounds ||
1077 0 : (! stroker->has_first_face && stroker->dash.dash_starts_on) ||
1078 0 : _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
1079 : {
1080 0 : status = _cairo_stroker_add_sub_edge (stroker,
1081 : &segment.p1, &segment.p2,
1082 : &dev_slope,
1083 : slope_dx, slope_dy,
1084 : &sub_start, &sub_end);
1085 0 : if (unlikely (status))
1086 0 : return status;
1087 :
1088 0 : if (stroker->has_current_face)
1089 : {
1090 : /* Join with final face from previous segment */
1091 0 : status = _cairo_stroker_join (stroker,
1092 0 : &stroker->current_face,
1093 : &sub_start);
1094 0 : if (unlikely (status))
1095 0 : return status;
1096 :
1097 0 : stroker->has_current_face = FALSE;
1098 : }
1099 0 : else if (! stroker->has_first_face &&
1100 0 : stroker->dash.dash_starts_on)
1101 : {
1102 : /* Save sub path's first face in case needed for closing join */
1103 0 : stroker->first_face = sub_start;
1104 0 : stroker->has_first_face = TRUE;
1105 : }
1106 : else
1107 : {
1108 : /* Cap dash start if not connecting to a previous segment */
1109 0 : status = _cairo_stroker_add_leading_cap (stroker, &sub_start);
1110 0 : if (unlikely (status))
1111 0 : return status;
1112 : }
1113 :
1114 0 : if (remain) {
1115 : /* Cap dash end if not at end of segment */
1116 0 : status = _cairo_stroker_add_trailing_cap (stroker, &sub_end);
1117 0 : if (unlikely (status))
1118 0 : return status;
1119 : } else {
1120 0 : stroker->current_face = sub_end;
1121 0 : stroker->has_current_face = TRUE;
1122 : }
1123 : } else {
1124 0 : if (stroker->has_current_face) {
1125 : /* Cap final face from previous segment */
1126 0 : status = _cairo_stroker_add_trailing_cap (stroker,
1127 0 : &stroker->current_face);
1128 0 : if (unlikely (status))
1129 0 : return status;
1130 :
1131 0 : stroker->has_current_face = FALSE;
1132 : }
1133 : }
1134 :
1135 0 : _cairo_stroker_dash_step (&stroker->dash, step_length);
1136 0 : segment.p1 = segment.p2;
1137 : }
1138 :
1139 0 : if (stroker->dash.dash_on && ! stroker->has_current_face) {
1140 : /* This segment ends on a transition to dash_on, compute a new face
1141 : * and add cap for the beginning of the next dash_on step.
1142 : *
1143 : * Note: this will create a degenerate cap if this is not the last line
1144 : * in the path. Whether this behaviour is desirable or not is debatable.
1145 : * On one side these degenerate caps can not be reproduced with regular
1146 : * path stroking.
1147 : * On the other hand, Acroread 7 also produces the degenerate caps.
1148 : */
1149 0 : _compute_face (p2, &dev_slope,
1150 : slope_dx, slope_dy,
1151 : stroker,
1152 : &stroker->current_face);
1153 :
1154 0 : status = _cairo_stroker_add_leading_cap (stroker,
1155 0 : &stroker->current_face);
1156 0 : if (unlikely (status))
1157 0 : return status;
1158 :
1159 0 : stroker->has_current_face = TRUE;
1160 : }
1161 :
1162 0 : stroker->current_point = *p2;
1163 :
1164 0 : return CAIRO_STATUS_SUCCESS;
1165 : }
1166 :
1167 : static cairo_status_t
1168 0 : _cairo_stroker_curve_to (void *closure,
1169 : const cairo_point_t *b,
1170 : const cairo_point_t *c,
1171 : const cairo_point_t *d)
1172 : {
1173 0 : cairo_stroker_t *stroker = closure;
1174 : cairo_spline_t spline;
1175 : cairo_line_join_t line_join_save;
1176 : cairo_stroke_face_t face;
1177 : double slope_dx, slope_dy;
1178 : cairo_path_fixed_line_to_func_t *line_to;
1179 0 : cairo_status_t status = CAIRO_STATUS_SUCCESS;
1180 :
1181 0 : line_to = stroker->dash.dashed ?
1182 : _cairo_stroker_line_to_dashed :
1183 : _cairo_stroker_line_to;
1184 :
1185 0 : if (! _cairo_spline_init (&spline,
1186 : line_to, stroker,
1187 0 : &stroker->current_point, b, c, d))
1188 : {
1189 0 : return line_to (closure, d);
1190 : }
1191 :
1192 : /* If the line width is so small that the pen is reduced to a
1193 : single point, then we have nothing to do. */
1194 0 : if (stroker->pen.num_vertices <= 1)
1195 0 : return CAIRO_STATUS_SUCCESS;
1196 :
1197 : /* Compute the initial face */
1198 0 : if (! stroker->dash.dashed || stroker->dash.dash_on) {
1199 0 : slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx);
1200 0 : slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy);
1201 0 : if (_compute_normalized_device_slope (&slope_dx, &slope_dy,
1202 : stroker->ctm_inverse, NULL))
1203 : {
1204 0 : _compute_face (&stroker->current_point,
1205 : &spline.initial_slope,
1206 : slope_dx, slope_dy,
1207 : stroker, &face);
1208 : }
1209 0 : if (stroker->has_current_face) {
1210 0 : status = _cairo_stroker_join (stroker,
1211 0 : &stroker->current_face, &face);
1212 0 : if (unlikely (status))
1213 0 : return status;
1214 0 : } else if (! stroker->has_first_face) {
1215 0 : stroker->first_face = face;
1216 0 : stroker->has_first_face = TRUE;
1217 : }
1218 :
1219 0 : stroker->current_face = face;
1220 0 : stroker->has_current_face = TRUE;
1221 : }
1222 :
1223 : /* Temporarily modify the stroker to use round joins to guarantee
1224 : * smooth stroked curves. */
1225 0 : line_join_save = stroker->style.line_join;
1226 0 : stroker->style.line_join = CAIRO_LINE_JOIN_ROUND;
1227 :
1228 0 : status = _cairo_spline_decompose (&spline, stroker->tolerance);
1229 0 : if (unlikely (status))
1230 0 : return status;
1231 :
1232 : /* And join the final face */
1233 0 : if (! stroker->dash.dashed || stroker->dash.dash_on) {
1234 0 : slope_dx = _cairo_fixed_to_double (spline.final_slope.dx);
1235 0 : slope_dy = _cairo_fixed_to_double (spline.final_slope.dy);
1236 0 : if (_compute_normalized_device_slope (&slope_dx, &slope_dy,
1237 : stroker->ctm_inverse, NULL))
1238 : {
1239 0 : _compute_face (&stroker->current_point,
1240 : &spline.final_slope,
1241 : slope_dx, slope_dy,
1242 : stroker, &face);
1243 : }
1244 :
1245 0 : status = _cairo_stroker_join (stroker, &stroker->current_face, &face);
1246 0 : if (unlikely (status))
1247 0 : return status;
1248 :
1249 0 : stroker->current_face = face;
1250 : }
1251 :
1252 0 : stroker->style.line_join = line_join_save;
1253 :
1254 0 : return CAIRO_STATUS_SUCCESS;
1255 : }
1256 :
1257 : static cairo_status_t
1258 0 : _cairo_stroker_close_path (void *closure)
1259 : {
1260 0 : cairo_stroker_t *stroker = closure;
1261 : cairo_status_t status;
1262 :
1263 0 : if (stroker->dash.dashed)
1264 0 : status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point);
1265 : else
1266 0 : status = _cairo_stroker_line_to (stroker, &stroker->first_point);
1267 0 : if (unlikely (status))
1268 0 : return status;
1269 :
1270 0 : if (stroker->has_first_face && stroker->has_current_face) {
1271 : /* Join first and final faces of sub path */
1272 0 : status = _cairo_stroker_join (stroker,
1273 0 : &stroker->current_face,
1274 0 : &stroker->first_face);
1275 0 : if (unlikely (status))
1276 0 : return status;
1277 : } else {
1278 : /* Cap the start and end of the sub path as needed */
1279 0 : status = _cairo_stroker_add_caps (stroker);
1280 0 : if (unlikely (status))
1281 0 : return status;
1282 : }
1283 :
1284 0 : stroker->has_initial_sub_path = FALSE;
1285 0 : stroker->has_first_face = FALSE;
1286 0 : stroker->has_current_face = FALSE;
1287 :
1288 0 : return CAIRO_STATUS_SUCCESS;
1289 : }
1290 :
1291 : cairo_status_t
1292 0 : _cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path,
1293 : const cairo_stroke_style_t *stroke_style,
1294 : const cairo_matrix_t *ctm,
1295 : const cairo_matrix_t *ctm_inverse,
1296 : double tolerance,
1297 : cairo_status_t (*add_triangle) (void *closure,
1298 : const cairo_point_t triangle[3]),
1299 : cairo_status_t (*add_triangle_fan) (void *closure,
1300 : const cairo_point_t *midpt,
1301 : const cairo_point_t *points,
1302 : int npoints),
1303 : cairo_status_t (*add_convex_quad) (void *closure,
1304 : const cairo_point_t quad[4]),
1305 : void *closure)
1306 : {
1307 : cairo_stroker_t stroker;
1308 : cairo_status_t status;
1309 :
1310 0 : status = _cairo_stroker_init (&stroker, stroke_style,
1311 : ctm, ctm_inverse, tolerance);
1312 0 : if (unlikely (status))
1313 0 : return status;
1314 :
1315 0 : stroker.add_triangle = add_triangle;
1316 0 : stroker.add_triangle_fan = add_triangle_fan;
1317 0 : stroker.add_convex_quad = add_convex_quad;
1318 0 : stroker.closure = closure;
1319 :
1320 0 : status = _cairo_path_fixed_interpret (path,
1321 : CAIRO_DIRECTION_FORWARD,
1322 : _cairo_stroker_move_to,
1323 0 : stroker.dash.dashed ?
1324 : _cairo_stroker_line_to_dashed :
1325 : _cairo_stroker_line_to,
1326 : _cairo_stroker_curve_to,
1327 : _cairo_stroker_close_path,
1328 : &stroker);
1329 :
1330 0 : if (unlikely (status))
1331 0 : goto BAIL;
1332 :
1333 : /* Cap the start and end of the final sub path as needed */
1334 0 : status = _cairo_stroker_add_caps (&stroker);
1335 :
1336 : BAIL:
1337 0 : _cairo_stroker_fini (&stroker);
1338 :
1339 0 : return status;
1340 : }
1341 :
1342 : cairo_status_t
1343 0 : _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path,
1344 : const cairo_stroke_style_t *stroke_style,
1345 : const cairo_matrix_t *ctm,
1346 : const cairo_matrix_t *ctm_inverse,
1347 : double tolerance,
1348 : cairo_polygon_t *polygon)
1349 : {
1350 : cairo_stroker_t stroker;
1351 : cairo_status_t status;
1352 :
1353 0 : status = _cairo_stroker_init (&stroker, stroke_style,
1354 : ctm, ctm_inverse, tolerance);
1355 0 : if (unlikely (status))
1356 0 : return status;
1357 :
1358 0 : stroker.add_external_edge = _cairo_polygon_add_external_edge,
1359 0 : stroker.closure = polygon;
1360 :
1361 0 : if (polygon->num_limits)
1362 0 : _cairo_stroker_limit (&stroker, polygon->limits, polygon->num_limits);
1363 :
1364 0 : status = _cairo_path_fixed_interpret (path,
1365 : CAIRO_DIRECTION_FORWARD,
1366 : _cairo_stroker_move_to,
1367 0 : stroker.dash.dashed ?
1368 : _cairo_stroker_line_to_dashed :
1369 : _cairo_stroker_line_to,
1370 : _cairo_stroker_curve_to,
1371 : _cairo_stroker_close_path,
1372 : &stroker);
1373 :
1374 0 : if (unlikely (status))
1375 0 : goto BAIL;
1376 :
1377 : /* Cap the start and end of the final sub path as needed */
1378 0 : status = _cairo_stroker_add_caps (&stroker);
1379 :
1380 : BAIL:
1381 0 : _cairo_stroker_fini (&stroker);
1382 :
1383 0 : return status;
1384 : }
1385 :
1386 : cairo_status_t
1387 0 : _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path,
1388 : const cairo_stroke_style_t *stroke_style,
1389 : const cairo_matrix_t *ctm,
1390 : const cairo_matrix_t *ctm_inverse,
1391 : double tolerance,
1392 : cairo_traps_t *traps)
1393 : {
1394 : cairo_status_t status;
1395 : cairo_polygon_t polygon;
1396 :
1397 : /* Before we do anything else, we attempt the rectilinear
1398 : * stroker. It's careful to generate trapezoids that align to
1399 : * device-pixel boundaries when possible. Many backends can render
1400 : * those much faster than non-aligned trapezoids, (by using clip
1401 : * regions, etc.) */
1402 0 : if (path->is_rectilinear) {
1403 0 : status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
1404 : stroke_style,
1405 : ctm,
1406 : traps);
1407 0 : if (status != CAIRO_INT_STATUS_UNSUPPORTED)
1408 0 : return status;
1409 : }
1410 :
1411 0 : _cairo_polygon_init (&polygon);
1412 0 : if (traps->num_limits)
1413 0 : _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits);
1414 :
1415 0 : status = _cairo_path_fixed_stroke_to_polygon (path,
1416 : stroke_style,
1417 : ctm,
1418 : ctm_inverse,
1419 : tolerance,
1420 : &polygon);
1421 0 : if (unlikely (status))
1422 0 : goto BAIL;
1423 :
1424 0 : status = _cairo_polygon_status (&polygon);
1425 0 : if (unlikely (status))
1426 0 : goto BAIL;
1427 :
1428 0 : status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon,
1429 : CAIRO_FILL_RULE_WINDING);
1430 :
1431 : BAIL:
1432 0 : _cairo_polygon_fini (&polygon);
1433 :
1434 0 : return status;
1435 : }
1436 :
1437 : typedef struct _segment_t {
1438 : cairo_point_t p1, p2;
1439 : cairo_bool_t is_horizontal;
1440 : cairo_bool_t has_join;
1441 : } segment_t;
1442 :
1443 : typedef struct _cairo_rectilinear_stroker {
1444 : const cairo_stroke_style_t *stroke_style;
1445 : const cairo_matrix_t *ctm;
1446 :
1447 : cairo_fixed_t half_line_width;
1448 : cairo_bool_t do_traps;
1449 : void *container;
1450 : cairo_point_t current_point;
1451 : cairo_point_t first_point;
1452 : cairo_bool_t open_sub_path;
1453 :
1454 : cairo_stroker_dash_t dash;
1455 :
1456 : cairo_bool_t has_bounds;
1457 : cairo_box_t bounds;
1458 :
1459 : int num_segments;
1460 : int segments_size;
1461 : segment_t *segments;
1462 : segment_t segments_embedded[8]; /* common case is a single rectangle */
1463 : } cairo_rectilinear_stroker_t;
1464 :
1465 : static void
1466 0 : _cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
1467 : const cairo_box_t *boxes,
1468 : int num_boxes)
1469 : {
1470 0 : stroker->has_bounds = TRUE;
1471 0 : _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
1472 :
1473 0 : stroker->bounds.p1.x -= stroker->half_line_width;
1474 0 : stroker->bounds.p2.x += stroker->half_line_width;
1475 :
1476 0 : stroker->bounds.p1.y -= stroker->half_line_width;
1477 0 : stroker->bounds.p2.y += stroker->half_line_width;
1478 0 : }
1479 :
1480 : static cairo_bool_t
1481 0 : _cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker,
1482 : const cairo_stroke_style_t *stroke_style,
1483 : const cairo_matrix_t *ctm,
1484 : cairo_bool_t do_traps,
1485 : void *container)
1486 : {
1487 : /* This special-case rectilinear stroker only supports
1488 : * miter-joined lines (not curves) and a translation-only matrix
1489 : * (though it could probably be extended to support a matrix with
1490 : * uniform, integer scaling).
1491 : *
1492 : * It also only supports horizontal and vertical line_to
1493 : * elements. But we don't catch that here, but instead return
1494 : * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any
1495 : * non-rectilinear line_to is encountered.
1496 : */
1497 0 : if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER)
1498 0 : return FALSE;
1499 :
1500 : /* If the miter limit turns right angles into bevels, then we
1501 : * can't use this optimization. Remember, the ratio is
1502 : * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2,
1503 : * which we round for safety. */
1504 0 : if (stroke_style->miter_limit < M_SQRT2)
1505 0 : return FALSE;
1506 :
1507 0 : if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
1508 0 : stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
1509 : {
1510 0 : return FALSE;
1511 : }
1512 :
1513 0 : if (! _cairo_matrix_has_unity_scale (ctm))
1514 0 : return FALSE;
1515 :
1516 0 : stroker->stroke_style = stroke_style;
1517 0 : stroker->ctm = ctm;
1518 :
1519 0 : stroker->half_line_width =
1520 0 : _cairo_fixed_from_double (stroke_style->line_width / 2.0);
1521 0 : stroker->open_sub_path = FALSE;
1522 0 : stroker->segments = stroker->segments_embedded;
1523 0 : stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
1524 0 : stroker->num_segments = 0;
1525 :
1526 0 : _cairo_stroker_dash_init (&stroker->dash, stroke_style);
1527 :
1528 0 : stroker->has_bounds = FALSE;
1529 :
1530 0 : stroker->do_traps = do_traps;
1531 0 : stroker->container = container;
1532 :
1533 0 : return TRUE;
1534 : }
1535 :
1536 : static void
1537 0 : _cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker)
1538 : {
1539 0 : if (stroker->segments != stroker->segments_embedded)
1540 0 : free (stroker->segments);
1541 0 : }
1542 :
1543 : static cairo_status_t
1544 0 : _cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
1545 : const cairo_point_t *p1,
1546 : const cairo_point_t *p2,
1547 : cairo_bool_t is_horizontal,
1548 : cairo_bool_t has_join)
1549 : {
1550 : if (CAIRO_INJECT_FAULT ())
1551 : return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1552 :
1553 0 : if (stroker->num_segments == stroker->segments_size) {
1554 0 : int new_size = stroker->segments_size * 2;
1555 : segment_t *new_segments;
1556 :
1557 0 : if (stroker->segments == stroker->segments_embedded) {
1558 0 : new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t));
1559 0 : if (unlikely (new_segments == NULL))
1560 0 : return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1561 :
1562 0 : memcpy (new_segments, stroker->segments,
1563 0 : stroker->num_segments * sizeof (segment_t));
1564 : } else {
1565 0 : new_segments = _cairo_realloc_ab (stroker->segments,
1566 : new_size, sizeof (segment_t));
1567 0 : if (unlikely (new_segments == NULL))
1568 0 : return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1569 : }
1570 :
1571 0 : stroker->segments_size = new_size;
1572 0 : stroker->segments = new_segments;
1573 : }
1574 :
1575 0 : stroker->segments[stroker->num_segments].p1 = *p1;
1576 0 : stroker->segments[stroker->num_segments].p2 = *p2;
1577 0 : stroker->segments[stroker->num_segments].has_join = has_join;
1578 0 : stroker->segments[stroker->num_segments].is_horizontal = is_horizontal;
1579 0 : stroker->num_segments++;
1580 :
1581 0 : return CAIRO_STATUS_SUCCESS;
1582 : }
1583 :
1584 : static cairo_status_t
1585 0 : _cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
1586 : {
1587 : cairo_status_t status;
1588 0 : cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
1589 0 : cairo_fixed_t half_line_width = stroker->half_line_width;
1590 : int i;
1591 :
1592 0 : for (i = 0; i < stroker->num_segments; i++) {
1593 : cairo_point_t *a, *b;
1594 : cairo_bool_t lengthen_initial, shorten_final, lengthen_final;
1595 :
1596 0 : a = &stroker->segments[i].p1;
1597 0 : b = &stroker->segments[i].p2;
1598 :
1599 : /* For each segment we generate a single rectangular
1600 : * trapezoid. This rectangle is based on a perpendicular
1601 : * extension (by half the line width) of the segment endpoints
1602 : * after some adjustments of the endpoints to account for caps
1603 : * and joins.
1604 : */
1605 :
1606 : /* We adjust the initial point of the segment to extend the
1607 : * rectangle to include the previous cap or join, (this
1608 : * adjustment applies to all segments except for the first
1609 : * segment of open, butt-capped paths).
1610 : */
1611 0 : lengthen_initial = TRUE;
1612 0 : if (i == 0 && stroker->open_sub_path && line_cap == CAIRO_LINE_CAP_BUTT)
1613 0 : lengthen_initial = FALSE;
1614 :
1615 : /* The adjustment of the final point is trickier. For all but
1616 : * the last segment we shorten the segment at the final
1617 : * endpoint to not overlap with the subsequent join. For the
1618 : * last segment we do the same shortening if the path is
1619 : * closed. If the path is open and butt-capped we do no
1620 : * adjustment, while if it's open and square-capped we do a
1621 : * lengthening adjustment instead to include the cap.
1622 : */
1623 0 : shorten_final = TRUE;
1624 0 : lengthen_final = FALSE;
1625 0 : if (i == stroker->num_segments - 1 && stroker->open_sub_path) {
1626 0 : shorten_final = FALSE;
1627 0 : if (line_cap == CAIRO_LINE_CAP_SQUARE)
1628 0 : lengthen_final = TRUE;
1629 : }
1630 :
1631 : /* Perform the adjustments of the endpoints. */
1632 0 : if (a->y == b->y) {
1633 0 : if (a->x < b->x) {
1634 0 : if (lengthen_initial)
1635 0 : a->x -= half_line_width;
1636 0 : if (shorten_final)
1637 0 : b->x -= half_line_width;
1638 0 : else if (lengthen_final)
1639 0 : b->x += half_line_width;
1640 : } else {
1641 0 : if (lengthen_initial)
1642 0 : a->x += half_line_width;
1643 0 : if (shorten_final)
1644 0 : b->x += half_line_width;
1645 0 : else if (lengthen_final)
1646 0 : b->x -= half_line_width;
1647 : }
1648 :
1649 0 : if (a->x > b->x) {
1650 : cairo_point_t *t;
1651 :
1652 0 : t = a;
1653 0 : a = b;
1654 0 : b = t;
1655 : }
1656 : } else {
1657 0 : if (a->y < b->y) {
1658 0 : if (lengthen_initial)
1659 0 : a->y -= half_line_width;
1660 0 : if (shorten_final)
1661 0 : b->y -= half_line_width;
1662 0 : else if (lengthen_final)
1663 0 : b->y += half_line_width;
1664 : } else {
1665 0 : if (lengthen_initial)
1666 0 : a->y += half_line_width;
1667 0 : if (shorten_final)
1668 0 : b->y += half_line_width;
1669 0 : else if (lengthen_final)
1670 0 : b->y -= half_line_width;
1671 : }
1672 :
1673 0 : if (a->y > b->y) {
1674 : cairo_point_t *t;
1675 :
1676 0 : t = a;
1677 0 : a = b;
1678 0 : b = t;
1679 : }
1680 : }
1681 :
1682 : /* Form the rectangle by expanding by half the line width in
1683 : * either perpendicular direction. */
1684 0 : if (a->y == b->y) {
1685 0 : a->y -= half_line_width;
1686 0 : b->y += half_line_width;
1687 : } else {
1688 0 : a->x -= half_line_width;
1689 0 : b->x += half_line_width;
1690 : }
1691 :
1692 0 : if (stroker->do_traps) {
1693 0 : status = _cairo_traps_tessellate_rectangle (stroker->container, a, b);
1694 : } else {
1695 : cairo_box_t box;
1696 :
1697 0 : box.p1 = *a;
1698 0 : box.p2 = *b;
1699 :
1700 0 : status = _cairo_boxes_add (stroker->container, &box);
1701 : }
1702 0 : if (unlikely (status))
1703 0 : return status;
1704 : }
1705 :
1706 0 : stroker->num_segments = 0;
1707 :
1708 0 : return CAIRO_STATUS_SUCCESS;
1709 : }
1710 :
1711 : static cairo_status_t
1712 0 : _cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker)
1713 : {
1714 : cairo_status_t status;
1715 0 : cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
1716 0 : cairo_fixed_t half_line_width = stroker->half_line_width;
1717 : int i;
1718 :
1719 0 : for (i = 0; i < stroker->num_segments; i++) {
1720 : cairo_point_t *a, *b;
1721 : cairo_bool_t is_horizontal;
1722 :
1723 0 : a = &stroker->segments[i].p1;
1724 0 : b = &stroker->segments[i].p2;
1725 :
1726 0 : is_horizontal = stroker->segments[i].is_horizontal;
1727 :
1728 : /* Handle the joins for a potentially degenerate segment. */
1729 0 : if (line_cap == CAIRO_LINE_CAP_BUTT &&
1730 0 : stroker->segments[i].has_join &&
1731 0 : (i != stroker->num_segments - 1 ||
1732 0 : (! stroker->open_sub_path && stroker->dash.dash_starts_on)))
1733 : {
1734 0 : cairo_point_t p1 = stroker->segments[i].p1;
1735 0 : cairo_point_t p2 = stroker->segments[i].p2;
1736 : cairo_slope_t out_slope;
1737 0 : int j = (i + 1) % stroker->num_segments;
1738 :
1739 0 : _cairo_slope_init (&out_slope,
1740 0 : &stroker->segments[j].p1,
1741 0 : &stroker->segments[j].p2);
1742 :
1743 0 : if (is_horizontal) {
1744 0 : if (p1.x <= p2.x) {
1745 0 : p1.x = p2.x;
1746 0 : p2.x += half_line_width;
1747 : } else {
1748 0 : p1.x = p2.x - half_line_width;
1749 : }
1750 0 : if (out_slope.dy >= 0)
1751 0 : p1.y -= half_line_width;
1752 0 : if (out_slope.dy <= 0)
1753 0 : p2.y += half_line_width;
1754 : } else {
1755 0 : if (p1.y <= p2.y) {
1756 0 : p1.y = p2.y;
1757 0 : p2.y += half_line_width;
1758 : } else {
1759 0 : p1.y = p2.y - half_line_width;
1760 : }
1761 0 : if (out_slope.dx >= 0)
1762 0 : p1.x -= half_line_width;
1763 0 : if (out_slope.dx <= 0)
1764 0 : p2.x += half_line_width;
1765 : }
1766 :
1767 0 : if (stroker->do_traps) {
1768 0 : status = _cairo_traps_tessellate_rectangle (stroker->container, &p1, &p2);
1769 : } else {
1770 : cairo_box_t box;
1771 :
1772 0 : box.p1 = p1;
1773 0 : box.p2 = p2;
1774 :
1775 0 : status = _cairo_boxes_add (stroker->container, &box);
1776 : }
1777 0 : if (unlikely (status))
1778 0 : return status;
1779 : }
1780 :
1781 : /* Perform the adjustments of the endpoints. */
1782 0 : if (is_horizontal) {
1783 0 : if (line_cap == CAIRO_LINE_CAP_SQUARE) {
1784 0 : if (a->x <= b->x) {
1785 0 : a->x -= half_line_width;
1786 0 : b->x += half_line_width;
1787 : } else {
1788 0 : a->x += half_line_width;
1789 0 : b->x -= half_line_width;
1790 : }
1791 : }
1792 :
1793 0 : if (a->x > b->x) {
1794 : cairo_point_t *t;
1795 :
1796 0 : t = a;
1797 0 : a = b;
1798 0 : b = t;
1799 : }
1800 :
1801 0 : a->y -= half_line_width;
1802 0 : b->y += half_line_width;
1803 : } else {
1804 0 : if (line_cap == CAIRO_LINE_CAP_SQUARE) {
1805 0 : if (a->y <= b->y) {
1806 0 : a->y -= half_line_width;
1807 0 : b->y += half_line_width;
1808 : } else {
1809 0 : a->y += half_line_width;
1810 0 : b->y -= half_line_width;
1811 : }
1812 : }
1813 :
1814 0 : if (a->y > b->y) {
1815 : cairo_point_t *t;
1816 :
1817 0 : t = a;
1818 0 : a = b;
1819 0 : b = t;
1820 : }
1821 :
1822 0 : a->x -= half_line_width;
1823 0 : b->x += half_line_width;
1824 : }
1825 :
1826 0 : if (a->x == b->x && a->y == b->y)
1827 0 : continue;
1828 :
1829 0 : if (stroker->do_traps) {
1830 0 : status = _cairo_traps_tessellate_rectangle (stroker->container, a, b);
1831 : } else {
1832 : cairo_box_t box;
1833 :
1834 0 : box.p1 = *a;
1835 0 : box.p2 = *b;
1836 :
1837 0 : status = _cairo_boxes_add (stroker->container, &box);
1838 : }
1839 0 : if (unlikely (status))
1840 0 : return status;
1841 : }
1842 :
1843 0 : stroker->num_segments = 0;
1844 :
1845 0 : return CAIRO_STATUS_SUCCESS;
1846 : }
1847 :
1848 : static cairo_status_t
1849 0 : _cairo_rectilinear_stroker_move_to (void *closure,
1850 : const cairo_point_t *point)
1851 : {
1852 0 : cairo_rectilinear_stroker_t *stroker = closure;
1853 : cairo_status_t status;
1854 :
1855 0 : if (stroker->dash.dashed)
1856 0 : status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
1857 : else
1858 0 : status = _cairo_rectilinear_stroker_emit_segments (stroker);
1859 0 : if (unlikely (status))
1860 0 : return status;
1861 :
1862 : /* reset the dash pattern for new sub paths */
1863 0 : _cairo_stroker_dash_start (&stroker->dash);
1864 :
1865 0 : stroker->current_point = *point;
1866 0 : stroker->first_point = *point;
1867 :
1868 0 : return CAIRO_STATUS_SUCCESS;
1869 : }
1870 :
1871 : static cairo_status_t
1872 0 : _cairo_rectilinear_stroker_line_to (void *closure,
1873 : const cairo_point_t *b)
1874 : {
1875 0 : cairo_rectilinear_stroker_t *stroker = closure;
1876 0 : cairo_point_t *a = &stroker->current_point;
1877 : cairo_status_t status;
1878 :
1879 : /* We only support horizontal or vertical elements. */
1880 0 : assert (a->x == b->x || a->y == b->y);
1881 :
1882 : /* We don't draw anything for degenerate paths. */
1883 0 : if (a->x == b->x && a->y == b->y)
1884 0 : return CAIRO_STATUS_SUCCESS;
1885 :
1886 0 : status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
1887 0 : a->y == b->y,
1888 : TRUE);
1889 :
1890 0 : stroker->current_point = *b;
1891 0 : stroker->open_sub_path = TRUE;
1892 :
1893 0 : return status;
1894 : }
1895 :
1896 : static cairo_status_t
1897 0 : _cairo_rectilinear_stroker_line_to_dashed (void *closure,
1898 : const cairo_point_t *point)
1899 : {
1900 0 : cairo_rectilinear_stroker_t *stroker = closure;
1901 0 : const cairo_point_t *a = &stroker->current_point;
1902 0 : const cairo_point_t *b = point;
1903 : cairo_bool_t fully_in_bounds;
1904 : double sign, remain;
1905 : cairo_fixed_t mag;
1906 : cairo_status_t status;
1907 : cairo_line_t segment;
1908 0 : cairo_bool_t dash_on = FALSE;
1909 : cairo_bool_t is_horizontal;
1910 :
1911 : /* We don't draw anything for degenerate paths. */
1912 0 : if (a->x == b->x && a->y == b->y)
1913 0 : return CAIRO_STATUS_SUCCESS;
1914 :
1915 : /* We only support horizontal or vertical elements. */
1916 0 : assert (a->x == b->x || a->y == b->y);
1917 :
1918 0 : fully_in_bounds = TRUE;
1919 0 : if (stroker->has_bounds &&
1920 0 : (! _cairo_box_contains_point (&stroker->bounds, a) ||
1921 0 : ! _cairo_box_contains_point (&stroker->bounds, b)))
1922 : {
1923 0 : fully_in_bounds = FALSE;
1924 : }
1925 :
1926 0 : is_horizontal = a->y == b->y;
1927 0 : if (is_horizontal)
1928 0 : mag = b->x - a->x;
1929 : else
1930 0 : mag = b->y - a->y;
1931 0 : if (mag < 0) {
1932 0 : remain = _cairo_fixed_to_double (-mag);
1933 0 : sign = 1.;
1934 : } else {
1935 0 : remain = _cairo_fixed_to_double (mag);
1936 0 : sign = -1.;
1937 : }
1938 :
1939 0 : segment.p2 = segment.p1 = *a;
1940 0 : while (remain > 0.) {
1941 : double step_length;
1942 :
1943 0 : step_length = MIN (stroker->dash.dash_remain, remain);
1944 0 : remain -= step_length;
1945 :
1946 0 : mag = _cairo_fixed_from_double (sign*remain);
1947 0 : if (is_horizontal)
1948 0 : segment.p2.x = b->x + mag;
1949 : else
1950 0 : segment.p2.y = b->y + mag;
1951 :
1952 0 : if (stroker->dash.dash_on &&
1953 0 : (fully_in_bounds ||
1954 0 : _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
1955 : {
1956 0 : status = _cairo_rectilinear_stroker_add_segment (stroker,
1957 : &segment.p1,
1958 : &segment.p2,
1959 : is_horizontal,
1960 : remain <= 0.);
1961 0 : if (unlikely (status))
1962 0 : return status;
1963 :
1964 0 : dash_on = TRUE;
1965 : }
1966 : else
1967 : {
1968 0 : dash_on = FALSE;
1969 : }
1970 :
1971 0 : _cairo_stroker_dash_step (&stroker->dash, step_length);
1972 0 : segment.p1 = segment.p2;
1973 : }
1974 :
1975 0 : if (stroker->dash.dash_on && ! dash_on &&
1976 0 : (fully_in_bounds ||
1977 0 : _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
1978 : {
1979 :
1980 : /* This segment ends on a transition to dash_on, compute a new face
1981 : * and add cap for the beginning of the next dash_on step.
1982 : */
1983 :
1984 0 : status = _cairo_rectilinear_stroker_add_segment (stroker,
1985 : &segment.p1,
1986 : &segment.p1,
1987 : is_horizontal,
1988 : TRUE);
1989 0 : if (unlikely (status))
1990 0 : return status;
1991 : }
1992 :
1993 0 : stroker->current_point = *point;
1994 0 : stroker->open_sub_path = TRUE;
1995 :
1996 0 : return CAIRO_STATUS_SUCCESS;
1997 : }
1998 :
1999 : static cairo_status_t
2000 0 : _cairo_rectilinear_stroker_close_path (void *closure)
2001 : {
2002 0 : cairo_rectilinear_stroker_t *stroker = closure;
2003 : cairo_status_t status;
2004 :
2005 : /* We don't draw anything for degenerate paths. */
2006 0 : if (! stroker->open_sub_path)
2007 0 : return CAIRO_STATUS_SUCCESS;
2008 :
2009 0 : if (stroker->dash.dashed) {
2010 0 : status = _cairo_rectilinear_stroker_line_to_dashed (stroker,
2011 0 : &stroker->first_point);
2012 : } else {
2013 0 : status = _cairo_rectilinear_stroker_line_to (stroker,
2014 0 : &stroker->first_point);
2015 : }
2016 0 : if (unlikely (status))
2017 0 : return status;
2018 :
2019 0 : stroker->open_sub_path = FALSE;
2020 :
2021 0 : if (stroker->dash.dashed)
2022 0 : status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
2023 : else
2024 0 : status = _cairo_rectilinear_stroker_emit_segments (stroker);
2025 0 : if (unlikely (status))
2026 0 : return status;
2027 :
2028 0 : return CAIRO_STATUS_SUCCESS;
2029 : }
2030 :
2031 : cairo_int_status_t
2032 0 : _cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path,
2033 : const cairo_stroke_style_t *stroke_style,
2034 : const cairo_matrix_t *ctm,
2035 : cairo_traps_t *traps)
2036 : {
2037 : cairo_rectilinear_stroker_t rectilinear_stroker;
2038 : cairo_int_status_t status;
2039 :
2040 0 : assert (path->is_rectilinear);
2041 :
2042 0 : if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
2043 : stroke_style, ctm,
2044 : TRUE, traps))
2045 : {
2046 0 : return CAIRO_INT_STATUS_UNSUPPORTED;
2047 : }
2048 :
2049 0 : if (traps->num_limits) {
2050 0 : _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
2051 : traps->limits,
2052 : traps->num_limits);
2053 : }
2054 :
2055 0 : status = _cairo_path_fixed_interpret (path,
2056 : CAIRO_DIRECTION_FORWARD,
2057 : _cairo_rectilinear_stroker_move_to,
2058 0 : rectilinear_stroker.dash.dashed ?
2059 : _cairo_rectilinear_stroker_line_to_dashed :
2060 : _cairo_rectilinear_stroker_line_to,
2061 : NULL,
2062 : _cairo_rectilinear_stroker_close_path,
2063 : &rectilinear_stroker);
2064 0 : if (unlikely (status))
2065 0 : goto BAIL;
2066 :
2067 0 : if (rectilinear_stroker.dash.dashed)
2068 0 : status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
2069 : else
2070 0 : status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
2071 :
2072 0 : traps->is_rectilinear = 1;
2073 0 : traps->is_rectangular = 1;
2074 : /* As we incrementally tessellate, we do not eliminate self-intersections */
2075 0 : traps->has_intersections = traps->num_traps > 1;
2076 : BAIL:
2077 0 : _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
2078 :
2079 0 : if (unlikely (status))
2080 0 : _cairo_traps_clear (traps);
2081 :
2082 0 : return status;
2083 : }
2084 :
2085 : cairo_int_status_t
2086 0 : _cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path,
2087 : const cairo_stroke_style_t *stroke_style,
2088 : const cairo_matrix_t *ctm,
2089 : cairo_boxes_t *boxes)
2090 : {
2091 : cairo_rectilinear_stroker_t rectilinear_stroker;
2092 : cairo_int_status_t status;
2093 :
2094 0 : assert (path->is_rectilinear);
2095 :
2096 0 : if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
2097 : stroke_style, ctm,
2098 : FALSE, boxes))
2099 : {
2100 0 : return CAIRO_INT_STATUS_UNSUPPORTED;
2101 : }
2102 :
2103 0 : if (boxes->num_limits) {
2104 0 : _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
2105 : boxes->limits,
2106 : boxes->num_limits);
2107 : }
2108 :
2109 0 : status = _cairo_path_fixed_interpret (path,
2110 : CAIRO_DIRECTION_FORWARD,
2111 : _cairo_rectilinear_stroker_move_to,
2112 0 : rectilinear_stroker.dash.dashed ?
2113 : _cairo_rectilinear_stroker_line_to_dashed :
2114 : _cairo_rectilinear_stroker_line_to,
2115 : NULL,
2116 : _cairo_rectilinear_stroker_close_path,
2117 : &rectilinear_stroker);
2118 0 : if (unlikely (status))
2119 0 : goto BAIL;
2120 :
2121 0 : if (rectilinear_stroker.dash.dashed)
2122 0 : status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
2123 : else
2124 0 : status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
2125 0 : if (unlikely (status))
2126 0 : goto BAIL;
2127 :
2128 : /* As we incrementally tessellate, we do not eliminate self-intersections */
2129 0 : status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
2130 : CAIRO_FILL_RULE_WINDING,
2131 : boxes);
2132 0 : if (unlikely (status))
2133 0 : goto BAIL;
2134 :
2135 0 : _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
2136 :
2137 0 : return CAIRO_STATUS_SUCCESS;
2138 :
2139 : BAIL:
2140 0 : _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
2141 0 : _cairo_boxes_clear (boxes);
2142 0 : return status;
2143 : }
|