Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/cairo/src/cairo-rectangular-scan-converter.c
Line
Count
Source (jump to first uncovered line)
1
/* cairo - a vector graphics library with display and print output
2
 *
3
 * Copyright © 2009 Intel Corporation
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it either under the terms of the GNU Lesser General Public
7
 * License version 2.1 as published by the Free Software Foundation
8
 * (the "LGPL") or, at your option, under the terms of the Mozilla
9
 * Public License Version 1.1 (the "MPL"). If you do not alter this
10
 * notice, a recipient may use your version of this file under either
11
 * the MPL or the LGPL.
12
 *
13
 * You should have received a copy of the LGPL along with this library
14
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16
 * You should have received a copy of the MPL along with this library
17
 * in the file COPYING-MPL-1.1
18
 *
19
 * The contents of this file are subject to the Mozilla Public License
20
 * Version 1.1 (the "License"); you may not use this file except in
21
 * compliance with the License. You may obtain a copy of the License at
22
 * http://www.mozilla.org/MPL/
23
 *
24
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26
 * the specific language governing rights and limitations.
27
 *
28
 * The Original Code is the cairo graphics library.
29
 *
30
 * Contributor(s):
31
 *  Chris Wilson <chris@chris-wilson.co.uk>
32
 */
33
34
#include "cairoint.h"
35
36
#include "cairo-combsort-inline.h"
37
#include "cairo-error-private.h"
38
#include "cairo-freelist-private.h"
39
#include "cairo-list-private.h"
40
#include "cairo-spans-private.h"
41
42
#include <setjmp.h>
43
44
typedef struct _rectangle {
45
    struct _rectangle *next, *prev;
46
    cairo_fixed_t left, right;
47
    cairo_fixed_t top, bottom;
48
    int32_t top_y, bottom_y;
49
    int dir;
50
} rectangle_t;
51
52
3.35k
#define UNROLL3(x) x x x
53
54
/* the parent is always given by index/2 */
55
322
#define PQ_PARENT_INDEX(i) ((i) >> 1)
56
2.14k
#define PQ_FIRST_ENTRY 1
57
58
/* left and right children are index * 2 and (index * 2) +1 respectively */
59
426
#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
60
61
typedef struct _pqueue {
62
    int size, max_size;
63
64
    rectangle_t **elements;
65
    rectangle_t *elements_embedded[1024];
66
} pqueue_t;
67
68
typedef struct {
69
    rectangle_t **start;
70
    pqueue_t stop;
71
    rectangle_t head, tail;
72
    rectangle_t *insert_cursor;
73
    int32_t current_y;
74
    int32_t xmin, xmax;
75
76
    struct coverage {
77
  struct cell {
78
      struct cell *prev, *next;
79
      int x, covered, uncovered;
80
  } head, tail, *cursor;
81
  unsigned int count;
82
  cairo_freepool_t pool;
83
    } coverage;
84
85
    cairo_half_open_span_t spans_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_half_open_span_t)];
86
    cairo_half_open_span_t *spans;
87
    unsigned int num_spans;
88
    unsigned int size_spans;
89
90
    jmp_buf jmpbuf;
91
} sweep_line_t;
92
93
static inline int
94
rectangle_compare_start (const rectangle_t *a,
95
       const rectangle_t *b)
96
639
{
97
639
    int cmp;
98
99
639
    cmp = a->top_y - b->top_y;
100
639
    if (cmp)
101
535
  return cmp;
102
103
104
    return a->left - b->left;
104
639
}
105
106
static inline int
107
rectangle_compare_stop (const rectangle_t *a,
108
      const rectangle_t *b)
