Coverage Report

Created: 2026-09-28 08:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cairo/src/cairo-boxes.c
Line
Count
Source
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-box-inline.h"
37
#include "cairo-boxes-private.h"
38
#include "cairo-error-private.h"
39
40
void
41
_cairo_boxes_init (cairo_boxes_t *boxes)
42
1.00k
{
43
1.00k
    boxes->status = CAIRO_STATUS_SUCCESS;
44
1.00k
    boxes->num_limits = 0;
45
1.00k
    boxes->num_boxes = 0;
46
47
1.00k
    boxes->tail = &boxes->chunks;
48
1.00k
    boxes->chunks.next = NULL;
49
1.00k
    boxes->chunks.base = boxes->boxes_embedded;
50
1.00k
    boxes->chunks.size = ARRAY_LENGTH (boxes->boxes_embedded);
51
1.00k
    boxes->chunks.count = 0;
52
53
1.00k
    boxes->is_pixel_aligned = TRUE;
54
1.00k
}
55
56
void
57
_cairo_boxes_init_from_rectangle (cairo_boxes_t *boxes,
58
          int x, int y, int w, int h)
59
0
{
60
0
    _cairo_boxes_init (boxes);
61
62
0
    _cairo_box_from_integers (&boxes->chunks.base[0], x, y, w, h);
63
0
    boxes->num_boxes = 1;
64
0
}
65
66
void
67
_cairo_boxes_init_with_clip (cairo_boxes_t *boxes,
68
           cairo_clip_t *clip)
69
0
{
70
0
    _cairo_boxes_init (boxes);
71
0
    if (clip)
72
0
  _cairo_boxes_limit (boxes, clip->boxes, clip->num_boxes);
73
0
}
74
75
void
76
_cairo_boxes_init_for_array (cairo_boxes_t *boxes,
77
           cairo_box_t *array,
78
           int num_boxes)
79
5.02k
{
80
5.02k
    int n;
81
82
5.02k
    boxes->status = CAIRO_STATUS_SUCCESS;
83
5.02k
    boxes->num_limits = 0;
84
5.02k
    boxes->num_boxes = num_boxes;
85
86
5.02k
    boxes->tail = &boxes->chunks;
87
5.02k
    boxes->chunks.next = NULL;
88
5.02k
    boxes->chunks.base = array;
89
5.02k
    boxes->chunks.size = num_boxes;
90
5.02k
    boxes->chunks.count = num_boxes;
91
92
9.11k
    for (n = 0; n < num_boxes; n++) {
93
5.02k
  if (! _cairo_fixed_is_integer (array[n].p1.x) ||
94
4.76k
      ! _cairo_fixed_is_integer (array[n].p1.y) ||
95
4.68k
      ! _cairo_fixed_is_integer (array[n].p2.x) ||
96
4.14k
      ! _cairo_fixed_is_integer (array[n].p2.y))
97
935
  {
98
935
      break;
99
935
  }
100
5.02k
    }
101
102
5.02k
    boxes->is_pixel_aligned = n == num_boxes;
103
5.02k
}
104
105
/**
106
 * _cairo_boxes_limit:
107
 * @boxes:        the box set to be filled (return buffer)
108
 * @limits:       array of the limiting boxes to compute the bounding
109
 *                box from
110
 * @num_limits:   length of the limits array
111
 *
112
 * Computes the minimum bounding box of the given list of boxes and assign
113
 * it to the given boxes set. It also assigns that list as the list of
114
 * limiting boxes in the box set.
115
 */
116
void
117
_cairo_boxes_limit (cairo_boxes_t *boxes,
118
        const cairo_box_t *limits,
119
        int      num_limits)
120
606
{
121
606
    int n;
122
123
606
    boxes->limits = limits;
124
606
    boxes->num_limits = num_limits;
125
126
606
    if (boxes->num_limits) {
127
606
  boxes->limit = limits[0];
128
606
  for (n = 1; n < num_limits; n++) {
129
0
      if (limits[n].p1.x < boxes->limit.p1.x)
130
0
    boxes->limit.p1.x = limits[n].p1.x;
131
132
0
      if (limits[n].p1.y < boxes->limit.p1.y)
133
0
    boxes->limit.p1.y = limits[n].p1.y;
134
135
0
      if (limits[n].p2.x > boxes->limit.p2.x)
136
0
    boxes->limit.p2.x = limits[n].p2.x;
137
138
0
      if (limits[n].p2.y > boxes->limit.p2.y)
139
0
    boxes->limit.p2.y = limits[n].p2.y;
140
0
  }
141
606
    }
142
606
}
143
144
static void
145
_cairo_boxes_add_internal (cairo_boxes_t *boxes,
146
         const cairo_box_t *box)
