Coverage Report

Created: 2026-05-16 07:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cairo/src/cairo-bentley-ottmann-rectilinear.c
Line
Count
Source
1
/*
2
 * Copyright © 2004 Carl Worth
3
 * Copyright © 2006 Red Hat, Inc.
4
 * Copyright © 2008 Chris Wilson
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 Carl Worth
32
 *
33
 * Contributor(s):
34
 *  Carl D. Worth <cworth@cworth.org>
35
 *  Chris Wilson <chris@chris-wilson.co.uk>
36
 */
37
38
/* Provide definitions for standalone compilation */
39
#include "cairoint.h"
40
41
#include "cairo-boxes-private.h"
42
#include "cairo-combsort-inline.h"
43
#include "cairo-error-private.h"
44
#include "cairo-traps-private.h"
45
46
typedef struct _cairo_bo_edge cairo_bo_edge_t;
47
typedef struct _cairo_bo_trap cairo_bo_trap_t;
48
49
/* A deferred trapezoid of an edge */
50
struct _cairo_bo_trap {
51
    cairo_bo_edge_t *right;
52
    int32_t top;
53
};
54
55
struct _cairo_bo_edge {
56
    cairo_edge_t edge;
57
    cairo_bo_edge_t *prev;
58
    cairo_bo_edge_t *next;
59
    cairo_bo_trap_t deferred_trap;
60
};
61
62
typedef enum {
63
    CAIRO_BO_EVENT_TYPE_START,
64
    CAIRO_BO_EVENT_TYPE_STOP
65
} cairo_bo_event_type_t;
66
67
typedef struct _cairo_bo_event {
68
    cairo_bo_event_type_t type;
69
    cairo_point_t point;
70
    cairo_bo_edge_t *edge;
71
} cairo_bo_event_t;
72
73
typedef struct _cairo_bo_sweep_line {
74
    cairo_bo_event_t **events;
75
    cairo_bo_edge_t *head;
76
    cairo_bo_edge_t *stopped;
77
    int32_t current_y;
78
    cairo_bo_edge_t *current_edge;
79
} cairo_bo_sweep_line_t;
80
81
static inline int
82
_cairo_point_compare (const cairo_point_t *a,
83
          const cairo_point_t *b)
84
5.45k
{
85
5.45k
    int cmp;
86
87
5.45k
    cmp = a->y - b->y;
88
5.45k
    if (likely (cmp))
89
1.55k
  return cmp;
90
91
3.90k
    return a->x - b->x;
92
5.45k
}
93
94
static inline int
95
_cairo_bo_edge_compare (const cairo_bo_edge_t *a,
96
      const cairo_bo_edge_t *b)
97
529
{
98
529
    int cmp;
99
100
529
    cmp = a->edge.line.p1.x - b->edge.line.p1.x;
101
529
    if (likely (cmp))
102
523
  return cmp;
103
104
6
    return b->edge.bottom - a->edge.bottom;
105
529
}
106
107
static inline int
108
cairo_bo_event_compare (const cairo_bo_event_t *a,
109
      const cairo_bo_event_t *b)
110
5.45k
{
111
5.45k
    int cmp;
112
113
5.45k
    cmp = _cairo_point_compare (&a->point, &b->point);
114
5.45k
    if (likely (cmp))
115
5.44k
  return cmp;
116
117
12
    cmp = a->type - b->type;
118
12
    if (cmp)
119
12
  return cmp;
120
121
0
    return a - b;
122
12
}
123
124
static inline cairo_bo_event_t *
125
_cairo_bo_event_dequeue (cairo_bo_sweep_line_t *sweep_line)
126
1.48k
{
127
1.48k
    return *sweep_line->events++;
128
1.48k
}
129
130
CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort,
131
      cairo_bo_event_t *,
132
      cairo_bo_event_compare)
133
134
static void
135
_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line,
136
         cairo_bo_event_t **events,
137
         int        num_events)
138
161
{
139
161
    _cairo_bo_event_queue_sort (events, num_events);
140
161
    events[num_events] = NULL;
141
161
    sweep_line->events = events;
142
143
161
    sweep_line->head = NULL;
144
161
    sweep_line->current_y = INT32_MIN;
145
161
    sweep_line->current_edge = NULL;
146
161
}
147
148
static void
149
_cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t  *sweep_line,
150
           cairo_bo_edge_t    *edge)
