Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/cairo/src/cairo-path-stroke.c
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
2
/* cairo - a vector graphics library with display and print output
3
 *
4
 * Copyright © 2002 University of Southern California
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it either under the terms of the GNU Lesser General Public
8
 * License version 2.1 as published by the Free Software Foundation
9
 * (the "LGPL") or, at your option, under the terms of the Mozilla
10
 * Public License Version 1.1 (the "MPL"). If you do not alter this
11
 * notice, a recipient may use your version of this file under either
12
 * the MPL or the LGPL.
13
 *
14
 * You should have received a copy of the LGPL along with this library
15
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17
 * You should have received a copy of the MPL along with this library
18
 * in the file COPYING-MPL-1.1
19
 *
20
 * The contents of this file are subject to the Mozilla Public License
21
 * Version 1.1 (the "License"); you may not use this file except in
22
 * compliance with the License. You may obtain a copy of the License at
23
 * http://www.mozilla.org/MPL/
24
 *
25
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27
 * the specific language governing rights and limitations.
28
 *
29
 * The Original Code is the cairo graphics library.
30
 *
31
 * The Initial Developer of the Original Code is University of Southern
32
 * California.
33
 *
34
 * Contributor(s):
35
 *  Carl D. Worth <cworth@cworth.org>
36
 *  Chris Wilson <chris@chris-wilson.co.uk>
37
 */
38
39
#define _DEFAULT_SOURCE /* for hypot() */
40
#include "cairoint.h"
41
42
#include "cairo-box-inline.h"
43
#include "cairo-boxes-private.h"
44
#include "cairo-error-private.h"
45
#include "cairo-path-fixed-private.h"
46
#include "cairo-slope-private.h"
47
#include "cairo-stroke-dash-private.h"
48
#include "cairo-traps-private.h"
49
50
typedef struct cairo_stroker {
51
    cairo_stroke_style_t style;
52
53
    const cairo_matrix_t *ctm;
54
    const cairo_matrix_t *ctm_inverse;
55
    double half_line_width;
56
    double tolerance;
57
    double spline_cusp_tolerance;
58
    double ctm_determinant;
59
    cairo_bool_t ctm_det_positive;
60
61
    void *closure;
62
    cairo_status_t (*add_external_edge) (void *closure,
63
           const cairo_point_t *p1,
64
           const cairo_point_t *p2);
65
    cairo_status_t (*add_triangle) (void *closure,
66
            const cairo_point_t triangle[3]);
67
    cairo_status_t (*add_triangle_fan) (void *closure,
68
          const cairo_point_t *midpt,
69
          const cairo_point_t *points,
70
          int npoints);
71
    cairo_status_t (*add_convex_quad) (void *closure,
72
               const cairo_point_t quad[4]);
73
74
    cairo_pen_t   pen;
75
76
    cairo_point_t current_point;
77
    cairo_point_t first_point;
78
79
    cairo_bool_t has_initial_sub_path;
80
81
    cairo_bool_t has_current_face;
82
    cairo_stroke_face_t current_face;
83
84
    cairo_bool_t has_first_face;
85
    cairo_stroke_face_t first_face;
86
87
    cairo_stroker_dash_t dash;
88
89
    cairo_bool_t has_bounds;
90
    cairo_box_t bounds;
91
} cairo_stroker_t;
92
93
static void
94
_cairo_stroker_limit (cairo_stroker_t *stroker,
95
          const cairo_path_fixed_t *path,
96
          const cairo_box_t *boxes,
97
          int num_boxes)
98
0
{
99
0
    double dx, dy;
100
0
    cairo_fixed_t fdx, fdy;
101
102
0
    stroker->has_bounds = TRUE;
103
0
    _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
104
105
    /* Extend the bounds in each direction to account for the maximum area
106
     * we might generate trapezoids, to capture line segments that are outside
107
     * of the bounds but which might generate rendering that's within bounds.
108
     */
109
110
0
    _cairo_stroke_style_max_distance_from_path (&stroker->style, path,
111
0
            stroker->ctm, &dx, &dy);
112
113
0
    fdx = _cairo_fixed_from_double (dx);
114
0
    fdy = _cairo_fixed_from_double (dy);
115
116
0
    stroker->bounds.p1.x -= fdx;
117
0
    stroker->bounds.p2.x += fdx;
118
119
0
    stroker->bounds.p1.y -= fdy;
120
0
    stroker->bounds.p2.y += fdy;
121
0
}
122
123
static cairo_status_t
124
_cairo_stroker_init (cairo_stroker_t    *stroker,
125
         const cairo_path_fixed_t *path,
126
         const cairo_stroke_style_t *stroke_style,
127
         const cairo_matrix_t *ctm,
128
         const cairo_matrix_t *ctm_inverse,
129
         double      tolerance,
130
         const cairo_box_t    *limits,
131
         int       num_limits)
132
0
{
133
0
    cairo_status_t status;
134
135
0
    stroker->style = *stroke_style;
136
0
    stroker->ctm = ctm;
137
0
    stroker->ctm_inverse = ctm_inverse;
138
0
    stroker->tolerance = tolerance;
139
0
    stroker->half_line_width = stroke_style->line_width / 2.0;
140
141
    /* To test whether we need to join two segments of a spline using
142
     * a round-join or a bevel-join, we can inspect the angle between the
143
     * two segments. If the difference between the chord distance
144
     * (half-line-width times the cosine of the bisection angle) and the
145
     * half-line-width itself is greater than tolerance then we need to
146
     * inject a point.
147
     */
148
0
    stroker->spline_cusp_tolerance = 1 - tolerance / stroker->half_line_width;
149
0
    stroker->spline_cusp_tolerance *= stroker->spline_cusp_tolerance;
150
0
    stroker->spline_cusp_tolerance *= 2;
151
0
    stroker->spline_cusp_tolerance -= 1;
152
153
0
    stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm);
154
0
    stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0;
155
156
0
    status = _cairo_pen_init (&stroker->pen,
157
0
            stroker->half_line_width, tolerance, ctm);
158
0
    if (unlikely (status))
159
0
  return status;
160
161
0
    stroker->has_current_face = FALSE;
162
0
    stroker->has_first_face = FALSE;
163
0
    stroker->has_initial_sub_path = FALSE;
164
165
0
    _cairo_stroker_dash_init (&stroker->dash, stroke_style);
166
167
0
    stroker->add_external_edge = NULL;
168
169
0
    stroker->has_bounds = FALSE;
170
0
    if (num_limits)
171
0
  _cairo_stroker_limit (stroker, path, limits, num_limits);
172
173
0
    return CAIRO_STATUS_SUCCESS;
174
0
}
175
176
static void
177
_cairo_stroker_fini (cairo_stroker_t *stroker)
178
0
{
179
0
    _cairo_pen_fini (&stroker->pen);
180
0
}
181
182
static void
183
_translate_point (cairo_point_t *point, const cairo_point_t *offset)
184
0
{
185
0
    point->x += offset->x;
186
0
    point->y += offset->y;
187
0
}
188
189
static int
190
_cairo_stroker_join_is_clockwise (const cairo_stroke_face_t *in,
191
          const cairo_stroke_face_t *out)
