Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/workdir/UnpackedTarball/cairo/src/cairo-path-stroke-traps.c
Line
Count
Source
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
 * Copyright © 2013 Intel Corporation
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it either under the terms of the GNU Lesser General Public
9
 * License version 2.1 as published by the Free Software Foundation
10
 * (the "LGPL") or, at your option, under the terms of the Mozilla
11
 * Public License Version 1.1 (the "MPL"). If you do not alter this
12
 * notice, a recipient may use your version of this file under either
13
 * the MPL or the LGPL.
14
 *
15
 * You should have received a copy of the LGPL along with this library
16
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
 * You should have received a copy of the MPL along with this library
19
 * in the file COPYING-MPL-1.1
20
 *
21
 * The contents of this file are subject to the Mozilla Public License
22
 * Version 1.1 (the "License"); you may not use this file except in
23
 * compliance with the License. You may obtain a copy of the License at
24
 * http://www.mozilla.org/MPL/
25
 *
26
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
27
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
28
 * the specific language governing rights and limitations.
29
 *
30
 * The Original Code is the cairo graphics library.
31
 *
32
 * The Initial Developer of the Original Code is University of Southern
33
 * California.
34
 *
35
 * Contributor(s):
36
 *  Carl D. Worth <cworth@cworth.org>
37
 *  Chris Wilson <chris@chris-wilson.co.uk>
38
 */
39
40
#include "cairoint.h"
41
42
#include "cairo-box-inline.h"
43
#include "cairo-path-fixed-private.h"
44
#include "cairo-slope-private.h"
45
#include "cairo-stroke-dash-private.h"
46
#include "cairo-traps-private.h"
47
48
#include <float.h>
49
50
struct stroker {
51
    const cairo_stroke_style_t  *style;
52
53
    const cairo_matrix_t *ctm;
54
    const cairo_matrix_t *ctm_inverse;
55
    double spline_cusp_tolerance;
56
    double half_line_width;
57
    double tolerance;
58
    double ctm_determinant;
59
    cairo_bool_t ctm_det_positive;
60
    cairo_line_join_t line_join;
61
62
    cairo_traps_t *traps;
63
64
    cairo_pen_t pen;
65
66
    cairo_point_t first_point;
67
68
    cairo_bool_t has_initial_sub_path;
69
70
    cairo_bool_t has_current_face;
71
    cairo_stroke_face_t current_face;
72
73
    cairo_bool_t has_first_face;
74
    cairo_stroke_face_t first_face;
75
76
    cairo_stroker_dash_t dash;
77
78
    cairo_bool_t has_bounds;
79
    cairo_box_t tight_bounds;
80
    cairo_box_t line_bounds;
81
    cairo_box_t join_bounds;
82
};
83
84
static cairo_status_t
85
stroker_init (struct stroker    *stroker,
86
        const cairo_path_fixed_t  *path,
87
        const cairo_stroke_style_t  *style,
88
        const cairo_matrix_t  *ctm,
89
        const cairo_matrix_t  *ctm_inverse,
90
        double       tolerance,
91
        cairo_traps_t   *traps)
92
0
{
93
0
    cairo_status_t status;
94
95
0
    stroker->style = style;
96
0
    stroker->ctm = ctm;
97
0
    stroker->ctm_inverse = NULL;
98
0
    if (! _cairo_matrix_is_identity (ctm_inverse))
99
0
  stroker->ctm_inverse = ctm_inverse;
100
0
    stroker->line_join = style->line_join;
101
0
    stroker->half_line_width = style->line_width / 2.0;
102
0
    stroker->tolerance = tolerance;
103
0
    stroker->traps = traps;
104
105
    /* If `CAIRO_LINE_JOIN_ROUND` is selected and a joint's `arc height`
106
     * is greater than `tolerance` then two segments are joined with
107
     * round-join, otherwise bevel-join is used.
108
     *
109
     * `Arc height` is the difference of the "half of a line width" and
110
     * the "half of a line width" times `cos(half the angle between segment vectors)`.
111
     *
112
     * See detailed description in the `_cairo_path_fixed_stroke_to_polygon()`
113
     * function in the `cairo-path-stroke-polygon.c` file or follow the
114
     * https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/372#note_1698225
115
     * link to see the detailed description with an illustration.
116
     */
117
0
    double scaled_hlw = hypot(stroker->half_line_width * ctm->xx,
118
0
            stroker->half_line_width * ctm->yx);
119
120
0
    if (scaled_hlw <= tolerance) {
121
0
  stroker->spline_cusp_tolerance = -1.0;
122
0
    } else {
123
0
  stroker->spline_cusp_tolerance = 1 - tolerance / scaled_hlw;
124
0
  stroker->spline_cusp_tolerance *= stroker->spline_cusp_tolerance;
125
0
  stroker->spline_cusp_tolerance *= 2;
126
0
  stroker->spline_cusp_tolerance -= 1;
127
0
    }
128
129
0
    stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm);
130
0
    stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0;
131
132
0
    status = _cairo_pen_init (&stroker->pen,
133
0
                  stroker->half_line_width,
134
0
            tolerance, ctm);
135
0
    if (unlikely (status))
136
0
  return status;
137
138
0
    stroker->has_current_face = FALSE;
139
0
    stroker->has_first_face = FALSE;
140
0
    stroker->has_initial_sub_path = FALSE;
141
142
0
    _cairo_stroker_dash_init (&stroker->dash, style);
143
144
0
    stroker->has_bounds = traps->num_limits;
