Coverage Report

Created: 2025-11-16 09:57

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