Coverage Report

Created: 2025-07-23 08:13

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