Coverage Report

Created: 2025-07-12 07:23

/src/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
155
{
43
155
    boxes->status = CAIRO_STATUS_SUCCESS;
44
155
    boxes->num_limits = 0;
45
155
    boxes->num_boxes = 0;
46
47
155
    boxes->tail = &boxes->chunks;
48
155
    boxes->chunks.next = NULL;
49
155
    boxes->chunks.base = boxes->boxes_embedded;
50
155
    boxes->chunks.size = ARRAY_LENGTH (boxes->boxes_embedded);
51
155
    boxes->chunks.count = 0;
52
53
155
    boxes->is_pixel_aligned = TRUE;
54
155
}
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
167
{
80
167
    int n;
81
82
167
    boxes->status = CAIRO_STATUS_SUCCESS;
83
167
    boxes->num_limits = 0;
84
167
    boxes->num_boxes = num_boxes;
85
86
167
    boxes->tail = &boxes->chunks;
87
167
    boxes->chunks.next = NULL;
88
167
    boxes->chunks.base = array;
89
167
    boxes->chunks.size = num_boxes;
90
167
    boxes->chunks.count = num_boxes;
91
92
179
    for (n = 0; n < num_boxes; n++) {
93
167
  if (! _cairo_fixed_is_integer (array[n].p1.x) ||
94
167
      ! _cairo_fixed_is_integer (array[n].p1.y) ||
95
167
      ! _cairo_fixed_is_integer (array[n].p2.x) ||
96
167
      ! _cairo_fixed_is_integer (array[n].p2.y))
97
155
  {
98
155
      break;
99
155
  }
100
167
    }
101
102
167
    boxes->is_pixel_aligned = n == num_boxes;
103
167
}
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
154
{
121
154
    int n;
122
123
154
    boxes->limits = limits;
124
154
    boxes->num_limits = num_limits;
125
126
154
    if (boxes->num_limits) {
127
154
  boxes->limit = limits[0];
128
154
  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
154
    }
142
154
}
143
144
static void
145
_cairo_boxes_add_internal (cairo_boxes_t *boxes,
146
         const cairo_box_t *box)
