Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/cairo/src/cairo-path-stroke-boxes.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
49
typedef struct _segment_t {
50
    cairo_point_t p1, p2;
51
    unsigned flags;
52
97.9k
#define HORIZONTAL 0x1
53
0
#define FORWARDS 0x2
54
48.9k
#define JOIN 0x4
55
} segment_t;
56
57
typedef struct _cairo_rectilinear_stroker {
58
    const cairo_stroke_style_t *stroke_style;
59
    const cairo_matrix_t *ctm;
60
    cairo_antialias_t antialias;
61
62
    cairo_fixed_t half_line_x, half_line_y;
63
    cairo_boxes_t *boxes;
64
    cairo_point_t current_point;
65
    cairo_point_t first_point;
66
    cairo_bool_t open_sub_path;
67
68
    cairo_stroker_dash_t dash;
69
70
    cairo_bool_t has_bounds;
71
    cairo_box_t bounds;
72
73
    int num_segments;
74
    int segments_size;
75
    segment_t *segments;
76
    segment_t segments_embedded[8]; /* common case is a single rectangle */
77
} cairo_rectilinear_stroker_t;
78
79
static void
80
_cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
81
          const cairo_box_t *boxes,
82
          int num_boxes)
83
14.5k
{
84
14.5k
    stroker->has_bounds = TRUE;
85
14.5k
    _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
86
87
14.5k
    stroker->bounds.p1.x -= stroker->half_line_x;
88
14.5k
    stroker->bounds.p2.x += stroker->half_line_x;
89
90
14.5k
    stroker->bounds.p1.y -= stroker->half_line_y;
91
14.5k
    stroker->bounds.p2.y += stroker->half_line_y;
92
14.5k
}
93
94
static cairo_bool_t
95
_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t  *stroker,
96
         const cairo_stroke_style_t *stroke_style,
97
         const cairo_matrix_t   *ctm,
98
         cairo_antialias_t     antialias,
99
         cairo_boxes_t      *boxes)
100
20.6k
{
101
    /* This special-case rectilinear stroker only supports
102
     * miter-joined lines (not curves) and a translation-only matrix
103
     * (though it could probably be extended to support a matrix with
104
     * uniform, integer scaling).
105
     *
106
     * It also only supports horizontal and vertical line_to
107
     * elements. But we don't catch that here, but instead return
108
     * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any
109
     * non-rectilinear line_to is encountered.
110
     */
111
20.6k
    if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER)
112
0
  return FALSE;
113
114
    /* If the miter limit turns right angles into bevels, then we
115
     * can't use this optimization. Remember, the ratio is
116
     * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2,
117
     * which we round for safety. */
118
20.6k
    if (stroke_style->miter_limit < M_SQRT2)
119
0
  return FALSE;
120
121
20.6k
    if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
122
20.6k
     stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
123
0
    {
124
0
  return FALSE;
125
0
    }
126
127
20.6k
    if (! _cairo_matrix_is_scale (ctm))
128
0
  return FALSE;
129
130
20.6k
    stroker->stroke_style = stroke_style;
131
20.6k
    stroker->ctm = ctm;
132
20.6k
    stroker->antialias = antialias;
133
134
20.6k
    stroker->half_line_x =
135
20.6k
  _cairo_fixed_from_double (fabs(ctm->xx) * stroke_style->line_width / 2.0);
136
20.6k
    stroker->half_line_y =
137
20.6k
  _cairo_fixed_from_double (fabs(ctm->yy) * stroke_style->line_width / 2.0);
138
139
20.6k
    stroker->open_sub_path = FALSE;
140
20.6k
    stroker->segments = stroker->segments_embedded;
141
20.6k
    stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
142
20.6k
    stroker->num_segments = 0;
143
144
20.6k
    _cairo_stroker_dash_init (&stroker->dash, stroke_style);
145
146
20.6k
    stroker->has_bounds = FALSE;
147
148
20.6k
    stroker->boxes = boxes;
149
150
20.6k
    return TRUE;
151
20.6k
}
152
153
static void
154
_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t  *stroker)
155
20.6k
{
156
20.6k
    if (stroker->segments != stroker->segments_embedded)
157
297
  free (stroker->segments);
158
20.6k
}
159
160
static cairo_status_t
161
_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
162
          const cairo_point_t *p1,
163
          const cairo_point_t *p2,
164
          unsigned     flags)