192
0
{
193
0
    cairo_slope_t in_slope, out_slope;
194
195
0
    _cairo_slope_init (&in_slope, &in->point, &in->cw);
196
0
    _cairo_slope_init (&out_slope, &out->point, &out->cw);
197
198
0
    return _cairo_slope_compare (&in_slope, &out_slope) < 0;
199
0
}
200
201
/**
202
 * _cairo_slope_compare_sgn:
203
 *
204
 * Return -1, 0 or 1 depending on the relative slopes of
205
 * two lines.
206
 **/
207
static int
208
_cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
209
0
{
210
0
    double  c = (dx1 * dy2 - dx2 * dy1);
211
212
0
    if (c > 0) return 1;
213
0
    if (c < 0) return -1;
214
0
    return 0;
215
0
}
216
217
static inline int
218
_range_step (int i, int step, int max)
219
0
{
220
0
    i += step;
221
0
    if (i < 0)
222
0
  i = max - 1;
223
0
    if (i >= max)
224
0
  i = 0;
225
0
    return i;
226
0
}
227
228
/*
229
 * Construct a fan around the midpoint using the vertices from pen between
230
 * inpt and outpt.
231
 */
232
static cairo_status_t
233
_tessellate_fan (cairo_stroker_t *stroker,
234
     const cairo_slope_t *in_vector,
235
     const cairo_slope_t *out_vector,
236
     const cairo_point_t *midpt,
237
     const cairo_point_t *inpt,
238
     const cairo_point_t *outpt,
239
     cairo_bool_t clockwise)
240
0
{
241
0
    cairo_point_t stack_points[64], *points = stack_points;
242
0
    cairo_pen_t *pen = &stroker->pen;
243
0
    int start, stop, num_points = 0;
244
0
    cairo_status_t status;
245
246
0
    if (stroker->has_bounds &&
247
0
  ! _cairo_box_contains_point (&stroker->bounds, midpt))
248
0
  goto BEVEL;
249
250
0
    assert (stroker->pen.num_vertices);
251
252
0
    if (clockwise) {
253
0
  _cairo_pen_find_active_ccw_vertices (pen,
254
0
               in_vector, out_vector,
255
0
               &start, &stop);
256
0
  if (stroker->add_external_edge) {
257
0
      cairo_point_t last;
258
0
      last = *inpt;
259
0
      while (start != stop) {
260
0
    cairo_point_t p = *midpt;
261
0
    _translate_point (&p, &pen->vertices[start].point);
262
263
0
    status = stroker->add_external_edge (stroker->closure,
264
0
                 &last, &p);
265
0
    if (unlikely (status))
266
0
        return status;
267
0
    last = p;
268
269
0
    if (start-- == 0)
270
0
        start += pen->num_vertices;
271
0
      }
272
0
      status = stroker->add_external_edge (stroker->closure,
273
0
             &last, outpt);
274
0
  } else {
275
0
      if (start == stop)
276
0
    goto BEVEL;
277
278
0
      num_points = stop - start;
279
0
      if (num_points < 0)
280
0
    num_points += pen->num_vertices;
281
0
      num_points += 2;
282
0
      if (num_points > ARRAY_LENGTH(stack_points)) {
283
0
    points = _cairo_malloc_ab (num_points, sizeof (cairo_point_t));
284
0
    if (unlikely (points == NULL))
285
0
        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
286
0
      }
287
288
0
      points[0] = *inpt;
289
0
      num_points = 1;
290
0
      while (start != stop) {
291
0
    points[num_points] = *midpt;
292
0
    _translate_point (&points[num_points], &pen->vertices[start].point);
293
0
    num_points++;
294
295
0
    if (start-- == 0)
296
0
        start += pen->num_vertices;
297
0
      }
298
0
      points[num_points++] = *outpt;
299
0
  }
300
0
    } else {
301
0
  _cairo_pen_find_active_cw_vertices (pen,
302
0
              in_vector, out_vector,
303
0
              &start, &stop);
304
0
  if (stroker->add_external_edge) {
305
0
      cairo_point_t last;
306
0
      last = *inpt;
307
0
      while (start != stop) {
308
0
    cairo_point_t p = *midpt;
309
0
    _translate_point (&p, &pen->vertices[start].point);
310
311
0
    status = stroker->add_external_edge (stroker->closure,
312
0
                 &p, &last);
313
0
    if (unlikely (status))
314
0
        return status;
315
0
    last = p;
316
317
0
    if (++start == pen->num_vertices)
318
0
        start = 0;
319
0
      }
320
0
      status = stroker->add_external_edge (stroker->closure,
321
0
             outpt, &last);
322
0
  } else {
323
0
      if (start == stop)
324
0
    goto BEVEL;
325
326
0
      num_points = stop - start;
327
0
      if (num_points < 0)
328
0
    num_points += pen->num_vertices;
329
0
      num_points += 2;
330
0
      if (num_points > ARRAY_LENGTH(stack_points)) {
331
0
    points = _cairo_malloc_ab (num_points, sizeof (cairo_point_t));
332
0
    if (unlikely (points == NULL))
333
0
        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
334
0
      }
335
336
0
      points[0] = *inpt;
337
0
      num_points = 1;
338
0
      while (start != stop) {
339
0
    points[num_points] = *midpt;
340
0
    _translate_point (&points[num_points], &pen->vertices[start].point);
341
0
    num_points++;
342
343
0
    if (++start == pen->num_vertices)
344
0
        start = 0;
345
0
      }
346
0
      points[num_points++] = *outpt;
347
0
  }
348
0
    }
349
350
0
    if (num_points) {
351
0
  status = stroker->add_triangle_fan (stroker->closure,
352
0
              midpt, points, num_points);
353
0
    }
354
355
0
    if (points != stack_points)
356
0
  free (points);
357
358
0
    return status;
359
360
0
BEVEL:
361
    /* Ensure a leak free connection... */
362
0
    if (stroker->add_external_edge != NULL) {
363
0
  if (clockwise)
364
0
      return stroker->add_external_edge (stroker->closure, inpt, outpt);
365
0
  else
366
0
      return stroker->add_external_edge (stroker->closure, outpt, inpt);
367
0
    } else {
368
0
  stack_points[0] = *midpt;
369
0
  stack_points[1] = *inpt;
370
0
  stack_points[2] = *outpt;
371
0
  return stroker->add_triangle (stroker->closure, stack_points);
372
0
    }
373
0
}
374
375
static cairo_status_t
376
_cairo_stroker_join (cairo_stroker_t *stroker,
377
         const cairo_stroke_face_t *in,
378
         const cairo_stroke_face_t *out)