151
660
{
152
660
    if (sweep_line->current_edge != NULL) {
153
499
  cairo_bo_edge_t *prev, *next;
154
499
  int cmp;
155
156
499
  cmp = _cairo_bo_edge_compare (sweep_line->current_edge, edge);
157
499
  if (cmp < 0) {
158
492
      prev = sweep_line->current_edge;
159
492
      next = prev->next;
160
492
      while (next != NULL && _cairo_bo_edge_compare (next, edge) < 0)
161
0
    prev = next, next = prev->next;
162
163
492
      prev->next = edge;
164
492
      edge->prev = prev;
165
492
      edge->next = next;
166
492
      if (next != NULL)
167
23
    next->prev = edge;
168
492
  } else if (cmp > 0) {
169
7
      next = sweep_line->current_edge;
170
7
      prev = next->prev;
171
13
      while (prev != NULL && _cairo_bo_edge_compare (prev, edge) > 0)
172
6
    next = prev, prev = next->prev;
173
174
7
      next->prev = edge;
175
7
      edge->next = next;
176
7
      edge->prev = prev;
177
7
      if (prev != NULL)
178
1
    prev->next = edge;
179
6
      else
180
6
    sweep_line->head = edge;
181
7
  } else {
182
0
      prev = sweep_line->current_edge;
183
0
      edge->prev = prev;
184
0
      edge->next = prev->next;
185
0
      if (prev->next != NULL)
186
0
    prev->next->prev = edge;
187
0
      prev->next = edge;
188
0
  }
189
499
    } else {
190
161
  sweep_line->head = edge;
191
161
    }
192
193
660
    sweep_line->current_edge = edge;
194
660
}
195
196
static void
197
_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t  *sweep_line,
198
           cairo_bo_edge_t  *edge)
199
660
{
200
660
    if (edge->prev != NULL)
201
18
  edge->prev->next = edge->next;
202
642
    else
203
642
  sweep_line->head = edge->next;
204
205
660
    if (edge->next != NULL)
206
499
  edge->next->prev = edge->prev;
207
208
660
    if (sweep_line->current_edge == edge)
209
169
  sweep_line->current_edge = edge->prev ? edge->prev : edge->next;
210
660
}
211
212
static inline cairo_bool_t
213
edges_collinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b)
214
505
{
215
505
    return a->edge.line.p1.x == b->edge.line.p1.x;
216
505
}
217
218
static cairo_status_t
219
_cairo_bo_edge_end_trap (cairo_bo_edge_t  *left,
220
       int32_t     bot,
221
       cairo_bool_t    do_traps,
222
       void     *container)
223
336
{
224
336
    cairo_bo_trap_t *trap = &left->deferred_trap;
225
336
    cairo_status_t status = CAIRO_STATUS_SUCCESS;
226
227
    /* Only emit (trivial) non-degenerate trapezoids with positive height. */
228
336
    if (likely (trap->top < bot)) {
229
336
  if (do_traps) {
230
0
      _cairo_traps_add_trap (container,
231
0
           trap->top, bot,
232
0
           &left->edge.line, &trap->right->edge.line);
233
0
      status =  _cairo_traps_status ((cairo_traps_t *) container);
234
336
  } else {
235
336
      cairo_box_t box;
236
237
336
      box.p1.x = left->edge.line.p1.x;
238
336
      box.p1.y = trap->top;
239
336
      box.p2.x = trap->right->edge.line.p1.x;
240
336
      box.p2.y = bot;
241
336
      status = _cairo_boxes_add (container, CAIRO_ANTIALIAS_DEFAULT, &box);
242
336
  }
243
336
    }
244
245
336
    trap->right = NULL;
246
247
336
    return status;
248
336
}
249
250
/* Start a new trapezoid at the given top y coordinate, whose edges
251
 * are `edge' and `edge->next'. If `edge' already has a trapezoid,
252
 * then either add it to the traps in `traps', if the trapezoid's
253
 * right edge differs from `edge->next', or do nothing if the new
254
 * trapezoid would be a continuation of the existing one. */
255
static inline cairo_status_t
256
_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t  *left,
257
               cairo_bo_edge_t  *right,
258
               int               top,
259
               cairo_bool_t  do_traps,
260
               void   *container)