165
48.9k
{
166
48.9k
    if (CAIRO_INJECT_FAULT ())
167
0
  return _cairo_error (CAIRO_STATUS_NO_MEMORY);
168
169
48.9k
    if (stroker->num_segments == stroker->segments_size) {
170
401
  int new_size = stroker->segments_size * 2;
171
401
  segment_t *new_segments;
172
173
401
  if (stroker->segments == stroker->segments_embedded) {
174
297
      new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t));
175
297
      if (unlikely (new_segments == NULL))
176
0
    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
177
178
297
      memcpy (new_segments, stroker->segments,
179
297
        stroker->num_segments * sizeof (segment_t));
180
297
  } else {
181
104
      new_segments = _cairo_realloc_ab (stroker->segments,
182
104
                new_size, sizeof (segment_t));
183
104
      if (unlikely (new_segments == NULL))
184
0
    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
185
104
  }
186
187
401
  stroker->segments_size = new_size;
188
401
  stroker->segments = new_segments;
189
401
    }
190
191
48.9k
    stroker->segments[stroker->num_segments].p1 = *p1;
192
48.9k
    stroker->segments[stroker->num_segments].p2 = *p2;
193
48.9k
    stroker->segments[stroker->num_segments].flags = flags;
194
48.9k
    stroker->num_segments++;
195
196
48.9k
    return CAIRO_STATUS_SUCCESS;
