Coverage Report

Created: 2025-07-23 08:13

/src/cairo/src/cairo-clip-boxes.c
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
2
/* cairo - a vector graphics library with display and print output
3
 *
4
 * Copyright © 2002 University of Southern California
5
 * Copyright © 2005 Red Hat, Inc.
6
 * Copyright © 2009 Chris Wilson
7
 *
8
 * This library is free software; you can redistribute it and/or
9
 * modify it either under the terms of the GNU Lesser General Public
10
 * License version 2.1 as published by the Free Software Foundation
11
 * (the "LGPL") or, at your option, under the terms of the Mozilla
12
 * Public License Version 1.1 (the "MPL"). If you do not alter this
13
 * notice, a recipient may use your version of this file under either
14
 * the MPL or the LGPL.
15
 *
16
 * You should have received a copy of the LGPL along with this library
17
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
19
 * You should have received a copy of the MPL along with this library
20
 * in the file COPYING-MPL-1.1
21
 *
22
 * The contents of this file are subject to the Mozilla Public License
23
 * Version 1.1 (the "License"); you may not use this file except in
24
 * compliance with the License. You may obtain a copy of the License at
25
 * http://www.mozilla.org/MPL/
26
 *
27
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
28
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
29
 * the specific language governing rights and limitations.
30
 *
31
 * The Original Code is the cairo graphics library.
32
 *
33
 * The Initial Developer of the Original Code is University of Southern
34
 * California.
35
 *
36
 * Contributor(s):
37
 *  Carl D. Worth <cworth@cworth.org>
38
 *  Kristian Høgsberg <krh@redhat.com>
39
 *  Chris Wilson <chris@chris-wilson.co.uk>
40
 */
41
42
#include "cairoint.h"
43
44
#include "cairo-box-inline.h"
45
#include "cairo-clip-inline.h"
46
#include "cairo-clip-private.h"
47
#include "cairo-error-private.h"
48
#include "cairo-freed-pool-private.h"
49
#include "cairo-gstate-private.h"
50
#include "cairo-path-fixed-private.h"
51
#include "cairo-pattern-private.h"
52
#include "cairo-composite-rectangles-private.h"
53
#include "cairo-region-private.h"
54
55
static cairo_bool_t
56
_cairo_clip_contains_rectangle_box (const cairo_clip_t *clip,
57
            const cairo_rectangle_int_t *rect,
58
            const cairo_box_t *box)
59
2.51M
{
60
2.51M
    int i;
61
62
    /* clip == NULL means no clip, so the clip contains everything */
63
2.51M
    if (clip == NULL)
64
1.56k
  return TRUE;
65
66
2.51M
    if (_cairo_clip_is_all_clipped (clip))
67
0
  return FALSE;
68
69
    /* If we have a non-trivial path, just say no */
70
2.51M
    if (clip->path)
71
1.15k
  return FALSE;
72
73
2.51M
    if (! _cairo_rectangle_contains_rectangle (&clip->extents, rect))
74
2.54k
  return FALSE;
75
76
2.50M
    if (clip->num_boxes == 0)
77
2
  return TRUE;
78
79
    /* Check for a clip-box that wholly contains the rectangle */
80
2.52M
    for (i = 0; i < clip->num_boxes; i++) {
81
2.50M
  if (box->p1.x >= clip->boxes[i].p1.x &&
82
2.50M
      box->p1.y >= clip->boxes[i].p1.y &&
83
2.50M
      box->p2.x <= clip->boxes[i].p2.x &&
84
2.50M
      box->p2.y <= clip->boxes[i].p2.y)
85
2.49M
  {
86
2.49M
      return TRUE;
87
2.49M
  }
88
2.50M
    }
89
90
11.9k
    return FALSE;
91
2.50M
}
92
93
cairo_bool_t
94
_cairo_clip_contains_box (const cairo_clip_t *clip,
95
        const cairo_box_t *box)