147
3.44k
{
148
3.44k
    struct _cairo_boxes_chunk *chunk;
149
150
3.44k
    if (unlikely (boxes->status))
151
0
  return;
152
153
3.44k
    chunk = boxes->tail;
154
3.44k
    if (unlikely (chunk->count == chunk->size)) {
155
0
  int size;
156
157
0
  size = chunk->size * 2;
158
0
  chunk->next = _cairo_malloc_ab_plus_c (size,
159
0
                 sizeof (cairo_box_t),
160
0
                 sizeof (struct _cairo_boxes_chunk));
161
162
0
  if (unlikely (chunk->next == NULL)) {
163
0
      boxes->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
164
0
      return;
165
0
  }
166
167
0
  chunk = chunk->next;
168
0
  boxes->tail = chunk;
169
170
0
  chunk->next = NULL;
171
0
  chunk->count = 0;
172
0
  chunk->size = size;
173
0
  chunk->base = (cairo_box_t *) (chunk + 1);
174
0
    }
175
176
3.44k
    chunk->base[chunk->count++] = *box;
177
3.44k
    boxes->num_boxes++;
178
179
3.44k
    if (boxes->is_pixel_aligned)
180
1.98k
  boxes->is_pixel_aligned = _cairo_box_is_pixel_aligned (box);
181
3.44k
}
182
183
cairo_status_t
184
_cairo_boxes_add (cairo_boxes_t *boxes,
185
      cairo_antialias_t antialias,
186
      const cairo_box_t *box)
187
3.52k
{
188
3.52k
    cairo_box_t b;
189
190
3.52k
    if (antialias == CAIRO_ANTIALIAS_NONE) {
191
0
  b.p1.x = _cairo_fixed_round_down (box->p1.x);
192
0
  b.p1.y = _cairo_fixed_round_down (box->p1.y);
193
0
  b.p2.x = _cairo_fixed_round_down (box->p2.x);
194
0
  b.p2.y = _cairo_fixed_round_down (box->p2.y);
195
0
  box = &b;
196
0
    }
197
198
3.52k
    if (box->p1.y == box->p2.y)
199
0
  return CAIRO_STATUS_SUCCESS;
200
201
3.52k
    if (box->p1.x == box->p2.x)
202
0
  return CAIRO_STATUS_SUCCESS;
203
204
3.52k
    if (boxes->num_limits) {
205
1.35k
  cairo_point_t p1, p2;
206
1.35k
  cairo_bool_t reversed = FALSE;
207
1.35k
  int n;
208
209
  /* support counter-clockwise winding for rectangular tessellation */
210
1.35k
  if (box->p1.x < box->p2.x) {
211
1.35k
      p1.x = box->p1.x;
212
1.35k
      p2.x = box->p2.x;
213
1.35k
  } else {
214
0
      p2.x = box->p1.x;
215
0
      p1.x = box->p2.x;
216
0
      reversed = ! reversed;
217
0
  }
218
219
1.35k
  if (p1.x >= boxes->limit.p2.x || p2.x <= boxes->limit.p1.x)
220
36
      return CAIRO_STATUS_SUCCESS;
221
222
1.31k
  if (box->p1.y < box->p2.y) {
223
1.31k
      p1.y = box->p1.y;
224
1.31k
      p2.y = box->p2.y;
225
1.31k
  } else {
226
0
      p2.y = box->p1.y;
227
0
      p1.y = box->p2.y;
228
0
      reversed = ! reversed;
229
0
  }
230
231
1.31k
  if (p1.y >= boxes->limit.p2.y || p2.y <= boxes->limit.p1.y)
232
48
      return CAIRO_STATUS_SUCCESS;
233
234
2.54k
  for (n = 0; n < boxes->num_limits; n++) {
235
1.27k
      const cairo_box_t *limits = &boxes->limits[n];
236
1.27k
      cairo_box_t _box;
237
1.27k
      cairo_point_t _p1, _p2;
238
239
1.27k
      if (p1.x >= limits->p2.x || p2.x <= limits->p1.x)
240
0
    continue;
241
1.27k
      if (p1.y >= limits->p2.y || p2.y <= limits->p1.y)
242
0
    continue;
243
244
      /* Otherwise, clip the box to the limits. */
245
1.27k
      _p1 = p1;
246
1.27k
      if (_p1.x < limits->p1.x)
247
112
    _p1.x = limits->p1.x;
248
1.27k
      if (_p1.y < limits->p1.y)
249
17
    _p1.y = limits->p1.y;
250
251
1.27k
      _p2 = p2;
252
1.27k
      if (_p2.x > limits->p2.x)
253
110
    _p2.x = limits->p2.x;
254
1.27k
      if (_p2.y > limits->p2.y)
255
41
    _p2.y = limits->p2.y;
256
257
1.27k
      if (_p2.y <= _p1.y || _p2.x <= _p1.x)
258
0
    continue;
259
260
1.27k
      _box.p1.y = _p1.y;
261
1.27k
      _box.p2.y = _p2.y;
262
1.27k
      if (reversed) {
263
0
    _box.p1.x = _p2.x;
264
0
    _box.p2.x = _p1.x;
265
1.27k
      } else {
266
1.27k
    _box.p1.x = _p1.x;
267
1.27k
    _box.p2.x = _p2.x;
268
1.27k
      }
269
270
1.27k
      _cairo_boxes_add_internal (boxes, &_box);
271
1.27k
  }
272
2.17k
    } else {
273
2.17k
  _cairo_boxes_add_internal (boxes, box);
274
2.17k
    }
275
276
3.44k
    return boxes->status;
277
3.52k
}
278
279
/**
280
 * _cairo_boxes_extents:
281
 * @boxes:     The box set whose minimum bounding is computed.
282
 * @box:       Return buffer for the computed result.
283
 *
284
 * Computes the minimum bounding box of the given box set and stores
285
 * it in the given box.
286
 */