261
336
{
262
336
    cairo_status_t status;
263
264
336
    if (left->deferred_trap.right == right)
265
0
  return CAIRO_STATUS_SUCCESS;
266
267
336
    if (left->deferred_trap.right != NULL) {
268
3
  if (right != NULL && edges_collinear (left->deferred_trap.right, right))
269
0
  {
270
      /* continuation on right, so just swap edges */
271
0
      left->deferred_trap.right = right;
272
0
      return CAIRO_STATUS_SUCCESS;
273
0
  }
274
275
3
  status = _cairo_bo_edge_end_trap (left, top, do_traps, container);
276
3
  if (unlikely (status))
277
0
      return status;
278
3
    }
279
280
336
    if (right != NULL && ! edges_collinear (left, right)) {
281
336
  left->deferred_trap.top = top;
282
336
  left->deferred_trap.right = right;
283
336
    }
284
285
336
    return CAIRO_STATUS_SUCCESS;
286
336
}
287
288
static inline cairo_status_t
289
_active_edges_to_traps (cairo_bo_edge_t   *left,
290
      int32_t      top,
291
      cairo_fill_rule_t  fill_rule,
292
      cairo_bool_t     do_traps,
293
      void      *container)
294
331
{
295
331
    cairo_bo_edge_t *right;
296
331
    cairo_status_t status;
297
298
331
    if (fill_rule == CAIRO_FILL_RULE_WINDING) {
299
21
  while (left != NULL) {
300
16
      int in_out;
301
302
      /* Greedily search for the closing edge, so that we generate the
303
       * maximal span width with the minimal number of trapezoids.
304
       */
305
16
      in_out = left->edge.dir;
306
307
      /* Check if there is a co-linear edge with an existing trap */
308
16
      right = left->next;
309
16
      if (left->deferred_trap.right == NULL) {
310
86
    while (right != NULL && right->deferred_trap.right == NULL)
311
73
        right = right->next;
312
313
13
    if (right != NULL && edges_collinear (left, right)) {
314
        /* continuation on left */
315
0
        left->deferred_trap = right->deferred_trap;
316
0
        right->deferred_trap.right = NULL;
317
0
    }
318
13
      }
319
320
      /* End all subsumed traps */
321
16
      right = left->next;
322
16
      while (right != NULL) {
323
16
    if (right->deferred_trap.right != NULL) {
324
0
        status = _cairo_bo_edge_end_trap (right, top, do_traps, container);
325
0
        if (unlikely (status))
326
0
      return status;
327
0
    }
328
329
16
    in_out += right->edge.dir;
330
16
    if (in_out == 0) {
331
        /* skip co-linear edges */
332
16
        if (right->next == NULL ||
333
12
      ! edges_collinear (right, right->next))
334
16
        {
335
16
      break;
336
16
        }
337
16
    }
338
339
0
    right = right->next;
340
0
      }
341
342
16
      status = _cairo_bo_edge_start_or_continue_trap (left, right, top,
343
16
                  do_traps, container);
344
16
      if (unlikely (status))
345
0
    return status;
346
347
16
      left = right;
348
16
      if (left != NULL)
349
16
    left = left->next;
350
16
  }
351
326
    } else {
352
646
  while (left != NULL) {
353
320
      int in_out = 0;
354
355
320
      right = left->next;
356
320
      while (right != NULL) {
357
320
    if (right->deferred_trap.right != NULL) {
358
0
        status = _cairo_bo_edge_end_trap (right, top, do_traps, container);
359
0
        if (unlikely (status))
360
0
      return status;
361
0
    }
362
363
320
    if ((in_out++ & 1) == 0) {
364
320
        cairo_bo_edge_t *next;
365
320
        cairo_bool_t skip = FALSE;
366
367
        /* skip co-linear edges */
368
320
        next = right->next;
369
320
        if (next != NULL)
370
154
      skip = edges_collinear (right, next);
371
372
320
        if (! skip)
373
320
      break;
374
320
    }
375
376
0
    right = right->next;
377
0
      }
378
379
320
      status = _cairo_bo_edge_start_or_continue_trap (left, right, top,
380
320
                  do_traps, container);
381
320
      if (unlikely (status))
382
0
    return status;
383
384
320
      left = right;
385
320
      if (left != NULL)
386
320
    left = left->next;
387
320
  }
388
326
    }
389
390
331
    return CAIRO_STATUS_SUCCESS;
391
331
}
392
393
static cairo_status_t
394
_cairo_bentley_ottmann_tessellate_rectilinear (cairo_bo_event_t   **start_events,
395
                 int       num_events,
396
                 cairo_fill_rule_t   fill_rule,
397
                 cairo_bool_t    do_traps,
398
                 void     *container)
