Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/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 inline int
56
pot (int v)
57
0
{
58
0
    v--;
59
0
    v |= v >> 1;
60
0
    v |= v >> 2;
61
0
    v |= v >> 4;
62
0
    v |= v >> 8;
63
0
    v |= v >> 16;
64
0
    v++;
65
0
    return v;
66
0
}
67
68
static cairo_bool_t
69
_cairo_clip_contains_rectangle_box (const cairo_clip_t *clip,
70
            const cairo_rectangle_int_t *rect,
71
            const cairo_box_t *box)
72
1.81M
{
73
1.81M
    int i;
74
75
    /* clip == NULL means no clip, so the clip contains everything */
76
1.81M
    if (clip == NULL)
77
1.24M
  return TRUE;
78
79
563k
    if (_cairo_clip_is_all_clipped (clip))
80
0
  return FALSE;
81
82
    /* If we have a non-trivial path, just say no */
83
563k
    if (clip->path)
84
0
  return FALSE;
85
86
563k
    if (! _cairo_rectangle_contains_rectangle (&clip->extents, rect))
87
20.5k
  return FALSE;
88
89
542k
    if (clip->num_boxes == 0)
90
0
  return TRUE;
91
92
    /* Check for a clip-box that wholly contains the rectangle */
93
542k
    for (i = 0; i < clip->num_boxes; i++) {
94
542k
  if (box->p1.x >= clip->boxes[i].p1.x &&
95
542k
      box->p1.y >= clip->boxes[i].p1.y &&
96
542k
      box->p2.x <= clip->boxes[i].p2.x &&
97
542k
      box->p2.y <= clip->boxes[i].p2.y)
98
542k
  {
99
542k
      return TRUE;
100
542k
  }
101
542k
    }
102
103
0
    return FALSE;
104
542k
}
105
106
cairo_bool_t
107
_cairo_clip_contains_box (const cairo_clip_t *clip,
108
        const cairo_box_t *box)
109
547
{
110
547
    cairo_rectangle_int_t rect;
111
112
547
    _cairo_box_round_to_rectangle (box, &rect);
113
547
    return _cairo_clip_contains_rectangle_box(clip, &rect, box);
114
547
}
115
116
cairo_bool_t
117
_cairo_clip_contains_rectangle (const cairo_clip_t *clip,
118
        const cairo_rectangle_int_t *rect)
119
1.81M
{
120
1.81M
    cairo_box_t box;
121
122
1.81M
    _cairo_box_from_rectangle_int (&box, rect);
123
1.81M
    return _cairo_clip_contains_rectangle_box (clip, rect, &box);
124
1.81M
}
125
126
cairo_clip_t *
127
_cairo_clip_intersect_rectilinear_path (cairo_clip_t *clip,
128
          const cairo_path_fixed_t *path,
129
          cairo_fill_rule_t fill_rule,
130
          cairo_antialias_t antialias)
131
1.37k
{
132
1.37k
    cairo_status_t status;
133
1.37k
    cairo_boxes_t boxes;
134
135
1.37k
    _cairo_boxes_init (&boxes);
136
1.37k
    status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
137
1.37k
                fill_rule,
138
1.37k
                antialias,
139
1.37k
                &boxes);
140
1.37k
    if (likely (status == CAIRO_STATUS_SUCCESS && boxes.num_boxes))
141
1.26k
  clip = _cairo_clip_intersect_boxes (clip, &boxes);
142
108
    else
143
108
  clip = _cairo_clip_set_all_clipped (clip);
144
1.37k
    _cairo_boxes_fini (&boxes);
145
146
1.37k
    return clip;
147
1.37k
}
148
149
static cairo_clip_t *
150
_cairo_clip_intersect_rectangle_box (cairo_clip_t *clip,
151
             const cairo_rectangle_int_t *r,
152
             const cairo_box_t *box)