145
0
    if (stroker->has_bounds) {
146
  /* Extend the bounds in each direction to account for the maximum area
147
   * we might generate trapezoids, to capture line segments that are outside
148
   * of the bounds but which might generate rendering that's within bounds.
149
   */
150
0
  double dx, dy;
151
0
  cairo_fixed_t fdx, fdy;
152
153
0
  stroker->tight_bounds = traps->bounds;
154
155
0
  _cairo_stroke_style_max_distance_from_path (stroker->style, path,
156
0
                stroker->ctm, &dx, &dy);
157
158
0
  _cairo_stroke_style_max_line_distance_from_path (stroker->style, path,
159
0
               stroker->ctm, &dx, &dy);
160
161
0
  fdx = _cairo_fixed_from_double (dx);
162
0
  fdy = _cairo_fixed_from_double (dy);
163
164
0
  stroker->line_bounds = stroker->tight_bounds;
165
0
  stroker->line_bounds.p1.x -= fdx;
166
0
  stroker->line_bounds.p2.x += fdx;
167
0
  stroker->line_bounds.p1.y -= fdy;
168
0
  stroker->line_bounds.p2.y += fdy;
169
170
0
  _cairo_stroke_style_max_join_distance_from_path (stroker->style, path,
171
0
               stroker->ctm, &dx, &dy);
172
173
0
  fdx = _cairo_fixed_from_double (dx);
174
0
  fdy = _cairo_fixed_from_double (dy);
175
176
0
  stroker->join_bounds = stroker->tight_bounds;
177
0
  stroker->join_bounds.p1.x -= fdx;
178
0
  stroker->join_bounds.p2.x += fdx;
179
0
  stroker->join_bounds.p1.y -= fdy;
180
0
  stroker->join_bounds.p2.y += fdy;
181
0
    }
182
183
0
    return CAIRO_STATUS_SUCCESS;
184
0
}
185
186
static void
187
stroker_fini (struct stroker *stroker)
188
0
{
189
0
    _cairo_pen_fini (&stroker->pen);
190
0
}
191
192
static void
193
translate_point (cairo_point_t *point, cairo_point_t *offset)
194
0
{
195
0
    point->x += offset->x;
196
0
    point->y += offset->y;
197
0
}
198
199
static int
200
join_is_clockwise (const cairo_stroke_face_t *in,
201
       const cairo_stroke_face_t *out)
202
0
{
203
0
    return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0;
204
0
}
205
206
static int
207
slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
208
0
{
209
0
    double c = dx1 * dy2 - dx2 * dy1;
210
0
    if (c > 0) return 1;
211
0
    if (c < 0) return -1;
212
0
    return 0;
213
0
}
214
215
static cairo_bool_t
216
stroker_intersects_join (const struct stroker *stroker,
217
       const cairo_point_t *in,
218
       const cairo_point_t *out)
219
0
{
220
0
    cairo_line_t segment;
221
222
0
    if (! stroker->has_bounds)
223
0
  return TRUE;
224
225
0
    segment.p1 = *in;
226
0
    segment.p2 = *out;
227
0
    return _cairo_box_intersects_line_segment (&stroker->join_bounds, &segment);
228
0
}
229
230
static void
231
join (struct stroker *stroker,
232
      cairo_stroke_face_t *in,
233
      cairo_stroke_face_t *out)