399
161
{
400
161
    cairo_bo_sweep_line_t sweep_line;
401
161
    cairo_bo_event_t *event;
402
161
    cairo_status_t status;
403
404
161
    _cairo_bo_sweep_line_init (&sweep_line, start_events, num_events);
405
406
1.48k
    while ((event = _cairo_bo_event_dequeue (&sweep_line))) {
407
1.32k
  if (event->point.y != sweep_line.current_y) {
408
331
      status = _active_edges_to_traps (sweep_line.head,
409
331
               sweep_line.current_y,
410
331
               fill_rule, do_traps, container);
411
331
      if (unlikely (status))
412
0
    return status;
413
414
331
      sweep_line.current_y = event->point.y;
415
331
  }
416
417
1.32k
  switch (event->type) {
418
660
  case CAIRO_BO_EVENT_TYPE_START:
419
660
      _cairo_bo_sweep_line_insert (&sweep_line, event->edge);
420
660
      break;
421
422
660
  case CAIRO_BO_EVENT_TYPE_STOP:
423
660
      _cairo_bo_sweep_line_delete (&sweep_line, event->edge);
424
425
660
      if (event->edge->deferred_trap.right != NULL) {
426
333
    status = _cairo_bo_edge_end_trap (event->edge,
427
333
              sweep_line.current_y,
428
333
              do_traps, container);
429
333
    if (unlikely (status))
430
0
        return status;
431
333
      }
432
433
660
      break;
434
1.32k
  }
435
1.32k
    }
436
437
161
    return CAIRO_STATUS_SUCCESS;
438
161
}
439
440
cairo_status_t
441
_cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_polygon_t *polygon,
442
                cairo_fill_rule_t   fill_rule,
443
                cairo_boxes_t *boxes)
444
161
{
445
161
    cairo_status_t status;
446
161
    cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)];
447
161
    cairo_bo_event_t *events;
448
161
    cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
449
161
    cairo_bo_event_t **event_ptrs;
450
161
    cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)];
451
161
    cairo_bo_edge_t *edges;
452
161
    int num_events;
453
161
    int i, j;
454
455
161
    if (unlikely (polygon->num_edges == 0))
456
0
  return CAIRO_STATUS_SUCCESS;
457
458
161
    num_events = 2 * polygon->num_edges;
459
460
161
    events = stack_events;
461
161
    event_ptrs = stack_event_ptrs;
462
161
    edges = stack_edges;
463
161
    if (num_events > ARRAY_LENGTH (stack_events)) {
464
0
  events = _cairo_malloc_ab_plus_c (num_events,
465
0
            sizeof (cairo_bo_event_t) +
466
0
            sizeof (cairo_bo_edge_t) +
467
0
            sizeof (cairo_bo_event_t *),
468
0
            sizeof (cairo_bo_event_t *));
469
0
  if (unlikely (events == NULL))
470
0
      return _cairo_error (CAIRO_STATUS_NO_MEMORY);
471
472
0
  event_ptrs = (cairo_bo_event_t **) (events + num_events);
473
0
  edges = (cairo_bo_edge_t *) (event_ptrs + num_events + 1);
474
0
    }
475
476
821
    for (i = j = 0; i < polygon->num_edges; i++) {
477
660
  edges[i].edge = polygon->edges[i];
478
660
  edges[i].deferred_trap.right = NULL;
479
660
  edges[i].prev = NULL;
480
660
  edges[i].next = NULL;
481
482
660
  event_ptrs[j] = &events[j];
483
660
  events[j].type = CAIRO_BO_EVENT_TYPE_START;
484
660
  events[j].point.y = polygon->edges[i].top;
485
660
  events[j].point.x = polygon->edges[i].line.p1.x;
486
660
  events[j].edge = &edges[i];
487
660
  j++;
488
489
660
  event_ptrs[j] = &events[j];
490
660
  events[j].type = CAIRO_BO_EVENT_TYPE_STOP;
491
660
  events[j].point.y = polygon->edges[i].bottom;
492
660
  events[j].point.x = polygon->edges[i].line.p1.x;
493
660
  events[j].edge = &edges[i];
494
660
  j++;
495
660
    }
496
497
161
    status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j,
498
161
                  fill_rule,