379
0
{
380
0
    int  clockwise = _cairo_stroker_join_is_clockwise (out, in);
381
0
    const cairo_point_t *inpt, *outpt;
382
0
    cairo_point_t points[4];
383
0
    cairo_status_t status;
384
385
0
    if (in->cw.x  == out->cw.x  && in->cw.y  == out->cw.y &&
386
0
  in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y)
387
0
    {
388
0
  return CAIRO_STATUS_SUCCESS;
389
0
    }
390
391
0
    if (clockwise) {
392
0
  if (stroker->add_external_edge != NULL) {
393
0
      status = stroker->add_external_edge (stroker->closure,
394
0
             &out->cw, &in->point);
395
0
      if (unlikely (status))
396
0
    return status;
397
398
0
      status = stroker->add_external_edge (stroker->closure,
399
0
             &in->point, &in->cw);
400
0
      if (unlikely (status))
401
0
    return status;
402
0
  }
403
404
0
  inpt = &in->ccw;
405
0
  outpt = &out->ccw;
406
0
    } else {
407
0
  if (stroker->add_external_edge != NULL) {
408
0
      status = stroker->add_external_edge (stroker->closure,
409
0
             &in->ccw, &in->point);
410
0
      if (unlikely (status))
411
0
    return status;
412
413
0
      status = stroker->add_external_edge (stroker->closure,
414
0
             &in->point, &out->ccw);
415
0
      if (unlikely (status))
416
0
    return status;
417
0
  }
418
419
0
  inpt = &in->cw;
420
0
  outpt = &out->cw;
421
0
    }
422
423
0
    switch (stroker->style.line_join) {
424
0
    case CAIRO_LINE_JOIN_ROUND:
425
  /* construct a fan around the common midpoint */
426
0
  return _tessellate_fan (stroker,
427
0
        &in->dev_vector,
428
0
        &out->dev_vector,
429
0
        &in->point, inpt, outpt,
430
0
        clockwise);
431
432
0
    case CAIRO_LINE_JOIN_MITER:
433
0
    default: {
434
  /* dot product of incoming slope vector with outgoing slope vector */
435
0
  double  in_dot_out = -in->usr_vector.x * out->usr_vector.x +
436
0
           -in->usr_vector.y * out->usr_vector.y;
437
0
  double  ml = stroker->style.miter_limit;
438
439
  /* Check the miter limit -- lines meeting at an acute angle
440
   * can generate long miters, the limit converts them to bevel
441
   *
442
   * Consider the miter join formed when two line segments
443
   * meet at an angle psi:
444
   *
445
   *     /.\
446
   *    /. .\
447
   *   /./ \.\
448
   *  /./psi\.\
449
   *
450
   * We can zoom in on the right half of that to see:
451
   *
452
   *      |\
453
   *      | \ psi/2
454
   *      |  \
455
   *      |   \
456
   *      |    \
457
   *      |     \
458
   *    miter    \
459
   *   length     \
460
   *      |        \
461
   *      |        .\
462
   *      |    .     \
463
   *      |.   line   \
464
   *       \    width  \
465
   *        \           \
466
   *
467
   *
468
   * The right triangle in that figure, (the line-width side is
469
   * shown faintly with three '.' characters), gives us the
470
   * following expression relating miter length, angle and line
471
   * width:
472
   *
473
   *  1 /sin (psi/2) = miter_length / line_width
474
   *
475
   * The right-hand side of this relationship is the same ratio
476
   * in which the miter limit (ml) is expressed. We want to know
477
   * when the miter length is within the miter limit. That is
478
   * when the following condition holds:
479
   *
480
   *  1/sin(psi/2) <= ml
481
   *  1 <= ml sin(psi/2)
482
   *  1 <= ml² sin²(psi/2)
483
   *  2 <= ml² 2 sin²(psi/2)
484
   *        2·sin²(psi/2) = 1-cos(psi)
485
   *  2 <= ml² (1-cos(psi))
486
   *
487
   *        in · out = |in| |out| cos (psi)
488
   *
489
   * in and out are both unit vectors, so:
490
   *
491
   *        in · out = cos (psi)
492
   *
493
   *  2 <= ml² (1 - in · out)
494
   *
495
   */
496
0
  if (2 <= ml * ml * (1 - in_dot_out)) {
497
0
      double    x1, y1, x2, y2;
498
0
      double    mx, my;
499
0
      double    dx1, dx2, dy1, dy2;
500
0
      double    ix, iy;
501
0
      double    fdx1, fdy1, fdx2, fdy2;
502
0
      double    mdx, mdy;
503
504
      /*
505
       * we've got the points already transformed to device
506
       * space, but need to do some computation with them and
507
       * also need to transform the slope from user space to
508
       * device space
509
       */
510
      /* outer point of incoming line face */
511
0
      x1 = _cairo_fixed_to_double (inpt->x);
512
0
      y1 = _cairo_fixed_to_double (inpt->y);
513
0
      dx1 = in->usr_vector.x;
514
0
      dy1 = in->usr_vector.y;
515
0
      cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
516
517
      /* outer point of outgoing line face */
518
0
      x2 = _cairo_fixed_to_double (outpt->x);
519
0
      y2 = _cairo_fixed_to_double (outpt->y);
520
0
      dx2 = out->usr_vector.x;
521
0
      dy2 = out->usr_vector.y;
522
0
      cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
523
524
      /*
525
       * Compute the location of the outer corner of the miter.
526
       * That's pretty easy -- just the intersection of the two
527
       * outer edges.  We've got slopes and points on each
528
       * of those edges.  Compute my directly, then compute
529
       * mx by using the edge with the larger dy; that avoids
530
       * dividing by values close to zero.
531
       */
532
0
      my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
533
0
      (dx1 * dy2 - dx2 * dy1));
534
0
      if (fabs (dy1) >= fabs (dy2))
535
0
    mx = (my - y1) * dx1 / dy1 + x1;
536
0
      else
537
0
    mx = (my - y2) * dx2 / dy2 + x2;
538
539
      /*
540
       * When the two outer edges are nearly parallel, slight
541
       * perturbations in the position of the outer points of the lines
542
       * caused by representing them in fixed point form can cause the
543
       * intersection point of the miter to move a large amount. If
544
       * that moves the miter intersection from between the two faces,
545
       * then draw a bevel instead.
546
       */
547
548
0
      ix = _cairo_fixed_to_double (in->point.x);
549
0
      iy = _cairo_fixed_to_double (in->point.y);
550
551
      /* slope of one face */
552
0
      fdx1 = x1 - ix; fdy1 = y1 - iy;
553
554
      /* slope of the other face */
555
0
      fdx2 = x2 - ix; fdy2 = y2 - iy;
556
557
      /* slope from the intersection to the miter point */
558
0
      mdx = mx - ix; mdy = my - iy;
559
560
      /*
561
       * Make sure the miter point line lies between the two
562
       * faces by comparing the slopes
563
       */
564
0
      if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
565
0
    _cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy))
566
0
      {
567
0
    if (stroker->add_external_edge != NULL) {
568
0
        points[0].x = _cairo_fixed_from_double (mx);
569
0
        points[0].y = _cairo_fixed_from_double (my);
570
571
0
        if (clockwise) {
572
0
      status = stroker->add_external_edge (stroker->closure,
573
0
                   inpt, &points[0]);
574
0
      if (unlikely (status))
575
0
          return status;
576
577
0
      status = stroker->add_external_edge (stroker->closure,
578
0
                   &points[0], outpt);
579
0
      if (unlikely (status))
580
0
          return status;
581
0
        } else {
582
0
      status = stroker->add_external_edge (stroker->closure,
583
0
                   outpt, &points[0]);
584
0
      if (unlikely (status))
585
0
          return status;
586
587
0
      status = stroker->add_external_edge (stroker->closure,
588
0
                   &points[0], inpt);
589
0
      if (unlikely (status))
590
0
          return status;
591
0
        }
592
593
0
        return CAIRO_STATUS_SUCCESS;
594
0
    } else {
595
0
        points[0] = in->point;
596
0
        points[1] = *inpt;
597
0
        points[2].x = _cairo_fixed_from_double (mx);
598
0
        points[2].y = _cairo_fixed_from_double (my);
599
0
        points[3] = *outpt;
600
601
0
        return stroker->add_convex_quad (stroker->closure, points);
602
0
    }
603
0
      }
604
0
  }