153
1.56M
{
154
1.56M
    cairo_box_t extents_box;
155
1.56M
    cairo_bool_t changed = FALSE;
156
1.56M
    int i, j;
157
158
1.56M
    if (clip == NULL) {
159
1.54M
  clip = _cairo_clip_create ();
160
1.54M
  if (clip == NULL)
161
0
      return _cairo_clip_set_all_clipped (clip);
162
1.54M
    }
163
164
1.56M
    if (clip->num_boxes == 0) {
165
1.56M
  clip->boxes = &clip->embedded_box;
166
1.56M
  clip->boxes[0] = *box;
167
1.56M
  clip->num_boxes = 1;
168
1.56M
  if (clip->path == NULL) {
169
1.56M
      clip->extents = *r;
170
1.56M
  } else {
171
0
      if (! _cairo_rectangle_intersect (&clip->extents, r))
172
0
    return _cairo_clip_set_all_clipped (clip);
173
0
  }
174
1.56M
  if (clip->path == NULL)
175
1.56M
      clip->is_region = _cairo_box_is_pixel_aligned (box);
176
1.56M
  return clip;
177
1.56M
    }
178
179
    /* Does the new box wholly subsume the clip? Perform a cheap check
180
     * for the common condition of a single clip rectangle.
181
     */
182
923
    if (clip->num_boxes == 1 &&
183
923
  clip->boxes[0].p1.x >= box->p1.x &&
184
923
  clip->boxes[0].p1.y >= box->p1.y &&
185
923
  clip->boxes[0].p2.x <= box->p2.x &&
186
923
  clip->boxes[0].p2.y <= box->p2.y)
187
335
    {
188
335
  return clip;
189
335
    }
190
191
1.33k
    for (i = j = 0; i < clip->num_boxes; i++) {
192
747
  cairo_box_t *b = &clip->boxes[j];
193
194
747
  if (j != i)
195
65
      *b = clip->boxes[i];
196
197
747
  if (box->p1.x > b->p1.x)
198
247
      b->p1.x = box->p1.x, changed = TRUE;
199
747
  if (box->p2.x < b->p2.x)
200
81
      b->p2.x = box->p2.x, changed = TRUE;
201
202
747
  if (box->p1.y > b->p1.y)
203
111
      b->p1.y = box->p1.y, changed = TRUE;
204
747
  if (box->p2.y < b->p2.y)
205
285
      b->p2.y = box->p2.y, changed = TRUE;
206
207
747
  j += b->p2.x > b->p1.x && b->p2.y > b->p1.y;
208
747
    }
209
588
    clip->num_boxes = j;
210
211
588
    if (clip->num_boxes == 0)
212
397
  return _cairo_clip_set_all_clipped (clip);
213
214
191
    if (! changed)
215
2
  return clip;
216
217
189
    extents_box = clip->boxes[0];
218
189
    for (i = 1; i < clip->num_boxes; i++) {
219
0
      if (clip->boxes[i].p1.x < extents_box.p1.x)
220
0
    extents_box.p1.x = clip->boxes[i].p1.x;
221
222
0
      if (clip->boxes[i].p1.y < extents_box.p1.y)
223
0
    extents_box.p1.y = clip->boxes[i].p1.y;
224
225
0
      if (clip->boxes[i].p2.x > extents_box.p2.x)
226
0
    extents_box.p2.x = clip->boxes[i].p2.x;
227
228
0
      if (clip->boxes[i].p2.y > extents_box.p2.y)
229
0
    extents_box.p2.y = clip->boxes[i].p2.y;
230
0
    }
231
232
189
    if (clip->path == NULL) {
233
189
  _cairo_box_round_to_rectangle (&extents_box, &clip->extents);
234
189
    } else {
235
0
  cairo_rectangle_int_t extents_rect;
236
237
0
  _cairo_box_round_to_rectangle (&extents_box, &extents_rect);
238
0
  if (! _cairo_rectangle_intersect (&clip->extents, &extents_rect))
239
0
      return _cairo_clip_set_all_clipped (clip);
240
0
    }
241
242
189
    if (clip->region) {
243
0
  cairo_region_destroy (clip->region);
244
0
  clip->region = NULL;
245
0
    }
246
247
189
    clip->is_region = FALSE;
248
189
    return clip;
249
189
}
250
251
cairo_clip_t *
252
_cairo_clip_intersect_box (cairo_clip_t *clip,
253
         const cairo_box_t *box)
254
306k
{
255
306k
    cairo_rectangle_int_t r;
256
257
306k
    if (_cairo_clip_is_all_clipped (clip))
258
0
  return clip;
259
260
306k
    _cairo_box_round_to_rectangle (box, &r);
261
306k
    if (r.width == 0 || r.height == 0)
262
19.4k
  return _cairo_clip_set_all_clipped (clip);
263
264
287k
    return _cairo_clip_intersect_rectangle_box (clip, &r, box);
265
306k
}
266
267
/* Copy a box set to a clip
268
 *
269
 * @param boxes  The box set to copy from.
270
 * @param clip   The clip to copy to (return buffer).
271
 * @returns      Zero if the allocation failed (the clip will be set to
272
 *               all-clipped), otherwise non-zero.
273
 */