96
1.00M
{
97
1.00M
    cairo_rectangle_int_t rect;
98
99
1.00M
    _cairo_box_round_to_rectangle (box, &rect);
100
1.00M
    return _cairo_clip_contains_rectangle_box(clip, &rect, box);
101
1.00M
}
102
103
cairo_bool_t
104
_cairo_clip_contains_rectangle (const cairo_clip_t *clip,
105
        const cairo_rectangle_int_t *rect)
106
1.50M
{
107
1.50M
    cairo_box_t box;
108
109
1.50M
    _cairo_box_from_rectangle_int (&box, rect);
110
1.50M
    return _cairo_clip_contains_rectangle_box (clip, rect, &box);
111
1.50M
}
112
113
cairo_clip_t *
114
_cairo_clip_intersect_rectilinear_path (cairo_clip_t *clip,
115
          const cairo_path_fixed_t *path,
116
          cairo_fill_rule_t fill_rule,
117
          cairo_antialias_t antialias)
118
144
{
119
144
    cairo_status_t status;
120
144
    cairo_boxes_t boxes;
121
122
144
    _cairo_boxes_init (&boxes);
123
144
    status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
124
144
                fill_rule,
125
144
                antialias,
126
144
                &boxes);
127
144
    if (likely (status == CAIRO_STATUS_SUCCESS && boxes.num_boxes))
128
143
  clip = _cairo_clip_intersect_boxes (clip, &boxes);
129
1
    else
130
1
  clip = _cairo_clip_set_all_clipped (clip);
131
144
    _cairo_boxes_fini (&boxes);
132
133
144
    return clip;
134
144
}
135
136
static cairo_clip_t *
137
_cairo_clip_intersect_rectangle_box (cairo_clip_t *clip,
138
             const cairo_rectangle_int_t *r,
139
             const cairo_box_t *box)
