Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/cairo/src/cairo-boxes.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-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
534k
{
43
534k
    boxes->status = CAIRO_STATUS_SUCCESS;
44
534k
    boxes->limits = NULL;
45
534k
    boxes->num_limits = 0;
46
534k
    boxes->num_boxes = 0;
47
48
534k
    boxes->tail = &boxes->chunks;
49
534k
    boxes->chunks.next = NULL;
50
534k
    boxes->chunks.base = boxes->boxes_embedded;
51
534k
    boxes->chunks.size = ARRAY_LENGTH (boxes->boxes_embedded);
52
534k
    boxes->chunks.count = 0;
53
54
534k
    boxes->is_pixel_aligned = TRUE;
55
534k
}
56
57
void
58
_cairo_boxes_init_from_rectangle (cairo_boxes_t *boxes,
59
          int x, int y, int w, int h)
60
0
{
61
0
    _cairo_boxes_init (boxes);
62
63
0
    _cairo_box_from_integers (&boxes->chunks.base[0], x, y, w, h);
64
0
    boxes->num_boxes = 1;
65
0
}
66
67
void
68
_cairo_boxes_init_with_clip (cairo_boxes_t *boxes,
69
           cairo_clip_t *clip)
70
0
{
71
0
    _cairo_boxes_init (boxes);
72
0
    if (clip)
73
0
  _cairo_boxes_limit (boxes, clip->boxes, clip->num_boxes);
74
0
}
75
76
void
77
_cairo_boxes_init_for_array (cairo_boxes_t *boxes,
78
           cairo_box_t *array,
79
           int num_boxes)
80
686k
{
81
686k
    int n;
82
83
686k
    boxes->status = CAIRO_STATUS_SUCCESS;
84
686k
    boxes->num_limits = 0;
85
686k
    boxes->num_boxes = num_boxes;
86
87
686k
    boxes->tail = &boxes->chunks;
88
686k
    boxes->chunks.next = NULL;
89
686k
    boxes->chunks.base = array;
90
686k
    boxes->chunks.size = num_boxes;
91
686k
    boxes->chunks.count = num_boxes;
92
93
1.37M
    for (n = 0; n < num_boxes; n++) {
94
686k
  if (! _cairo_fixed_is_integer (array[n].p1.x) ||
95
686k
      ! _cairo_fixed_is_integer (array[n].p1.y) ||
96
686k
      ! _cairo_fixed_is_integer (array[n].p2.x) ||
97
686k
      ! _cairo_fixed_is_integer (array[n].p2.y))
98
0
  {
99
0
      break;
100
0
  }
101
686k
    }
102
103
686k
    boxes->is_pixel_aligned = n == num_boxes;
104
686k
}
105
106
/** _cairo_boxes_limit:
107
 *
108
 * Computes the minimum bounding box of the given list of boxes and assign
109
 * it to the given boxes set. It also assigns that list as the list of
110
 * limiting boxes in the box set.
111
 *
112
 * @param boxes        the box set to be filled (return buffer)
113
 * @param limits       array of the limiting boxes to compute the bounding
114
 *                     box from
115
 * @param num_limits   length of the limits array
116
 */
117
void
118
_cairo_boxes_limit (cairo_boxes_t *boxes,
119
        const cairo_box_t *limits,
120
        int      num_limits)
121
20.5k
{
122
20.5k
    int n;
123
124
20.5k
    boxes->limits = limits;
125
20.5k
    boxes->num_limits = num_limits;
126
127
20.5k
    if (boxes->num_limits) {
128
20.5k
  boxes->limit = limits[0];
129
20.5k
  for (n = 1; n < num_limits; n++) {
130
0
      if (limits[n].p1.x < boxes->limit.p1.x)
131
0
    boxes->limit.p1.x = limits[n].p1.x;
132
133
0
      if (limits[n].p1.y < boxes->limit.p1.y)
134
0
    boxes->limit.p1.y = limits[n].p1.y;
135
136
0
      if (limits[n].p2.x > boxes->limit.p2.x)
137
0
    boxes->limit.p2.x = limits[n].p2.x;
138
139
0
      if (limits[n].p2.y > boxes->limit.p2.y)
140
0
    boxes->limit.p2.y = limits[n].p2.y;
141
0
  }
142
20.5k
    }
143
20.5k
}
144
145
static void
146
_cairo_boxes_add_internal (cairo_boxes_t *boxes,
147
         const cairo_box_t *box)