109
530
{
110
530
    return a->bottom_y - b->bottom_y;
111
530
}
112
113
static inline void
114
pqueue_init (pqueue_t *pq)
115
109
{
116
109
    pq->max_size = ARRAY_LENGTH (pq->elements_embedded);
117
109
    pq->size = 0;
118
119
109
    pq->elements = pq->elements_embedded;
120
109
    pq->elements[PQ_FIRST_ENTRY] = NULL;
121
109
}
122
123
static inline void
124
pqueue_fini (pqueue_t *pq)
125
109
{
126
109
    if (pq->elements != pq->elements_embedded)
127
0
  free (pq->elements);
128
109
}
129
130
static cairo_bool_t
131
pqueue_grow (pqueue_t *pq)
132
0
{
133
0
    rectangle_t **new_elements;
134
0
    pq->max_size *= 2;
135
136
0
    if (pq->elements == pq->elements_embedded) {
137
0
  new_elements = _cairo_malloc_ab (pq->max_size,
138
0
           sizeof (rectangle_t *));
139
0
  if (unlikely (new_elements == NULL))
140
0
      return FALSE;
141
142
0
  memcpy (new_elements, pq->elements_embedded,
143
0
    sizeof (pq->elements_embedded));
144
0
    } else {
145
0
  new_elements = _cairo_realloc_ab (pq->elements,
146
0
            pq->max_size,
147
0
            sizeof (rectangle_t *));
148
0
  if (unlikely (new_elements == NULL))
149
0
      return FALSE;
150
0
    }
151
152
0
    pq->elements = new_elements;
153
0
    return TRUE;
154
0
}
155
156
static inline void
157
pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle)
158
431
{
159
431
    rectangle_t **elements;
160
431
    int i, parent;
161
162
431
    if (unlikely (sweep->stop.size + 1 == sweep->stop.max_size)) {
163
0
  if (unlikely (! pqueue_grow (&sweep->stop)))
164
0
      longjmp (sweep->jmpbuf,
165
0
         _cairo_error (CAIRO_STATUS_NO_MEMORY));
166
0
    }
167
168
431
    elements = sweep->stop.elements;
169
431
    for (i = ++sweep->stop.size;
170
431
   i != PQ_FIRST_ENTRY &&
171
431
   rectangle_compare_stop (rectangle,
172
322
         elements[parent = PQ_PARENT_INDEX (i)]) < 0;
173
431
   i = parent)
174
0
    {
175
0
  elements[i] = elements[parent];
176
0
    }
177
178
431
    elements[i] = rectangle;
179
431
}
180
181
static inline void
182
pqueue_pop (pqueue_t *pq)
183
425
{
184
425
    rectangle_t **elements = pq->elements;
185
425
    rectangle_t *tail;
186
425
    int child, i;
187
188
425
    tail = elements[pq->size--];
189
425
    if (pq->size == 0) {
190
103
  elements[PQ_FIRST_ENTRY] = NULL;
191
103
  return;
192
103
    }
193
194
322
    for (i = PQ_FIRST_ENTRY;
195
426
   (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size;
196
322
   i = child)
197
208
    {
198
208
  if (child != pq->size &&
199
208
      rectangle_compare_stop (elements[child+1],
200
0
            elements[child]) < 0)
201
0
  {
202
0
      child++;
203
0
  }
204
205
208
  if (rectangle_compare_stop (elements[child], tail) >= 0)
206
104
      break;
207
208
104
  elements[i] = elements[child];
209
104
    }
210
322
    elements[i] = tail;
211
322
}
212
213
static inline rectangle_t *
214
peek_stop (sweep_line_t *sweep)
215
752
{
216
752
    return sweep->stop.elements[PQ_FIRST_ENTRY];
217
752
}
218
219
CAIRO_COMBSORT_DECLARE (rectangle_sort, rectangle_t *, rectangle_compare_start)
220
221
static void
222
sweep_line_init (sweep_line_t *sweep)
223
109
{
224
109
    sweep->head.left = INT_MIN;
225
109
    sweep->head.next = &sweep->tail;
226
109
    sweep->tail.left = INT_MAX;
227
109
    sweep->tail.prev = &sweep->head;
228
109
    sweep->insert_cursor = &sweep->tail;
229
230
109
    _cairo_freepool_init (&sweep->coverage.pool, sizeof (struct cell));
231
232
109
    sweep->spans = sweep->spans_stack;
233
109
    sweep->size_spans = ARRAY_LENGTH (sweep->spans_stack);
234
235
109
    sweep->coverage.head.prev = NULL;
236
109
    sweep->coverage.head.x = INT_MIN;
237
109
    sweep->coverage.tail.next = NULL;
238
109
    sweep->coverage.tail.x = INT_MAX;
239
240
109
    pqueue_init (&sweep->stop);
241
109
}
242
243
static void
244
sweep_line_fini (sweep_line_t *sweep)
245
109
{
246
109
    _cairo_freepool_fini (&sweep->coverage.pool);
247
109
    pqueue_fini (&sweep->stop);
248
249
109
    if (sweep->spans != sweep->spans_stack)
250
0
  free (sweep->spans);
251
109
}
252
253
static inline void
254
add_cell (sweep_line_t *sweep, int x, int covered, int uncovered)
255
1.75k
{
256
1.75k
    struct cell *cell;
257
258
1.75k
    cell = sweep->coverage.cursor;
259
1.75k
    if (cell->x > x) {
260
768
  do {
261
768
      UNROLL3({
262
306
    if (cell->prev->x < x)
263
306
        break;
264
306
    cell = cell->prev;
265
306
      })
266
306
  } while (TRUE);
267
988
    } else {
268
988
  if (cell->x == x)
269
98
      goto found;
270
271
890
  do {
272
890
      UNROLL3({
273
104
    cell = cell->next;
274
104
    if (cell->x >= x)
275
104
        break;
276
104
      })
277
104
  } while (TRUE);
278
890
    }
279
280
1.65k
    if (x != cell->x) {
281
1.14k
  struct cell *c;
282
283
1.14k
  sweep->coverage.count++;
284
285
1.14k
  c = _cairo_freepool_alloc (&sweep->coverage.pool);
286
1.14k
  if (unlikely (c == NULL)) {
287
0
      longjmp (sweep->jmpbuf,
288
0
         _cairo_error (CAIRO_STATUS_NO_MEMORY));
289
0
  }
290
291
1.14k
  cell->prev->next = c;
292
1.14k
  c->prev = cell->prev;
293
1.14k
  c->next = cell;
294
1.14k
  cell->prev = c;
295
296
1.14k
  c->x = x;
297
1.14k
  c->covered = 0;
298
1.14k
  c->uncovered = 0;
299
300
1.14k
  cell = c;
301
1.14k
    }
302
303
1.75k
found:
304
1.75k
    cell->covered += covered;
305
1.75k
    cell->uncovered += uncovered;
306
1.75k
    sweep->coverage.cursor = cell;
307
1.75k
}
308
309
static inline void
310
_active_edges_to_spans (sweep_line_t  *sweep)
311
446
{
312
446
    int32_t y = sweep->current_y;
313
446
    rectangle_t *rectangle;
314
446
    int coverage, prev_coverage;
315
446
    int prev_x;
316
446
    struct cell *cell;
317
318
446
    sweep->num_spans = 0;
319
446
    if (sweep->head.next == &sweep->tail)
320
0
  return;
321
322
446
    sweep->coverage.head.next = &sweep->coverage.tail;
323
446
    sweep->coverage.tail.prev = &sweep->coverage.head;
324
446
    sweep->coverage.cursor = &sweep->coverage.tail;
325
446
    sweep->coverage.count = 0;
326
327
    /* XXX cell coverage only changes when a rectangle appears or
328
     * disappears. Try only modifying coverage at such times.
329
     */
330
446
    for (rectangle = sweep->head.next;
331
1.32k
   rectangle != &sweep->tail;
332
878
   rectangle = rectangle->next)
333
878
    {
334
878
  int height;
335
878
  int frac, i;
336
337
878
  if (y == rectangle->bottom_y) {
338
425
      height = rectangle->bottom & CAIRO_FIXED_FRAC_MASK;
339
425
      if (height == 0)
340
0
    continue;
341
425
  } else
342
453
      height = CAIRO_FIXED_ONE;
343
878
  if (y == rectangle->top_y)
344
431
      height -= rectangle->top & CAIRO_FIXED_FRAC_MASK;
345
878
  height *= rectangle->dir;
346
347
878
  i = _cairo_fixed_integer_part (rectangle->left),
348
878
  frac = _cairo_fixed_fractional_part (rectangle->left);
349
878
  add_cell (sweep, i,
350
878
      (CAIRO_FIXED_ONE-frac) * height,
351
878
      frac * height);
352
353
878
  i = _cairo_fixed_integer_part (rectangle->right),
354
878
  frac = _cairo_fixed_fractional_part (rectangle->right);
355
878
  add_cell (sweep, i,
356
878
      -(CAIRO_FIXED_ONE-frac) * height,
357
878
      -frac * height);
358
878
    }
359
360
446
    if (2*sweep->coverage.count >= sweep->size_spans) {
361
0
  unsigned size;
362
363
0
  size = sweep->size_spans;
364
0
  while (size <= 2*sweep->coverage.count)
365
0
      size <<= 1;
366
367
0
  if (sweep->spans != sweep->spans_stack)
368
0
      free (sweep->spans);
369
370
0
  sweep->spans = _cairo_malloc_ab (size, sizeof (cairo_half_open_span_t));
371
0
  if (unlikely (sweep->spans == NULL))
372
0
      longjmp (sweep->jmpbuf, _cairo_error (CAIRO_STATUS_NO_MEMORY));
373
374
0
  sweep->size_spans = size;
375
0
    }
376
377
446
    prev_coverage = coverage = 0;
378
446
    prev_x = INT_MIN;
379
1.59k
    for (cell = sweep->coverage.head.next; cell != &sweep->coverage.tail; cell = cell->next) {
380
1.14k
  if (cell->x != prev_x && coverage != prev_coverage) {
381
240
      int n = sweep->num_spans++;
382
240
      int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
383
240
      sweep->spans[n].x = prev_x;
384
240
      sweep->spans[n].inverse = 0;
385
240
      sweep->spans[n].coverage = c - (c >> 8);
386
240
      prev_coverage = coverage;
387
240
  }
388
389
1.14k
  coverage += cell->covered;
390
1.14k
  if (coverage != prev_coverage) {
391
1.14k
      int n = sweep->num_spans++;
392
1.14k
      int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
393
1.14k
      sweep->spans[n].x = cell->x;
394
1.14k
      sweep->spans[n].inverse = 0;
395
1.14k
      sweep->spans[n].coverage = c - (c >> 8);
396
1.14k
      prev_coverage = coverage;
397
1.14k
  }
398
1.14k
  coverage += cell->uncovered;
399
1.14k
  prev_x = cell->x + 1;
400
1.14k
    }
401
446
    _cairo_freepool_reset (&sweep->coverage.pool);
402
403
446
    if (sweep->num_spans) {
404
446
  if (prev_x <= sweep->xmax) {
405
422
      int n = sweep->num_spans++;
406
422
      int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
407
422
      sweep->spans[n].x = prev_x;
408
422
      sweep->spans[n].inverse = 0;
409
422
      sweep->spans[n].coverage = c - (c >> 8);
410
422
  }
411
412
446
  if (coverage && prev_x < sweep->xmax) {
413
0
      int n = sweep->num_spans++;
414
0
      sweep->spans[n].x = sweep->xmax;
415
0
      sweep->spans[n].inverse = 1;
416
0
      sweep->spans[n].coverage = 0;
417
0
  }
418
446
    }
419
446
}
420
421
static inline void
422
sweep_line_delete (sweep_line_t *sweep,
423
           rectangle_t  *rectangle)
424
425
{
425
425
    if (sweep->insert_cursor == rectangle)
426
103
  sweep->insert_cursor = rectangle->next;
427
428
425
    rectangle->prev->next = rectangle->next;
429
425
    rectangle->next->prev = rectangle->prev;
430
431
425
    pqueue_pop (&sweep->stop);
432
425
}
433
434
static inline void
435
sweep_line_insert (sweep_line_t *sweep,
436
       rectangle_t  *rectangle)
437
431
{
438
431
    rectangle_t *pos;
439
440
431
    pos = sweep->insert_cursor;
441
431
    if (pos->left != rectangle->left) {
442
327
  if (pos->left > rectangle->left) {
443
218
      do {
444
218
    UNROLL3({
445
104
        if (pos->prev->left < rectangle->left)
446
104
      break;
447
104
        pos = pos->prev;
448
104
    })
449
104
      } while (TRUE);
450
218
  } else {
451
109
      do {
452
109
    UNROLL3({
453
104
        pos = pos->next;
454
104
        if (pos->left >= rectangle->left)
455
104
      break;
456
104
    });
457
0
      } while (TRUE);
458
109
  }
459
327
    }
460
461
0
    pos->prev->next = rectangle;
462
431
    rectangle->prev = pos->prev;
463
431
    rectangle->next = pos;
464
431
    pos->prev = rectangle;
465
431
    sweep->insert_cursor = rectangle;
466
467
431
    pqueue_push (sweep, rectangle);
468
431
}
469
470
static void
471
render_rows (sweep_line_t *sweep_line,
472
       cairo_span_renderer_t *renderer,
473
       int height)
474
446
{
475
446
    cairo_status_t status;
476
477
446
    _active_edges_to_spans (sweep_line);
478
479
446
    status = renderer->render_rows (renderer,
480
446
            sweep_line->current_y, height,
481
446
            sweep_line->spans,
482
446
            sweep_line->num_spans);
483
446
    if (unlikely (status))
484
0
  longjmp (sweep_line->jmpbuf, status);
485
446
}
486
487
static cairo_status_t
488
generate (cairo_rectangular_scan_converter_t *self,
489
    cairo_span_renderer_t *renderer,
490
    rectangle_t **rectangles)
491
109
{
492
109
    sweep_line_t sweep_line;
493
109
    rectangle_t *start, *stop;
494
109
    cairo_status_t status;
495
496
109
    sweep_line_init (&sweep_line);
497
109
    sweep_line.xmin = _cairo_fixed_integer_part (self->extents.p1.x);
498
109
    sweep_line.xmax = _cairo_fixed_integer_part (self->extents.p2.x);
499
109
    sweep_line.start = rectangles;
500
109
    if ((status = setjmp (sweep_line.jmpbuf)))
501
0
  goto out;
502
503
109
    sweep_line.current_y = _cairo_fixed_integer_part (self->extents.p1.y);
504
109
    start = *sweep_line.start++;
505
327
    do {
506
327
  if (start->top_y != sweep_line.current_y) {
507
11
      render_rows (&sweep_line, renderer,
508
11
       start->top_y - sweep_line.current_y);
509
11
      sweep_line.current_y = start->top_y;
510
11
  }
511
512
431
  do {
513
431
      sweep_line_insert (&sweep_line, start);
514
431
      start = *sweep_line.start++;
515
431
      if (start == NULL)
516
109
    goto end;
517
322
      if (start->top_y != sweep_line.current_y)
518
218
    break;
519
322
  } while (TRUE);
520
521
218
  render_rows (&sweep_line, renderer, 1);
522
523
218
  stop = peek_stop (&sweep_line);
524
327
  while (stop->bottom_y == sweep_line.current_y) {
525
109
      sweep_line_delete (&sweep_line, stop);
526
109
      stop = peek_stop (&sweep_line);
527
109
      if (stop == NULL)
528
0
    break;
529
109
  }
530
531
218
  sweep_line.current_y++;
532
533
218
  while (stop != NULL && stop->bottom_y < start->top_y) {
534
0
      if (stop->bottom_y != sweep_line.current_y) {
535
0
    render_rows (&sweep_line, renderer,
536
0
           stop->bottom_y - sweep_line.current_y);
537
0
    sweep_line.current_y = stop->bottom_y;
538
0
      }
539
540
0
      render_rows (&sweep_line, renderer, 1);
541
542
0
      do {
543
0
    sweep_line_delete (&sweep_line, stop);
544
0
    stop = peek_stop (&sweep_line);
545
0
      } while (stop != NULL && stop->bottom_y == sweep_line.current_y);
546
547
0
      sweep_line.current_y++;
548
0
  }
549
218
    } while (TRUE);
550
551
109
  end:
552
109
    render_rows (&sweep_line, renderer, 1);
553
554
109
    stop = peek_stop (&sweep_line);
555
322
    while (stop->bottom_y == sweep_line.current_y) {
556
213
  sweep_line_delete (&sweep_line, stop);
557
213
  stop = peek_stop (&sweep_line);
558
213
  if (stop == NULL)
559
0
      goto out;
560
213
    }
561
562
109
    while (++sweep_line.current_y < _cairo_fixed_integer_part (self->extents.p2.y)) {
563
103
  if (stop->bottom_y != sweep_line.current_y) {
564
5
      render_rows (&sweep_line, renderer,
565
5
       stop->bottom_y - sweep_line.current_y);
566
5
      sweep_line.current_y = stop->bottom_y;
567
5
  }
568
569
103
  render_rows (&sweep_line, renderer, 1);
570
571
103
  do {
572
103
      sweep_line_delete (&sweep_line, stop);
573
103
      stop = peek_stop (&sweep_line);
574
103
      if (stop == NULL)
575
103
    goto out;
576
103
  } while (stop->bottom_y == sweep_line.current_y);
577
578
103
    }
579
580
109
  out:
581
109
    sweep_line_fini (&sweep_line);
582
583
109
    return status;
584
109
}
585
static void generate_row(cairo_span_renderer_t *renderer,
586
       const rectangle_t *r,
587
       int y, int h,
588
       uint16_t coverage)
589
1.17k
{
590
1.17k
    cairo_half_open_span_t spans[4];
591
1.17k
    unsigned int num_spans = 0;
592
1.17k
    int x1 = _cairo_fixed_integer_part (r->left);
593
1.17k
    int x2 = _cairo_fixed_integer_part (r->right);
594
1.17k
    if (x2 > x1) {
595
1.17k
  if (! _cairo_fixed_is_integer (r->left)) {
596
1.17k
      spans[num_spans].x = x1;
597
1.17k
      spans[num_spans].coverage =
598
1.17k
    coverage * (256 - _cairo_fixed_fractional_part (r->left)) >> 8;
599
1.17k
      num_spans++;
600
1.17k
      x1++;
601
1.17k
  }
602
603
1.17k
  if (x2 > x1) {
604
1.09k
      spans[num_spans].x = x1;
605
1.09k
      spans[num_spans].coverage = coverage - (coverage >> 8);
606
1.09k
      num_spans++;
607
1.09k
  }
608
609
1.17k
  if (! _cairo_fixed_is_integer (r->right)) {
610
159
      spans[num_spans].x = x2++;
611
159
      spans[num_spans].coverage =
612
159
    coverage * _cairo_fixed_fractional_part (r->right) >> 8;
613
159
      num_spans++;
614
159
  }
615
1.17k
    } else {
616
0
  spans[num_spans].x = x2++;
617
0
  spans[num_spans].coverage = coverage * (r->right - r->left) >> 8;
618
0
  num_spans++;
619
0
    }
620
621
1.17k
    spans[num_spans].x = x2;
622
1.17k
    spans[num_spans].coverage = 0;
623
1.17k
    num_spans++;
624
625
1.17k
    renderer->render_rows (renderer, y, h, spans, num_spans);
626
1.17k
}
627
628
static cairo_status_t
629
generate_box (cairo_rectangular_scan_converter_t *self,
630
        cairo_span_renderer_t *renderer)
631
438
{
632
438
    const rectangle_t *r = self->chunks.base;
633
438
    int y1 = _cairo_fixed_integer_part (r->top);
634
438
    int y2 = _cairo_fixed_integer_part (r->bottom);
635
438
    if (y2 > y1) {
636
438
  if (! _cairo_fixed_is_integer (r->top)) {
637
438
      generate_row(renderer, r, y1, 1,
638
438
       256 - _cairo_fixed_fractional_part (r->top));
639
438
      y1++;
640
438
  }
641
642
438
  if (y2 > y1)
643
428
      generate_row(renderer, r, y1, y2-y1, 256);
644
645
438
  if (! _cairo_fixed_is_integer (r->bottom))
646
311
      generate_row(renderer, r, y2, 1,
647
311
       _cairo_fixed_fractional_part (r->bottom));
648
438
    } else
649
0
  generate_row(renderer, r, y1, 1, r->bottom - r->top);
650
651
438
    return CAIRO_STATUS_SUCCESS;
652
438
}
653
654
static cairo_status_t
655
_cairo_rectangular_scan_converter_generate (void      *converter,
656
              cairo_span_renderer_t *renderer)
657
547
{
658
547
    cairo_rectangular_scan_converter_t *self = converter;
659
547
    rectangle_t *rectangles_stack[CAIRO_STACK_ARRAY_LENGTH (rectangle_t *)];
660
547
    rectangle_t **rectangles;
661
547
    struct _cairo_rectangular_scan_converter_chunk *chunk;
662
547
    cairo_status_t status;
663
547
    int i, j;
664
665
547
    if (unlikely (self->num_rectangles == 0)) {
666
0
  return renderer->render_rows (renderer,
667
0
              _cairo_fixed_integer_part (self->extents.p1.y),
668
0
              _cairo_fixed_integer_part (self->extents.p2.y - self->extents.p1.y),
669
0
              NULL, 0);
670
0
    }
671
672
547
    if (self->num_rectangles == 1)
673
438
  return generate_box (self, renderer);
674
675
109
    rectangles = rectangles_stack;
676
109
    if (unlikely (self->num_rectangles >= ARRAY_LENGTH (rectangles_stack))) {
677
0
  rectangles = _cairo_malloc_ab (self->num_rectangles + 1,
678
0
               sizeof (rectangle_t *));
679
0
  if (unlikely (rectangles == NULL))
680
0
      return _cairo_error (CAIRO_STATUS_NO_MEMORY);
681
0
    }
682
683
109
    j = 0;
684
218
    for (chunk = &self->chunks; chunk != NULL; chunk = chunk->next) {
685
109
  rectangle_t *rectangle;
686
687
109
  rectangle = chunk->base;
688
540
  for (i = 0; i < chunk->count; i++)
689
431
      rectangles[j++] = &rectangle[i];
690
109
    }
691
109
    rectangle_sort (rectangles, j);
692
109
    rectangles[j] = NULL;
693
694
109
    status = generate (self, renderer, rectangles);
695
696
109
    if (rectangles != rectangles_stack)
697
0
  free (rectangles);
698
699
109
    return status;
700
109
}
701
702
static rectangle_t *
703
_allocate_rectangle (cairo_rectangular_scan_converter_t *self)
704
869
{
705
869
    rectangle_t *rectangle;
706
869
    struct _cairo_rectangular_scan_converter_chunk *chunk;
707
708
869
    chunk = self->tail;
709
869
    if (chunk->count == chunk->size) {
710
0
  int size;
711
712
0
  size = chunk->size * 2;
713
0
  chunk->next = _cairo_malloc_ab_plus_c (size,
714
0
                 sizeof (rectangle_t),
715
0
                 sizeof (struct _cairo_rectangular_scan_converter_chunk));
716
717
0
  if (unlikely (chunk->next == NULL))
718
0
      return NULL;
719
720
0
  chunk = chunk->next;
721
0
  chunk->next = NULL;
722
0
  chunk->count = 0;
723
0
  chunk->size = size;
724
0
  chunk->base = chunk + 1;
725
0
  self->tail = chunk;
726
0
    }
727
728
869
    rectangle = chunk->base;
729
869
    return rectangle + chunk->count++;
730
869
}
731
732
cairo_status_t
733
_cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t *self,
734
             const cairo_box_t *box,
735
             int dir)