197
48.9k
}
198
199
static cairo_status_t
200
_cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
201
67.2k
{
202
67.2k
    cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
203
67.2k
    cairo_fixed_t half_line_x = stroker->half_line_x;
204
67.2k
    cairo_fixed_t half_line_y = stroker->half_line_y;
205
67.2k
    cairo_status_t status;
206
67.2k
    int i, j;
207
208
    /* For each segment we generate a single rectangle.
209
     * This rectangle is based on a perpendicular extension (by half the
210
     * line width) of the segment endpoints * after some adjustments of the
211
     * endpoints to account for caps and joins.
212
     */
213
116k
    for (i = 0; i < stroker->num_segments; i++) {
214
48.9k
  cairo_bool_t lengthen_initial, lengthen_final;
215
48.9k
  cairo_point_t *a, *b;
216
48.9k
  cairo_box_t box;
217
218
48.9k
  a = &stroker->segments[i].p1;
219
48.9k
  b = &stroker->segments[i].p2;
220
221
  /* We adjust the initial point of the segment to extend the
222
   * rectangle to include the previous cap or join, (this
223
   * adjustment applies to all segments except for the first
224
   * segment of open, butt-capped paths). However, we must be
225
   * careful not to emit a miter join across a degenerate segment
226
   * which has been elided.
227
   *
228
   * Overlapping segments will be eliminated by the tessellation.
229
   * Ideally, we would not emit these self-intersections at all,
230
   * but that is tricky with segments shorter than half_line_width.
231
   */
232
48.9k
  j = i == 0 ? stroker->num_segments - 1 : i-1;
233
48.9k
  lengthen_initial = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL;
234
48.9k
  j = i == stroker->num_segments - 1 ? 0 : i+1;
235
48.9k
  lengthen_final = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL;
236
48.9k
  if (stroker->open_sub_path) {
237
32.0k
      if (i == 0)
238
16.2k
    lengthen_initial = line_cap != CAIRO_LINE_CAP_BUTT;
239
240
32.0k
      if (i == stroker->num_segments - 1)
241
16.2k
    lengthen_final = line_cap != CAIRO_LINE_CAP_BUTT;
242
32.0k
  }
243
244
  /* Perform the adjustments of the endpoints. */
245
48.9k
  if (lengthen_initial | lengthen_final) {
246
5.96k
      if (a->y == b->y) {
247
3.17k
    if (a->x < b->x) {
248
1.58k
        if (lengthen_initial)
249
1.27k
      a->x -= half_line_x;
250
1.58k
        if (lengthen_final)
251
1.06k
      b->x += half_line_x;
252
1.59k
    } else {
253
1.59k
        if (lengthen_initial)
254
1.23k
      a->x += half_line_x;
255
1.59k
        if (lengthen_final)
256
1.03k
      b->x -= half_line_x;
257
1.59k
    }
258
3.17k
      } else {
259
2.79k
    if (a->y < b->y) {
260
1.47k
        if (lengthen_initial)
261
1.06k
      a->y -= half_line_y;
262
1.47k
        if (lengthen_final)
263
1.26k
      b->y += half_line_y;
264
1.47k
    } else {
265
1.32k
        if (lengthen_initial)
266
1.03k
      a->y += half_line_y;
267
1.32k
        if (lengthen_final)
268
1.24k
      b->y -= half_line_y;
269
1.32k
    }
270
2.79k
      }
271
5.96k
  }
272
273
  /* Form the rectangle by expanding by half the line width in
274
   * either perpendicular direction. */
275
48.9k
  if (a->y == b->y) {
276
22.6k
      a->y -= half_line_y;
277
22.6k
      b->y += half_line_y;
278
26.3k
  } else {
279
26.3k
      a->x -= half_line_x;
280
26.3k
      b->x += half_line_x;
281
26.3k
  }
282
283
48.9k
  if (a->x < b->x) {
284
37.6k
      box.p1.x = a->x;
285
37.6k
      box.p2.x = b->x;
286
37.6k
  } else {
287
11.3k
      box.p1.x = b->x;
288
11.3k
      box.p2.x = a->x;
289
11.3k
  }
290
48.9k
  if (a->y < b->y) {
291
34.6k
      box.p1.y = a->y;
292
34.6k
      box.p2.y = b->y;
293
34.6k
  } else {
294
14.2k
      box.p1.y = b->y;
295
14.2k
      box.p2.y = a->y;
296
14.2k
  }
297
298
48.9k
  status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
299
48.9k
  if (unlikely (status))
300
0
      return status;
301
48.9k
    }
302
303
67.2k
    stroker->num_segments = 0;
304
67.2k
    return CAIRO_STATUS_SUCCESS;
305
67.2k
}
306
307
static cairo_status_t
308
_cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker)
309
0
{
310
0
    cairo_status_t status;
311
0
    cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
312
0
    cairo_fixed_t half_line_x = stroker->half_line_x;
313
0
    cairo_fixed_t half_line_y = stroker->half_line_y;
314
0
    int i;
315
316
0
    for (i = 0; i < stroker->num_segments; i++) {
317
0
  cairo_point_t *a, *b;
318
0
  cairo_bool_t is_horizontal;
319
0
  cairo_box_t box;
320
321
0
  a = &stroker->segments[i].p1;
322
0
  b = &stroker->segments[i].p2;
323
324
0
  is_horizontal = stroker->segments[i].flags & HORIZONTAL;
325
326
  /* Handle the joins for a potentially degenerate segment. */
327
0
  if (line_cap == CAIRO_LINE_CAP_BUTT &&
328
0
      stroker->segments[i].flags & JOIN &&
329
0
      (i != stroker->num_segments - 1 ||
330
0
       (! stroker->open_sub_path && stroker->dash.dash_starts_on)))
331
0
  {
332
0
      cairo_slope_t out_slope;
333
0
      int j = (i + 1) % stroker->num_segments;
334
0
      cairo_bool_t forwards = !!(stroker->segments[i].flags & FORWARDS);
335
336
0
      _cairo_slope_init (&out_slope,
337
0
             &stroker->segments[j].p1,
338
0
             &stroker->segments[j].p2);
339
0
      box.p2 = box.p1 = stroker->segments[i].p2;
340
341
0
      if (is_horizontal) {
342
0
    if (forwards)
343
0
        box.p2.x += half_line_x;
344
0
    else
345
0
        box.p1.x -= half_line_x;
346
347
0
    if (out_slope.dy > 0)
348
0
        box.p1.y -= half_line_y;
349
0
    else
350
0
        box.p2.y += half_line_y;
351
0
      } else {
352
0
    if (forwards)
353
0
        box.p2.y += half_line_y;
354
0
    else
355
0
        box.p1.y -= half_line_y;
356
357
0
    if (out_slope.dx > 0)
358
0
        box.p1.x -= half_line_x;
359
0
    else
360
0
        box.p2.x += half_line_x;
361
0
      }
362
363
0
      status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
364
0
      if (unlikely (status))
365
0
    return status;
366
0
  }
367
368
  /* Perform the adjustments of the endpoints. */
369
0
  if (is_horizontal) {
370
0
      if (line_cap == CAIRO_LINE_CAP_SQUARE) {
371
0
    if (a->x <= b->x) {
372
0
        a->x -= half_line_x;
373
0
        b->x += half_line_x;
374
0
    } else {
375
0
        a->x += half_line_x;
376
0
        b->x -= half_line_x;
377
0
    }
378
0
      }
379
380
0
      a->y += half_line_y;
381
0
      b->y -= half_line_y;
382
0
  } else {
383
0
      if (line_cap == CAIRO_LINE_CAP_SQUARE) {
384
0
    if (a->y <= b->y) {
385
0
        a->y -= half_line_y;
386
0
        b->y += half_line_y;
387
0
    } else {
388
0
        a->y += half_line_y;
389
0
        b->y -= half_line_y;
390
0
    }
391
0
      }
392
393
0
      a->x += half_line_x;
394
0
      b->x -= half_line_x;
395
0
  }
396
397
0
  if (a->x == b->x && a->y == b->y)
398
0
      continue;
399
400
0
  if (a->x < b->x) {
401
0
      box.p1.x = a->x;
402
0
      box.p2.x = b->x;
403
0
  } else {
404
0
      box.p1.x = b->x;
405
0
      box.p2.x = a->x;
406
0
  }
407
0
  if (a->y < b->y) {
408
0
      box.p1.y = a->y;
409
0
      box.p2.y = b->y;
410
0
  } else {
411
0
      box.p1.y = b->y;
412
0
      box.p2.y = a->y;
413
0
  }
414
415
0
  status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
416
0
  if (unlikely (status))
417
0
      return status;
418
0
    }
419
420
0
    stroker->num_segments = 0;
421
422
0
    return CAIRO_STATUS_SUCCESS;
423
0
}
424
425
static cairo_status_t
426
_cairo_rectilinear_stroker_move_to (void    *closure,
427
            const cairo_point_t *point)
