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
4 * Copyright © 2002 University of Southern California
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.
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
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/
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.
29 * The Original Code is the cairo graphics library.
31 * The Initial Developer of the Original Code is University of Southern
35 * Carl D. Worth <cworth@cworth.org>
36 * Chris Wilson <chris@chris-wilson.co.uk>
39 #define _BSD_SOURCE /* for hypot() */
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"
49 typedef struct _segment_t
{
52 #define HORIZONTAL 0x1
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
;
62 cairo_fixed_t half_line_x
, half_line_y
;
64 cairo_point_t current_point
;
65 cairo_point_t first_point
;
66 cairo_bool_t open_sub_path
;
68 cairo_stroker_dash_t dash
;
70 cairo_bool_t has_bounds
;
76 segment_t segments_embedded
[8]; /* common case is a single rectangle */
77 } cairo_rectilinear_stroker_t
;
80 _cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t
*stroker
,
81 const cairo_box_t
*boxes
,
84 stroker
->has_bounds
= TRUE
;
85 _cairo_boxes_get_extents (boxes
, num_boxes
, &stroker
->bounds
);
87 stroker
->bounds
.p1
.x
-= stroker
->half_line_x
;
88 stroker
->bounds
.p2
.x
+= stroker
->half_line_x
;
90 stroker
->bounds
.p1
.y
-= stroker
->half_line_y
;
91 stroker
->bounds
.p2
.y
+= stroker
->half_line_y
;
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
,
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).
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.
111 if (stroke_style
->line_join
!= CAIRO_LINE_JOIN_MITER
)
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 if (stroke_style
->miter_limit
< M_SQRT2
)
121 if (! (stroke_style
->line_cap
== CAIRO_LINE_CAP_BUTT
||
122 stroke_style
->line_cap
== CAIRO_LINE_CAP_SQUARE
))
127 if (! _cairo_matrix_is_scale (ctm
))
130 stroker
->stroke_style
= stroke_style
;
132 stroker
->antialias
= antialias
;
134 stroker
->half_line_x
=
135 _cairo_fixed_from_double (fabs(ctm
->xx
) * stroke_style
->line_width
/ 2.0);
136 stroker
->half_line_y
=
137 _cairo_fixed_from_double (fabs(ctm
->yy
) * stroke_style
->line_width
/ 2.0);
139 stroker
->open_sub_path
= FALSE
;
140 stroker
->segments
= stroker
->segments_embedded
;
141 stroker
->segments_size
= ARRAY_LENGTH (stroker
->segments_embedded
);
142 stroker
->num_segments
= 0;
144 _cairo_stroker_dash_init (&stroker
->dash
, stroke_style
);
146 stroker
->has_bounds
= FALSE
;
148 stroker
->boxes
= boxes
;
154 _cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t
*stroker
)
156 if (stroker
->segments
!= stroker
->segments_embedded
)
157 free (stroker
->segments
);
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
,
166 if (CAIRO_INJECT_FAULT ())
167 return _cairo_error (CAIRO_STATUS_NO_MEMORY
);
169 if (stroker
->num_segments
== stroker
->segments_size
) {
170 int new_size
= stroker
->segments_size
* 2;
171 segment_t
*new_segments
;
173 if (stroker
->segments
== stroker
->segments_embedded
) {
174 new_segments
= _cairo_malloc_ab (new_size
, sizeof (segment_t
));
175 if (unlikely (new_segments
== NULL
))
176 return _cairo_error (CAIRO_STATUS_NO_MEMORY
);
178 memcpy (new_segments
, stroker
->segments
,
179 stroker
->num_segments
* sizeof (segment_t
));
181 new_segments
= _cairo_realloc_ab (stroker
->segments
,
182 new_size
, sizeof (segment_t
));
183 if (unlikely (new_segments
== NULL
))
184 return _cairo_error (CAIRO_STATUS_NO_MEMORY
);
187 stroker
->segments_size
= new_size
;
188 stroker
->segments
= new_segments
;
191 stroker
->segments
[stroker
->num_segments
].p1
= *p1
;
192 stroker
->segments
[stroker
->num_segments
].p2
= *p2
;
193 stroker
->segments
[stroker
->num_segments
].flags
= flags
;
194 stroker
->num_segments
++;
196 return CAIRO_STATUS_SUCCESS
;
199 static cairo_status_t
200 _cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t
*stroker
)
202 cairo_line_cap_t line_cap
= stroker
->stroke_style
->line_cap
;
203 cairo_fixed_t half_line_x
= stroker
->half_line_x
;
204 cairo_fixed_t half_line_y
= stroker
->half_line_y
;
205 cairo_status_t status
;
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.
213 for (i
= 0; i
< stroker
->num_segments
; i
++) {
214 cairo_bool_t lengthen_initial
, lengthen_final
;
215 cairo_point_t
*a
, *b
;
218 a
= &stroker
->segments
[i
].p1
;
219 b
= &stroker
->segments
[i
].p2
;
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.
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.
232 j
= i
== 0 ? stroker
->num_segments
- 1 : i
-1;
233 lengthen_initial
= (stroker
->segments
[i
].flags
^ stroker
->segments
[j
].flags
) & HORIZONTAL
;
234 j
= i
== stroker
->num_segments
- 1 ? 0 : i
+1;
235 lengthen_final
= (stroker
->segments
[i
].flags
^ stroker
->segments
[j
].flags
) & HORIZONTAL
;
236 if (stroker
->open_sub_path
) {
238 lengthen_initial
= line_cap
!= CAIRO_LINE_CAP_BUTT
;
240 if (i
== stroker
->num_segments
- 1)
241 lengthen_final
= line_cap
!= CAIRO_LINE_CAP_BUTT
;
244 /* Perform the adjustments of the endpoints. */
245 if (lengthen_initial
| lengthen_final
) {
248 if (lengthen_initial
)
253 if (lengthen_initial
)
260 if (lengthen_initial
)
265 if (lengthen_initial
)
273 /* Form the rectangle by expanding by half the line width in
274 * either perpendicular direction. */
298 status
= _cairo_boxes_add (stroker
->boxes
, stroker
->antialias
, &box
);
299 if (unlikely (status
))
303 stroker
->num_segments
= 0;
304 return CAIRO_STATUS_SUCCESS
;
307 static cairo_status_t
308 _cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t
*stroker
)
310 cairo_status_t status
;
311 cairo_line_cap_t line_cap
= stroker
->stroke_style
->line_cap
;
312 cairo_fixed_t half_line_x
= stroker
->half_line_x
;
313 cairo_fixed_t half_line_y
= stroker
->half_line_y
;
316 for (i
= 0; i
< stroker
->num_segments
; i
++) {
317 cairo_point_t
*a
, *b
;
318 cairo_bool_t is_horizontal
;
321 a
= &stroker
->segments
[i
].p1
;
322 b
= &stroker
->segments
[i
].p2
;
324 is_horizontal
= stroker
->segments
[i
].flags
& HORIZONTAL
;
326 /* Handle the joins for a potentially degenerate segment. */
327 if (line_cap
== CAIRO_LINE_CAP_BUTT
&&
328 stroker
->segments
[i
].flags
& JOIN
&&
329 (i
!= stroker
->num_segments
- 1 ||
330 (! stroker
->open_sub_path
&& stroker
->dash
.dash_starts_on
)))
332 cairo_slope_t out_slope
;
333 int j
= (i
+ 1) % stroker
->num_segments
;
334 cairo_bool_t forwards
= !!(stroker
->segments
[i
].flags
& FORWARDS
);
336 _cairo_slope_init (&out_slope
,
337 &stroker
->segments
[j
].p1
,
338 &stroker
->segments
[j
].p2
);
339 box
.p2
= box
.p1
= stroker
->segments
[i
].p2
;
343 box
.p2
.x
+= half_line_x
;
345 box
.p1
.x
-= half_line_x
;
347 if (out_slope
.dy
> 0)
348 box
.p1
.y
-= half_line_y
;
350 box
.p2
.y
+= half_line_y
;
353 box
.p2
.y
+= half_line_y
;
355 box
.p1
.y
-= half_line_y
;
357 if (out_slope
.dx
> 0)
358 box
.p1
.x
-= half_line_x
;
360 box
.p2
.x
+= half_line_x
;
363 status
= _cairo_boxes_add (stroker
->boxes
, stroker
->antialias
, &box
);
364 if (unlikely (status
))
368 /* Perform the adjustments of the endpoints. */
370 if (line_cap
== CAIRO_LINE_CAP_SQUARE
) {
383 if (line_cap
== CAIRO_LINE_CAP_SQUARE
) {
397 if (a
->x
== b
->x
&& a
->y
== b
->y
)
415 status
= _cairo_boxes_add (stroker
->boxes
, stroker
->antialias
, &box
);
416 if (unlikely (status
))
420 stroker
->num_segments
= 0;
422 return CAIRO_STATUS_SUCCESS
;
425 static cairo_status_t
426 _cairo_rectilinear_stroker_move_to (void *closure
,
427 const cairo_point_t
*point
)
429 cairo_rectilinear_stroker_t
*stroker
= closure
;
430 cairo_status_t status
;
432 if (stroker
->dash
.dashed
)
433 status
= _cairo_rectilinear_stroker_emit_segments_dashed (stroker
);
435 status
= _cairo_rectilinear_stroker_emit_segments (stroker
);
436 if (unlikely (status
))
439 /* reset the dash pattern for new sub paths */
440 _cairo_stroker_dash_start (&stroker
->dash
);
442 stroker
->current_point
= *point
;
443 stroker
->first_point
= *point
;
445 return CAIRO_STATUS_SUCCESS
;
448 static cairo_status_t
449 _cairo_rectilinear_stroker_line_to (void *closure
,
450 const cairo_point_t
*b
)
452 cairo_rectilinear_stroker_t
*stroker
= closure
;
453 cairo_point_t
*a
= &stroker
->current_point
;
454 cairo_status_t status
;
456 /* We only support horizontal or vertical elements. */
457 assert (a
->x
== b
->x
|| a
->y
== b
->y
);
459 /* We don't draw anything for degenerate paths. */
460 if (a
->x
== b
->x
&& a
->y
== b
->y
)
461 return CAIRO_STATUS_SUCCESS
;
463 status
= _cairo_rectilinear_stroker_add_segment (stroker
, a
, b
,
464 (a
->y
== b
->y
) | JOIN
);
466 stroker
->current_point
= *b
;
467 stroker
->open_sub_path
= TRUE
;
472 static cairo_status_t
473 _cairo_rectilinear_stroker_line_to_dashed (void *closure
,
474 const cairo_point_t
*point
)
476 cairo_rectilinear_stroker_t
*stroker
= closure
;
477 const cairo_point_t
*a
= &stroker
->current_point
;
478 const cairo_point_t
*b
= point
;
479 cairo_bool_t fully_in_bounds
;
480 double sf
, sign
, remain
;
482 cairo_status_t status
;
483 cairo_line_t segment
;
484 cairo_bool_t dash_on
= FALSE
;
485 unsigned is_horizontal
;
487 /* We don't draw anything for degenerate paths. */
488 if (a
->x
== b
->x
&& a
->y
== b
->y
)
489 return CAIRO_STATUS_SUCCESS
;
491 /* We only support horizontal or vertical elements. */
492 assert (a
->x
== b
->x
|| a
->y
== b
->y
);
494 fully_in_bounds
= TRUE
;
495 if (stroker
->has_bounds
&&
496 (! _cairo_box_contains_point (&stroker
->bounds
, a
) ||
497 ! _cairo_box_contains_point (&stroker
->bounds
, b
)))
499 fully_in_bounds
= FALSE
;
502 is_horizontal
= a
->y
== b
->y
;
505 sf
= fabs (stroker
->ctm
->xx
);
508 sf
= fabs (stroker
->ctm
->yy
);
511 remain
= _cairo_fixed_to_double (-mag
);
514 remain
= _cairo_fixed_to_double (mag
);
515 is_horizontal
|= FORWARDS
;
519 segment
.p2
= segment
.p1
= *a
;
520 while (remain
> 0.) {
523 step_length
= MIN (sf
* stroker
->dash
.dash_remain
, remain
);
524 remain
-= step_length
;
526 mag
= _cairo_fixed_from_double (sign
*remain
);
527 if (is_horizontal
& 0x1)
528 segment
.p2
.x
= b
->x
+ mag
;
530 segment
.p2
.y
= b
->y
+ mag
;
532 if (stroker
->dash
.dash_on
&&
534 _cairo_box_intersects_line_segment (&stroker
->bounds
, &segment
)))
536 status
= _cairo_rectilinear_stroker_add_segment (stroker
,
539 is_horizontal
| (remain
<= 0.) << 2);
540 if (unlikely (status
))
550 _cairo_stroker_dash_step (&stroker
->dash
, step_length
/ sf
);
551 segment
.p1
= segment
.p2
;
554 if (stroker
->dash
.dash_on
&& ! dash_on
&&
556 _cairo_box_intersects_line_segment (&stroker
->bounds
, &segment
)))
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.
563 status
= _cairo_rectilinear_stroker_add_segment (stroker
,
566 is_horizontal
| JOIN
);
567 if (unlikely (status
))
571 stroker
->current_point
= *point
;
572 stroker
->open_sub_path
= TRUE
;
574 return CAIRO_STATUS_SUCCESS
;
577 static cairo_status_t
578 _cairo_rectilinear_stroker_close_path (void *closure
)
580 cairo_rectilinear_stroker_t
*stroker
= closure
;
581 cairo_status_t status
;
583 /* We don't draw anything for degenerate paths. */
584 if (! stroker
->open_sub_path
)
585 return CAIRO_STATUS_SUCCESS
;
587 if (stroker
->dash
.dashed
) {
588 status
= _cairo_rectilinear_stroker_line_to_dashed (stroker
,
589 &stroker
->first_point
);
591 status
= _cairo_rectilinear_stroker_line_to (stroker
,
592 &stroker
->first_point
);
594 if (unlikely (status
))
597 stroker
->open_sub_path
= FALSE
;
599 if (stroker
->dash
.dashed
)
600 status
= _cairo_rectilinear_stroker_emit_segments_dashed (stroker
);
602 status
= _cairo_rectilinear_stroker_emit_segments (stroker
);
603 if (unlikely (status
))
606 return CAIRO_STATUS_SUCCESS
;
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
)
616 cairo_rectilinear_stroker_t rectilinear_stroker
;
617 cairo_int_status_t status
;
620 assert (_cairo_path_fixed_stroke_is_rectilinear (path
));
622 if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker
,
623 stroke_style
, ctm
, antialias
,
626 return CAIRO_INT_STATUS_UNSUPPORTED
;
629 if (! rectilinear_stroker
.dash
.dashed
&&
630 _cairo_path_fixed_is_stroke_box (path
, &box
) &&
631 /* if the segments overlap we need to feed them into the tessellator */
632 box
.p2
.x
- box
.p1
.x
> 2* rectilinear_stroker
.half_line_x
&&
633 box
.p2
.y
- box
.p1
.y
> 2* rectilinear_stroker
.half_line_y
)
638 b
.p1
.x
= box
.p1
.x
- rectilinear_stroker
.half_line_x
;
639 b
.p2
.x
= box
.p2
.x
+ rectilinear_stroker
.half_line_x
;
640 b
.p1
.y
= box
.p1
.y
- rectilinear_stroker
.half_line_y
;
641 b
.p2
.y
= box
.p1
.y
+ rectilinear_stroker
.half_line_y
;
642 status
= _cairo_boxes_add (boxes
, antialias
, &b
);
643 assert (status
== CAIRO_INT_STATUS_SUCCESS
);
645 /* left (excluding top/bottom) */
646 b
.p1
.x
= box
.p1
.x
- rectilinear_stroker
.half_line_x
;
647 b
.p2
.x
= box
.p1
.x
+ rectilinear_stroker
.half_line_x
;
648 b
.p1
.y
= box
.p1
.y
+ rectilinear_stroker
.half_line_y
;
649 b
.p2
.y
= box
.p2
.y
- rectilinear_stroker
.half_line_y
;
650 status
= _cairo_boxes_add (boxes
, antialias
, &b
);
651 assert (status
== CAIRO_INT_STATUS_SUCCESS
);
653 /* right (excluding top/bottom) */
654 b
.p1
.x
= box
.p2
.x
- rectilinear_stroker
.half_line_x
;
655 b
.p2
.x
= box
.p2
.x
+ rectilinear_stroker
.half_line_x
;
656 b
.p1
.y
= box
.p1
.y
+ rectilinear_stroker
.half_line_y
;
657 b
.p2
.y
= box
.p2
.y
- rectilinear_stroker
.half_line_y
;
658 status
= _cairo_boxes_add (boxes
, antialias
, &b
);
659 assert (status
== CAIRO_INT_STATUS_SUCCESS
);
662 b
.p1
.x
= box
.p1
.x
- rectilinear_stroker
.half_line_x
;
663 b
.p2
.x
= box
.p2
.x
+ rectilinear_stroker
.half_line_x
;
664 b
.p1
.y
= box
.p2
.y
- rectilinear_stroker
.half_line_y
;
665 b
.p2
.y
= box
.p2
.y
+ rectilinear_stroker
.half_line_y
;
666 status
= _cairo_boxes_add (boxes
, antialias
, &b
);
667 assert (status
== CAIRO_INT_STATUS_SUCCESS
);
672 if (boxes
->num_limits
) {
673 _cairo_rectilinear_stroker_limit (&rectilinear_stroker
,
678 status
= _cairo_path_fixed_interpret (path
,
679 _cairo_rectilinear_stroker_move_to
,
680 rectilinear_stroker
.dash
.dashed
?
681 _cairo_rectilinear_stroker_line_to_dashed
:
682 _cairo_rectilinear_stroker_line_to
,
684 _cairo_rectilinear_stroker_close_path
,
685 &rectilinear_stroker
);
686 if (unlikely (status
))
689 if (rectilinear_stroker
.dash
.dashed
)
690 status
= _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker
);
692 status
= _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker
);
693 if (unlikely (status
))
696 /* As we incrementally tessellate, we do not eliminate self-intersections */
697 status
= _cairo_bentley_ottmann_tessellate_boxes (boxes
,
698 CAIRO_FILL_RULE_WINDING
,
700 if (unlikely (status
))
704 _cairo_rectilinear_stroker_fini (&rectilinear_stroker
);
705 return CAIRO_STATUS_SUCCESS
;
708 _cairo_rectilinear_stroker_fini (&rectilinear_stroker
);
709 _cairo_boxes_clear (boxes
);