605
0
    }
606
607
    /* fall through ... */
608
609
0
    case CAIRO_LINE_JOIN_BEVEL:
610
0
  if (stroker->add_external_edge != NULL) {
611
0
      if (clockwise) {
612
0
    return stroker->add_external_edge (stroker->closure,
613
0
               inpt, outpt);
614
0
      } else {
615
0
    return stroker->add_external_edge (stroker->closure,
616
0
               outpt, inpt);
617
0
      }
618
0
  } else {
619
0
      points[0] = in->point;
620
0
      points[1] = *inpt;
621
0
      points[2] = *outpt;
622
623
0
      return stroker->add_triangle (stroker->closure, points);
624
0
  }
625
0
    }
626
0
}
627
628
static cairo_status_t
629
_cairo_stroker_add_cap (cairo_stroker_t *stroker,
630
      const cairo_stroke_face_t *f)
631
0
{
632
0
    switch (stroker->style.line_cap) {
633
0
    case CAIRO_LINE_CAP_ROUND: {
634
0
  cairo_slope_t slope;
635
636
0
  slope.dx = -f->dev_vector.dx;
637
0
  slope.dy = -f->dev_vector.dy;
638
639
0
  return _tessellate_fan (stroker,
640
0
        &f->dev_vector,
641
0
        &slope,
642
0
        &f->point, &f->cw, &f->ccw,
643
0
        FALSE);
644
645
0
    }
646
647
0
    case CAIRO_LINE_CAP_SQUARE: {
648
0
  double dx, dy;
649
0
  cairo_slope_t fvector;
650
0
  cairo_point_t quad[4];
651
652
0
  dx = f->usr_vector.x;
653
0
  dy = f->usr_vector.y;
654
0
  dx *= stroker->half_line_width;
655
0
  dy *= stroker->half_line_width;
656
0
  cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
657
0
  fvector.dx = _cairo_fixed_from_double (dx);
658
0
  fvector.dy = _cairo_fixed_from_double (dy);
659
660
0
  quad[0] = f->ccw;
661
0
  quad[1].x = f->ccw.x + fvector.dx;
662
0
  quad[1].y = f->ccw.y + fvector.dy;
663
0
  quad[2].x = f->cw.x + fvector.dx;
664
0
  quad[2].y = f->cw.y + fvector.dy;
665
0
  quad[3] = f->cw;
666
667
0
  if (stroker->add_external_edge != NULL) {
668
0
      cairo_status_t status;
669
670
0
      status = stroker->add_external_edge (stroker->closure,
671
0
             &quad[0], &quad[1]);
672
0
      if (unlikely (status))
673
0
    return status;
674
675
0
      status = stroker->add_external_edge (stroker->closure,
676
0
             &quad[1], &quad[2]);
677
0
      if (unlikely (status))
678
0
    return status;
679
680
0
      status = stroker->add_external_edge (stroker->closure,
681
0
             &quad[2], &quad[3]);
682
0
      if (unlikely (status))
683
0
    return status;
684
685
0
      return CAIRO_STATUS_SUCCESS;
686
0
  } else {
687
0
      return stroker->add_convex_quad (stroker->closure, quad);
688
0
  }
689
0
    }
690
691
0
    case CAIRO_LINE_CAP_BUTT:
692
0
    default:
693
0
  if (stroker->add_external_edge != NULL) {
694
0
      return stroker->add_external_edge (stroker->closure,
695
0
                 &f->ccw, &f->cw);
696
0
  } else {
697
0
      return CAIRO_STATUS_SUCCESS;
698
0
  }
699
0
    }
700
0
}
701
702
static cairo_status_t
703
_cairo_stroker_add_leading_cap (cairo_stroker_t     *stroker,
704
        const cairo_stroke_face_t *face)
705
0
{
706
0
    cairo_stroke_face_t reversed;
707
0
    cairo_point_t t;
708
709
0
    reversed = *face;
710
711
    /* The initial cap needs an outward facing vector. Reverse everything */
712
0
    reversed.usr_vector.x = -reversed.usr_vector.x;
713
0
    reversed.usr_vector.y = -reversed.usr_vector.y;
714
0
    reversed.dev_vector.dx = -reversed.dev_vector.dx;
715
0
    reversed.dev_vector.dy = -reversed.dev_vector.dy;
716
0
    t = reversed.cw;
717
0
    reversed.cw = reversed.ccw;
718
0
    reversed.ccw = t;
719
720
0
    return _cairo_stroker_add_cap (stroker, &reversed);
721
0
}
722
723
static cairo_status_t
724
_cairo_stroker_add_trailing_cap (cairo_stroker_t     *stroker,
725
         const cairo_stroke_face_t *face)
726
0
{
727
0
    return _cairo_stroker_add_cap (stroker, face);
728
0
}
729
730
static inline cairo_bool_t
731
_compute_normalized_device_slope (double *dx, double *dy,
732
          const cairo_matrix_t *ctm_inverse,
733
          double *mag_out)
734
0
{
735
0
    double dx0 = *dx, dy0 = *dy;
736
0
    double mag;
737
738
0
    cairo_matrix_transform_distance (ctm_inverse, &dx0, &dy0);
739
740
0
    if (dx0 == 0.0 && dy0 == 0.0) {
741
0
  if (mag_out)
742
0
      *mag_out = 0.0;
743
0
  return FALSE;
744
0
    }
745
746
0
    if (dx0 == 0.0) {
747
0
  *dx = 0.0;
748
0
  if (dy0 > 0.0) {
749
0
      mag = dy0;
750
0
      *dy = 1.0;
751
0
  } else {
752
0
      mag = -dy0;
753
0
      *dy = -1.0;
754
0
  }
755
0
    } else if (dy0 == 0.0) {
756
0
  *dy = 0.0;
757
0
  if (dx0 > 0.0) {
758
0
      mag = dx0;
759
0
      *dx = 1.0;
760
0
  } else {
761
0
      mag = -dx0;
762
0
      *dx = -1.0;
763
0
  }
764
0
    } else {
765
0
  mag = hypot (dx0, dy0);
766
0
  *dx = dx0 / mag;
767
0
  *dy = dy0 / mag;
768
0
    }
769
770
0
    if (mag_out)
771
0
  *mag_out = mag;
772
773
0
    return TRUE;
774
0
}
775
776
static void
777
_compute_face (const cairo_point_t *point,
778
         const cairo_slope_t *dev_slope,
779
         double slope_dx,
780
         double slope_dy,
781
         cairo_stroker_t *stroker,
782
         cairo_stroke_face_t *face)