274
static cairo_bool_t
275
_cairo_boxes_copy_to_clip (const cairo_boxes_t *boxes, cairo_clip_t *clip)
276
1.26k
{
277
    /* XXX cow-boxes? */
278
1.26k
    if (boxes->num_boxes == 1) {
279
0
  clip->boxes = &clip->embedded_box;
280
0
  clip->boxes[0] = boxes->chunks.base[0];
281
0
  clip->num_boxes = 1;
282
0
  return TRUE;
283
0
    }
284
285
1.26k
    clip->boxes = _cairo_boxes_to_array (boxes, &clip->num_boxes);
286
1.26k
    if (unlikely (clip->boxes == NULL))
287
0
    {
288
0
  _cairo_clip_set_all_clipped (clip);
289
0
  return FALSE;
290
0
    }
291
292
1.26k
    return TRUE;
293
1.26k
}
294
295
cairo_clip_t *
296
_cairo_clip_intersect_boxes (cairo_clip_t *clip,
297
           const cairo_boxes_t *boxes)
298
1.26k
{
299
1.26k
    cairo_boxes_t clip_boxes;
300
1.26k
    cairo_box_t limits;
301
1.26k
    cairo_rectangle_int_t extents;
302
303
1.26k
    if (_cairo_clip_is_all_clipped (clip))
304
0
  return clip;
305
306
1.26k
    if (boxes->num_boxes == 0)
307
0
  return _cairo_clip_set_all_clipped (clip);
308
309
1.26k
    if (boxes->num_boxes == 1)
310
0
  return _cairo_clip_intersect_box (clip, boxes->chunks.base);
311
312
1.26k
    if (clip == NULL)
313
1.26k
  clip = _cairo_clip_create ();
314
315
1.26k
    if (clip->num_boxes) {
316
0
  _cairo_boxes_init_for_array (&clip_boxes, clip->boxes, clip->num_boxes);
317
0
  if (unlikely (_cairo_boxes_intersect (&clip_boxes, boxes, &clip_boxes))) {
318
0
      clip = _cairo_clip_set_all_clipped (clip);
319
0
      goto out;
320
0
  }
321
322
0
  if (clip->boxes != &clip->embedded_box)
323
0
      free (clip->boxes);
324
325
0
  clip->boxes = NULL;
326
0
  boxes = &clip_boxes;
327
0
    }
328
329
1.26k
    if (boxes->num_boxes == 0) {
330
0
  clip = _cairo_clip_set_all_clipped (clip);
331
0
  goto out;
332
0
    }
333
334
1.26k
    _cairo_boxes_copy_to_clip (boxes, clip);
335
336
1.26k
    _cairo_boxes_extents (boxes, &limits);
337
338
1.26k
    _cairo_box_round_to_rectangle (&limits, &extents);
339
1.26k
    if (clip->path == NULL) {
340
1.26k
  clip->extents = extents;
341
1.26k
    } else if (! _cairo_rectangle_intersect (&clip->extents, &extents)) {
342
0
  clip = _cairo_clip_set_all_clipped (clip);
343
0
  goto out;
344
0
    }
345
346
1.26k
    if (clip->region) {
347
0
  cairo_region_destroy (clip->region);
348
0
  clip->region = NULL;
349
0
    }
350
1.26k
    clip->is_region = FALSE;
351
352
1.26k
out:
353
1.26k
    if (boxes == &clip_boxes)
354
0
  _cairo_boxes_fini (&clip_boxes);
355
356
1.26k
    return clip;
357
1.26k
}
358
359
cairo_clip_t *
360
_cairo_clip_intersect_rectangle (cairo_clip_t       *clip,
361
         const cairo_rectangle_int_t *r)