428
41.5k
{
429
41.5k
    cairo_rectilinear_stroker_t *stroker = closure;
430
41.5k
    cairo_status_t status;
431
432
41.5k
    if (stroker->dash.dashed)
433
0
  status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
434
41.5k
    else
435
41.5k
  status = _cairo_rectilinear_stroker_emit_segments (stroker);
436
41.5k
    if (unlikely (status))
437
0
  return status;
438
439
    /* reset the dash pattern for new sub paths */
440
41.5k
    _cairo_stroker_dash_start (&stroker->dash);
441
442
41.5k
    stroker->current_point = *point;
443
41.5k
    stroker->first_point = *point;
444
445
41.5k
    return CAIRO_STATUS_SUCCESS;
446
41.5k
}
447
448
static cairo_status_t
449
_cairo_rectilinear_stroker_line_to (void    *closure,
450
            const cairo_point_t *b)
451
59.8k
{
452
59.8k
    cairo_rectilinear_stroker_t *stroker = closure;
453
59.8k
    cairo_point_t *a = &stroker->current_point;
454
59.8k
    cairo_status_t status;
455
456
    /* We only support horizontal or vertical elements. */
457
59.8k
    assert (a->x == b->x || a->y == b->y);
458
459
    /* We don't draw anything for degenerate paths. */
460
59.8k
    if (a->x == b->x && a->y == b->y)
461
10.9k
  return CAIRO_STATUS_SUCCESS;
462
463
48.9k
    status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
464
48.9k
                 (a->y == b->y) | JOIN);
465
466
48.9k
    stroker->current_point = *b;
467
48.9k
    stroker->open_sub_path = TRUE;
468
469
48.9k
    return status;
470
59.8k
}
471
472
static cairo_status_t
473
_cairo_rectilinear_stroker_line_to_dashed (void   *closure,
474
             const cairo_point_t  *point)