287
void
288
_cairo_boxes_extents (const cairo_boxes_t *boxes,
289
          cairo_box_t *box)
290
4.75k
{
291
4.75k
    const struct _cairo_boxes_chunk *chunk;
292
4.75k
    cairo_box_t b;
293
4.75k
    int i;
294
295
4.75k
    if (boxes->num_boxes == 0) {
296
10
  box->p1.x = box->p1.y = box->p2.x = box->p2.y = 0;
297
10
  return;
298
10
    }
299
300
4.74k
    b = boxes->chunks.base[0];
301
9.48k
    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
302
10.1k
  for (i = 0; i < chunk->count; i++) {
303
5.43k
      if (chunk->base[i].p1.x < b.p1.x)
304
50
    b.p1.x = chunk->base[i].p1.x;
305
306
5.43k
      if (chunk->base[i].p1.y < b.p1.y)
307
0
    b.p1.y = chunk->base[i].p1.y;
308
309
5.43k
      if (chunk->base[i].p2.x > b.p2.x)
310
376
    b.p2.x = chunk->base[i].p2.x;
311
312
5.43k
      if (chunk->base[i].p2.y > b.p2.y)
313
223
    b.p2.y = chunk->base[i].p2.y;
314
5.43k
  }
315
4.74k
    }
316
4.74k
    *box = b;
317
4.74k
}
318
319
void
320
_cairo_boxes_clear (cairo_boxes_t *boxes)
321
1.34k
{
322
1.34k
    struct _cairo_boxes_chunk *chunk, *next;
323
324
1.34k
    for (chunk = boxes->chunks.next; chunk != NULL; chunk = next) {
325
0
  next = chunk->next;
326
0
  free (chunk);
327
0
    }
328
329
1.34k
    boxes->tail = &boxes->chunks;
330
1.34k
    boxes->chunks.next = 0;
331
1.34k
    boxes->chunks.count = 0;
332
1.34k
    boxes->chunks.base = boxes->boxes_embedded;
333
1.34k
    boxes->chunks.size = ARRAY_LENGTH (boxes->boxes_embedded);
334
1.34k
    boxes->num_boxes = 0;
335
336
1.34k
    boxes->is_pixel_aligned = TRUE;
337
1.34k
}
338
339
/**
340
 * _cairo_boxes_to_array:
341
 * @boxes      The box set to be converted.
342
 * @num_boxes  Return buffer for the number of boxes (array count).
343
 *
344
 * Linearize a box set of possibly multiple chunks into one big chunk
345
 * and returns an array of boxes
346
 *
347
 * Return value: Pointer to the newly allocated array of boxes (the number o
348
 * elements is given in num_boxes).
349
 */
350
cairo_box_t *
351
_cairo_boxes_to_array (const cairo_boxes_t *boxes,
352
           int *num_boxes)
353
502
{
354
502
    const struct _cairo_boxes_chunk *chunk;
355
502
    cairo_box_t *box;
356
502
    int i, j;
357
358
502
    *num_boxes = boxes->num_boxes;
359
360
502
    box = _cairo_malloc_ab (boxes->num_boxes, sizeof (cairo_box_t));
361
502
    if (box == NULL) {
362
0
  _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
363
0
  return NULL;
364
0
    }
365
366
502
    j = 0;
367
1.00k
    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
368
1.65k
  for (i = 0; i < chunk->count; i++)
369
1.15k
      box[j++] = chunk->base[i];
370
502
    }
371
372
502
    return box;