234
0
{
235
0
    int clockwise = join_is_clockwise (out, in);
236
0
    cairo_point_t *inpt, *outpt;
237
238
0
    if (in->cw.x == out->cw.x &&
239
0
  in->cw.y == out->cw.y &&
240
0
  in->ccw.x == out->ccw.x &&
241
0
  in->ccw.y == out->ccw.y)
242
0
    {
243
0
  return;
244
0
    }
245
246
0
    if (clockwise) {
247
0
  inpt = &in->ccw;
248
0
  outpt = &out->ccw;
249
0
    } else {
250
0
  inpt = &in->cw;
251
0
  outpt = &out->cw;
252
0
    }
253
254
0
    if (! stroker_intersects_join (stroker, inpt, outpt))
255
0
      return;
256
257
0
    switch (stroker->line_join) {
258
0
    case CAIRO_LINE_JOIN_ROUND:
259
  /* construct a fan around the common midpoint */
260
0
  if ((in->dev_slope.x * out->dev_slope.x +
261
0
       in->dev_slope.y * out->dev_slope.y) < stroker->spline_cusp_tolerance)
262
0
  {
263
0
      int start, stop;
264
0
      cairo_point_t tri[3], edges[4];
265
0
      cairo_pen_t *pen = &stroker->pen;
266
267
0
      edges[0] = in->cw;
268
0
      edges[1] = in->ccw;
269
0
      tri[0] = in->point;
270
0
      tri[1] = *inpt;
271
0
      if (clockwise) {
272
0
    _cairo_pen_find_active_ccw_vertices (pen,
273
0
                 &in->dev_vector, &out->dev_vector,
274
0
                 &start, &stop);
275
0
    while (start != stop) {
276
0
        tri[2] = in->point;
277
0
        translate_point (&tri[2], &pen->vertices[start].point);
278
0
        edges[2] = in->point;
279
0
        edges[3] = tri[2];
280
0
        _cairo_traps_tessellate_triangle_with_edges (stroker->traps,
281
0
                 tri, edges);
282
0
        tri[1] = tri[2];
283
0
        edges[0] = edges[2];
284
0
        edges[1] = edges[3];
285
286
0
        if (start-- == 0)
287
0
      start += pen->num_vertices;
288
0
    }
289
0
      } else {
290
0
    _cairo_pen_find_active_cw_vertices (pen,
291
0
                &in->dev_vector, &out->dev_vector,
292
0
                &start, &stop);
293
0
    while (start != stop) {
294
0
        tri[2] = in->point;
295
0
        translate_point (&tri[2], &pen->vertices[start].point);
296
0
        edges[2] = in->point;
297
0
        edges[3] = tri[2];
298
0
        _cairo_traps_tessellate_triangle_with_edges (stroker->traps,
299
0
                 tri, edges);
300
0
        tri[1] = tri[2];
301
0
        edges[0] = edges[2];
302
0
        edges[1] = edges[3];
303
304
0
        if (++start == pen->num_vertices)
305
0
      start = 0;
306
0
    }
307
0
      }
308
0
      tri[2] = *outpt;
309
0
      edges[2] = out->cw;
310
0
      edges[3] = out->ccw;
311
0
      _cairo_traps_tessellate_triangle_with_edges (stroker->traps,
312
0
               tri, edges);
313
0
  } else {
314
0
      cairo_point_t t[] = { { in->point.x, in->point.y}, { inpt->x, inpt->y }, { outpt->x, outpt->y } };
315
0
      cairo_point_t e[] = { { in->cw.x, in->cw.y}, { in->ccw.x, in->ccw.y },
316
0
          { out->cw.x, out->cw.y}, { out->ccw.x, out->ccw.y } };
317
0
      _cairo_traps_tessellate_triangle_with_edges (stroker->traps, t, e);
318
0
  }
319
0
  break;
320
321
0
    case CAIRO_LINE_JOIN_MITER:
322
0
    default: {
323
  /* dot product of incoming slope vector with outgoing slope vector */
324
0
  double in_dot_out = (-in->usr_vector.x * out->usr_vector.x +
325
0
           -in->usr_vector.y * out->usr_vector.y);
326
0
  double ml = stroker->style->miter_limit;
327
328
  /* Check the miter limit -- lines meeting at an acute angle
329
   * can generate long miters, the limit converts them to bevel
330
   *
331
   * Consider the miter join formed when two line segments
332
   * meet at an angle psi:
333
   *
334
   *     /.\
335
   *    /. .\
336
   *   /./ \.\
337
   *  /./psi\.\
338
   *
339
   * We can zoom in on the right half of that to see:
340
   *
341
   *      |\
342
   *      | \ psi/2
343
   *      |  \
344
   *      |   \
345
   *      |    \
346
   *      |     \
347
   *    miter    \
348
   *   length     \
349
   *      |        \
350
   *      |        .\
351
   *      |    .     \
352
   *      |.   line   \
353
   *       \    width  \
354
   *        \           \
355
   *
356
   *
357
   * The right triangle in that figure, (the line-width side is
358
   * shown faintly with three '.' characters), gives us the
359
   * following expression relating miter length, angle and line
360
   * width:
361
   *
362
   *  1 /sin (psi/2) = miter_length / line_width
363
   *
364
   * The right-hand side of this relationship is the same ratio
365
   * in which the miter limit (ml) is expressed. We want to know
366
   * when the miter length is within the miter limit. That is
367
   * when the following condition holds:
368
   *
369
   *  1/sin(psi/2) <= ml
370
   *  1 <= ml sin(psi/2)
371
   *  1 <= ml² sin²(psi/2)
372
   *  2 <= ml² 2 sin²(psi/2)
373
   *        2·sin²(psi/2) = 1-cos(psi)
374
   *  2 <= ml² (1-cos(psi))
375
   *
376
   *        in · out = |in| |out| cos (psi)
377
   *
378
   * in and out are both unit vectors, so:
379
   *
380
   *        in · out = cos (psi)
381
   *
382
   *  2 <= ml² (1 - in · out)
383
   *
384
   */
385
0
  if (2 <= ml * ml * (1 - in_dot_out)) {
386
0
      double    x1, y1, x2, y2;
387
0
      double    mx, my;
388
0
      double    dx1, dx2, dy1, dy2;
389
0
      cairo_point_t outer;
390
0
      cairo_point_t quad[4];
391
0
      double    ix, iy;
392
0
      double    fdx1, fdy1, fdx2, fdy2;
393
0
      double    mdx, mdy;
394
395
      /*
396
       * we've got the points already transformed to device
397
       * space, but need to do some computation with them and
398
       * also need to transform the slope from user space to
399
       * device space
400
       */
401
      /* outer point of incoming line face */
402
0
      x1 = _cairo_fixed_to_double (inpt->x);
403
0
      y1 = _cairo_fixed_to_double (inpt->y);
404
0
      dx1 = in->usr_vector.x;
405
0
      dy1 = in->usr_vector.y;
406
0
      cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
407
408
      /* outer point of outgoing line face */
409
0
      x2 = _cairo_fixed_to_double (outpt->x);
410
0
      y2 = _cairo_fixed_to_double (outpt->y);
411
0
      dx2 = out->usr_vector.x;
412
0
      dy2 = out->usr_vector.y;
413
0
      cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
414
415
      /*
416
       * Compute the location of the outer corner of the miter.
417
       * That's pretty easy -- just the intersection of the two
418
       * outer edges.  We've got slopes and points on each
419
       * of those edges.  Compute my directly, then compute
420
       * mx by using the edge with the larger dy; that avoids
421
       * dividing by values close to zero.
422
       */
423
0
      my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
424
0
      (dx1 * dy2 - dx2 * dy1));
425
0
      if (fabs (dy1) >= fabs (dy2))
426
0
    mx = (my - y1) * dx1 / dy1 + x1;
427
0
      else
428
0
    mx = (my - y2) * dx2 / dy2 + x2;
429
430
      /*
431
       * When the two outer edges are nearly parallel, slight
432
       * perturbations in the position of the outer points of the lines
433
       * caused by representing them in fixed point form can cause the
434
       * intersection point of the miter to move a large amount. If
435
       * that moves the miter intersection from between the two faces,
436
       * then draw a bevel instead.
437
       */
438
439
0
      ix = _cairo_fixed_to_double (in->point.x);
440
0
      iy = _cairo_fixed_to_double (in->point.y);
441
442
      /* slope of one face */
443
0
      fdx1 = x1 - ix; fdy1 = y1 - iy;
444
445
      /* slope of the other face */
446
0
      fdx2 = x2 - ix; fdy2 = y2 - iy;
447
448
      /* slope from the intersection to the miter point */
449
0
      mdx = mx - ix; mdy = my - iy;
450
451
      /*
452
       * Make sure the miter point line lies between the two
453
       * faces by comparing the slopes
454
       */
455
0
      if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
456
0
    slope_compare_sgn (fdx2, fdy2, mdx, mdy))
457
0
      {
458
    /*
459
     * Draw the quadrilateral
460
     */
461
0
    outer.x = _cairo_fixed_from_double (mx);
462
0
    outer.y = _cairo_fixed_from_double (my);
463
464
0
    quad[0] = in->point;
465
0
    quad[1] = *inpt;
466
0
    quad[2] = outer;
467
0
    quad[3] = *outpt;
468
469
0
    _cairo_traps_tessellate_convex_quad (stroker->traps, quad);
470
0
    break;
471
0
      }
472
0
  }
473
0
    }