148
563k
{
149
563k
    struct _cairo_boxes_chunk *chunk;
150
151
563k
    if (unlikely (boxes->status))
152
0
  return;
153
154
563k
    chunk = boxes->tail;
155
563k
    if (unlikely (chunk->count == chunk->size)) {
156
53
  int size;
157
158
53
  size = chunk->size * 2;
159
53
  chunk->next = _cairo_malloc_ab_plus_c (size,
160
53
                 sizeof (cairo_box_t),
161
53
                 sizeof (struct _cairo_boxes_chunk));
162
163
53
  if (unlikely (chunk->next == NULL)) {
164
0
      boxes->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
165
0
      return;
166
0
  }
167
168
53
  chunk = chunk->next;
169
53
  boxes->tail = chunk;
170
171
53
  chunk->next = NULL;
172
53
  chunk->count = 0;
173
53
  chunk->size = size;
174
53
  chunk->base = (cairo_box_t *) (chunk + 1);
175
53
    }
176
177
563k
    chunk->base[chunk->count++] = *box;
178
563k
    boxes->num_boxes++;
179
180
563k
    if (boxes->is_pixel_aligned)
181
562k
  boxes->is_pixel_aligned = _cairo_box_is_pixel_aligned (box);
182
563k
}
183
184
cairo_status_t
185
_cairo_boxes_add (cairo_boxes_t *boxes,
186
      cairo_antialias_t antialias,
187
      const cairo_box_t *box)
188
723k
{
189
723k
    cairo_box_t b;
190
191
723k
    if (antialias == CAIRO_ANTIALIAS_NONE) {
192
705k
  b.p1.x = _cairo_fixed_round_down (box->p1.x);
193
705k
  b.p1.y = _cairo_fixed_round_down (box->p1.y);
194
705k
  b.p2.x = _cairo_fixed_round_down (box->p2.x);
195
705k
  b.p2.y = _cairo_fixed_round_down (box->p2.y);
196
705k
  box = &b;
197
705k
    }
198
199
723k
    if (box->p1.y == box->p2.y)
200
161
  return CAIRO_STATUS_SUCCESS;
201
202
723k
    if (box->p1.x == box->p2.x)
203
978
  return CAIRO_STATUS_SUCCESS;
204
205
722k
    if (boxes->num_limits) {
206
207k
  cairo_point_t p1, p2;
207
207k
  cairo_bool_t reversed = FALSE;
208
207k
  int n;
209
210
  /* support counter-clockwise winding for rectangular tessellation */
211
207k
  if (box->p1.x < box->p2.x) {
212
203k
      p1.x = box->p1.x;
213
203k
      p2.x = box->p2.x;
214
203k
  } else {
215
3.81k
      p2.x = box->p1.x;
216
3.81k
      p1.x = box->p2.x;
217
3.81k
      reversed = ! reversed;
218
3.81k
  }
219
220
207k
  if (p1.x >= boxes->limit.p2.x || p2.x <= boxes->limit.p1.x)
221
150k
      return CAIRO_STATUS_SUCCESS;
222
223
56.9k
  if (box->p1.y < box->p2.y) {
224
56.9k
      p1.y = box->p1.y;
225
56.9k
      p2.y = box->p2.y;
226
56.9k
  } else {
227
0
      p2.y = box->p1.y;
228
0
      p1.y = box->p2.y;
229
0
      reversed = ! reversed;
230
0
  }
231
232
56.9k
  if (p1.y >= boxes->limit.p2.y || p2.y <= boxes->limit.p1.y)
233
8.61k
      return CAIRO_STATUS_SUCCESS;
234
235
96.6k
  for (n = 0; n < boxes->num_limits; n++) {
236
48.3k
      const cairo_box_t *limits = &boxes->limits[n];
237
48.3k
      cairo_box_t _box;
238
48.3k
      cairo_point_t _p1, _p2;
239
240
48.3k
      if (p1.x >= limits->p2.x || p2.x <= limits->p1.x)
241
0
    continue;
242
48.3k
      if (p1.y >= limits->p2.y || p2.y <= limits->p1.y)
243
0
    continue;
244
245
      /* Otherwise, clip the box to the limits. */
246
48.3k
      _p1 = p1;
247
48.3k
      if (_p1.x < limits->p1.x)
248
8.66k
    _p1.x = limits->p1.x;
249
48.3k
      if (_p1.y < limits->p1.y)
250
8.54k
    _p1.y = limits->p1.y;
251
252
48.3k
      _p2 = p2;
253
48.3k
      if (_p2.x > limits->p2.x)
254
18.0k
    _p2.x = limits->p2.x;
255
48.3k
      if (_p2.y > limits->p2.y)
256
21.3k
    _p2.y = limits->p2.y;
257
258
48.3k
      if (_p2.y <= _p1.y || _p2.x <= _p1.x)
259
0
    continue;
260
261
48.3k
      _box.p1.y = _p1.y;
262
48.3k
      _box.p2.y = _p2.y;
263
48.3k
      if (reversed) {
264
2.19k
    _box.p1.x = _p2.x;
265
2.19k
    _box.p2.x = _p1.x;
266
46.1k
      } else {
267
46.1k
    _box.p1.x = _p1.x;
268
46.1k
    _box.p2.x = _p2.x;
269
46.1k
      }
270
271
48.3k
      _cairo_boxes_add_internal (boxes, &_box);
272
48.3k
  }
273
515k
    } else {
274
515k
  _cairo_boxes_add_internal (boxes, box);
275
515k
    }
276
277
563k
    return boxes->status;
278
722k
}
279
280
/** _cairo_boxes_extents:
281
 *
282
 * Computes the minimum bounding box of the given box set and stores
283
 * it in the given box.
284
 *
285
 * @param boxes      The box set whose minimum bounding is computed.
286
 * @param box        Return buffer for the computed result.
287
 */