362
1.27M
{
363
1.27M
    cairo_box_t box;
364
365
1.27M
    if (_cairo_clip_is_all_clipped (clip))
366
0
  return clip;
367
368
1.27M
    if (r->width == 0 || r->height == 0)
369
16
  return _cairo_clip_set_all_clipped (clip);
370
371
1.27M
    _cairo_box_from_rectangle_int (&box, r);
372
373
1.27M
    return _cairo_clip_intersect_rectangle_box (clip, r, &box);
374
1.27M
}
375
376
struct reduce {
377
    cairo_clip_t *clip;
378
    cairo_box_t limit;
379
    cairo_box_t extents;
380
    cairo_bool_t inside;
381
382
    cairo_point_t current_point;
383
    cairo_point_t last_move_to;
384
};
385
386
static void
387
_add_clipped_edge (struct reduce *r,
388
       const cairo_point_t *p1,
389
       const cairo_point_t *p2,
390
       int y1, int y2)
391
0
{
392
0
    cairo_fixed_t x;
393
0
394
0
    x = _cairo_edge_compute_intersection_x_for_y (p1, p2, y1);
395
0
    if (x < r->extents.p1.x)
396
0
  r->extents.p1.x = x;
397
0
398
0
    x = _cairo_edge_compute_intersection_x_for_y (p1, p2, y2);
399
0
    if (x > r->extents.p2.x)
400
0
  r->extents.p2.x = x;
401
0
402
0
    if (y1 < r->extents.p1.y)
403
0
  r->extents.p1.y = y1;
404
0
405
0
    if (y2 > r->extents.p2.y)
406
0
  r->extents.p2.y = y2;
407
0
408
0
    r->inside = TRUE;
409
0
}
410
411
static void
412
_add_edge (struct reduce *r,
413
     const cairo_point_t *p1,
414
     const cairo_point_t *p2)
415
0
{
416
0
    int top, bottom;
417
0
    int top_y, bot_y;
418
0
    int n;
419
0
420
0
    if (p1->y < p2->y) {
421
0
  top = p1->y;
422
0
  bottom = p2->y;
423
0
    } else {
424
0
  top = p2->y;
425
0
  bottom = p1->y;
426
0
    }
427
0
428
0
    if (bottom < r->limit.p1.y || top > r->limit.p2.y)
429
0
  return;
430
0
431
0
    if (p1->x > p2->x) {
432
0
  const cairo_point_t *t = p1;
433
0
  p1 = p2;
434
0
  p2 = t;
435
0
    }
436
0
437
0
    if (p2->x <= r->limit.p1.x || p1->x >= r->limit.p2.x)
438
0
  return;
439
0
440
0
    for (n = 0; n < r->clip->num_boxes; n++) {
441
0
  const cairo_box_t *limits = &r->clip->boxes[n];
442
0
443
0
  if (bottom < limits->p1.y || top > limits->p2.y)
444
0
      continue;
445
0
446
0
  if (p2->x <= limits->p1.x || p1->x >= limits->p2.x)
447
0
      continue;
448
0
449
0
  if (p1->x >= limits->p1.x && p2->x <= limits->p1.x) {
450
0
      top_y = top;
451
0
      bot_y = bottom;
452
0
  } else {
453
0
      int p1_y, p2_y;
454
0
455
0
      p1_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
456
0
                   limits->p1.x);
457
0
      p2_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
458
0
                   limits->p2.x);
459
0
      if (p1_y < p2_y) {
460
0
    top_y = p1_y;
461
0
    bot_y = p2_y;
462
0
      } else {
463
0
    top_y = p2_y;
464
0
    bot_y = p1_y;
465
0
      }
466
0
467
0
      if (top_y < top)
468
0
    top_y = top;
469
0
      if (bot_y > bottom)
470
0
    bot_y = bottom;
471
0
  }
472
0
473
0
  if (top_y < limits->p1.y)
474
0
      top_y = limits->p1.y;
475
0
476
0
  if (bot_y > limits->p2.y)
477
0
      bot_y = limits->p2.y;
478
0
  if (bot_y > top_y)
479
0
      _add_clipped_edge (r, p1, p2, top_y, bot_y);
480
0
    }
481
0
}
482
483
static cairo_status_t
484
_reduce_line_to (void *closure,
485
           const cairo_point_t *point)
486
0
{
487
0
    struct reduce *r = closure;
488
0
489
0
    _add_edge (r, &r->current_point, point);
490
0
    r->current_point = *point;
491
0
492
0
    return CAIRO_STATUS_SUCCESS;
493
0
}
494
495
static cairo_status_t
496
_reduce_close (void *closure)
497
0
{
498
0
    struct reduce *r = closure;
499
0
500
0
    return _reduce_line_to (r, &r->last_move_to);
501
0
}
502
503
static cairo_status_t
504
_reduce_move_to (void *closure,
505
     const cairo_point_t *point)