474
    /* fall through ... */
475
0
    case CAIRO_LINE_JOIN_BEVEL: {
476
0
  cairo_point_t t[] = { { in->point.x, in->point.y }, { inpt->x, inpt->y }, { outpt->x, outpt->y } };
477
0
  cairo_point_t e[] = { { in->cw.x, in->cw.y }, { in->ccw.x, in->ccw.y },
478
0
            { out->cw.x, out->cw.y }, { out->ccw.x, out->ccw.y } };
479
0
  _cairo_traps_tessellate_triangle_with_edges (stroker->traps, t, e);
480
0
  break;
481
0
    }
482
0
    }
483
0
}
484
485
static void
486
add_cap (struct stroker *stroker, cairo_stroke_face_t *f)
487
0
{
488
0
    switch (stroker->style->line_cap) {
489
0
    case CAIRO_LINE_CAP_ROUND: {
490
0
  int start, stop;
491
0
  cairo_slope_t in_slope, out_slope;
492
0
  cairo_point_t tri[3], edges[4];
493
0
  cairo_pen_t *pen = &stroker->pen;
494
495
0
  in_slope = f->dev_vector;
496
0
  out_slope.dx = -in_slope.dx;
497
0
  out_slope.dy = -in_slope.dy;
498
0
  _cairo_pen_find_active_cw_vertices (pen, &in_slope, &out_slope,
499
0
              &start, &stop);
500
0
  edges[0] = f->cw;
501
0
  edges[1] = f->ccw;
502
0
  tri[0] = f->point;
503
0
  tri[1] = f->cw;
504
0
  while (start != stop) {
505
0
      tri[2] = f->point;
506
0
      translate_point (&tri[2], &pen->vertices[start].point);
507
0
      edges[2] = f->point;
508
0
      edges[3] = tri[2];
509
0
      _cairo_traps_tessellate_triangle_with_edges (stroker->traps,
510
0
               tri, edges);
511
512
0
      tri[1] = tri[2];
513
0
      edges[0] = edges[2];
514
0
      edges[1] = edges[3];
515
0
      if (++start == pen->num_vertices)
516
0
    start = 0;
517
0
  }
518
0
  tri[2] = f->ccw;
519
0
  edges[2] = f->cw;
520
0
  edges[3] = f->ccw;
521
0
  _cairo_traps_tessellate_triangle_with_edges (stroker->traps,
522
0
                 tri, edges);
523
0
  break;
524
0
    }
525
526
0
    case CAIRO_LINE_CAP_SQUARE: {
527
0
  double dx, dy;
528
0
  cairo_slope_t fvector;
529
0
  cairo_point_t quad[4];
530
531
0
  dx = f->usr_vector.x;
532
0
  dy = f->usr_vector.y;
533
0
  dx *= stroker->half_line_width;
534
0
  dy *= stroker->half_line_width;
535
0
  cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
536
0
  fvector.dx = _cairo_fixed_from_double (dx);
537
0
  fvector.dy = _cairo_fixed_from_double (dy);
538
539
0
  quad[0] = f->cw;
540
0
  quad[1].x = f->cw.x + fvector.dx;
541
0
  quad[1].y = f->cw.y + fvector.dy;
542
0
  quad[2].x = f->ccw.x + fvector.dx;
543
0
  quad[2].y = f->ccw.y + fvector.dy;
544
0
  quad[3] = f->ccw;
545
546
0
  _cairo_traps_tessellate_convex_quad (stroker->traps, quad);
547
0
  break;
548
0
    }
549
550
0
    case CAIRO_LINE_CAP_BUTT:
551
0
    default:
552
0
  break;
553
0
    }
554
0
}
555
556
static void
557
add_leading_cap (struct stroker     *stroker,
558
     cairo_stroke_face_t *face)