140
2.50M
{
141
2.50M
    cairo_box_t extents_box;
142
2.50M
    cairo_bool_t changed = FALSE;
143
2.50M
    int i, j;
144
145
2.50M
    if (clip == NULL) {
146
2.48M
  clip = _cairo_clip_create ();
147
2.48M
  if (clip == NULL)
148
0
      return _cairo_clip_set_all_clipped (clip);
149
2.48M
    }
150
151
2.50M
    if (clip->num_boxes == 0) {
152
2.48M
  clip->boxes = &clip->embedded_box;
153
2.48M
  clip->boxes[0] = *box;
154
2.48M
  clip->num_boxes = 1;
155
2.48M
  if (clip->path == NULL) {
156
2.48M
      clip->extents = *r;
157
2.48M
  } else {
158
5
      if (! _cairo_rectangle_intersect (&clip->extents, r))
159
0
    return _cairo_clip_set_all_clipped (clip);
160
5
  }
161
2.48M
  if (clip->path == NULL)
162
2.48M
      clip->is_region = _cairo_box_is_pixel_aligned (box);
163
2.48M
  return clip;
164
2.48M
    }
165
166
    /* Does the new box wholly subsume the clip? Perform a cheap check
167
     * for the common condition of a single clip rectangle.
168
     */
169
22.8k
    if (clip->num_boxes == 1 &&
170
22.8k
  clip->boxes[0].p1.x >= box->p1.x &&
171
22.8k
  clip->boxes[0].p1.y >= box->p1.y &&
172
22.8k
  clip->boxes[0].p2.x <= box->p2.x &&
173
22.8k
  clip->boxes[0].p2.y <= box->p2.y)
174
13.7k
    {
175
13.7k
  return clip;
176
13.7k
    }
177
178
19.2k
    for (i = j = 0; i < clip->num_boxes; i++) {
179
10.2k
  cairo_box_t *b = &clip->boxes[j];
180
181
10.2k
  if (j != i)
182
0
      *b = clip->boxes[i];
183
184
10.2k
  if (box->p1.x > b->p1.x)
185
7.25k
      b->p1.x = box->p1.x, changed = TRUE;
186
10.2k
  if (box->p2.x < b->p2.x)
187
7.40k
      b->p2.x = box->p2.x, changed = TRUE;
188
189
10.2k
  if (box->p1.y > b->p1.y)
190
8.09k
      b->p1.y = box->p1.y, changed = TRUE;
191
10.2k
  if (box->p2.y < b->p2.y)
192
7.50k
      b->p2.y = box->p2.y, changed = TRUE;
193
194
10.2k
  j += b->p2.x > b->p1.x && b->p2.y > b->p1.y;
195
10.2k
    }
196
9.07k
    clip->num_boxes = j;
197
198
9.07k
    if (clip->num_boxes == 0)
199
7
  return _cairo_clip_set_all_clipped (clip);
200
201
9.07k
    if (! changed)
202
636
  return clip;
203
204
8.43k
    extents_box = clip->boxes[0];
205
8.66k
    for (i = 1; i < clip->num_boxes; i++) {
206
231
      if (clip->boxes[i].p1.x < extents_box.p1.x)
207
0
    extents_box.p1.x = clip->boxes[i].p1.x;
208
209
231
      if (clip->boxes[i].p1.y < extents_box.p1.y)
210
0
    extents_box.p1.y = clip->boxes[i].p1.y;
211
212
231
      if (clip->boxes[i].p2.x > extents_box.p2.x)
213
0
    extents_box.p2.x = clip->boxes[i].p2.x;
214
215
231
      if (clip->boxes[i].p2.y > extents_box.p2.y)
216
154
    extents_box.p2.y = clip->boxes[i].p2.y;
217
231
    }
218
219
8.43k
    if (clip->path == NULL) {
220
8.31k
  _cairo_box_round_to_rectangle (&extents_box, &clip->extents);
221
8.31k
    } else {
222
120
  cairo_rectangle_int_t extents_rect;
223
224
120
  _cairo_box_round_to_rectangle (&extents_box, &extents_rect);
225
120
  if (! _cairo_rectangle_intersect (&clip->extents, &extents_rect))
226
0
      return _cairo_clip_set_all_clipped (clip);
227
120
    }
228
229
8.43k
    if (clip->region) {
230
0
  cairo_region_destroy (clip->region);
231
0
  clip->region = NULL;
232
0
    }
233
234
8.43k
    clip->is_region = FALSE;
235
8.43k
    return clip;
236
8.43k
}
237
238
cairo_clip_t *
239
_cairo_clip_intersect_box (cairo_clip_t *clip,
240
         const cairo_box_t *box)
241
8.85k
{
242
8.85k
    cairo_rectangle_int_t r;
243
244
8.85k
    if (_cairo_clip_is_all_clipped (clip))
245
0
  return clip;
246
247
8.85k
    _cairo_box_round_to_rectangle (box, &r);
248
8.85k
    if (r.width == 0 || r.height == 0)
249
0
  return _cairo_clip_set_all_clipped (clip);
250
251
8.85k
    return _cairo_clip_intersect_rectangle_box (clip, &r, box);
252
8.85k
}
253
254
/* Copy a box set to a clip
255
 *
256
 * @param boxes  The box set to copy from.
257
 * @param clip   The clip to copy to (return buffer).
258
 * @returns      Zero if the allocation failed (the clip will be set to
259
 *               all-clipped), otherwise non-zero.
260
 */
261
static cairo_bool_t
262
_cairo_boxes_copy_to_clip (const cairo_boxes_t *boxes, cairo_clip_t *clip)
263
143
{
264
    /* XXX cow-boxes? */
265
143
    if (boxes->num_boxes == 1) {
266
0
  clip->boxes = &clip->embedded_box;
267
0
  clip->boxes[0] = boxes->chunks.base[0];
268
0
  clip->num_boxes = 1;
269
0
  return TRUE;
270
0
    }
271
272
143
    clip->boxes = _cairo_boxes_to_array (boxes, &clip->num_boxes);
273
143
    if (unlikely (clip->boxes == NULL))
274
0
    {
275
0
  _cairo_clip_set_all_clipped (clip);
276
0
  return FALSE;
277
0
    }
278
279
143
    return TRUE;
280
143
}
281
282
cairo_clip_t *
283
_cairo_clip_intersect_boxes (cairo_clip_t *clip,
284
           const cairo_boxes_t *boxes)