288
void
289
_cairo_boxes_extents (const cairo_boxes_t *boxes,
290
          cairo_box_t *box)
291
1.22M
{
292
1.22M
    const struct _cairo_boxes_chunk *chunk;
293
1.22M
    cairo_box_t b;
294
1.22M
    int i;
295
296
1.22M
    if (boxes->num_boxes == 0) {
297
10.2k
  box->p1.x = box->p1.y = box->p2.x = box->p2.y = 0;
298
10.2k
  return;
299
10.2k
    }
300
301
1.21M
    b = boxes->chunks.base[0];
302
2.42M
    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
303
2.42M
  for (i = 0; i < chunk->count; i++) {
304
1.21M
      if (chunk->base[i].p1.x < b.p1.x)
305
5
    b.p1.x = chunk->base[i].p1.x;
306
307
1.21M
      if (chunk->base[i].p1.y < b.p1.y)
308
0
    b.p1.y = chunk->base[i].p1.y;
309
310
1.21M
      if (chunk->base[i].p2.x > b.p2.x)
311
1.25k
    b.p2.x = chunk->base[i].p2.x;
312
313
1.21M
      if (chunk->base[i].p2.y > b.p2.y)
314
1.48k
    b.p2.y = chunk->base[i].p2.y;
315
1.21M
  }
316
1.21M
    }
317
1.21M
    *box = b;
318
1.21M
}
319
320
void
321
_cairo_boxes_clear (cairo_boxes_t *boxes)
322
25.8k
{
323
25.8k
    struct _cairo_boxes_chunk *chunk, *next;
324
325
25.9k
    for (chunk = boxes->chunks.next; chunk != NULL; chunk = next) {
326
53
  next = chunk->next;
327
53
  free (chunk);
328
53
    }
329
330
25.8k
    boxes->tail = &boxes->chunks;
331
25.8k
    boxes->chunks.next = 0;
332
25.8k
    boxes->chunks.count = 0;
333
25.8k
    boxes->chunks.base = boxes->boxes_embedded;
334
25.8k
    boxes->chunks.size = ARRAY_LENGTH (boxes->boxes_embedded);
335
25.8k
    boxes->num_boxes = 0;
336
337
25.8k
    boxes->is_pixel_aligned = TRUE;
338
25.8k
}
339
340
/** _cairo_boxes_to_array:
341
 *
342
 * Linearize a box set of possibly multiple chunks into one big chunk
343
 * and returns an array of boxes
344
 *
345
 * @param boxes      The box set to be converted.
346
 * @param num_boxes  Return buffer for the number of boxes (array count).
347
 * @return           Pointer to the newly allocated array of boxes
348
 *                   (the number o 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
1.26k
{
354
1.26k
    const struct _cairo_boxes_chunk *chunk;
355
1.26k
    cairo_box_t *box;
356
1.26k
    int i, j;
357
358
1.26k
    *num_boxes = boxes->num_boxes;
359
360
1.26k
    box = _cairo_malloc_ab (boxes->num_boxes, sizeof (cairo_box_t));
361
1.26k
    if (box == NULL) {
362
0
  _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
363
0
  return NULL;
364
0
    }
365
366
1.26k
    j = 0;
367
2.53k
    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
368
3.80k
  for (i = 0; i < chunk->count; i++)
369
2.53k
      box[j++] = chunk->base[i];
370
1.26k
    }
371
372
1.26k
    return box;
373
1.26k
}
374
375
void
376
_cairo_boxes_fini (cairo_boxes_t *boxes)
377
534k
{
378
534k
    struct _cairo_boxes_chunk *chunk, *next;
379
380
534k
    for (chunk = boxes->chunks.next; chunk != NULL; chunk = next) {
381
0
  next = chunk->next;
382
0
  free (chunk);
383
0
    }
384
534k
}
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
0
      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
}