559
0
{
560
0
    cairo_stroke_face_t reversed;
561
0
    cairo_point_t t;
562
563
0
    reversed = *face;
564
565
    /* The initial cap needs an outward facing vector. Reverse everything */
566
0
    reversed.usr_vector.x = -reversed.usr_vector.x;
567
0
    reversed.usr_vector.y = -reversed.usr_vector.y;
568
0
    reversed.dev_vector.dx = -reversed.dev_vector.dx;
569
0
    reversed.dev_vector.dy = -reversed.dev_vector.dy;
570
0
    t = reversed.cw;
571
0
    reversed.cw = reversed.ccw;
572
0
    reversed.ccw = t;
573
574
0
    add_cap (stroker, &reversed);
575
0
}
576
577
static void
578
add_trailing_cap (struct stroker *stroker, cairo_stroke_face_t *face)
579
0
{
580
0
    add_cap (stroker, face);
581
0
}
582
583
static inline double
584
normalize_slope (double *dx, double *dy)
585
0
{
586
0
    double dx0 = *dx, dy0 = *dy;
587
588
0
    if (dx0 == 0.0 && dy0 == 0.0)
589
0
  return 0;
590
591
0
    if (dx0 == 0.0) {
592
0
  *dx = 0.0;
593
0
  if (dy0 > 0.0) {
594
0
      *dy = 1.0;
595
0
      return dy0;
596
0
  } else {
597
0
      *dy = -1.0;
598
0
      return -dy0;
599
0
  }
600
0
    } else if (dy0 == 0.0) {
601
0
  *dy = 0.0;
602
0
  if (dx0 > 0.0) {
603
0
      *dx = 1.0;
604
0
      return dx0;
605
0
  } else {
606
0
      *dx = -1.0;
607
0
      return -dx0;
608
0
  }
609
0
    } else {
610
0
  double mag = hypot (dx0, dy0);
611
0
  *dx = dx0 / mag;
612
0
  *dy = dy0 / mag;
613
0
  return mag;
614
0
    }
615
0
}
616
617
static void
618
compute_face (const cairo_point_t *point,
619
        const cairo_slope_t *dev_slope,
620
        struct stroker *stroker,
621
        cairo_stroke_face_t *face)
622
0
{
623
0
    double face_dx, face_dy;
624
0
    cairo_point_t offset_ccw, offset_cw;
625
0
    double slope_dx, slope_dy;
626
627
0
    slope_dx = _cairo_fixed_to_double (dev_slope->dx);
628
0
    slope_dy = _cairo_fixed_to_double (dev_slope->dy);
629
0
    face->length = normalize_slope (&slope_dx, &slope_dy);
630
0
    face->dev_slope.x = slope_dx;
631
0
    face->dev_slope.y = slope_dy;
632
633
    /*
634
     * rotate to get a line_width/2 vector along the face, note that
635
     * the vector must be rotated the right direction in device space,
636
     * but by 90° in user space. So, the rotation depends on
637
     * whether the ctm reflects or not, and that can be determined
638
     * by looking at the determinant of the matrix.
639
     */
640
0
    if (stroker->ctm_inverse) {
641
0
  cairo_matrix_transform_distance (stroker->ctm_inverse, &slope_dx, &slope_dy);
642
0
  normalize_slope (&slope_dx, &slope_dy);
643
644
0
  if (stroker->ctm_det_positive) {
645
0
      face_dx = - slope_dy * stroker->half_line_width;
646
0
      face_dy = slope_dx * stroker->half_line_width;
647
0
  } else {
648
0
      face_dx = slope_dy * stroker->half_line_width;
649
0
      face_dy = - slope_dx * stroker->half_line_width;
650
0
  }
651
652
  /* back to device space */
653
0
  cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
654
0
    } else {
655
0
  face_dx = - slope_dy * stroker->half_line_width;
656
0
  face_dy = slope_dx * stroker->half_line_width;
657
0
    }
658
659
0
    offset_ccw.x = _cairo_fixed_from_double (face_dx);
660
0
    offset_ccw.y = _cairo_fixed_from_double (face_dy);
661
0
    offset_cw.x = -offset_ccw.x;
662
0
    offset_cw.y = -offset_ccw.y;
663
664
0
    face->ccw = *point;
665
0
    translate_point (&face->ccw, &offset_ccw);
666
667
0
    face->point = *point;
668
669
0
    face->cw = *point;
670
0
    translate_point (&face->cw, &offset_cw);
671
672
0
    face->usr_vector.x = slope_dx;
673
0
    face->usr_vector.y = slope_dy;
674
675
0
    face->dev_vector = *dev_slope;
676
0
}
677
678
static void
679
add_caps (struct stroker *stroker)
680
0
{
681
    /* check for a degenerative sub_path */
682
0
    if (stroker->has_initial_sub_path &&
683
0
  !stroker->has_first_face &&
684
0
  !stroker->has_current_face &&
685
0
  stroker->style->line_cap == CAIRO_LINE_CAP_ROUND)
686
0
    {
687
  /* pick an arbitrary slope to use */
688
0
  cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
689
0
  cairo_stroke_face_t face;
690
691
  /* arbitrarily choose first_point
692
   * first_point and current_point should be the same */
693
0
  compute_face (&stroker->first_point, &slope, stroker, &face);
694
695
0
  add_leading_cap (stroker, &face);
696
0
  add_trailing_cap (stroker, &face);
697
0
    }
698
699
0
    if (stroker->has_first_face)
700
0
  add_leading_cap (stroker, &stroker->first_face);
701
702
0
    if (stroker->has_current_face)
703
0
  add_trailing_cap (stroker, &stroker->current_face);
704
0
}
705
706
static cairo_bool_t
707
stroker_intersects_edge (const struct stroker *stroker,
708
       const cairo_stroke_face_t *start,
709
       const cairo_stroke_face_t *end)
710
0
{
711
0
    cairo_box_t box;
712
713
0
    if (! stroker->has_bounds)
714
0
  return TRUE;
715
716
0
    if (_cairo_box_contains_point (&stroker->tight_bounds, &start->cw))
717
0
  return TRUE;
718
0
    box.p2 = box.p1 = start->cw;
719
720
0
    if (_cairo_box_contains_point (&stroker->tight_bounds, &start->ccw))
721
0
  return TRUE;
722
0
    _cairo_box_add_point (&box, &start->ccw);
723
724
0
    if (_cairo_box_contains_point (&stroker->tight_bounds, &end->cw))
725
0
  return TRUE;
726
0
    _cairo_box_add_point (&box, &end->cw);
727
728
0
    if (_cairo_box_contains_point (&stroker->tight_bounds, &end->ccw))
729
0
  return TRUE;
730
0
    _cairo_box_add_point (&box, &end->ccw);
731
732
0
    return (box.p2.x > stroker->tight_bounds.p1.x &&
733
0
      box.p1.x < stroker->tight_bounds.p2.x &&
734
0
      box.p2.y > stroker->tight_bounds.p1.y &&
735
0
      box.p1.y < stroker->tight_bounds.p2.y);
736
0
}
737
738
static void
739
add_sub_edge (struct stroker *stroker,
740
        const cairo_point_t *p1, const cairo_point_t *p2,
741
        const cairo_slope_t *dev_slope,
742
        cairo_stroke_face_t *start, cairo_stroke_face_t *end)