783
0
{
784
0
    double face_dx, face_dy;
785
0
    cairo_point_t offset_ccw, offset_cw;
786
787
    /*
788
     * rotate to get a line_width/2 vector along the face, note that
789
     * the vector must be rotated the right direction in device space,
790
     * but by 90° in user space. So, the rotation depends on
791
     * whether the ctm reflects or not, and that can be determined
792
     * by looking at the determinant of the matrix.
793
     */
794
0
    if (stroker->ctm_det_positive)
795
0
    {
796
0
  face_dx = - slope_dy * stroker->half_line_width;
797
0
  face_dy = slope_dx * stroker->half_line_width;
798
0
    }
799
0
    else
800
0
    {
801
0
  face_dx = slope_dy * stroker->half_line_width;
802
0
  face_dy = - slope_dx * stroker->half_line_width;
803
0
    }
804
805
    /* back to device space */
806
0
    cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
807
808
0
    offset_ccw.x = _cairo_fixed_from_double (face_dx);
809
0
    offset_ccw.y = _cairo_fixed_from_double (face_dy);
810
0
    offset_cw.x = -offset_ccw.x;
811
0
    offset_cw.y = -offset_ccw.y;
812
813
0
    face->ccw = *point;
814
0
    _translate_point (&face->ccw, &offset_ccw);
815
816
0
    face->point = *point;
817
818
0
    face->cw = *point;
819
0
    _translate_point (&face->cw, &offset_cw);
820
821
0
    face->usr_vector.x = slope_dx;
822
0
    face->usr_vector.y = slope_dy;
823
824
0
    face->dev_vector = *dev_slope;
825
0
}
826
827
static cairo_status_t
828
_cairo_stroker_add_caps (cairo_stroker_t *stroker)
829
0
{
830
0
    cairo_status_t status;
831
832
    /* check for a degenerative sub_path */
833
0
    if (stroker->has_initial_sub_path
834
0
  && ! stroker->has_first_face
835
0
  && ! stroker->has_current_face
836
0
  && stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
837
0
    {
838
  /* pick an arbitrary slope to use */
839
0
  double dx = 1.0, dy = 0.0;
840
0
  cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
841
0
  cairo_stroke_face_t face;
842
843
0
  _compute_normalized_device_slope (&dx, &dy,
844
0
            stroker->ctm_inverse, NULL);
845
846
  /* arbitrarily choose first_point
847
   * first_point and current_point should be the same */
848
0
  _compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face);
849
850
0
  status = _cairo_stroker_add_leading_cap (stroker, &face);
851
0
  if (unlikely (status))
852
0
      return status;
853
854
0
  status = _cairo_stroker_add_trailing_cap (stroker, &face);
855
0
  if (unlikely (status))
856
0
      return status;
857
0
    }
858
859
0
    if (stroker->has_first_face) {
860
0
  status = _cairo_stroker_add_leading_cap (stroker,
861
0
             &stroker->first_face);
862
0
  if (unlikely (status))
863
0
      return status;
864
0
    }
865
866
0
    if (stroker->has_current_face) {
867
0
  status = _cairo_stroker_add_trailing_cap (stroker,
868
0
              &stroker->current_face);
869
0
  if (unlikely (status))
870
0
      return status;
871
0
    }
872
873
0
    return CAIRO_STATUS_SUCCESS;
874
0
}
875
876
static cairo_status_t
877
_cairo_stroker_add_sub_edge (cairo_stroker_t *stroker,
878
           const cairo_point_t *p1,
879
           const cairo_point_t *p2,
880
           cairo_slope_t *dev_slope,
881
           double slope_dx, double slope_dy,
882
           cairo_stroke_face_t *start,
883
           cairo_stroke_face_t *end)
884
0
{
885
0
    _compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start);
886
0
    *end = *start;
887
888
0
    if (p1->x == p2->x && p1->y == p2->y)
889
0
  return CAIRO_STATUS_SUCCESS;
890
891
0
    end->point = *p2;
892
0
    end->ccw.x += p2->x - p1->x;
893
0
    end->ccw.y += p2->y - p1->y;
894
0
    end->cw.x += p2->x - p1->x;
895
0
    end->cw.y += p2->y - p1->y;
896
897
0
    if (stroker->add_external_edge != NULL) {
898
0
  cairo_status_t status;
899
900
0
  status = stroker->add_external_edge (stroker->closure,
901
0
               &end->cw, &start->cw);
902
0
  if (unlikely (status))
903
0
      return status;
904
905
0
  status = stroker->add_external_edge (stroker->closure,
906
0
               &start->ccw, &end->ccw);
907
0
  if (unlikely (status))
908
0
      return status;
909
910
0
  return CAIRO_STATUS_SUCCESS;
911
0
    } else {
912
0
  cairo_point_t quad[4];
913
914
0
  quad[0] = start->cw;
915
0
  quad[1] = end->cw;
916
0
  quad[2] = end->ccw;
917
0
  quad[3] = start->ccw;
918
919
0
  return stroker->add_convex_quad (stroker->closure, quad);
920
0
    }
921
0
}
922
923
static cairo_status_t
924
_cairo_stroker_move_to (void *closure,
925
      const cairo_point_t *point)
926
0
{
927
0
    cairo_stroker_t *stroker = closure;
928
0
    cairo_status_t status;
929
930
    /* reset the dash pattern for new sub paths */
931
0
    _cairo_stroker_dash_start (&stroker->dash);
932
933
    /* Cap the start and end of the previous sub path as needed */
934
0
    status = _cairo_stroker_add_caps (stroker);
935
0
    if (unlikely (status))
936
0
  return status;
937
938
0
    stroker->first_point = *point;
939
0
    stroker->current_point = *point;
940
941
0
    stroker->has_first_face = FALSE;
942
0
    stroker->has_current_face = FALSE;
943
0
    stroker->has_initial_sub_path = FALSE;
944
945
0
    return CAIRO_STATUS_SUCCESS;
946
0
}
947
948
static cairo_status_t
949
_cairo_stroker_line_to (void *closure,
950
      const cairo_point_t *point)
951
0
{
952
0
    cairo_stroker_t *stroker = closure;
953
0
    cairo_stroke_face_t start, end;
954
0
    cairo_point_t *p1 = &stroker->current_point;
955
0
    cairo_slope_t dev_slope;
956
0
    double slope_dx, slope_dy;
957
0
    cairo_status_t status;
958
959
0
    stroker->has_initial_sub_path = TRUE;
960
961
0
    if (p1->x == point->x && p1->y == point->y)
962
0
  return CAIRO_STATUS_SUCCESS;
963
964
0
    _cairo_slope_init (&dev_slope, p1, point);
965
0
    slope_dx = _cairo_fixed_to_double (point->x - p1->x);
966
0
    slope_dy = _cairo_fixed_to_double (point->y - p1->y);
967
0
    _compute_normalized_device_slope (&slope_dx, &slope_dy,
968
0
              stroker->ctm_inverse, NULL);
969
970
0
    status = _cairo_stroker_add_sub_edge (stroker,
971
0
            p1, point,
972
0
            &dev_slope,
973
0
            slope_dx, slope_dy,
974
0
            &start, &end);
975
0
    if (unlikely (status))
976
0
  return status;
977
978
0
    if (stroker->has_current_face) {
979
  /* Join with final face from previous segment */
980
0
  status = _cairo_stroker_join (stroker,
981
0
              &stroker->current_face,
982
0
              &start);
983
0
  if (unlikely (status))
984
0
      return status;
985
0
    } else if (! stroker->has_first_face) {
986
  /* Save sub path's first face in case needed for closing join */
987
0
  stroker->first_face = start;
988
0
  stroker->has_first_face = TRUE;
989
0
    }
990
0
    stroker->current_face = end;
991
0
    stroker->has_current_face = TRUE;
992
993
0
    stroker->current_point = *point;
994
995
0
    return CAIRO_STATUS_SUCCESS;
996
0
}
997
998
static cairo_status_t
999
_cairo_stroker_add_point_line_to (void *closure,
1000
          const cairo_point_t *point,
1001
          const cairo_slope_t *tangent)