373
502
}
374
375
void
376
_cairo_boxes_fini (cairo_boxes_t *boxes)
377
1.50k
{
378
1.50k
    struct _cairo_boxes_chunk *chunk, *next;
379
380
1.50k
    for (chunk = boxes->chunks.next; chunk != NULL; chunk = next) {
381
0
  next = chunk->next;
382
0
  free (chunk);
383
0
    }
384
1.50k
}
385
386
cairo_bool_t
387
_cairo_boxes_for_each_box (cairo_boxes_t *boxes,
388
         cairo_bool_t (*func) (cairo_box_t *box, void *data),
389
         void *data)
390
0
{
391
0
    struct _cairo_boxes_chunk *chunk;
392
0
    int i;
393
394
0
    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
395
0
  for (i = 0; i < chunk->count; i++)
396
0
      if (! func (&chunk->base[i], data))
397
0
    return FALSE;
398
0
    }
399
400
0
    return TRUE;
401
0
}
402
403
struct cairo_box_renderer {
404
    cairo_span_renderer_t base;
405
    cairo_boxes_t *boxes;
406
};
407
408
static cairo_status_t
409
span_to_boxes (void *abstract_renderer, int y, int h,
410
         const cairo_half_open_span_t *spans, unsigned num_spans)
411
0
{
412
0
    struct cairo_box_renderer *r = abstract_renderer;
413
0
    cairo_status_t status = CAIRO_STATUS_SUCCESS;
414
0
    cairo_box_t box;
415
416
0
    if (num_spans == 0)
417
0
  return CAIRO_STATUS_SUCCESS;
418
419
0
    box.p1.y = _cairo_fixed_from_int (y);
420
0
    box.p2.y = _cairo_fixed_from_int (y + h);
421
0
    do {
422
0
  if (spans[0].coverage) {
423
0
      box.p1.x = _cairo_fixed_from_int(spans[0].x);
424
0
      box.p2.x = _cairo_fixed_from_int(spans[1].x);
425
0
      status = _cairo_boxes_add (r->boxes, CAIRO_ANTIALIAS_DEFAULT, &box);
426
0
  }
427
0
  spans++;
428
0
    } while (--num_spans > 1 && status == CAIRO_STATUS_SUCCESS);
429
430
0
    return status;
431
0
}
432
433
cairo_status_t
434
_cairo_rasterise_polygon_to_boxes (cairo_polygon_t      *polygon,
435
           cairo_fill_rule_t       fill_rule,
436
           cairo_boxes_t *boxes)
437
0
{
438
0
    struct cairo_box_renderer renderer;
439
0
    cairo_scan_converter_t *converter;
440
0
    cairo_int_status_t status;
441
0
    cairo_rectangle_int_t r;
442
443
0
    TRACE ((stderr, "%s: fill_rule=%d\n", __FUNCTION__, fill_rule));
444
445
0
    _cairo_box_round_to_rectangle (&polygon->extents, &r);
446
0
    converter = _cairo_mono_scan_converter_create (r.x, r.y,
447
0
               r.x + r.width,
448
0
               r.y + r.height,
449
0
               fill_rule);
450
0
    status = _cairo_mono_scan_converter_add_polygon (converter, polygon);
451
0
    if (unlikely (status))
452
0
  goto cleanup_converter;
453
454
0
    renderer.boxes = boxes;
455
0
    renderer.base.render_rows = span_to_boxes;
456
457
0
    status = converter->generate (converter, &renderer.base);
458
0
cleanup_converter:
459
0
    converter->destroy (converter);
460
0
    return status;
461
0
}
462
463
void
464
_cairo_debug_print_boxes (FILE *stream, const cairo_boxes_t *boxes)
465
0
{
466
0
    const struct _cairo_boxes_chunk *chunk;
467
0
    cairo_box_t extents;
468
0
    int i;
469
470
0
    _cairo_boxes_extents (boxes, &extents);
471
0
    fprintf (stream, "boxes x %d: (%f, %f) x (%f, %f)\n",
472
0
       boxes->num_boxes,
473
0
       _cairo_fixed_to_double (extents.p1.x),
474
0
       _cairo_fixed_to_double (extents.p1.y),
475
0
       _cairo_fixed_to_double (extents.p2.x),
476
0
       _cairo_fixed_to_double (extents.p2.y));
477
478
0
    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
479
0
  for (i = 0; i < chunk->count; i++) {
480
      fprintf (stderr, "  box[%d]: (%f, %f), (%f, %f)\n", i,
481
0
         _cairo_fixed_to_double (chunk->base[i].p1.x),
482
0
         _cairo_fixed_to_double (chunk->base[i].p1.y),
483
0
         _cairo_fixed_to_double (chunk->base[i].p2.x),
484
0
         _cairo_fixed_to_double (chunk->base[i].p2.y));
485
0
  }
486
0
    }
487
0
}