743
0
{
744
0
    cairo_point_t rectangle[4];
745
746
0
    compute_face (p1, dev_slope, stroker, start);
747
748
0
    *end = *start;
749
0
    end->point = *p2;
750
0
    rectangle[0].x = p2->x - p1->x;
751
0
    rectangle[0].y = p2->y - p1->y;
752
0
    translate_point (&end->ccw, &rectangle[0]);
753
0
    translate_point (&end->cw, &rectangle[0]);
754
755
0
    if (p1->x == p2->x && p1->y == p2->y)
756
0
  return;
757
758
0
    if (! stroker_intersects_edge (stroker, start, end))
759
0
  return;
760
761
0
    rectangle[0] = start->cw;
762
0
    rectangle[1] = start->ccw;
763
0
    rectangle[2] = end->ccw;
764
0
    rectangle[3] = end->cw;
765
766
0
    _cairo_traps_tessellate_convex_quad (stroker->traps, rectangle);
767
0
}
768
769
static cairo_status_t
770
move_to (void *closure, const cairo_point_t *point)
771
0
{
772
0
    struct stroker *stroker = closure;
773
774
    /* Cap the start and end of the previous sub path as needed */
775
0
    add_caps (stroker);
776
777
0
    stroker->first_point = *point;
778
0
    stroker->current_face.point = *point;
779
780
0
    stroker->has_first_face = FALSE;
781
0
    stroker->has_current_face = FALSE;
782
0
    stroker->has_initial_sub_path = FALSE;
783
784
0
    return CAIRO_STATUS_SUCCESS;
785
0
}
786
787
static cairo_status_t
788
move_to_dashed (void *closure, const cairo_point_t *point)
789
0
{
790
    /* reset the dash pattern for new sub paths */
791
0
    struct stroker *stroker = closure;
792
793
0
    _cairo_stroker_dash_start (&stroker->dash);
794
0
    return move_to (closure, point);
795
0
}
796
797
static cairo_status_t
798
line_to (void *closure, const cairo_point_t *point)
799
0
{
800
0
    struct stroker *stroker = closure;
801
0
    cairo_stroke_face_t start, end;
802
0
    const cairo_point_t *p1 = &stroker->current_face.point;
803
0
    const cairo_point_t *p2 = point;
804
0
    cairo_slope_t dev_slope;
805
806
0
    stroker->has_initial_sub_path = TRUE;
807
808
0
    if (p1->x == p2->x && p1->y == p2->y)
809
0
  return CAIRO_STATUS_SUCCESS;
810
811
0
    _cairo_slope_init (&dev_slope, p1, p2);
812
0
    add_sub_edge (stroker, p1, p2, &dev_slope, &start, &end);
813
814
0
    if (stroker->has_current_face) {
815
  /* Join with final face from previous segment */
816
0
  join (stroker, &stroker->current_face, &start);
817
0
    } else if (!stroker->has_first_face) {
818
  /* Save sub path's first face in case needed for closing join */
819
0
  stroker->first_face = start;
820
0
  stroker->has_first_face = TRUE;
821
0
    }
822
0
    stroker->current_face = end;
823
0
    stroker->has_current_face = TRUE;
824
825
0
    return CAIRO_STATUS_SUCCESS;
826
0
}
827
828
/*
829
 * Dashed lines.  Cap each dash end, join around turns when on
830
 */