1002
0
{
1003
0
    return _cairo_stroker_line_to (closure, point);
1004
0
};
1005
1006
static cairo_status_t
1007
_cairo_stroker_spline_to (void *closure,
1008
        const cairo_point_t *point,
1009
        const cairo_slope_t *tangent)
1010
0
{
1011
0
    cairo_stroker_t *stroker = closure;
1012
0
    cairo_stroke_face_t new_face;
1013
0
    double slope_dx, slope_dy;
1014
0
    cairo_point_t points[3];
1015
0
    cairo_point_t intersect_point;
1016
1017
0
    stroker->has_initial_sub_path = TRUE;
1018
1019
0
    if (stroker->current_point.x == point->x &&
1020
0
  stroker->current_point.y == point->y)
1021
0
  return CAIRO_STATUS_SUCCESS;
1022
1023
0
    slope_dx = _cairo_fixed_to_double (tangent->dx);
1024
0
    slope_dy = _cairo_fixed_to_double (tangent->dy);
1025
1026
0
    if (! _compute_normalized_device_slope (&slope_dx, &slope_dy,
1027
0
              stroker->ctm_inverse, NULL))
1028
0
  return CAIRO_STATUS_SUCCESS;
1029
1030
0
    _compute_face (point, tangent,
1031
0
       slope_dx, slope_dy,
1032
0
       stroker, &new_face);
1033
1034
0
    assert (stroker->has_current_face);
1035
1036
0
    if ((new_face.dev_slope.x * stroker->current_face.dev_slope.x +
1037
0
         new_face.dev_slope.y * stroker->current_face.dev_slope.y) < stroker->spline_cusp_tolerance) {
1038
1039
0
  const cairo_point_t *inpt, *outpt;
1040
0
  int clockwise = _cairo_stroker_join_is_clockwise (&new_face,
1041
0
                &stroker->current_face);
1042
1043
0
  if (clockwise) {
1044
0
      inpt = &stroker->current_face.cw;
1045
0
      outpt = &new_face.cw;
1046
0
  } else {
1047
0
      inpt = &stroker->current_face.ccw;
1048
0
      outpt = &new_face.ccw;
1049
0
  }
1050
1051
0
  _tessellate_fan (stroker,
1052
0
       &stroker->current_face.dev_vector,
1053
0
       &new_face.dev_vector,
1054
0
       &stroker->current_face.point,
1055
0
       inpt, outpt,
1056
0
       clockwise);
1057
0
    }
1058
1059
0
    if (_slow_segment_intersection (&stroker->current_face.cw,
1060
0
            &stroker->current_face.ccw,
1061
0
            &new_face.cw,
1062
0
            &new_face.ccw,
1063
0
            &intersect_point)) {
1064
0
  points[0] = stroker->current_face.ccw;
1065
0
  points[1] = new_face.ccw;
1066
0
  points[2] = intersect_point;
1067
0
  stroker->add_triangle (stroker->closure, points);
1068
1069
0
  points[0] = stroker->current_face.cw;
1070
0
  points[1] = new_face.cw;
1071
0
  stroker->add_triangle (stroker->closure, points);
1072
0
    } else {
1073
0
  points[0] = stroker->current_face.ccw;
1074
0
  points[1] = stroker->current_face.cw;
1075
0
  points[2] = new_face.cw;
1076
0
  stroker->add_triangle (stroker->closure, points);
1077
1078
0
  points[0] = stroker->current_face.ccw;
1079
0
  points[1] = new_face.cw;
1080
0
  points[2] = new_face.ccw;
1081
0
  stroker->add_triangle (stroker->closure, points);
1082
0
    }
1083
1084
0
    stroker->current_face = new_face;
1085
0
    stroker->has_current_face = TRUE;
1086
0
    stroker->current_point = *point;
1087
1088
0
    return CAIRO_STATUS_SUCCESS;
1089
0
}
1090
1091
/*
1092
 * Dashed lines.  Cap each dash end, join around turns when on
1093
 */
1094
static cairo_status_t
1095
_cairo_stroker_line_to_dashed (void *closure,
1096
             const cairo_point_t *p2)