285
146
{
286
146
    cairo_boxes_t clip_boxes;
287
146
    cairo_box_t limits;
288
146
    cairo_rectangle_int_t extents;
289
290
146
    if (_cairo_clip_is_all_clipped (clip))
291
0
  return clip;
292
293
146
    if (boxes->num_boxes == 0)
294
0
  return _cairo_clip_set_all_clipped (clip);
295
296
146
    if (boxes->num_boxes == 1)
297
3
  return _cairo_clip_intersect_box (clip, boxes->chunks.base);
298
299
143
    if (clip == NULL)
300
0
  clip = _cairo_clip_create ();
301
302
143
    if (clip->num_boxes) {
303
143
  _cairo_boxes_init_for_array (&clip_boxes, clip->boxes, clip->num_boxes);
304
143
  if (unlikely (_cairo_boxes_intersect (&clip_boxes, boxes, &clip_boxes))) {
305
0
      clip = _cairo_clip_set_all_clipped (clip);
306
0
      goto out;
307
0
  }
308
309
143
  if (clip->boxes != &clip->embedded_box)
310
0
      free (clip->boxes);
311
312
143
  clip->boxes = NULL;
313
143
  boxes = &clip_boxes;
314
143
    }
315
316
143
    if (boxes->num_boxes == 0) {
317
0
  clip = _cairo_clip_set_all_clipped (clip);
318
0
  goto out;
319
0
    }
320
321
143
    if (!_cairo_boxes_copy_to_clip (boxes, clip)) {
322
0
  clip = _cairo_clip_set_all_clipped (clip);
323
0
  goto out;
324
0
    }
325
326
143
    _cairo_boxes_extents (boxes, &limits);
327
328
143
    _cairo_box_round_to_rectangle (&limits, &extents);
329
143
    if (clip->path == NULL) {
330
143
  clip->extents = extents;
331
143
    } else if (! _cairo_rectangle_intersect (&clip->extents, &extents)) {
332
0
  clip = _cairo_clip_set_all_clipped (clip);
333
0
  goto out;
334
0
    }
335
336
143
    if (clip->region) {
337
0
  cairo_region_destroy (clip->region);
338
0
  clip->region = NULL;
339
0
    }
340
143
    clip->is_region = FALSE;
341
342
143
out:
343
143
    if (boxes == &clip_boxes)
344
143
  _cairo_boxes_fini (&clip_boxes);
345
346
143
    return clip;
347
143
}
348
349
cairo_clip_t *
350
_cairo_clip_intersect_rectangle (cairo_clip_t       *clip,
351
         const cairo_rectangle_int_t *r)
352
2.50M
{
353
2.50M
    cairo_box_t box;
354
355
2.50M
    if (_cairo_clip_is_all_clipped (clip))
356
0
  return clip;
357
358
2.50M
    if (r->width == 0 || r->height == 0)
359
0
  return _cairo_clip_set_all_clipped (clip);
360
361
2.50M
    _cairo_box_from_rectangle_int (&box, r);
362
363
2.50M
    return _cairo_clip_intersect_rectangle_box (clip, r, &box);
364
2.50M
}
365
366
struct reduce {
367
    cairo_clip_t *clip;
368
    cairo_box_t limit;
369
    cairo_box_t extents;
370
    cairo_bool_t inside;
371
372
    cairo_point_t current_point;
373
    cairo_point_t last_move_to;
374
};
375
376
static void
377
_add_clipped_edge (struct reduce *r,
378
       const cairo_point_t *p1,
379
       const cairo_point_t *p2,
380
       int y1, int y2)