506
0
{
507
0
    struct reduce *r = closure;
508
0
    cairo_status_t status;
509
0
510
0
    /* close current subpath */
511
0
    status = _reduce_close (closure);
512
0
513
0
    /* make sure that the closure represents a degenerate path */
514
0
    r->current_point = *point;
515
0
    r->last_move_to = *point;
516
0
517
0
    return status;
518
0
}
519
520
static cairo_clip_t *
521
_cairo_clip_reduce_to_boxes (cairo_clip_t *clip)
522
0
{
523
0
    struct reduce r;
524
0
    cairo_clip_path_t *clip_path;
525
0
    cairo_status_t status;
526
527
0
  return clip;
528
0
    if (clip->path == NULL)
529
0
  return clip;
530
531
0
    r.clip = clip;
532
0
    r.extents.p1.x = r.extents.p1.y = INT_MAX;
533
0
    r.extents.p2.x = r.extents.p2.y = INT_MIN;
534
0
    r.inside = FALSE;
535
536
0
    r.limit.p1.x = _cairo_fixed_from_int (clip->extents.x);
537
0
    r.limit.p1.y = _cairo_fixed_from_int (clip->extents.y);
538
0
    r.limit.p2.x = _cairo_fixed_from_int (clip->extents.x + clip->extents.width);
539
0
    r.limit.p2.y = _cairo_fixed_from_int (clip->extents.y + clip->extents.height);
540
541
0
    clip_path = clip->path;
542
0
    do {
543
0
  r.current_point.x = 0;
544
0
  r.current_point.y = 0;
545
0
  r.last_move_to = r.current_point;
546
547
0
  status = _cairo_path_fixed_interpret_flat (&clip_path->path,
548
0
               _reduce_move_to,
549
0
               _reduce_line_to,
550
0
               _reduce_close,
551
0
               &r,
552
0
               clip_path->tolerance);
553
0
  assert (status == CAIRO_STATUS_SUCCESS);
554
0
  _reduce_close (&r);
555
0
    } while ((clip_path = clip_path->prev));
556
557
0
    if (! r.inside) {
558
0
  _cairo_clip_path_destroy (clip->path);
559
0
  clip->path = NULL;
560
0
    }
561
562
0
    return _cairo_clip_intersect_box (clip, &r.extents);
563
0
}
564
565
cairo_clip_t *
566
_cairo_clip_reduce_to_rectangle (const cairo_clip_t *clip,
567
         const cairo_rectangle_int_t *r)
568
1.27M
{
569
1.27M
    cairo_clip_t *copy;
570
571
1.27M
    if (_cairo_clip_is_all_clipped (clip))
572
0
  return (cairo_clip_t *) clip;
573
574
1.27M
    if (_cairo_clip_contains_rectangle (clip, r))
575
1.27M
  return _cairo_clip_intersect_rectangle (NULL, r);
576
577
0
    copy = _cairo_clip_copy_intersect_rectangle (clip, r);
578
0
    if (_cairo_clip_is_all_clipped (copy))
579
0
  return copy;
580
581
0
    return _cairo_clip_reduce_to_boxes (copy);
582
0
}
583
584
cairo_clip_t *
585
_cairo_clip_reduce_for_composite (const cairo_clip_t *clip,
586
          cairo_composite_rectangles_t *extents)
587
1.27M
{
588
1.27M
    const cairo_rectangle_int_t *r;
589
590
1.27M
    r = extents->is_bounded ? &extents->bounded : &extents->unbounded;
591
1.27M
    return _cairo_clip_reduce_to_rectangle (clip, r);
592
1.27M
}
593
594
cairo_clip_t *
595
_cairo_clip_from_boxes (const cairo_boxes_t *boxes)
596
0
{
597
0
    cairo_box_t extents;
598
0
    cairo_clip_t *clip = _cairo_clip_create ();
599
0
    if (clip == NULL)
600
0
  return _cairo_clip_set_all_clipped (clip);
601
602
0
    if (unlikely (! _cairo_boxes_copy_to_clip (boxes, clip)))
603
0
  return clip;
604
605
0
    _cairo_boxes_extents (boxes, &extents);
606
0
    _cairo_box_round_to_rectangle (&extents, &clip->extents);
607
608
0
    return clip;
609
0
}