147
1.09k
{
148
1.09k
    struct _cairo_boxes_chunk *chunk;
149
150
1.09k
    if (unlikely (boxes->status))
151
0
  return;
152
153
1.09k
    chunk = boxes->tail;
154
1.09k
    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
1.09k
    chunk->base[chunk->count++] = *box;
177
1.09k
    boxes->num_boxes++;
178
179
1.09k
    if (boxes->is_pixel_aligned)
180
456
  boxes->is_pixel_aligned = _cairo_box_is_pixel_aligned (box);
181
1.09k
}
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
1.09k
{
188
1.09k
    cairo_box_t b;
189
190
1.09k
    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
1.09k
    if (box->p1.y == box->p2.y)
199
0
  return CAIRO_STATUS_SUCCESS;
200
201
1.09k
    if (box->p1.x == box->p2.x)
202
0
  return CAIRO_STATUS_SUCCESS;
203
204
1.09k
    if (boxes->num_limits) {
205
439
  cairo_point_t p1, p2;
206
439
  cairo_bool_t reversed = FALSE;
207
439
  int n;
208
209
  /* support counter-clockwise winding for rectangular tessellation */
210
439
  if (box->p1.x < box->p2.x) {
211
439
      p1.x = box->p1.x;
212
439
      p2.x = box->p2.x;
213
439
  } else {
214
0
      p2.x = box->p1.x;
215
0
      p1.x = box->p2.x;
216
0
      reversed = ! reversed;
217
0
  }
218
219
439
  if (p1.x >= boxes->limit.p2.x || p2.x <= boxes->limit.p1.x)
220
0
      return CAIRO_STATUS_SUCCESS;
221
222
439
  if (box->p1.y < box->p2.y) {
223
439
      p1.y = box->p1.y;
224
439
      p2.y = box->p2.y;
225
439
  } else {
226
0
      p2.y = box->p1.y;
227
0
      p1.y = box->p2.y;
228
0
      reversed = ! reversed;
229
0
  }
230
231
439
  if (p1.y >= boxes->limit.p2.y || p2.y <= boxes->limit.p1.y)
232
0
      return CAIRO_STATUS_SUCCESS;
233
234
878
  for (n = 0; n < boxes->num_limits; n++) {
235
439
      const cairo_box_t *limits = &boxes->limits[n];
236
439
      cairo_box_t _box;
237
439
      cairo_point_t _p1, _p2;
238
239
439
      if (p1.x >= limits->p2.x || p2.x <= limits->p1.x)
240
0
    continue;
241
439
      if (p1.y >= limits->p2.y || p2.y <= limits->p1.y)
242
0
    continue;
243
244
      /* Otherwise, clip the box to the limits. */
245
439
      _p1 = p1;
246
439
      if (_p1.x < limits->p1.x)
247
99
    _p1.x = limits->p1.x;
248
439
      if (_p1.y < limits->p1.y)
249
0
    _p1.y = limits->p1.y;
250
251
439
      _p2 = p2;
252
439
      if (_p2.x > limits->p2.x)
253
99
    _p2.x = limits->p2.x;
254
439
      if (_p2.y > limits->p2.y)
255
33
    _p2.y = limits->p2.y;
256
257
439
      if (_p2.y <= _p1.y || _p2.x <= _p1.x)
258
0
    continue;
259
260
439
      _box.p1.y = _p1.y;
261
439
      _box.p2.y = _p2.y;
262
439
      if (reversed) {
263
0
    _box.p1.x = _p2.x;
264
0
    _box.p2.x = _p1.x;
265
439
      } else {
266
439
    _box.p1.x = _p1.x;
267
439
    _box.p2.x = _p2.x;
268
439
      }
269
270
439
      _cairo_boxes_add_internal (boxes, &_box);
271
439
  }
272
654
    } else {
273
654
  _cairo_boxes_add_internal (boxes, box);
274
654
    }
275
276
1.09k
    return boxes->status;
277
1.09k
}
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
170
{
291
170
    const struct _cairo_boxes_chunk *chunk;
292
170
    cairo_box_t b;
293
170
    int i;
294
295
170
    if (boxes->num_boxes == 0) {
296
0
  box->p1.x = box->p1.y = box->p2.x = box->p2.y = 0;
297
0
  return;
298
0
    }
299
300
170
    b = boxes->chunks.base[0];
301
340
    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
302
625
  for (i = 0; i < chunk->count; i++) {
303
455
      if (chunk->base[i].p1.x < b.p1.x)
304
0
    b.p1.x = chunk->base[i].p1.x;
305
306
455
      if (chunk->base[i].p1.y < b.p1.y)
307
0
    b.p1.y = chunk->base[i].p1.y;
308
309
455
      if (chunk->base[i].p2.x > b.p2.x)
310
84
    b.p2.x = chunk->base[i].p2.x;
311
312
455
      if (chunk->base[i].p2.y > b.p2.y)
313
134
    b.p2.y = chunk->base[i].p2.y;
314
455
  }
315
170
    }
316
170
    *box = b;
317
170
}
318
319
void
320
_cairo_boxes_clear (cairo_boxes_t *boxes)
321
303
{
322
303
    struct _cairo_boxes_chunk *chunk, *next;
323
324
303
    for (chunk = boxes->chunks.next; chunk != NULL; chunk = next) {
325
0
  next = chunk->next;
326
0
  free (chunk);
327
0
    }
328
329
303
    boxes->tail = &boxes->chunks;
330
303
    boxes->chunks.next = 0;
331
303
    boxes->chunks.count = 0;
332
303
    boxes->chunks.base = boxes->boxes_embedded;
333
303
    boxes->chunks.size = ARRAY_LENGTH (boxes->boxes_embedded);
334
303
    boxes->num_boxes = 0;
335
336
303
    boxes->is_pixel_aligned = TRUE;
337
303
}
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
151
{
354
151
    const struct _cairo_boxes_chunk *chunk;
355
151
    cairo_box_t *box;
356
151
    int i, j;
357
358
151
    *num_boxes = boxes->num_boxes;
359
360
151
    box = _cairo_malloc_ab (boxes->num_boxes, sizeof (cairo_box_t));
361
151
    if (box == NULL) {
362
0
  _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
363
0
  return NULL;
364
0
    }
365
366
151
    j = 0;
367
302
    for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
368
587
  for (i = 0; i < chunk->count; i++)
369
436
      box[j++] = chunk->base[i];
370
151
    }
371
372
151
    return box;
373
151
}
374
375
void
376
_cairo_boxes_fini (cairo_boxes_t *boxes)
377
306
{
378
306
    struct _cairo_boxes_chunk *chunk, *next;
379
380
306
    for (chunk = boxes->chunks.next; chunk != NULL; chunk = next) {
381
0
  next = chunk->next;
382
0
  free (chunk);
383
0
    }
384
306
}
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
}