381
0
{
382
0
    cairo_fixed_t x;
383
0
384
0
    x = _cairo_edge_compute_intersection_x_for_y (p1, p2, y1);
385
0
    if (x < r->extents.p1.x)
386
0
  r->extents.p1.x = x;
387
0
388
0
    x = _cairo_edge_compute_intersection_x_for_y (p1, p2, y2);
389
0
    if (x > r->extents.p2.x)
390
0
  r->extents.p2.x = x;
391
0
392
0
    if (y1 < r->extents.p1.y)
393
0
  r->extents.p1.y = y1;
394
0
395
0
    if (y2 > r->extents.p2.y)
396
0
  r->extents.p2.y = y2;
397
0
398
0
    r->inside = TRUE;
399
0
}
400
401
static void
402
_add_edge (struct reduce *r,
403
     const cairo_point_t *p1,
404
     const cairo_point_t *p2)
405
0
{
406
0
    int top, bottom;
407
0
    int top_y, bot_y;
408
0
    int n;
409
0
410
0
    if (p1->y < p2->y) {
411
0
  top = p1->y;
412
0
  bottom = p2->y;
413
0
    } else {
414
0
  top = p2->y;
415
0
  bottom = p1->y;
416
0
    }
417
0
418
0
    if (bottom < r->limit.p1.y || top > r->limit.p2.y)
419
0
  return;
420
0
421
0
    if (p1->x > p2->x) {
422
0
  const cairo_point_t *t = p1;
423
0
  p1 = p2;
424
0
  p2 = t;
425
0
    }
426
0
427
0
    if (p2->x <= r->limit.p1.x || p1->x >= r->limit.p2.x)
428
0
  return;
429
0
430
0
    for (n = 0; n < r->clip->num_boxes; n++) {
431
0
  const cairo_box_t *limits = &r->clip->boxes[n];
432
0
433
0
  if (bottom < limits->p1.y || top > limits->p2.y)
434
0
      continue;
435
0
436
0
  if (p2->x <= limits->p1.x || p1->x >= limits->p2.x)
437
0
      continue;
438
0
439
0
  if (p1->x >= limits->p1.x && p2->x <= limits->p1.x) {
440
0
      top_y = top;
441
0
      bot_y = bottom;
442
0
  } else {
443
0
      int p1_y, p2_y;
444
0
445
0
      p1_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
446
0
                   limits->p1.x);
447
0
      p2_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
448
0
                   limits->p2.x);
449
0
      if (p1_y < p2_y) {
450
0
    top_y = p1_y;
451
0
    bot_y = p2_y;
452
0
      } else {
453
0
    top_y = p2_y;
454
0
    bot_y = p1_y;
455
0
      }
456
0
457
0
      if (top_y < top)
458
0
    top_y = top;
459
0
      if (bot_y > bottom)
460
0
    bot_y = bottom;
461
0
  }
462
0
463
0
  if (top_y < limits->p1.y)
464
0
      top_y = limits->p1.y;
465
0
466
0
  if (bot_y > limits->p2.y)
467
0
      bot_y = limits->p2.y;
468
0
  if (bot_y > top_y)
469
0
      _add_clipped_edge (r, p1, p2, top_y, bot_y);
470
0
    }
471
0
}
472
473
static cairo_status_t
474
_reduce_line_to (void *closure,
475
           const cairo_point_t *point)
476
0
{
477
0
    struct reduce *r = closure;
478
0
479
0
    _add_edge (r, &r->current_point, point);
480
0
    r->current_point = *point;
481
0
482
0
    return CAIRO_STATUS_SUCCESS;
483
0
}
484
485
static cairo_status_t
486
_reduce_close (void *closure)
487
0
{
488
0
    struct reduce *r = closure;
489
0
490
0
    return _reduce_line_to (r, &r->last_move_to);
491
0
}
492
493
static cairo_status_t
494
_reduce_move_to (void *closure,
495
     const cairo_point_t *point)
