beta-0.89.2
[luatex.git] / source / libs / cairo / cairo-src / src / cairo-path-stroke-boxes.c
blob7f25bf76cf744837320331d1d859c73bddc2918c
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
32 * California.
34 * Contributor(s):
35 * Carl D. Worth <cworth@cworth.org>
36 * Chris Wilson <chris@chris-wilson.co.uk>
39 #define _BSD_SOURCE /* for hypot() */
40 #include "cairoint.h"
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 {
50 cairo_point_t p1, p2;
51 unsigned flags;
52 #define HORIZONTAL 0x1
53 #define FORWARDS 0x2
54 #define JOIN 0x4
55 } segment_t;
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;
63 cairo_boxes_t *boxes;
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;
71 cairo_box_t bounds;
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;
79 static void
80 _cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
81 const cairo_box_t *boxes,
82 int num_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;
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)
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)
112 return FALSE;
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)
119 return FALSE;
121 if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
122 stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
124 return FALSE;
127 if (! _cairo_matrix_is_scale (ctm))
128 return FALSE;
130 stroker->stroke_style = stroke_style;
131 stroker->ctm = ctm;
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;
150 return TRUE;
153 static void
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,
164 unsigned flags)
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));
180 } else {
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;
206 int i, j;
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;
216 cairo_box_t box;
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) {
237 if (i == 0)
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) {
246 if (a->y == b->y) {
247 if (a->x < b->x) {
248 if (lengthen_initial)
249 a->x -= half_line_x;
250 if (lengthen_final)
251 b->x += half_line_x;
252 } else {
253 if (lengthen_initial)
254 a->x += half_line_x;
255 if (lengthen_final)
256 b->x -= half_line_x;
258 } else {
259 if (a->y < b->y) {
260 if (lengthen_initial)
261 a->y -= half_line_y;
262 if (lengthen_final)
263 b->y += half_line_y;
264 } else {
265 if (lengthen_initial)
266 a->y += half_line_y;
267 if (lengthen_final)
268 b->y -= half_line_y;
273 /* Form the rectangle by expanding by half the line width in
274 * either perpendicular direction. */
275 if (a->y == b->y) {
276 a->y -= half_line_y;
277 b->y += half_line_y;
278 } else {
279 a->x -= half_line_x;
280 b->x += half_line_x;
283 if (a->x < b->x) {
284 box.p1.x = a->x;
285 box.p2.x = b->x;
286 } else {
287 box.p1.x = b->x;
288 box.p2.x = a->x;
290 if (a->y < b->y) {
291 box.p1.y = a->y;
292 box.p2.y = b->y;
293 } else {
294 box.p1.y = b->y;
295 box.p2.y = a->y;
298 status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
299 if (unlikely (status))
300 return 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;
314 int i;
316 for (i = 0; i < stroker->num_segments; i++) {
317 cairo_point_t *a, *b;
318 cairo_bool_t is_horizontal;
319 cairo_box_t box;
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;
341 if (is_horizontal) {
342 if (forwards)
343 box.p2.x += half_line_x;
344 else
345 box.p1.x -= half_line_x;
347 if (out_slope.dy > 0)
348 box.p1.y -= half_line_y;
349 else
350 box.p2.y += half_line_y;
351 } else {
352 if (forwards)
353 box.p2.y += half_line_y;
354 else
355 box.p1.y -= half_line_y;
357 if (out_slope.dx > 0)
358 box.p1.x -= half_line_x;
359 else
360 box.p2.x += half_line_x;
363 status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
364 if (unlikely (status))
365 return status;
368 /* Perform the adjustments of the endpoints. */
369 if (is_horizontal) {
370 if (line_cap == CAIRO_LINE_CAP_SQUARE) {
371 if (a->x <= b->x) {
372 a->x -= half_line_x;
373 b->x += half_line_x;
374 } else {
375 a->x += half_line_x;
376 b->x -= half_line_x;
380 a->y += half_line_y;
381 b->y -= half_line_y;
382 } else {
383 if (line_cap == CAIRO_LINE_CAP_SQUARE) {
384 if (a->y <= b->y) {
385 a->y -= half_line_y;
386 b->y += half_line_y;
387 } else {
388 a->y += half_line_y;
389 b->y -= half_line_y;
393 a->x += half_line_x;
394 b->x -= half_line_x;
397 if (a->x == b->x && a->y == b->y)
398 continue;
400 if (a->x < b->x) {
401 box.p1.x = a->x;
402 box.p2.x = b->x;
403 } else {
404 box.p1.x = b->x;
405 box.p2.x = a->x;
407 if (a->y < b->y) {
408 box.p1.y = a->y;
409 box.p2.y = b->y;
410 } else {
411 box.p1.y = b->y;
412 box.p2.y = a->y;
415 status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
416 if (unlikely (status))
417 return 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);
434 else
435 status = _cairo_rectilinear_stroker_emit_segments (stroker);
436 if (unlikely (status))
437 return 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;
469 return status;
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;
481 cairo_fixed_t mag;
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;
503 if (is_horizontal) {
504 mag = b->x - a->x;
505 sf = fabs (stroker->ctm->xx);
506 } else {
507 mag = b->y - a->y;
508 sf = fabs (stroker->ctm->yy);
510 if (mag < 0) {
511 remain = _cairo_fixed_to_double (-mag);
512 sign = 1.;
513 } else {
514 remain = _cairo_fixed_to_double (mag);
515 is_horizontal |= FORWARDS;
516 sign = -1.;
519 segment.p2 = segment.p1 = *a;
520 while (remain > 0.) {
521 double step_length;
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;
529 else
530 segment.p2.y = b->y + mag;
532 if (stroker->dash.dash_on &&
533 (fully_in_bounds ||
534 _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
536 status = _cairo_rectilinear_stroker_add_segment (stroker,
537 &segment.p1,
538 &segment.p2,
539 is_horizontal | (remain <= 0.) << 2);
540 if (unlikely (status))
541 return status;
543 dash_on = TRUE;
545 else
547 dash_on = FALSE;
550 _cairo_stroker_dash_step (&stroker->dash, step_length / sf);
551 segment.p1 = segment.p2;
554 if (stroker->dash.dash_on && ! dash_on &&
555 (fully_in_bounds ||
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,
564 &segment.p1,
565 &segment.p1,
566 is_horizontal | JOIN);
567 if (unlikely (status))
568 return 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);
590 } else {
591 status = _cairo_rectilinear_stroker_line_to (stroker,
592 &stroker->first_point);
594 if (unlikely (status))
595 return status;
597 stroker->open_sub_path = FALSE;
599 if (stroker->dash.dashed)
600 status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
601 else
602 status = _cairo_rectilinear_stroker_emit_segments (stroker);
603 if (unlikely (status))
604 return status;
606 return CAIRO_STATUS_SUCCESS;
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)
616 cairo_rectilinear_stroker_t rectilinear_stroker;
617 cairo_int_status_t status;
618 cairo_box_t box;
620 assert (_cairo_path_fixed_stroke_is_rectilinear (path));
622 if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
623 stroke_style, ctm, antialias,
624 boxes))
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)
635 cairo_box_t b;
637 /* top */
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);
661 /* bottom */
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);
669 goto done;
672 if (boxes->num_limits) {
673 _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
674 boxes->limits,
675 boxes->num_limits);
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,
683 NULL,
684 _cairo_rectilinear_stroker_close_path,
685 &rectilinear_stroker);
686 if (unlikely (status))
687 goto BAIL;
689 if (rectilinear_stroker.dash.dashed)
690 status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
691 else
692 status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
693 if (unlikely (status))
694 goto BAIL;
696 /* As we incrementally tessellate, we do not eliminate self-intersections */
697 status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
698 CAIRO_FILL_RULE_WINDING,
699 boxes);
700 if (unlikely (status))
701 goto BAIL;
703 done:
704 _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
705 return CAIRO_STATUS_SUCCESS;
707 BAIL:
708 _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
709 _cairo_boxes_clear (boxes);
710 return status;