831
static cairo_status_t
832
line_to_dashed (void *closure, const cairo_point_t *point)
833
0
{
834
0
    struct stroker *stroker = closure;
835
0
    double mag, remain, step_length = 0;
836
0
    double slope_dx, slope_dy;
837
0
    double dx2, dy2;
838
0
    cairo_stroke_face_t sub_start, sub_end;
839
0
    const cairo_point_t *p1 = &stroker->current_face.point;
840
0
    const cairo_point_t *p2 = point;
841
0
    cairo_slope_t dev_slope;
842
0
    cairo_line_t segment;
843
0
    cairo_bool_t fully_in_bounds;
844
845
0
    stroker->has_initial_sub_path = stroker->dash.dash_starts_on;
846
847
0
    if (p1->x == p2->x && p1->y == p2->y)
848
0
  return CAIRO_STATUS_SUCCESS;
849
850
0
    fully_in_bounds = TRUE;
851
0
    if (stroker->has_bounds &&
852
0
  (! _cairo_box_contains_point (&stroker->join_bounds, p1) ||
853
0
   ! _cairo_box_contains_point (&stroker->join_bounds, p2)))
854
0
    {
855
0
  fully_in_bounds = FALSE;
856
0
    }
857
858
0
    _cairo_slope_init (&dev_slope, p1, p2);
859
860
0
    slope_dx = _cairo_fixed_to_double (p2->x - p1->x);
861
0
    slope_dy = _cairo_fixed_to_double (p2->y - p1->y);
862
863
0
    if (stroker->ctm_inverse)
864
0
  cairo_matrix_transform_distance (stroker->ctm_inverse, &slope_dx, &slope_dy);
865
0
    mag = normalize_slope (&slope_dx, &slope_dy);
866
0
    if (mag <= DBL_EPSILON)
867
0
  return CAIRO_STATUS_SUCCESS;
868
869
0
    remain = mag;
870
0
    segment.p1 = *p1;
871
0
    while (remain) {
872
0
  step_length = MIN (stroker->dash.dash_remain, remain);
873
0
  remain -= step_length;
874
0
  dx2 = slope_dx * (mag - remain);
875
0
  dy2 = slope_dy * (mag - remain);
876
0
  cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
877
0
  segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x;
878
0
  segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y;
879
880
0
  if (stroker->dash.dash_on &&
881
0
      (fully_in_bounds ||
882
0
       (! stroker->has_first_face && stroker->dash.dash_starts_on) ||
883
0
       _cairo_box_intersects_line_segment (&stroker->join_bounds, &segment)))
884
0
  {
885
0
      add_sub_edge (stroker,
886
0
        &segment.p1, &segment.p2,
887
0
        &dev_slope,
888
0
        &sub_start, &sub_end);
889
890
0
      if (stroker->has_current_face) {
891
    /* Join with final face from previous segment */
892
0
    join (stroker, &stroker->current_face, &sub_start);
893
894
0
    stroker->has_current_face = FALSE;
895
0
      } else if (! stroker->has_first_face && stroker->dash.dash_starts_on) {
896
    /* Save sub path's first face in case needed for closing join */
897
0
    stroker->first_face = sub_start;
898
0
    stroker->has_first_face = TRUE;
899
0
      } else {
900
    /* Cap dash start if not connecting to a previous segment */
901
0
    add_leading_cap (stroker, &sub_start);
902
0
      }
903
904
0
      if (remain) {
905
    /* Cap dash end if not at end of segment */
906
0
    add_trailing_cap (stroker, &sub_end);
907
0
      } else {
908
0
    stroker->current_face = sub_end;
909
0
    stroker->has_current_face = TRUE;
910
0
      }
911
0
  } else {
912
0
      if (stroker->has_current_face) {
913
    /* Cap final face from previous segment */
914
0
    add_trailing_cap (stroker, &stroker->current_face);
915
916
0
    stroker->has_current_face = FALSE;
917
0
      }
918
0
  }
919
920
0
  _cairo_stroker_dash_step (&stroker->dash, step_length);
921
0
  segment.p1 = segment.p2;
922
0
    }
923
924
0
    if (stroker->dash.dash_on && ! stroker->has_current_face) {
925
  /* This segment ends on a transition to dash_on, compute a new face
926
   * and add cap for the beginning of the next dash_on step.
927
   *
928
   * Note: this will create a degenerate cap if this is not the last line
929
   * in the path. Whether this behaviour is desirable or not is debatable.
930
   * On one side these degenerate caps can not be reproduced with regular
931
   * path stroking.
932
   * On the other hand, Acroread 7 also produces the degenerate caps.
933
   */
934
0
  compute_face (point, &dev_slope, stroker, &stroker->current_face);
935
936
0
  add_leading_cap (stroker, &stroker->current_face);
937
938
0
  stroker->has_current_face = TRUE;
939
0
    } else
940
0
  stroker->current_face.point = *point;
941
942
0
    return CAIRO_STATUS_SUCCESS;
943
0
}
944
945
static cairo_status_t
946
add_point (void *closure,
947
     const cairo_point_t *point,
948
     const cairo_slope_t *tangent)
949
0
{
950
0
    return line_to_dashed (closure, point);
951
0
};
952
953
static cairo_status_t
954
spline_to (void *closure,
955
     const cairo_point_t *point,
956
     const cairo_slope_t *tangent)
957
0
{
958
0
    struct stroker *stroker = closure;
959
0
    cairo_stroke_face_t face;
960
961
0
    if ((tangent->dx | tangent->dy) == 0) {
962
0
  cairo_point_t t;
963
964
0
  face = stroker->current_face;
965
966
0
  face.usr_vector.x = -face.usr_vector.x;
967
0
  face.usr_vector.y = -face.usr_vector.y;
968
0
  face.dev_slope.x = -face.dev_slope.x;
969
0
  face.dev_slope.y = -face.dev_slope.y;
970
0
  face.dev_vector.dx = -face.dev_vector.dx;
971
0
  face.dev_vector.dy = -face.dev_vector.dy;
972
973
0
  t = face.cw;
974
0
  face.cw = face.ccw;
975
0
  face.ccw = t;
976
977
0
  join (stroker, &stroker->current_face, &face);
978
0
    } else {
979
0
  cairo_point_t rectangle[4];
980
981
0
  compute_face (&stroker->current_face.point, tangent, stroker, &face);
982
0
  join (stroker, &stroker->current_face, &face);
983
984
0
  rectangle[0] = face.cw;
985
0
  rectangle[1] = face.ccw;
986
987
0
  rectangle[2].x = point->x - face.point.x;
988
0
  rectangle[2].y = point->y - face.point.y;
989
0
  face.point = *point;
990
0
  translate_point (&face.ccw, &rectangle[2]);
991
0
  translate_point (&face.cw, &rectangle[2]);
992
993
0
  rectangle[2] = face.ccw;
994
0
  rectangle[3] = face.cw;
995
996
0
  _cairo_traps_tessellate_convex_quad (stroker->traps, rectangle);
997
0
    }
998
999
0
    stroker->current_face = face;
1000
1001
0
    return CAIRO_STATUS_SUCCESS;
1002
0
}
1003
1004
static cairo_status_t
1005
curve_to (void *closure,
1006
    const cairo_point_t *b,
1007
    const cairo_point_t *c,
1008
    const cairo_point_t *d)