499
161
                  FALSE, boxes);
500
161
    if (events != stack_events)
501
0
  free (events);
502
503
161
    return status;
504
161
}
505
506
cairo_status_t
507
_cairo_bentley_ottmann_tessellate_rectilinear_traps (cairo_traps_t *traps,
508
                 cairo_fill_rule_t fill_rule)
509
0
{
510
0
    cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)];
511
0
    cairo_bo_event_t *events;
512
0
    cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
513
0
    cairo_bo_event_t **event_ptrs;
514
0
    cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)];
515
0
    cairo_bo_edge_t *edges;
516
0
    cairo_status_t status;
517
0
    int i, j, k;
518
519
0
    if (unlikely (traps->num_traps == 0))
520
0
  return CAIRO_STATUS_SUCCESS;
521
522
0
    assert (traps->is_rectilinear);
523
524
0
    i = 4 * traps->num_traps;
525
526
0
    events = stack_events;
527
0
    event_ptrs = stack_event_ptrs;
528
0
    edges = stack_edges;
529
0
    if (i > ARRAY_LENGTH (stack_events)) {
530
0
  events = _cairo_malloc_ab_plus_c (i,
531
0
            sizeof (cairo_bo_event_t) +
532
0
            sizeof (cairo_bo_edge_t) +
533
0
            sizeof (cairo_bo_event_t *),
534
0
            sizeof (cairo_bo_event_t *));
535
0
  if (unlikely (events == NULL))
536
0
      return _cairo_error (CAIRO_STATUS_NO_MEMORY);
537
538
0
  event_ptrs = (cairo_bo_event_t **) (events + i);
539
0
  edges = (cairo_bo_edge_t *) (event_ptrs + i + 1);
540
0
    }
541
542
0
    for (i = j = k = 0; i < traps->num_traps; i++) {
543
0
  edges[k].edge.top = traps->traps[i].top;
544
0
  edges[k].edge.bottom = traps->traps[i].bottom;
545
0
  edges[k].edge.line = traps->traps[i].left;
546
0
  edges[k].edge.dir = 1;
547
0
  edges[k].deferred_trap.right = NULL;
548
0
  edges[k].prev = NULL;
549
0
  edges[k].next = NULL;
550
551
0
  event_ptrs[j] = &events[j];
552
0
  events[j].type = CAIRO_BO_EVENT_TYPE_START;
553
0
  events[j].point.y = traps->traps[i].top;
554
0
  events[j].point.x = traps->traps[i].left.p1.x;
555
0
  events[j].edge = &edges[k];
556
0
  j++;
557
558
0
  event_ptrs[j] = &events[j];
559
0
  events[j].type = CAIRO_BO_EVENT_TYPE_STOP;
560
0
  events[j].point.y = traps->traps[i].bottom;
561
0
  events[j].point.x = traps->traps[i].left.p1.x;
562
0
  events[j].edge = &edges[k];
563
0
  j++;
564
0
  k++;
565
566
0
  edges[k].edge.top = traps->traps[i].top;
567
0
  edges[k].edge.bottom = traps->traps[i].bottom;
568
0
  edges[k].edge.line = traps->traps[i].right;
569
0
  edges[k].edge.dir = -1;
570
0
  edges[k].deferred_trap.right = NULL;
571
0
  edges[k].prev = NULL;
572
0
  edges[k].next = NULL;
573
574
0
  event_ptrs[j] = &events[j];
575
0
  events[j].type = CAIRO_BO_EVENT_TYPE_START;
576
0
  events[j].point.y = traps->traps[i].top;
577
0
  events[j].point.x = traps->traps[i].right.p1.x;
578
0
  events[j].edge = &edges[k];
579
0
  j++;
580
581
0
  event_ptrs[j] = &events[j];
582
0
  events[j].type = CAIRO_BO_EVENT_TYPE_STOP;
583
0
  events[j].point.y = traps->traps[i].bottom;
584
0
  events[j].point.x = traps->traps[i].right.p1.x;
585
0
  events[j].edge = &edges[k];
586
0
  j++;
587
0
  k++;
588
0
    }
589
590
0
    _cairo_traps_clear (traps);
591
0
    status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j,
592
0
                  fill_rule,
593
0
                  TRUE, traps);
594
0
    traps->is_rectilinear = TRUE;
595
596
0
    if (events != stack_events)
597
0
  free (events);
598
599
0
    return status;
600
0
}