736
869
{
737
869
    rectangle_t *rectangle;
738
739
869
    rectangle = _allocate_rectangle (self);
740
869
    if (unlikely (rectangle == NULL))
741
0
  return _cairo_error (CAIRO_STATUS_NO_MEMORY);
742
743
869
    rectangle->dir = dir;
744
869
    rectangle->left  = MAX (box->p1.x, self->extents.p1.x);
745
869
    rectangle->right = MIN (box->p2.x, self->extents.p2.x);
746
869
    if (unlikely (rectangle->right <= rectangle->left)) {
747
0
  self->tail->count--;
748
0
  return CAIRO_STATUS_SUCCESS;
749
0
    }
750
751
869
    rectangle->top = MAX (box->p1.y, self->extents.p1.y);
752
869
    rectangle->top_y  = _cairo_fixed_integer_floor (rectangle->top);
753
869
    rectangle->bottom = MIN (box->p2.y, self->extents.p2.y);
754
869
    rectangle->bottom_y = _cairo_fixed_integer_floor (rectangle->bottom);
755
869
    if (likely (rectangle->bottom > rectangle->top))
756
869
  self->num_rectangles++;
757
0
    else
758
0
  self->tail->count--;
759
760
869
    return CAIRO_STATUS_SUCCESS;
761
869
}
762
763
static void
764
_cairo_rectangular_scan_converter_destroy (void *converter)
765
547
{
766
547
    cairo_rectangular_scan_converter_t *self = converter;
767
547
    struct _cairo_rectangular_scan_converter_chunk *chunk, *next;
768
769
547
    for (chunk = self->chunks.next; chunk != NULL; chunk = next) {
770
0
  next = chunk->next;
771
0
  free (chunk);
772
0
    }
773
547
}
774
775
void
776
_cairo_rectangular_scan_converter_init (cairo_rectangular_scan_converter_t *self,
777
          const cairo_rectangle_int_t *extents)
778
547
{
779
547
    self->base.destroy = _cairo_rectangular_scan_converter_destroy;
780
547
    self->base.generate = _cairo_rectangular_scan_converter_generate;
781
782
547
    _cairo_box_from_rectangle (&self->extents, extents);
783
784
547
    self->chunks.base = self->buf;
785
547
    self->chunks.next = NULL;
786
547
    self->chunks.count = 0;
787
547
    self->chunks.size = sizeof (self->buf) / sizeof (rectangle_t);
788
547
    self->tail = &self->chunks;
789
790
547
    self->num_rectangles = 0;
791
547
}