1009
0
{
1010
0
    struct stroker *stroker = closure;
1011
0
    cairo_line_join_t line_join_save;
1012
0
    cairo_spline_t spline;
1013
0
    cairo_stroke_face_t face;
1014
0
    cairo_status_t status;
1015
1016
0
    if (stroker->has_bounds &&
1017
0
  ! _cairo_spline_intersects (&stroker->current_face.point, b, c, d,
1018
0
            &stroker->line_bounds))
1019
0
  return line_to (closure, d);
1020
1021
0
    if (! _cairo_spline_init (&spline, spline_to, stroker,
1022
0
            &stroker->current_face.point, b, c, d))
1023
0
  return line_to (closure, d);
1024
1025
0
    compute_face (&stroker->current_face.point, &spline.initial_slope,
1026
0
      stroker, &face);
1027
1028
0
    if (stroker->has_current_face) {
1029
  /* Join with final face from previous segment */
1030
0
  join (stroker, &stroker->current_face, &face);
1031
0
    } else {
1032
0
  if (! stroker->has_first_face) {
1033
      /* Save sub path's first face in case needed for closing join */
1034
0
      stroker->first_face = face;
1035
0
      stroker->has_first_face = TRUE;
1036
0
  }
1037
0
  stroker->has_current_face = TRUE;
1038
0
    }
1039
0
    stroker->current_face = face;
1040
1041
    /* Temporarily modify the stroker to use round joins to guarantee
1042
     * smooth stroked curves. */
1043
0
    line_join_save = stroker->line_join;
1044
0
    stroker->line_join = CAIRO_LINE_JOIN_ROUND;
1045
1046
0
    status = _cairo_spline_decompose (&spline, stroker->tolerance);
1047
1048
0
    stroker->line_join = line_join_save;
1049
1050
0
    return status;
1051
0
}
1052
1053
static cairo_status_t
1054
curve_to_dashed (void *closure,
1055
     const cairo_point_t *b,
1056
     const cairo_point_t *c,
1057
     const cairo_point_t *d)
1058
0
{
1059
0
    struct stroker *stroker = closure;
1060
0
    cairo_spline_t spline;
1061
0
    cairo_line_join_t line_join_save;
1062
0
    cairo_spline_add_point_func_t func;
1063
0
    cairo_status_t status;
1064
1065
0
    func = add_point;
1066
1067
0
    if (stroker->has_bounds &&
1068
0
  ! _cairo_spline_intersects (&stroker->current_face.point, b, c, d,
1069
0
            &stroker->line_bounds))
1070
0
  return func (closure, d, NULL);
1071
1072
0
    if (! _cairo_spline_init (&spline, func, stroker,
1073
0
            &stroker->current_face.point, b, c, d))
1074
0
  return func (closure, d, NULL);
1075
1076
    /* Temporarily modify the stroker to use round joins to guarantee
1077
     * smooth stroked curves. */
1078
0
    line_join_save = stroker->line_join;
1079
0
    stroker->line_join = CAIRO_LINE_JOIN_ROUND;
1080
1081
0
    status = _cairo_spline_decompose (&spline, stroker->tolerance);
1082
1083
0
    stroker->line_join = line_join_save;
1084
1085
0
    return status;
1086
0
}
1087
1088
static cairo_status_t
1089
_close_path (struct stroker *stroker)
1090
0
{
1091
0
    if (stroker->has_first_face && stroker->has_current_face) {
1092
  /* Join first and final faces of sub path */
1093
0
  join (stroker, &stroker->current_face, &stroker->first_face);
1094
0
    } else {
1095
  /* Cap the start and end of the sub path as needed */
1096
0
  add_caps (stroker);
1097
0
    }
1098
1099
0
    stroker->has_initial_sub_path = FALSE;
1100
0
    stroker->has_first_face = FALSE;
1101
0
    stroker->has_current_face = FALSE;
1102
0
    return CAIRO_STATUS_SUCCESS;
1103
0
}
1104
1105
static cairo_status_t
1106
close_path (void *closure)
1107
0
{
1108
0
    struct stroker *stroker = closure;
1109
0
    cairo_status_t status;
1110
1111
0
    status = line_to (stroker, &stroker->first_point);
1112
0
    if (unlikely (status))
1113
0
  return status;
1114
1115
0
    return _close_path (stroker);
1116
0
}
1117
1118
static cairo_status_t
1119
close_path_dashed (void *closure)
1120
0
{
1121
0
    struct stroker *stroker = closure;
1122
0
    cairo_status_t status;
1123
1124
0
    status = line_to_dashed (stroker, &stroker->first_point);
1125
0
    if (unlikely (status))
1126
0
  return status;
1127
1128
0
    return _close_path (stroker);
1129
0
}
1130
1131
cairo_int_status_t
1132
_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path,
1133
           const cairo_stroke_style_t *style,
1134
           const cairo_matrix_t   *ctm,
1135
           const cairo_matrix_t   *ctm_inverse,
1136
           double      tolerance,
1137
           cairo_traps_t    *traps)
1138
0
{
1139
0
    struct stroker stroker;
1140
0
    cairo_status_t status;
1141
1142
0
    status = stroker_init (&stroker, path, style,
1143
0
         ctm, ctm_inverse, tolerance,
1144
0
         traps);
1145
0
    if (unlikely (status))
1146
0
  return status;
1147
1148
0
    if (stroker.dash.dashed)
1149
0
  status = _cairo_path_fixed_interpret (path,
1150
0
                move_to_dashed,
1151
0
                line_to_dashed,
1152
0
                curve_to_dashed,
1153
0
                close_path_dashed,
1154
0
                &stroker);
1155
0
    else
1156
0
  status = _cairo_path_fixed_interpret (path,
1157
0
                move_to,
1158
0
                line_to,
1159
0
                curve_to,
1160
0
                close_path,
1161
0
                &stroker);
1162
0
    assert(status == CAIRO_STATUS_SUCCESS);
1163
0
    add_caps (&stroker);
1164
1165
0
    stroker_fini (&stroker);
1166
1167
0
    return traps->status;
1168
0
}