496
0
{
497
0
    struct reduce *r = closure;
498
0
    cairo_status_t status;
499
0
500
0
    /* close current subpath */
501
0
    status = _reduce_close (closure);
502
0
503
0
    /* make sure that the closure represents a degenerate path */
504
0
    r->current_point = *point;
505
0
    r->last_move_to = *point;
506
0
507
0
    return status;
508
0
}
509
510
static cairo_clip_t *
511
_cairo_clip_reduce_to_boxes (cairo_clip_t *clip)
512
9.11k
{
513
9.11k
    struct reduce r;
514
9.11k
    cairo_clip_path_t *clip_path;
515
9.11k
    cairo_status_t status;
516
517
9.11k
  return clip;
518
0
    if (clip->path == NULL)
519
0
  return clip;
520
521
0
    r.clip = clip;
522
0
    r.extents.p1.x = r.extents.p1.y = INT_MAX;
523
0
    r.extents.p2.x = r.extents.p2.y = INT_MIN;
524
0
    r.inside = FALSE;
525
526
0
    r.limit.p1.x = _cairo_fixed_from_int (clip->extents.x);
527
0
    r.limit.p1.y = _cairo_fixed_from_int (clip->extents.y);
528
0
    r.limit.p2.x = _cairo_fixed_from_int (clip->extents.x + clip->extents.width);
529
0
    r.limit.p2.y = _cairo_fixed_from_int (clip->extents.y + clip->extents.height);
530
531
0
    clip_path = clip->path;
532
0
    do {
533
0
  r.current_point.x = 0;
534
0
  r.current_point.y = 0;
535
0
  r.last_move_to = r.current_point;
536
537
0
  status = _cairo_path_fixed_interpret_flat (&clip_path->path,
538
0
               _reduce_move_to,
539
0
               _reduce_line_to,
540
0
               _reduce_close,
541
0
               &r,
542
0
               clip_path->tolerance);
543
0
  assert (status == CAIRO_STATUS_SUCCESS);
544
0
  _reduce_close (&r);
545
0
    } while ((clip_path = clip_path->prev));
546
547
0
    if (! r.inside) {
548
0
  _cairo_clip_path_destroy (clip->path);
549
0
  clip->path = NULL;
550
0
    }
551
552
0
    return _cairo_clip_intersect_box (clip, &r.extents);
553
0
}
554
555
cairo_clip_t *
556
_cairo_clip_reduce_to_rectangle (const cairo_clip_t *clip,
557
         const cairo_rectangle_int_t *r)
558
1.50M
{
559
1.50M
    cairo_clip_t *copy;
560
561
1.50M
    if (_cairo_clip_is_all_clipped (clip))
562
0
  return (cairo_clip_t *) clip;
563
564
1.50M
    if (_cairo_clip_contains_rectangle (clip, r))
565
1.49M
  return _cairo_clip_intersect_rectangle (NULL, r);
566
567
9.11k
    copy = _cairo_clip_copy_intersect_rectangle (clip, r);
568
9.11k
    if (_cairo_clip_is_all_clipped (copy))
569
0
  return copy;
570
571
9.11k
    return _cairo_clip_reduce_to_boxes (copy);
572
9.11k
}
573
574
cairo_clip_t *
575
_cairo_clip_reduce_for_composite (const cairo_clip_t *clip,
576
          cairo_composite_rectangles_t *extents)
577
1.50M
{
578
1.50M
    const cairo_rectangle_int_t *r;
579
580
1.50M
    r = extents->is_bounded ? &extents->bounded : &extents->unbounded;
581
1.50M
    return _cairo_clip_reduce_to_rectangle (clip, r);
582
1.50M
}
583
584
cairo_clip_t *
585
_cairo_clip_from_boxes (const cairo_boxes_t *boxes)
586
0
{
587
0
    cairo_box_t extents;
588
0
    cairo_clip_t *clip = _cairo_clip_create ();
589
0
    if (clip == NULL)
590
0
  return _cairo_clip_set_all_clipped (clip);
591
592
0
    if (unlikely (! _cairo_boxes_copy_to_clip (boxes, clip)))
593
0
  return clip;
594
595
0
    _cairo_boxes_extents (boxes, &extents);
596
0
    _cairo_box_round_to_rectangle (&extents, &clip->extents);
597
598
0
    return clip;
599
0
}