1097
0
{
1098
0
    cairo_stroker_t *stroker = closure;
1099
0
    double mag, remain, step_length = 0;
1100
0
    double slope_dx, slope_dy;
1101
0
    double dx2, dy2;
1102
0
    cairo_stroke_face_t sub_start, sub_end;
1103
0
    cairo_point_t *p1 = &stroker->current_point;
1104
0
    cairo_slope_t dev_slope;
1105
0
    cairo_line_t segment;
1106
0
    cairo_bool_t fully_in_bounds;
1107
0
    cairo_status_t status;
1108
1109
0
    stroker->has_initial_sub_path = stroker->dash.dash_starts_on;
1110
1111
0
    if (p1->x == p2->x && p1->y == p2->y)
1112
0
  return CAIRO_STATUS_SUCCESS;
1113
1114
0
    fully_in_bounds = TRUE;
1115
0
    if (stroker->has_bounds &&
1116
0
  (! _cairo_box_contains_point (&stroker->bounds, p1) ||
1117
0
   ! _cairo_box_contains_point (&stroker->bounds, p2)))
1118
0
    {
1119
0
  fully_in_bounds = FALSE;
1120
0
    }
1121
1122
0
    _cairo_slope_init (&dev_slope, p1, p2);
1123
1124
0
    slope_dx = _cairo_fixed_to_double (p2->x - p1->x);
1125
0
    slope_dy = _cairo_fixed_to_double (p2->y - p1->y);
1126
1127
0
    if (! _compute_normalized_device_slope (&slope_dx, &slope_dy,
1128
0
              stroker->ctm_inverse, &mag))
1129
0
    {
1130
0
  return CAIRO_STATUS_SUCCESS;
1131
0
    }
1132
1133
0
    remain = mag;
1134
0
    segment.p1 = *p1;
1135
0
    while (remain) {
1136
0
  step_length = MIN (stroker->dash.dash_remain, remain);
1137
0
  remain -= step_length;
1138
0
  dx2 = slope_dx * (mag - remain);
1139
0
  dy2 = slope_dy * (mag - remain);
1140
0
  cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
1141
0
  segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x;
1142
0
  segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y;
1143
1144
0
  if (stroker->dash.dash_on &&
1145
0
      (fully_in_bounds ||
1146
0
       (! stroker->has_first_face && stroker->dash.dash_starts_on) ||
1147
0
       _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
1148
0
  {
1149
0
      status = _cairo_stroker_add_sub_edge (stroker,
1150
0
              &segment.p1, &segment.p2,
1151
0
              &dev_slope,
1152
0
              slope_dx, slope_dy,
1153
0
              &sub_start, &sub_end);
1154
0
      if (unlikely (status))
1155
0
    return status;
1156
1157
0
      if (stroker->has_current_face)
1158
0
      {
1159
    /* Join with final face from previous segment */
1160
0
    status = _cairo_stroker_join (stroker,
1161
0
                &stroker->current_face,
1162
0
                &sub_start);
1163
0
    if (unlikely (status))
1164
0
        return status;
1165
1166
0
    stroker->has_current_face = FALSE;
1167
0
      }
1168
0
      else if (! stroker->has_first_face &&
1169
0
           stroker->dash.dash_starts_on)
1170
0
      {
1171
    /* Save sub path's first face in case needed for closing join */
1172
0
    stroker->first_face = sub_start;
1173
0
    stroker->has_first_face = TRUE;
1174
0
      }
1175
0
      else
1176
0
      {
1177
    /* Cap dash start if not connecting to a previous segment */
1178
0
    status = _cairo_stroker_add_leading_cap (stroker, &sub_start);
1179
0
    if (unlikely (status))
1180
0
        return status;
1181
0
      }
1182
1183
0
      if (remain) {
1184
    /* Cap dash end if not at end of segment */
1185
0
    status = _cairo_stroker_add_trailing_cap (stroker, &sub_end);
1186
0
    if (unlikely (status))
1187
0
        return status;
1188
0
      } else {
1189
0
    stroker->current_face = sub_end;
1190
0
    stroker->has_current_face = TRUE;
1191
0
      }
1192
0
  } else {
1193
0
      if (stroker->has_current_face) {
1194
    /* Cap final face from previous segment */
1195
0
    status = _cairo_stroker_add_trailing_cap (stroker,
1196
0
                &stroker->current_face);
1197
0
    if (unlikely (status))
1198
0
        return status;
1199
1200
0
    stroker->has_current_face = FALSE;
1201
0
      }
1202
0
  }
1203
1204
0
  _cairo_stroker_dash_step (&stroker->dash, step_length);
1205
0
  segment.p1 = segment.p2;
1206
0
    }
1207
1208
0
    if (stroker->dash.dash_on && ! stroker->has_current_face) {
1209
  /* This segment ends on a transition to dash_on, compute a new face
1210
   * and add cap for the beginning of the next dash_on step.
1211
   *
1212
   * Note: this will create a degenerate cap if this is not the last line
1213
   * in the path. Whether this behaviour is desirable or not is debatable.
1214
   * On one side these degenerate caps can not be reproduced with regular
1215
   * path stroking.
1216
   * On the other hand, Acroread 7 also produces the degenerate caps.
1217
   */
1218
0
  _compute_face (p2, &dev_slope,
1219
0
           slope_dx, slope_dy,
1220
0
           stroker,
1221
0
           &stroker->current_face);
1222
1223
0
  status = _cairo_stroker_add_leading_cap (stroker,
1224
0
             &stroker->current_face);
1225
0
  if (unlikely (status))
1226
0
      return status;
1227
1228
0
  stroker->has_current_face = TRUE;
1229
0
    }
1230
1231
0
    stroker->current_point = *p2;
1232
1233
0
    return CAIRO_STATUS_SUCCESS;
1234
0
}
1235
1236
static cairo_status_t
1237
_cairo_stroker_add_point_line_to_dashed (void *closure,
1238
           const cairo_point_t *point,
1239
           const cairo_slope_t *tangent)
1240
0
{
1241
0
    return _cairo_stroker_line_to_dashed (closure, point);
1242
0
};
1243
1244
static cairo_status_t
1245
_cairo_stroker_curve_to (void *closure,
1246
       const cairo_point_t *b,
1247
       const cairo_point_t *c,
1248
       const cairo_point_t *d)
1249
0
{
1250
0
    cairo_stroker_t *stroker = closure;
1251
0
    cairo_spline_t spline;
1252
0
    cairo_line_join_t line_join_save;
1253
0
    cairo_stroke_face_t face;
1254
0
    double slope_dx, slope_dy;
1255
0
    cairo_spline_add_point_func_t line_to;
1256
0
    cairo_spline_add_point_func_t spline_to;
1257
0
    cairo_status_t status = CAIRO_STATUS_SUCCESS;
1258
1259
0
    line_to = stroker->dash.dashed ?
1260
0
  _cairo_stroker_add_point_line_to_dashed :
1261
0
  _cairo_stroker_add_point_line_to;
1262
1263
    /* spline_to is only capable of rendering non-degenerate splines. */
1264
0
    spline_to = stroker->dash.dashed ?
1265
0
  _cairo_stroker_add_point_line_to_dashed :
1266
0
  _cairo_stroker_spline_to;
1267
1268
0
    if (! _cairo_spline_init (&spline,
1269
0
            spline_to,
1270
0
            stroker,
1271
0
            &stroker->current_point, b, c, d))
1272
0
    {
1273
0
  cairo_slope_t fallback_slope;
1274
0
  _cairo_slope_init (&fallback_slope, &stroker->current_point, d);
1275
0
  return line_to (closure, d, &fallback_slope);
1276
0
    }
1277
1278
    /* If the line width is so small that the pen is reduced to a
1279
       single point, then we have nothing to do. */
1280
0
    if (stroker->pen.num_vertices <= 1)
1281
0
  return CAIRO_STATUS_SUCCESS;
1282
1283
    /* Compute the initial face */
1284
0
    if (! stroker->dash.dashed || stroker->dash.dash_on) {
1285
0
  slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx);
1286
0
  slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy);
1287
0
  if (_compute_normalized_device_slope (&slope_dx, &slope_dy,
1288
0
                stroker->ctm_inverse, NULL))
1289
0
  {
1290
0
      _compute_face (&stroker->current_point,
1291
0
         &spline.initial_slope,
1292
0
         slope_dx, slope_dy,
1293
0
         stroker, &face);
1294
0
  }
1295
0
  if (stroker->has_current_face) {
1296
0
      status = _cairo_stroker_join (stroker,
1297
0
            &stroker->current_face, &face);
1298
0
      if (unlikely (status))
1299
0
    return status;
1300
0
  } else if (! stroker->has_first_face) {
1301
0
      stroker->first_face = face;
1302
0
      stroker->has_first_face = TRUE;
1303
0
  }
1304
1305
0
  stroker->current_face = face;
1306
0
  stroker->has_current_face = TRUE;
1307
0
    }
1308
1309
    /* Temporarily modify the stroker to use round joins to guarantee
1310
     * smooth stroked curves. */
1311
0
    line_join_save = stroker->style.line_join;
1312
0
    stroker->style.line_join = CAIRO_LINE_JOIN_ROUND;
1313
1314
0
    status = _cairo_spline_decompose (&spline, stroker->tolerance);
1315
0
    if (unlikely (status))
1316
0
  return status;
1317
1318
    /* And join the final face */
1319
0
    if (! stroker->dash.dashed || stroker->dash.dash_on) {
1320
0
  slope_dx = _cairo_fixed_to_double (spline.final_slope.dx);
1321
0
  slope_dy = _cairo_fixed_to_double (spline.final_slope.dy);
1322
0
  if (_compute_normalized_device_slope (&slope_dx, &slope_dy,
1323
0
                stroker->ctm_inverse, NULL))
1324
0
  {
1325
0
      _compute_face (&stroker->current_point,
1326
0
         &spline.final_slope,
1327
0
         slope_dx, slope_dy,
1328
0
         stroker, &face);
1329
0
  }
1330
1331
0
  status = _cairo_stroker_join (stroker, &stroker->current_face, &face);
1332
0
  if (unlikely (status))
1333
0
      return status;
1334
1335
0
  stroker->current_face = face;
1336
0
    }
1337
1338
0
    stroker->style.line_join = line_join_save;
1339
1340
0
    return CAIRO_STATUS_SUCCESS;
1341
0
}
1342
1343
static cairo_status_t
1344
_cairo_stroker_close_path (void *closure)
1345
0
{
1346
0
    cairo_stroker_t *stroker = closure;
1347
0
    cairo_status_t status;
1348
1349
0
    if (stroker->dash.dashed)
1350
0
  status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point);