475
0
{
476
0
    cairo_rectilinear_stroker_t *stroker = closure;
477
0
    const cairo_point_t *a = &stroker->current_point;
478
0
    const cairo_point_t *b = point;
479
0
    cairo_bool_t fully_in_bounds;
480
0
    double sf, sign, remain;
481
0
    cairo_fixed_t mag;
482
0
    cairo_status_t status;
483
0
    cairo_line_t segment;
484
0
    cairo_bool_t dash_on = FALSE;
485
0
    unsigned is_horizontal;
486
487
    /* We don't draw anything for degenerate paths. */
488
0
    if (a->x == b->x && a->y == b->y)
489
0
  return CAIRO_STATUS_SUCCESS;
490
491
    /* We only support horizontal or vertical elements. */
492
0
    assert (a->x == b->x || a->y == b->y);
493
494
0
    fully_in_bounds = TRUE;
495
0
    if (stroker->has_bounds &&
496
0
  (! _cairo_box_contains_point (&stroker->bounds, a) ||
497
0
   ! _cairo_box_contains_point (&stroker->bounds, b)))
498
0
    {
499
0
  fully_in_bounds = FALSE;
500
0
    }
501
502
0
    is_horizontal = a->y == b->y;
503
0
    if (is_horizontal) {
504
0
  mag = b->x - a->x;
505
0
  sf = fabs (stroker->ctm->xx);
506
0
    } else {
507
0
  mag = b->y - a->y;
508
0
  sf = fabs (stroker->ctm->yy);
509
0
    }
510
0
    if (mag < 0) {
511
0
  remain = _cairo_fixed_to_double (-mag);
512
0
  sign = 1.;
513
0
    } else {
514
0
  remain = _cairo_fixed_to_double (mag);
515
0
  is_horizontal |= FORWARDS;
516
0
  sign = -1.;
517
0
    }
518
519
0
    segment.p2 = segment.p1 = *a;
520
0
    while (remain > 0.) {
521
0
  double step_length;
522
523
0
  step_length = MIN (sf * stroker->dash.dash_remain, remain);
524
0
  remain -= step_length;
525
526
0
  mag = _cairo_fixed_from_double (sign*remain);
527
0
  if (is_horizontal & 0x1)
528
0
      segment.p2.x = b->x + mag;
529
0
  else
530
0
      segment.p2.y = b->y + mag;
531
532
0
  if (stroker->dash.dash_on &&
533
0
      (fully_in_bounds ||
534
0
       _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
535
0
  {
536
0
      status = _cairo_rectilinear_stroker_add_segment (stroker,
537
0
                   &segment.p1,
538
0
                   &segment.p2,
539
0
                   is_horizontal | (remain <= 0.) << 2);
540
0
      if (unlikely (status))
541
0
    return status;
542
543
0
      dash_on = TRUE;
544
0
  }
545
0
  else
546
0
  {
547
0
      dash_on = FALSE;
548
0
  }
549
550
0
  _cairo_stroker_dash_step (&stroker->dash, step_length / sf);
551
0
  segment.p1 = segment.p2;
552
0
    }
553
554
0
    if (stroker->dash.dash_on && ! dash_on &&
555
0
  (fully_in_bounds ||
556
0
   _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
557
0
    {
558
559
  /* This segment ends on a transition to dash_on, compute a new face
560
   * and add cap for the beginning of the next dash_on step.
561
   */
562
563
0
  status = _cairo_rectilinear_stroker_add_segment (stroker,
564
0
               &segment.p1,
565
0
               &segment.p1,
566
0
               is_horizontal | JOIN);
567
0
  if (unlikely (status))
568
0
      return status;
569
0
    }
570
571
0
    stroker->current_point = *point;
572
0
    stroker->open_sub_path = TRUE;
573
574
0
    return CAIRO_STATUS_SUCCESS;
575
0
}
576
577
static cairo_status_t
578
_cairo_rectilinear_stroker_close_path (void *closure)
579
9.22k
{
580
9.22k
    cairo_rectilinear_stroker_t *stroker = closure;
581
9.22k
    cairo_status_t status;
582
583
    /* We don't draw anything for degenerate paths. */
584
9.22k
    if (! stroker->open_sub_path)
585
3.54k
  return CAIRO_STATUS_SUCCESS;
586
587
5.67k
    if (stroker->dash.dashed) {
588
0
  status = _cairo_rectilinear_stroker_line_to_dashed (stroker,
589
0
                  &stroker->first_point);
590
5.67k
    } else {
591
5.67k
  status = _cairo_rectilinear_stroker_line_to (stroker,
592
5.67k
                 &stroker->first_point);
593
5.67k
    }
594
5.67k
    if (unlikely (status))
595
0
  return status;
596
597
5.67k
    stroker->open_sub_path = FALSE;
598
599
5.67k
    if (stroker->dash.dashed)
600
0
  status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
601
5.67k
    else
602
5.67k
  status = _cairo_rectilinear_stroker_emit_segments (stroker);
603
5.67k
    if (unlikely (status))
604
0
  return status;
605
606
5.67k
    return CAIRO_STATUS_SUCCESS;
607
5.67k
}
608
609
cairo_int_status_t
610
_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path,
611
                 const cairo_stroke_style_t *stroke_style,
612
                 const cairo_matrix_t *ctm,
613
                 cairo_antialias_t   antialias,
614
                 cairo_boxes_t    *boxes)
615
20.6k
{
616
20.6k
    cairo_rectilinear_stroker_t rectilinear_stroker;
617
20.6k
    cairo_int_status_t status;
618
20.6k
    cairo_box_t box;
619
620
20.6k
    assert (_cairo_path_fixed_stroke_is_rectilinear (path));
621
622
20.6k
    if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
623
20.6k
             stroke_style, ctm, antialias,
624
20.6k
             boxes))
625
0
    {
626
0
  return CAIRO_INT_STATUS_UNSUPPORTED;
627
0
    }
628
629
20.6k
    if (! rectilinear_stroker.dash.dashed &&
630
20.6k
  _cairo_path_fixed_is_stroke_box (path, &box) &&
631
  /* if the segments overlap we need to feed them into the tessellator */
632
20.6k
  box.p2.x - box.p1.x > 2* rectilinear_stroker.half_line_x &&
633
20.6k
  box.p2.y - box.p1.y > 2* rectilinear_stroker.half_line_y)
634
620
    {
635
620
  cairo_box_t b;
636
637
  /* top */
638
620
  b.p1.x = box.p1.x - rectilinear_stroker.half_line_x;
639
620
  b.p2.x = box.p2.x + rectilinear_stroker.half_line_x;
640
620
  b.p1.y = box.p1.y - rectilinear_stroker.half_line_y;
641
620
  b.p2.y = box.p1.y + rectilinear_stroker.half_line_y;
642
620
  status = _cairo_boxes_add (boxes, antialias, &b);
643
620
  assert (status == CAIRO_INT_STATUS_SUCCESS);
644
645
  /* left  (excluding top/bottom) */
646
620
  b.p1.x = box.p1.x - rectilinear_stroker.half_line_x;
647
620
  b.p2.x = box.p1.x + rectilinear_stroker.half_line_x;
648
620
  b.p1.y = box.p1.y + rectilinear_stroker.half_line_y;
649
620
  b.p2.y = box.p2.y - rectilinear_stroker.half_line_y;
650
620
  status = _cairo_boxes_add (boxes, antialias, &b);
651
620
  assert (status == CAIRO_INT_STATUS_SUCCESS);
652
653
  /* right  (excluding top/bottom) */
654
620
  b.p1.x = box.p2.x - rectilinear_stroker.half_line_x;
655
620
  b.p2.x = box.p2.x + rectilinear_stroker.half_line_x;
656
620
  b.p1.y = box.p1.y + rectilinear_stroker.half_line_y;
657
620
  b.p2.y = box.p2.y - rectilinear_stroker.half_line_y;
658
620
  status = _cairo_boxes_add (boxes, antialias, &b);
659
620
  assert (status == CAIRO_INT_STATUS_SUCCESS);
660
661
  /* bottom */
662
620
  b.p1.x = box.p1.x - rectilinear_stroker.half_line_x;
663
620
  b.p2.x = box.p2.x + rectilinear_stroker.half_line_x;
664
620
  b.p1.y = box.p2.y - rectilinear_stroker.half_line_y;
665
620
  b.p2.y = box.p2.y + rectilinear_stroker.half_line_y;
666
620
  status = _cairo_boxes_add (boxes, antialias, &b);
667
620
  assert (status == CAIRO_INT_STATUS_SUCCESS);
668
669
620
  goto done;
670
620
    }
671
672
20.0k
    if (boxes->num_limits) {
673
14.5k
  _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
674
14.5k
            boxes->limits,
675
14.5k
            boxes->num_limits);
676
14.5k
    }
677
678
20.0k
    status = _cairo_path_fixed_interpret (path,
679
20.0k
            _cairo_rectilinear_stroker_move_to,
680
20.0k
            rectilinear_stroker.dash.dashed ?
681
0
            _cairo_rectilinear_stroker_line_to_dashed :
682
20.0k
            _cairo_rectilinear_stroker_line_to,
683
20.0k
            NULL,
684
20.0k
            _cairo_rectilinear_stroker_close_path,
685
20.0k
            &rectilinear_stroker);
686
20.0k
    if (unlikely (status))
687
0
  goto BAIL;
688
689
20.0k
    if (rectilinear_stroker.dash.dashed)
690
0
  status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
691
20.0k
    else
692
20.0k
  status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
693
20.0k
    if (unlikely (status))
694
0
  goto BAIL;
695
696
    /* As we incrementally tessellate, we do not eliminate self-intersections */
697
20.0k
    status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
698
20.0k
                  CAIRO_FILL_RULE_WINDING,
699
20.0k
                  boxes);
700
20.0k
    if (unlikely (status))
701
0
  goto BAIL;
702
703
20.6k
done:
704
20.6k
    _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
705
20.6k
    return CAIRO_STATUS_SUCCESS;
706
707
0
BAIL:
708
0
    _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
709
0
    _cairo_boxes_clear (boxes);
710
0
    return status;
711
20.0k
}