1351
0
    else
1352
0
  status = _cairo_stroker_line_to (stroker, &stroker->first_point);
1353
0
    if (unlikely (status))
1354
0
  return status;
1355
1356
0
    if (stroker->has_first_face && stroker->has_current_face) {
1357
  /* Join first and final faces of sub path */
1358
0
  status = _cairo_stroker_join (stroker,
1359
0
              &stroker->current_face,
1360
0
              &stroker->first_face);
1361
0
  if (unlikely (status))
1362
0
      return status;
1363
0
    } else {
1364
  /* Cap the start and end of the sub path as needed */
1365
0
  status = _cairo_stroker_add_caps (stroker);
1366
0
  if (unlikely (status))
1367
0
      return status;
1368
0
    }
1369
1370
0
    stroker->has_initial_sub_path = FALSE;
1371
0
    stroker->has_first_face = FALSE;
1372
0
    stroker->has_current_face = FALSE;
1373
1374
0
    return CAIRO_STATUS_SUCCESS;
1375
0
}
1376
1377
cairo_status_t
1378
_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t  *path,
1379
            const cairo_stroke_style_t  *stroke_style,
1380
            const cairo_matrix_t  *ctm,
1381
            const cairo_matrix_t  *ctm_inverse,
1382
            double     tolerance,
1383
            cairo_status_t (*add_triangle) (void *closure,
1384
                    const cairo_point_t triangle[3]),
1385
            cairo_status_t (*add_triangle_fan) (void *closure,
1386
                  const cairo_point_t *midpt,
1387
                  const cairo_point_t *points,
1388
                  int npoints),
1389
            cairo_status_t (*add_convex_quad) (void *closure,
1390
                       const cairo_point_t quad[4]),
1391
            void *closure)
1392
0
{
1393
0
    cairo_stroker_t stroker;
1394
0
    cairo_status_t status;
1395
1396
0
    status = _cairo_stroker_init (&stroker, path, stroke_style,
1397
0
                ctm, ctm_inverse, tolerance,
1398
0
          NULL, 0);
1399
0
    if (unlikely (status))
1400
0
  return status;
1401
1402
0
    stroker.add_triangle = add_triangle;
1403
0
    stroker.add_triangle_fan = add_triangle_fan;
1404
0
    stroker.add_convex_quad = add_convex_quad;
1405
0
    stroker.closure = closure;
1406
1407
0
    status = _cairo_path_fixed_interpret (path,
1408
0
            _cairo_stroker_move_to,
1409
0
            stroker.dash.dashed ?
1410
0
            _cairo_stroker_line_to_dashed :
1411
0
            _cairo_stroker_line_to,
1412
0
            _cairo_stroker_curve_to,
1413
0
            _cairo_stroker_close_path,
1414
0
            &stroker);
1415
1416
0
    if (unlikely (status))
1417
0
  goto BAIL;
1418
1419
    /* Cap the start and end of the final sub path as needed */
1420
0
    status = _cairo_stroker_add_caps (&stroker);
1421
1422
0
BAIL:
1423
0
    _cairo_stroker_fini (&stroker);
1424
1425
0
    return status;
1426
0
}
1427
1428
cairo_status_t
1429
_cairo_path_fixed_stroke_dashed_to_polygon (const cairo_path_fixed_t  *path,
1430
              const cairo_stroke_style_t  *stroke_style,
1431
              const cairo_matrix_t  *ctm,
1432
              const cairo_matrix_t  *ctm_inverse,
1433
              double     tolerance,
1434
              cairo_polygon_t *polygon)
1435
0
{
1436
0
    cairo_stroker_t stroker;
1437
0
    cairo_status_t status;
1438
1439
0
    status = _cairo_stroker_init (&stroker, path, stroke_style,
1440
0
                ctm, ctm_inverse, tolerance,
1441
0
          polygon->limits, polygon->num_limits);
1442
0
    if (unlikely (status))
1443
0
  return status;
1444
1445
0
    stroker.add_external_edge = _cairo_polygon_add_external_edge,
1446
0
    stroker.closure = polygon;
1447
1448
0
    status = _cairo_path_fixed_interpret (path,
1449
0
            _cairo_stroker_move_to,
1450
0
            stroker.dash.dashed ?
1451
0
            _cairo_stroker_line_to_dashed :
1452
0
            _cairo_stroker_line_to,
1453
0
            _cairo_stroker_curve_to,
1454
0
            _cairo_stroker_close_path,
1455
0
            &stroker);
1456
1457
0
    if (unlikely (status))
1458
0
  goto BAIL;
1459
1460
    /* Cap the start and end of the final sub path as needed */
1461
0
    status = _cairo_stroker_add_caps (&stroker);
1462
1463
0
BAIL:
1464
0
    _cairo_stroker_fini (&stroker);
1465
1466
0
    return status;
1467
0
}
1468
1469
cairo_int_status_t
1470
_cairo_path_fixed_stroke_polygon_to_traps (const cairo_path_fixed_t *path,
1471
                                           const cairo_stroke_style_t *stroke_style,
1472
                                           const cairo_matrix_t *ctm,
1473
                                           const cairo_matrix_t *ctm_inverse,
1474
                                           double    tolerance,
1475
                                           cairo_traps_t  *traps)
1476
0
{
1477
0
    cairo_int_status_t status;
1478
0
    cairo_polygon_t polygon;
1479
1480
0
    _cairo_polygon_init (&polygon, traps->limits, traps->num_limits);
1481
0
    status = _cairo_path_fixed_stroke_to_polygon (path,
1482
0
              stroke_style,
1483
0
              ctm,
1484
0
              ctm_inverse,
1485
0
              tolerance,
1486
0
              &polygon);
1487
0
    if (unlikely (status))
1488
0
  goto BAIL;
1489
1490
0
    status = _cairo_polygon_status (&polygon);
1491
0
    if (unlikely (status))
1492
0
  goto BAIL;
1493
1494
0
    status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon,
1495
0
              CAIRO_FILL_RULE_WINDING);
1496
1497
0
BAIL:
1498
0
    _cairo_polygon_fini (&polygon);
1499
1500
0
    return status;
1501
0
}