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 #include "cairo-clip-private.h"
44 #include "cairo-error-private.h"
45 #include "cairo-freed-pool-private.h"
46 #include "cairo-gstate-private.h"
47 #include "cairo-path-fixed-private.h"
48 #include "cairo-composite-rectangles-private.h"
49 #include "cairo-region-private.h"
50
51 #if HAS_FREED_POOL
52 static freed_pool_t clip_path_pool;
53 #endif
54
55 static cairo_clip_path_t *
_cairo_clip_path_create(cairo_clip_t * clip)56 _cairo_clip_path_create (cairo_clip_t *clip)
57 {
58 cairo_clip_path_t *clip_path;
59
60 clip_path = _freed_pool_get (&clip_path_pool);
61 if (unlikely (clip_path == NULL)) {
62 clip_path = malloc (sizeof (cairo_clip_path_t));
63 if (unlikely (clip_path == NULL))
64 return NULL;
65 }
66
67 CAIRO_REFERENCE_COUNT_INIT (&clip_path->ref_count, 1);
68
69 clip_path->flags = 0;
70 clip_path->region = NULL;
71 clip_path->surface = NULL;
72
73 clip_path->prev = clip->path;
74 clip->path = clip_path;
75
76 return clip_path;
77 }
78
79 static cairo_clip_path_t *
_cairo_clip_path_reference(cairo_clip_path_t * clip_path)80 _cairo_clip_path_reference (cairo_clip_path_t *clip_path)
81 {
82 assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
83
84 _cairo_reference_count_inc (&clip_path->ref_count);
85
86 return clip_path;
87 }
88
89 static void
_cairo_clip_path_destroy(cairo_clip_path_t * clip_path)90 _cairo_clip_path_destroy (cairo_clip_path_t *clip_path)
91 {
92 assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
93
94 if (! _cairo_reference_count_dec_and_test (&clip_path->ref_count))
95 return;
96
97 _cairo_path_fixed_fini (&clip_path->path);
98 if (clip_path->region != NULL)
99 cairo_region_destroy (clip_path->region);
100 if (clip_path->surface != NULL)
101 cairo_surface_destroy (clip_path->surface);
102
103 if (clip_path->prev != NULL)
104 _cairo_clip_path_destroy (clip_path->prev);
105
106 _freed_pool_put (&clip_path_pool, clip_path);
107 }
108
109 void
_cairo_clip_init(cairo_clip_t * clip)110 _cairo_clip_init (cairo_clip_t *clip)
111 {
112 clip->all_clipped = FALSE;
113 clip->path = NULL;
114 }
115
116 static void
_cairo_clip_set_all_clipped(cairo_clip_t * clip)117 _cairo_clip_set_all_clipped (cairo_clip_t *clip)
118 {
119 clip->all_clipped = TRUE;
120 if (clip->path != NULL) {
121 _cairo_clip_path_destroy (clip->path);
122 clip->path = NULL;
123 }
124 }
125
126 static cairo_status_t
_cairo_clip_intersect_rectangle(cairo_clip_t * clip,const cairo_rectangle_int_t * rect)127 _cairo_clip_intersect_rectangle (cairo_clip_t *clip,
128 const cairo_rectangle_int_t *rect)
129 {
130 cairo_clip_path_t *clip_path;
131 cairo_status_t status;
132
133 if (clip->path != NULL) {
134 if (rect->x <= clip->path->extents.x &&
135 rect->y <= clip->path->extents.y &&
136 rect->x + rect->width >= clip->path->extents.x + clip->path->extents.width &&
137 rect->y + rect->height >= clip->path->extents.y + clip->path->extents.height)
138 {
139 return CAIRO_STATUS_SUCCESS;
140 }
141 }
142
143 clip_path = _cairo_clip_path_create (clip);
144 if (unlikely (clip_path == NULL))
145 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
146
147 _cairo_path_fixed_init (&clip_path->path);
148
149 status = _cairo_path_fixed_move_to (&clip_path->path,
150 _cairo_fixed_from_int (rect->x),
151 _cairo_fixed_from_int (rect->y));
152 assert (status == CAIRO_STATUS_SUCCESS);
153 status = _cairo_path_fixed_rel_line_to (&clip_path->path,
154 _cairo_fixed_from_int (rect->width),
155 _cairo_fixed_from_int (0));
156 assert (status == CAIRO_STATUS_SUCCESS);
157 status = _cairo_path_fixed_rel_line_to (&clip_path->path,
158 _cairo_fixed_from_int (0),
159 _cairo_fixed_from_int (rect->height));
160 assert (status == CAIRO_STATUS_SUCCESS);
161 status = _cairo_path_fixed_rel_line_to (&clip_path->path,
162 _cairo_fixed_from_int (-rect->width),
163 _cairo_fixed_from_int (0));
164 assert (status == CAIRO_STATUS_SUCCESS);
165 status = _cairo_path_fixed_close_path (&clip_path->path);
166 assert (status == CAIRO_STATUS_SUCCESS);
167
168 clip_path->fill_rule = CAIRO_FILL_RULE_WINDING;
169 clip_path->tolerance = 1;
170 clip_path->antialias = CAIRO_ANTIALIAS_DEFAULT;
171 clip_path->flags |= CAIRO_CLIP_PATH_IS_BOX;
172
173 clip_path->extents = *rect;
174 if (clip_path->prev != NULL) {
175 if (! _cairo_rectangle_intersect (&clip_path->extents,
176 &clip_path->prev->extents))
177 {
178 _cairo_clip_set_all_clipped (clip);
179 }
180 }
181
182 /* could preallocate the region if it proves worthwhile */
183
184 return CAIRO_STATUS_SUCCESS;
185 }
186
187 cairo_clip_t *
_cairo_clip_init_copy(cairo_clip_t * clip,cairo_clip_t * other)188 _cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other)
189 {
190 if (other != NULL) {
191 clip->all_clipped = other->all_clipped;
192 if (other->path == NULL) {
193 clip->path = NULL;
194 if (! clip->all_clipped)
195 clip = NULL;
196 } else {
197 clip->path = _cairo_clip_path_reference (other->path);
198 }
199 } else {
200 _cairo_clip_init (clip);
201 clip = NULL;
202 }
203
204 return clip;
205 }
206
207 void
_cairo_clip_reset(cairo_clip_t * clip)208 _cairo_clip_reset (cairo_clip_t *clip)
209 {
210 clip->all_clipped = FALSE;
211 if (clip->path != NULL) {
212 _cairo_clip_path_destroy (clip->path);
213 clip->path = NULL;
214 }
215 }
216
217 static cairo_status_t
_cairo_clip_intersect_path(cairo_clip_t * clip,const cairo_path_fixed_t * path,cairo_fill_rule_t fill_rule,double tolerance,cairo_antialias_t antialias)218 _cairo_clip_intersect_path (cairo_clip_t *clip,
219 const cairo_path_fixed_t *path,
220 cairo_fill_rule_t fill_rule,
221 double tolerance,
222 cairo_antialias_t antialias)
223 {
224 cairo_clip_path_t *clip_path;
225 cairo_status_t status;
226 cairo_rectangle_int_t extents;
227 cairo_box_t box;
228 cairo_bool_t is_box = FALSE;
229
230 if (clip->path != NULL) {
231 if (clip->path->fill_rule == fill_rule &&
232 (path->is_rectilinear || tolerance == clip->path->tolerance) &&
233 antialias == clip->path->antialias &&
234 _cairo_path_fixed_equal (&clip->path->path, path))
235 {
236 return CAIRO_STATUS_SUCCESS;
237 }
238 }
239
240 _cairo_path_fixed_approximate_clip_extents (path, &extents);
241 if (extents.width == 0 || extents.height == 0) {
242 _cairo_clip_set_all_clipped (clip);
243 return CAIRO_STATUS_SUCCESS;
244 }
245
246 is_box = _cairo_path_fixed_is_box (path, &box);
247 if (clip->path != NULL) {
248 if (! _cairo_rectangle_intersect (&extents, &clip->path->extents)) {
249 _cairo_clip_set_all_clipped (clip);
250 return CAIRO_STATUS_SUCCESS;
251 }
252
253 /* does this clip wholly subsume the others? */
254 if (is_box &&
255 box.p1.x <= _cairo_fixed_from_int (clip->path->extents.x) &&
256 box.p2.x >= _cairo_fixed_from_int (clip->path->extents.x + clip->path->extents.width) &&
257 box.p1.y <= _cairo_fixed_from_int (clip->path->extents.y) &&
258 box.p2.y >= _cairo_fixed_from_int (clip->path->extents.y + clip->path->extents.height))
259 {
260 return CAIRO_STATUS_SUCCESS;
261 }
262 }
263
264 clip_path = _cairo_clip_path_create (clip);
265 if (unlikely (clip_path == NULL))
266 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
267
268 status = _cairo_path_fixed_init_copy (&clip_path->path, path);
269 if (unlikely (status)) {
270 clip->path = clip->path->prev;
271 _cairo_clip_path_destroy (clip_path);
272 return status;
273 }
274
275 clip_path->extents = extents;
276 clip_path->fill_rule = fill_rule;
277 clip_path->tolerance = tolerance;
278 clip_path->antialias = antialias;
279 if (is_box)
280 clip_path->flags |= CAIRO_CLIP_PATH_IS_BOX;
281
282 return CAIRO_STATUS_SUCCESS;
283 }
284
285 cairo_bool_t
_cairo_clip_equal(const cairo_clip_t * clip_a,const cairo_clip_t * clip_b)286 _cairo_clip_equal (const cairo_clip_t *clip_a,
287 const cairo_clip_t *clip_b)
288 {
289 const cairo_clip_path_t *clip_path_a, *clip_path_b;
290
291 clip_path_a = clip_a->path;
292 clip_path_b = clip_b->path;
293
294 while (clip_path_a && clip_path_b) {
295 if (clip_path_a == clip_path_b)
296 return TRUE;
297
298 if (clip_path_a->fill_rule != clip_path_b->fill_rule)
299 return FALSE;
300
301 if (clip_path_a->tolerance != clip_path_b->tolerance)
302 return FALSE;
303
304 if (clip_path_a->antialias != clip_path_b->antialias)
305 return FALSE;
306
307 if (! _cairo_path_fixed_equal (&clip_path_a->path, &clip_path_b->path))
308 return FALSE;
309
310 clip_path_a = clip_path_a->prev;
311 clip_path_b = clip_path_b->prev;
312 }
313
314 return clip_path_a == clip_path_b; /* ie both NULL */
315 }
316
317 cairo_status_t
_cairo_clip_clip(cairo_clip_t * clip,const cairo_path_fixed_t * path,cairo_fill_rule_t fill_rule,double tolerance,cairo_antialias_t antialias)318 _cairo_clip_clip (cairo_clip_t *clip,
319 const cairo_path_fixed_t *path,
320 cairo_fill_rule_t fill_rule,
321 double tolerance,
322 cairo_antialias_t antialias)
323 {
324 if (clip->all_clipped)
325 return CAIRO_STATUS_SUCCESS;
326
327 /* catch the empty clip path */
328 if (_cairo_path_fixed_fill_is_empty (path)) {
329 _cairo_clip_set_all_clipped (clip);
330 return CAIRO_STATUS_SUCCESS;
331 }
332
333 return _cairo_clip_intersect_path (clip,
334 path, fill_rule, tolerance,
335 antialias);
336 }
337
338 cairo_status_t
_cairo_clip_rectangle(cairo_clip_t * clip,const cairo_rectangle_int_t * rectangle)339 _cairo_clip_rectangle (cairo_clip_t *clip,
340 const cairo_rectangle_int_t *rectangle)
341 {
342 if (clip->all_clipped)
343 return CAIRO_STATUS_SUCCESS;
344
345 if (rectangle->width == 0 || rectangle->height == 0) {
346 _cairo_clip_set_all_clipped (clip);
347 return CAIRO_STATUS_SUCCESS;
348 }
349
350 /* if a smaller clip has already been set, ignore the new path */
351 if (clip->path != NULL) {
352 if (rectangle->x <= clip->path->extents.x &&
353 rectangle->y <= clip->path->extents.y &&
354 rectangle->x + rectangle->width >= clip->path->extents.x + clip->path->extents.width &&
355 rectangle->y + rectangle->height >= clip->path->extents.y + clip->path->extents.height)
356 {
357 return CAIRO_STATUS_SUCCESS;
358 }
359 }
360
361 return _cairo_clip_intersect_rectangle (clip, rectangle);
362 }
363
364 static cairo_status_t
_cairo_clip_path_reapply_clip_path_transform(cairo_clip_t * clip,cairo_clip_path_t * other_path,const cairo_matrix_t * matrix)365 _cairo_clip_path_reapply_clip_path_transform (cairo_clip_t *clip,
366 cairo_clip_path_t *other_path,
367 const cairo_matrix_t *matrix)
368 {
369 cairo_status_t status;
370 cairo_clip_path_t *clip_path;
371 cairo_bool_t is_empty;
372
373 if (other_path->prev != NULL) {
374 status = _cairo_clip_path_reapply_clip_path_transform (clip,
375 other_path->prev,
376 matrix);
377 if (unlikely (status))
378 return status;
379 }
380
381 clip_path = _cairo_clip_path_create (clip);
382 if (unlikely (clip_path == NULL))
383 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
384
385 status = _cairo_path_fixed_init_copy (&clip_path->path,
386 &other_path->path);
387 if (unlikely (status)) {
388 clip->path = clip->path->prev;
389 _cairo_clip_path_destroy (clip_path);
390 return status;
391 }
392
393 _cairo_path_fixed_transform (&clip_path->path, matrix);
394 _cairo_path_fixed_approximate_clip_extents (&clip_path->path,
395 &clip_path->extents);
396 if (clip_path->prev != NULL) {
397 is_empty = _cairo_rectangle_intersect (&clip_path->extents,
398 &clip_path->prev->extents);
399 }
400
401 clip_path->fill_rule = other_path->fill_rule;
402 clip_path->tolerance = other_path->tolerance;
403 clip_path->antialias = other_path->antialias;
404
405 return CAIRO_STATUS_SUCCESS;
406 }
407
408 static cairo_status_t
_cairo_clip_path_reapply_clip_path_translate(cairo_clip_t * clip,cairo_clip_path_t * other_path,int tx,int ty)409 _cairo_clip_path_reapply_clip_path_translate (cairo_clip_t *clip,
410 cairo_clip_path_t *other_path,
411 int tx, int ty)
412 {
413 cairo_status_t status;
414 cairo_clip_path_t *clip_path;
415
416 if (other_path->prev != NULL) {
417 status = _cairo_clip_path_reapply_clip_path_translate (clip,
418 other_path->prev,
419 tx, ty);
420 if (unlikely (status))
421 return status;
422 }
423
424 clip_path = _cairo_clip_path_create (clip);
425 if (unlikely (clip_path == NULL))
426 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
427
428 status = _cairo_path_fixed_init_copy (&clip_path->path,
429 &other_path->path);
430 if (unlikely (status)) {
431 clip->path = clip->path->prev;
432 _cairo_clip_path_destroy (clip_path);
433 return status;
434 }
435
436 _cairo_path_fixed_translate (&clip_path->path,
437 _cairo_fixed_from_int (tx),
438 _cairo_fixed_from_int (ty));
439
440 clip_path->fill_rule = other_path->fill_rule;
441 clip_path->tolerance = other_path->tolerance;
442 clip_path->antialias = other_path->antialias;
443
444 clip_path->flags = other_path->flags;
445 if (other_path->region != NULL) {
446 clip_path->region = cairo_region_copy (other_path->region);
447 status = clip_path->region->status;
448 if (unlikely (status)) {
449 clip->path = clip->path->prev;
450 _cairo_clip_path_destroy (clip_path);
451 return status;
452 }
453
454 cairo_region_translate (clip_path->region, tx, ty);
455 }
456 clip_path->surface = cairo_surface_reference (other_path->surface);
457
458 clip_path->extents = other_path->extents;
459 clip_path->extents.x += tx;
460 clip_path->extents.y += ty;
461
462 return CAIRO_STATUS_SUCCESS;
463 }
464
465 cairo_status_t
_cairo_clip_init_copy_transformed(cairo_clip_t * clip,cairo_clip_t * other,const cairo_matrix_t * matrix)466 _cairo_clip_init_copy_transformed (cairo_clip_t *clip,
467 cairo_clip_t *other,
468 const cairo_matrix_t *matrix)
469 {
470 cairo_status_t status = CAIRO_STATUS_SUCCESS;
471 int tx, ty;
472
473 if (other == NULL) {
474 _cairo_clip_init (clip);
475 return CAIRO_STATUS_SUCCESS;
476 }
477
478 if (other->all_clipped) {
479 _cairo_clip_init (clip);
480 clip->all_clipped = TRUE;
481 return CAIRO_STATUS_SUCCESS;
482 }
483
484 if (_cairo_matrix_is_identity (matrix)) {
485 _cairo_clip_init_copy (clip, other);
486 return CAIRO_STATUS_SUCCESS;
487 }
488
489 if (other->path != NULL) {
490 _cairo_clip_init (clip);
491
492 /* if we only need to translate, so we can reuse the caches... */
493 /* XXX we still loose the benefit of constructs when the copy is
494 * deleted though. Indirect clip_paths?
495 */
496 if (_cairo_matrix_is_integer_translation (matrix, &tx, &ty)) {
497 status = _cairo_clip_path_reapply_clip_path_translate (clip,
498 other->path,
499 tx, ty);
500 } else {
501 status = _cairo_clip_path_reapply_clip_path_transform (clip,
502 other->path,
503 matrix);
504 if (clip->path->extents.width == 0 &&
505 clip->path->extents.height == 0)
506 {
507 _cairo_clip_set_all_clipped (clip);
508 }
509 }
510 }
511
512 return status;
513 }
514
515 static cairo_status_t
_cairo_clip_apply_clip_path(cairo_clip_t * clip,const cairo_clip_path_t * path)516 _cairo_clip_apply_clip_path (cairo_clip_t *clip,
517 const cairo_clip_path_t *path)
518 {
519 cairo_status_t status;
520
521 if (path->prev != NULL)
522 status = _cairo_clip_apply_clip_path (clip, path->prev);
523
524 return _cairo_clip_intersect_path (clip,
525 &path->path,
526 path->fill_rule,
527 path->tolerance,
528 path->antialias);
529 }
530
531 cairo_status_t
_cairo_clip_apply_clip(cairo_clip_t * clip,const cairo_clip_t * other)532 _cairo_clip_apply_clip (cairo_clip_t *clip,
533 const cairo_clip_t *other)
534 {
535 cairo_status_t status;
536
537 if (clip->all_clipped)
538 return CAIRO_STATUS_SUCCESS;
539
540 if (other->all_clipped) {
541 _cairo_clip_set_all_clipped (clip);
542 return CAIRO_STATUS_SUCCESS;
543 }
544
545 status = CAIRO_STATUS_SUCCESS;
546 if (other->path != NULL)
547 status = _cairo_clip_apply_clip_path (clip, other->path);
548
549 return status;
550 }
551
552 static inline cairo_bool_t
_clip_paths_are_rectilinear(cairo_clip_path_t * clip_path)553 _clip_paths_are_rectilinear (cairo_clip_path_t *clip_path)
554 {
555 while (clip_path != NULL) {
556 if (! clip_path->path.is_rectilinear)
557 return FALSE;
558
559 clip_path = clip_path->prev;
560 }
561
562 return TRUE;
563 }
564
565 static cairo_int_status_t
_cairo_clip_path_to_region_geometric(cairo_clip_path_t * clip_path)566 _cairo_clip_path_to_region_geometric (cairo_clip_path_t *clip_path)
567 {
568 cairo_traps_t traps;
569 cairo_box_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (cairo_box_t)];
570 cairo_box_t *boxes = stack_boxes;
571 cairo_status_t status;
572 int n;
573
574 /* If we have nothing to intersect with this path, then it cannot
575 * magically be reduced into a region.
576 */
577 if (clip_path->prev == NULL)
578 goto UNSUPPORTED;
579
580 /* Start simple... Intersect some boxes with an arbitrary path. */
581 if (! clip_path->path.is_rectilinear)
582 goto UNSUPPORTED;
583 if (clip_path->prev->prev != NULL)
584 goto UNSUPPORTED;
585
586 _cairo_traps_init (&traps);
587 _cairo_box_from_rectangle (&boxes[0], &clip_path->extents);
588 _cairo_traps_limit (&traps, boxes, 1);
589
590 status = _cairo_path_fixed_fill_rectilinear_to_traps (&clip_path->path,
591 clip_path->fill_rule,
592 &traps);
593 if (unlikely (_cairo_status_is_error (status)))
594 return status;
595 if (status == CAIRO_INT_STATUS_UNSUPPORTED)
596 goto UNSUPPORTED;
597
598 if (unlikely (traps.num_traps == 0)) {
599 clip_path->region = cairo_region_create ();
600 clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION;
601 return CAIRO_STATUS_SUCCESS;
602 }
603
604 if (traps.num_traps > ARRAY_LENGTH (stack_boxes)) {
605 boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t));
606 if (unlikely (boxes == NULL))
607 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
608 }
609
610 for (n = 0; n < traps.num_traps; n++) {
611 boxes[n].p1.x = traps.traps[n].left.p1.x;
612 boxes[n].p1.y = traps.traps[n].top;
613 boxes[n].p2.x = traps.traps[n].right.p1.x;
614 boxes[n].p2.y = traps.traps[n].bottom;
615 }
616
617 _cairo_traps_clear (&traps);
618 _cairo_traps_limit (&traps, boxes, n);
619 status = _cairo_path_fixed_fill_to_traps (&clip_path->prev->path,
620 clip_path->prev->fill_rule,
621 clip_path->prev->tolerance,
622 &traps);
623 if (boxes != stack_boxes)
624 free (boxes);
625
626 if (unlikely (status))
627 return status;
628
629 status = _cairo_traps_extract_region (&traps, &clip_path->region);
630 _cairo_traps_fini (&traps);
631
632 if (status == CAIRO_INT_STATUS_UNSUPPORTED)
633 goto UNSUPPORTED;
634 if (unlikely (status))
635 return status;
636
637 clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION;
638 return CAIRO_STATUS_SUCCESS;
639
640 UNSUPPORTED:
641 clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
642 return CAIRO_INT_STATUS_UNSUPPORTED;
643 }
644
645 static cairo_int_status_t
_cairo_clip_path_to_region(cairo_clip_path_t * clip_path)646 _cairo_clip_path_to_region (cairo_clip_path_t *clip_path)
647 {
648 cairo_int_status_t status;
649 cairo_region_t *prev = NULL;
650
651 if (clip_path->flags &
652 (CAIRO_CLIP_PATH_HAS_REGION |
653 CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED))
654 {
655 return clip_path->flags & CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED ?
656 CAIRO_INT_STATUS_UNSUPPORTED :
657 CAIRO_STATUS_SUCCESS;
658 }
659
660 if (! clip_path->path.maybe_fill_region)
661 return _cairo_clip_path_to_region_geometric (clip_path);
662
663 /* first retrieve the region for our antecedents */
664 if (clip_path->prev != NULL) {
665 status = _cairo_clip_path_to_region (clip_path->prev);
666 if (status) {
667 if (status == CAIRO_INT_STATUS_UNSUPPORTED)
668 return _cairo_clip_path_to_region_geometric (clip_path);
669
670 return status;
671 }
672
673 prev = clip_path->prev->region;
674 }
675
676 /* now extract the region for ourselves */
677 clip_path->region =
678 _cairo_path_fixed_fill_rectilinear_to_region (&clip_path->path,
679 clip_path->fill_rule,
680 &clip_path->extents);
681 assert (clip_path->region != NULL);
682
683 status = clip_path->region->status;
684 if (unlikely (status))
685 return status;
686
687 if (prev != NULL) {
688 status = cairo_region_intersect (clip_path->region, prev);
689 if (unlikely (status))
690 return status;
691 }
692
693 clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION;
694 return CAIRO_STATUS_SUCCESS;
695 }
696
697 static inline int
pot(int v)698 pot (int v)
699 {
700 v--;
701 v |= v >> 1;
702 v |= v >> 2;
703 v |= v >> 4;
704 v |= v >> 8;
705 v |= v >> 16;
706 v++;
707 return v;
708 }
709
710 /* XXX there is likely a faster method! ;-) */
711 static cairo_status_t
_region_clip_to_boxes(const cairo_region_t * region,cairo_box_t ** boxes,int * num_boxes,int * size_boxes)712 _region_clip_to_boxes (const cairo_region_t *region,
713 cairo_box_t **boxes,
714 int *num_boxes,
715 int *size_boxes)
716 {
717 cairo_traps_t traps;
718 cairo_status_t status;
719 int n, num_rects;
720
721 _cairo_traps_init (&traps);
722 _cairo_traps_limit (&traps, *boxes, *num_boxes);
723 traps.is_rectilinear = TRUE;
724 traps.is_rectangular = TRUE;
725
726 num_rects = cairo_region_num_rectangles (region);
727 for (n = 0; n < num_rects; n++) {
728 cairo_rectangle_int_t rect;
729 cairo_point_t p1, p2;
730
731 cairo_region_get_rectangle (region, n, &rect);
732
733 p1.x = _cairo_fixed_from_int (rect.x);
734 p1.y = _cairo_fixed_from_int (rect.y);
735 p2.x = _cairo_fixed_from_int (rect.x + rect.width);
736 p2.y = _cairo_fixed_from_int (rect.y + rect.height);
737
738 status = _cairo_traps_tessellate_rectangle (&traps, &p1, &p2);
739 if (unlikely (status))
740 goto CLEANUP;
741 }
742
743 status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps, CAIRO_FILL_RULE_WINDING);
744 if (unlikely (status))
745 goto CLEANUP;
746
747 n = *size_boxes;
748 if (n < 0)
749 n = -n;
750
751 if (traps.num_traps > n) {
752 cairo_box_t *new_boxes;
753
754 new_boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t));
755 if (unlikely (new_boxes == NULL)) {
756 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
757 goto CLEANUP;
758 }
759
760 if (*size_boxes > 0)
761 free (*boxes);
762
763 *boxes = new_boxes;
764 *size_boxes = traps.num_traps;
765 }
766
767 for (n = 0; n < traps.num_traps; n++) {
768 (*boxes)[n].p1.x = traps.traps[n].left.p1.x;
769 (*boxes)[n].p1.y = traps.traps[n].top;
770 (*boxes)[n].p2.x = traps.traps[n].right.p1.x;
771 (*boxes)[n].p2.y = traps.traps[n].bottom;
772 }
773 *num_boxes = n;
774
775 CLEANUP:
776 _cairo_traps_fini (&traps);
777
778 return status;
779 }
780
781 static cairo_status_t
_rectilinear_clip_to_boxes(const cairo_path_fixed_t * path,cairo_fill_rule_t fill_rule,cairo_box_t ** boxes,int * num_boxes,int * size_boxes)782 _rectilinear_clip_to_boxes (const cairo_path_fixed_t *path,
783 cairo_fill_rule_t fill_rule,
784 cairo_box_t **boxes,
785 int *num_boxes,
786 int *size_boxes)
787 {
788 cairo_polygon_t polygon;
789 cairo_traps_t traps;
790 cairo_status_t status;
791
792 _cairo_traps_init (&traps);
793 _cairo_traps_limit (&traps, *boxes, *num_boxes);
794
795 _cairo_polygon_init (&polygon);
796 _cairo_polygon_limit (&polygon, *boxes, *num_boxes);
797
798 status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
799 fill_rule,
800 &traps);
801 if (unlikely (_cairo_status_is_error (status)))
802 goto CLEANUP;
803 if (status == CAIRO_STATUS_SUCCESS)
804 goto BOXES;
805
806 /* tolerance will be ignored as the path is rectilinear */
807 status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon);
808 if (unlikely (status))
809 goto CLEANUP;
810
811 if (polygon.num_edges == 0) {
812 *num_boxes = 0;
813 } else {
814 status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
815 &polygon,
816 fill_rule);
817 if (likely (status == CAIRO_STATUS_SUCCESS)) {
818 int i;
819
820 BOXES:
821 i = *size_boxes;
822 if (i < 0)
823 i = -i;
824
825 if (traps.num_traps > i) {
826 cairo_box_t *new_boxes;
827 int new_size;
828
829 new_size = pot (traps.num_traps);
830 new_boxes = _cairo_malloc_ab (new_size, sizeof (cairo_box_t));
831 if (unlikely (new_boxes == NULL)) {
832 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
833 goto CLEANUP;
834 }
835
836 if (*size_boxes > 0)
837 free (*boxes);
838
839 *boxes = new_boxes;
840 *size_boxes = new_size;
841 }
842
843 for (i = 0; i < traps.num_traps; i++) {
844 (*boxes)[i].p1.x = traps.traps[i].left.p1.x;
845 (*boxes)[i].p1.y = traps.traps[i].top;
846 (*boxes)[i].p2.x = traps.traps[i].right.p1.x;
847 (*boxes)[i].p2.y = traps.traps[i].bottom;
848 }
849 *num_boxes = i;
850 }
851 }
852
853 CLEANUP:
854 _cairo_polygon_fini (&polygon);
855 _cairo_traps_fini (&traps);
856
857 return status;
858 }
859
860 static cairo_int_status_t
_cairo_clip_path_to_boxes(cairo_clip_path_t * clip_path,cairo_box_t ** boxes,int * count)861 _cairo_clip_path_to_boxes (cairo_clip_path_t *clip_path,
862 cairo_box_t **boxes,
863 int *count)
864 {
865 int size = -*count;
866 int num_boxes = 0;
867 cairo_status_t status;
868
869 if (clip_path->region != NULL) {
870 int num_rects, n;
871
872 num_rects = cairo_region_num_rectangles (clip_path->region);
873 if (num_rects > -size) {
874 cairo_box_t *new_boxes;
875
876 new_boxes = _cairo_malloc_ab (num_rects, sizeof (cairo_box_t));
877 if (unlikely (new_boxes == NULL))
878 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
879
880 *boxes = new_boxes;
881 }
882
883 for (n = 0; n < num_rects; n++) {
884 cairo_rectangle_int_t rect;
885
886 cairo_region_get_rectangle (clip_path->region, n, &rect);
887 (*boxes)[n].p1.x = _cairo_fixed_from_int (rect.x);
888 (*boxes)[n].p1.y = _cairo_fixed_from_int (rect.y);
889 (*boxes)[n].p2.x = _cairo_fixed_from_int (rect.x + rect.width);
890 (*boxes)[n].p2.y = _cairo_fixed_from_int (rect.y + rect.height);
891 }
892
893 *count = num_rects;
894 return CAIRO_STATUS_SUCCESS;
895 }
896
897 /* keep it simple at first */
898 if (! _clip_paths_are_rectilinear (clip_path))
899 return CAIRO_INT_STATUS_UNSUPPORTED;
900
901 assert (-size >= 1);
902 if (_cairo_path_fixed_is_box (&clip_path->path, *boxes)) {
903 num_boxes = 1;
904 } else {
905 status = _rectilinear_clip_to_boxes (&clip_path->path,
906 clip_path->fill_rule,
907 boxes, &num_boxes, &size);
908 if (unlikely (status))
909 return status;
910 }
911
912 while (num_boxes > 0 && (clip_path = clip_path->prev) != NULL) {
913 cairo_box_t box;
914
915 if (clip_path->region != NULL) {
916 status = _region_clip_to_boxes (clip_path->region,
917 boxes, &num_boxes, &size);
918 if (unlikely (status))
919 return status;
920
921 break;
922 } else if (_cairo_path_fixed_is_box (&clip_path->path, &box)) {
923 int i, j;
924
925 for (i = j = 0; i < num_boxes; i++) {
926 if (j != i)
927 (*boxes)[j] = (*boxes)[i];
928
929 if (box.p1.x > (*boxes)[j].p1.x)
930 (*boxes)[j].p1.x = box.p1.x;
931 if (box.p2.x < (*boxes)[j].p2.x)
932 (*boxes)[j].p2.x = box.p2.x;
933
934 if (box.p1.y > (*boxes)[j].p1.y)
935 (*boxes)[j].p1.y = box.p1.y;
936 if (box.p2.y < (*boxes)[j].p2.y)
937 (*boxes)[j].p2.y = box.p2.y;
938
939 j += (*boxes)[j].p2.x > (*boxes)[j].p1.x &&
940 (*boxes)[j].p2.y > (*boxes)[j].p1.y;
941 }
942
943 num_boxes = j;
944 } else {
945 status = _rectilinear_clip_to_boxes (&clip_path->path,
946 clip_path->fill_rule,
947 boxes, &num_boxes, &size);
948 if (unlikely (status))
949 return status;
950 }
951 }
952
953 *count = num_boxes;
954 return CAIRO_STATUS_SUCCESS;
955 }
956
957 static cairo_surface_t *
_cairo_clip_path_get_surface(cairo_clip_path_t * clip_path,cairo_surface_t * target,int * tx,int * ty)958 _cairo_clip_path_get_surface (cairo_clip_path_t *clip_path,
959 cairo_surface_t *target,
960 int *tx, int *ty)
961 {
962 const cairo_rectangle_int_t *clip_extents = &clip_path->extents;
963 cairo_bool_t need_translate;
964 cairo_surface_t *surface;
965 cairo_clip_path_t *prev;
966 cairo_status_t status;
967
968 while (clip_path->prev != NULL &&
969 clip_path->flags & CAIRO_CLIP_PATH_IS_BOX &&
970 clip_path->path.maybe_fill_region)
971 {
972 clip_path = clip_path->prev;
973 }
974
975 clip_extents = &clip_path->extents;
976 if (clip_path->surface != NULL &&
977 clip_path->surface->backend == target->backend)
978 {
979 *tx = clip_extents->x;
980 *ty = clip_extents->y;
981 return clip_path->surface;
982 }
983
984 surface = _cairo_surface_create_similar_scratch (target,
985 CAIRO_CONTENT_ALPHA,
986 clip_extents->width,
987 clip_extents->height);
988 if (surface == NULL) {
989 surface = cairo_image_surface_create (CAIRO_FORMAT_A8,
990 clip_extents->width,
991 clip_extents->height);
992 }
993 if (unlikely (surface->status))
994 return surface;
995
996 need_translate = clip_extents->x | clip_extents->y;
997 if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX &&
998 clip_path->path.maybe_fill_region)
999 {
1000 status = _cairo_surface_paint (surface,
1001 CAIRO_OPERATOR_SOURCE,
1002 &_cairo_pattern_white.base,
1003 NULL);
1004 if (unlikely (status))
1005 goto BAIL;
1006 }
1007 else
1008 {
1009 status = _cairo_surface_paint (surface,
1010 CAIRO_OPERATOR_CLEAR,
1011 &_cairo_pattern_clear.base,
1012 NULL);
1013 if (unlikely (status))
1014 goto BAIL;
1015
1016 if (need_translate) {
1017 _cairo_path_fixed_translate (&clip_path->path,
1018 _cairo_fixed_from_int (-clip_extents->x),
1019 _cairo_fixed_from_int (-clip_extents->y));
1020 }
1021 status = _cairo_surface_fill (surface,
1022 CAIRO_OPERATOR_ADD,
1023 &_cairo_pattern_white.base,
1024 &clip_path->path,
1025 clip_path->fill_rule,
1026 clip_path->tolerance,
1027 clip_path->antialias,
1028 NULL);
1029 if (need_translate) {
1030 _cairo_path_fixed_translate (&clip_path->path,
1031 _cairo_fixed_from_int (clip_extents->x),
1032 _cairo_fixed_from_int (clip_extents->y));
1033 }
1034
1035 if (unlikely (status))
1036 goto BAIL;
1037 }
1038
1039 prev = clip_path->prev;
1040 while (prev != NULL) {
1041 if (prev->flags & CAIRO_CLIP_PATH_IS_BOX &&
1042 prev->path.maybe_fill_region)
1043 {
1044 /* a simple box only affects the extents */
1045 }
1046 else if (prev->path.is_rectilinear ||
1047 prev->surface == NULL ||
1048 prev->surface->backend != target->backend)
1049 {
1050 if (need_translate) {
1051 _cairo_path_fixed_translate (&prev->path,
1052 _cairo_fixed_from_int (-clip_extents->x),
1053 _cairo_fixed_from_int (-clip_extents->y));
1054 }
1055 status = _cairo_surface_fill (surface,
1056 CAIRO_OPERATOR_IN,
1057 &_cairo_pattern_white.base,
1058 &prev->path,
1059 prev->fill_rule,
1060 prev->tolerance,
1061 prev->antialias,
1062 NULL);
1063 if (need_translate) {
1064 _cairo_path_fixed_translate (&prev->path,
1065 _cairo_fixed_from_int (clip_extents->x),
1066 _cairo_fixed_from_int (clip_extents->y));
1067 }
1068
1069 if (unlikely (status))
1070 goto BAIL;
1071 }
1072 else
1073 {
1074 cairo_surface_pattern_t pattern;
1075 cairo_surface_t *prev_surface;
1076 int prev_tx, prev_ty;
1077
1078 prev_surface = _cairo_clip_path_get_surface (prev, target, &prev_tx, &prev_ty);
1079 status = prev_surface->status;
1080 if (unlikely (status))
1081 goto BAIL;
1082
1083 _cairo_pattern_init_for_surface (&pattern, prev_surface);
1084 pattern.base.filter = CAIRO_FILTER_NEAREST;
1085 cairo_matrix_init_translate (&pattern.base.matrix,
1086 clip_extents->x - prev_tx,
1087 clip_extents->y - prev_ty);
1088 status = _cairo_surface_paint (surface,
1089 CAIRO_OPERATOR_IN,
1090 &pattern.base,
1091 NULL);
1092 _cairo_pattern_fini (&pattern.base);
1093
1094 if (unlikely (status))
1095 goto BAIL;
1096
1097 break;
1098 }
1099
1100 prev = prev->prev;
1101 }
1102
1103 *tx = clip_extents->x;
1104 *ty = clip_extents->y;
1105 cairo_surface_destroy (clip_path->surface);
1106 return clip_path->surface = surface;
1107
1108 BAIL:
1109 cairo_surface_destroy (surface);
1110 return _cairo_surface_create_in_error (status);
1111 }
1112
1113 cairo_bool_t
_cairo_clip_contains_rectangle(cairo_clip_t * clip,const cairo_rectangle_int_t * rect)1114 _cairo_clip_contains_rectangle (cairo_clip_t *clip,
1115 const cairo_rectangle_int_t *rect)
1116 {
1117 cairo_clip_path_t *clip_path;
1118
1119 if (clip == NULL)
1120 return FALSE;
1121
1122 clip_path = clip->path;
1123 if (clip_path->extents.x > rect->x ||
1124 clip_path->extents.y > rect->y ||
1125 clip_path->extents.x + clip_path->extents.width < rect->x + rect->width ||
1126 clip_path->extents.y + clip_path->extents.height < rect->y + rect->height)
1127 {
1128 return FALSE;
1129 }
1130
1131 do {
1132 cairo_box_t box;
1133
1134 if ((clip_path->flags & CAIRO_CLIP_PATH_IS_BOX) == 0)
1135 return FALSE;
1136
1137 if (! _cairo_path_fixed_is_box (&clip_path->path, &box))
1138 return FALSE;
1139
1140 if (box.p1.x > _cairo_fixed_from_int (rect->x) ||
1141 box.p1.y > _cairo_fixed_from_int (rect->y) ||
1142 box.p2.x < _cairo_fixed_from_int (rect->x + rect->width) ||
1143 box.p2.y < _cairo_fixed_from_int (rect->y + rect->height))
1144 {
1145 return FALSE;
1146 }
1147 } while ((clip_path = clip_path->prev) != NULL);
1148
1149 return TRUE;
1150 }
1151
1152 cairo_bool_t
_cairo_clip_contains_extents(cairo_clip_t * clip,const cairo_composite_rectangles_t * extents)1153 _cairo_clip_contains_extents (cairo_clip_t *clip,
1154 const cairo_composite_rectangles_t *extents)
1155 {
1156 const cairo_rectangle_int_t *rect;
1157
1158 if (clip == NULL)
1159 return FALSE;
1160
1161 rect = extents->is_bounded ? &extents->bounded : &extents->unbounded;
1162 return _cairo_clip_contains_rectangle (clip, rect);
1163 }
1164
1165 void
_cairo_debug_print_clip(FILE * stream,cairo_clip_t * clip)1166 _cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip)
1167 {
1168 cairo_clip_path_t *clip_path;
1169
1170 if (clip == NULL) {
1171 fprintf (stream, "no clip\n");
1172 return;
1173 }
1174
1175 if (clip->all_clipped) {
1176 fprintf (stream, "clip: all-clipped\n");
1177 return;
1178 }
1179
1180 if (clip->path == NULL) {
1181 fprintf (stream, "clip: empty\n");
1182 return;
1183 }
1184
1185 fprintf (stream, "clip:\n");
1186
1187 clip_path = clip->path;
1188 do {
1189 fprintf (stream, "path: has region? %s, has surface? %s, aa=%d, tolerance=%f, rule=%d: ",
1190 clip_path->region == NULL ? "no" : "yes",
1191 clip_path->surface == NULL ? "no" : "yes",
1192 clip_path->antialias,
1193 clip_path->tolerance,
1194 clip_path->fill_rule);
1195 _cairo_debug_print_path (stream, &clip_path->path);
1196 fprintf (stream, "\n");
1197 } while ((clip_path = clip_path->prev) != NULL);
1198 }
1199
1200 cairo_surface_t *
_cairo_clip_get_surface(cairo_clip_t * clip,cairo_surface_t * target,int * tx,int * ty)1201 _cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target, int *tx, int *ty)
1202 {
1203 /* XXX is_clear -> all_clipped */
1204 assert (clip->path != NULL);
1205 return _cairo_clip_path_get_surface (clip->path, target, tx, ty);
1206 }
1207
1208 cairo_status_t
_cairo_clip_combine_with_surface(cairo_clip_t * clip,cairo_surface_t * dst,int dst_x,int dst_y)1209 _cairo_clip_combine_with_surface (cairo_clip_t *clip,
1210 cairo_surface_t *dst,
1211 int dst_x, int dst_y)
1212 {
1213 cairo_clip_path_t *clip_path = clip->path;
1214 cairo_bool_t need_translate;
1215 cairo_status_t status;
1216
1217 assert (clip_path != NULL);
1218
1219 need_translate = dst_x | dst_y;
1220 do {
1221 if (clip_path->surface != NULL &&
1222 clip_path->surface->backend == dst->backend)
1223 {
1224 cairo_surface_pattern_t pattern;
1225
1226 _cairo_pattern_init_for_surface (&pattern, clip_path->surface);
1227 cairo_matrix_init_translate (&pattern.base.matrix,
1228 dst_x - clip_path->extents.x,
1229 dst_y - clip_path->extents.y);
1230 pattern.base.filter = CAIRO_FILTER_NEAREST;
1231 status = _cairo_surface_paint (dst,
1232 CAIRO_OPERATOR_IN,
1233 &pattern.base,
1234 NULL);
1235
1236 _cairo_pattern_fini (&pattern.base);
1237
1238 return status;
1239 }
1240
1241 if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX &&
1242 clip_path->path.maybe_fill_region)
1243 {
1244 continue;
1245 }
1246
1247 if (need_translate) {
1248 _cairo_path_fixed_translate (&clip_path->path,
1249 _cairo_fixed_from_int (-dst_x),
1250 _cairo_fixed_from_int (-dst_y));
1251 }
1252 status = _cairo_surface_fill (dst,
1253 CAIRO_OPERATOR_IN,
1254 &_cairo_pattern_white.base,
1255 &clip_path->path,
1256 clip_path->fill_rule,
1257 clip_path->tolerance,
1258 clip_path->antialias,
1259 NULL);
1260 if (need_translate) {
1261 _cairo_path_fixed_translate (&clip_path->path,
1262 _cairo_fixed_from_int (dst_x),
1263 _cairo_fixed_from_int (dst_y));
1264 }
1265
1266 if (unlikely (status))
1267 return status;
1268 } while ((clip_path = clip_path->prev) != NULL);
1269
1270 return CAIRO_STATUS_SUCCESS;
1271 }
1272
1273 static const cairo_rectangle_int_t _cairo_empty_rectangle_int = { 0, 0, 0, 0 };
1274
1275 const cairo_rectangle_int_t *
_cairo_clip_get_extents(const cairo_clip_t * clip)1276 _cairo_clip_get_extents (const cairo_clip_t *clip)
1277 {
1278 if (clip->all_clipped)
1279 return &_cairo_empty_rectangle_int;
1280
1281 if (clip->path == NULL)
1282 return NULL;
1283
1284 return &clip->path->extents;
1285 }
1286
1287 void
_cairo_clip_drop_cache(cairo_clip_t * clip)1288 _cairo_clip_drop_cache (cairo_clip_t *clip)
1289 {
1290 cairo_clip_path_t *clip_path;
1291
1292 if (clip->path == NULL)
1293 return;
1294
1295 clip_path = clip->path;
1296 do {
1297 if (clip_path->region != NULL) {
1298 cairo_region_destroy (clip_path->region);
1299 clip_path->region = NULL;
1300 }
1301
1302 if (clip_path->surface != NULL) {
1303 cairo_surface_destroy (clip_path->surface);
1304 clip_path->surface = NULL;
1305 }
1306
1307 clip_path->flags &= ~CAIRO_CLIP_PATH_HAS_REGION;
1308 } while ((clip_path = clip_path->prev) != NULL);
1309 }
1310
1311 const cairo_rectangle_list_t _cairo_rectangles_nil =
1312 { CAIRO_STATUS_NO_MEMORY, NULL, 0 };
1313 static const cairo_rectangle_list_t _cairo_rectangles_not_representable =
1314 { CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, NULL, 0 };
1315
1316 static cairo_bool_t
_cairo_clip_int_rect_to_user(cairo_gstate_t * gstate,cairo_rectangle_int_t * clip_rect,cairo_rectangle_t * user_rect)1317 _cairo_clip_int_rect_to_user (cairo_gstate_t *gstate,
1318 cairo_rectangle_int_t *clip_rect,
1319 cairo_rectangle_t *user_rect)
1320 {
1321 cairo_bool_t is_tight;
1322
1323 double x1 = clip_rect->x;
1324 double y1 = clip_rect->y;
1325 double x2 = clip_rect->x + (int) clip_rect->width;
1326 double y2 = clip_rect->y + (int) clip_rect->height;
1327
1328 _cairo_gstate_backend_to_user_rectangle (gstate,
1329 &x1, &y1, &x2, &y2,
1330 &is_tight);
1331
1332 user_rect->x = x1;
1333 user_rect->y = y1;
1334 user_rect->width = x2 - x1;
1335 user_rect->height = y2 - y1;
1336
1337 return is_tight;
1338 }
1339
1340 cairo_int_status_t
_cairo_clip_get_region(cairo_clip_t * clip,cairo_region_t ** region)1341 _cairo_clip_get_region (cairo_clip_t *clip,
1342 cairo_region_t **region)
1343 {
1344 cairo_int_status_t status;
1345
1346 if (clip->all_clipped)
1347 goto CLIPPED;
1348
1349 assert (clip->path != NULL);
1350
1351 status = _cairo_clip_path_to_region (clip->path);
1352 if (status)
1353 return status;
1354
1355 if (cairo_region_is_empty (clip->path->region)) {
1356 _cairo_clip_set_all_clipped (clip);
1357 goto CLIPPED;
1358 }
1359
1360 if (region)
1361 *region = clip->path->region;
1362 return CAIRO_STATUS_SUCCESS;
1363
1364 CLIPPED:
1365 if (region)
1366 *region = NULL;
1367 return CAIRO_INT_STATUS_NOTHING_TO_DO;
1368 }
1369
1370 cairo_int_status_t
_cairo_clip_get_boxes(cairo_clip_t * clip,cairo_box_t ** boxes,int * count)1371 _cairo_clip_get_boxes (cairo_clip_t *clip,
1372 cairo_box_t **boxes,
1373 int *count)
1374 {
1375 cairo_int_status_t status;
1376
1377 if (clip->all_clipped)
1378 return CAIRO_INT_STATUS_NOTHING_TO_DO;
1379
1380 assert (clip->path != NULL);
1381
1382 status = _cairo_clip_path_to_boxes (clip->path, boxes, count);
1383 if (status)
1384 return status;
1385
1386 if (*count == 0) {
1387 _cairo_clip_set_all_clipped (clip);
1388 return CAIRO_INT_STATUS_NOTHING_TO_DO;
1389 }
1390
1391 return CAIRO_STATUS_SUCCESS;
1392 }
1393
1394 static cairo_bool_t
box_is_aligned(const cairo_box_t * box)1395 box_is_aligned (const cairo_box_t *box)
1396 {
1397 return
1398 _cairo_fixed_is_integer (box->p1.x) &&
1399 _cairo_fixed_is_integer (box->p1.y) &&
1400 _cairo_fixed_is_integer (box->p2.x) &&
1401 _cairo_fixed_is_integer (box->p2.y);
1402 }
1403
1404 static void
intersect_with_boxes(cairo_composite_rectangles_t * extents,cairo_box_t * boxes,int num_boxes)1405 intersect_with_boxes (cairo_composite_rectangles_t *extents,
1406 cairo_box_t *boxes,
1407 int num_boxes)
1408 {
1409 cairo_rectangle_int_t rect;
1410 cairo_box_t box;
1411 cairo_bool_t is_empty;
1412
1413 box.p1.x = box.p1.y = INT_MIN;
1414 box.p2.x = box.p2.y = INT_MAX;
1415 while (num_boxes--) {
1416 if (boxes->p1.x < box.p1.x)
1417 box.p1.x = boxes->p1.x;
1418 if (boxes->p1.y < box.p1.y)
1419 box.p1.y = boxes->p1.y;
1420
1421 if (boxes->p2.x > box.p2.x)
1422 box.p2.x = boxes->p2.x;
1423 if (boxes->p2.y > box.p2.y)
1424 box.p2.y = boxes->p2.y;
1425 }
1426
1427 _cairo_box_round_to_rectangle (&box, &rect);
1428 is_empty = _cairo_rectangle_intersect (&extents->bounded, &rect);
1429 is_empty = _cairo_rectangle_intersect (&extents->unbounded, &rect);
1430 }
1431
1432 cairo_status_t
_cairo_clip_to_boxes(cairo_clip_t ** clip,cairo_composite_rectangles_t * extents,cairo_box_t ** boxes,int * num_boxes)1433 _cairo_clip_to_boxes (cairo_clip_t **clip,
1434 cairo_composite_rectangles_t *extents,
1435 cairo_box_t **boxes,
1436 int *num_boxes)
1437 {
1438 cairo_status_t status;
1439 const cairo_rectangle_int_t *rect;
1440
1441 rect = extents->is_bounded ? &extents->bounded : &extents->unbounded;
1442
1443 if (*clip == NULL)
1444 goto EXTENTS;
1445
1446 status = _cairo_clip_rectangle (*clip, rect);
1447 if (unlikely (status))
1448 return status;
1449
1450 status = _cairo_clip_get_boxes (*clip, boxes, num_boxes);
1451 switch ((int) status) {
1452 case CAIRO_STATUS_SUCCESS:
1453 intersect_with_boxes (extents, *boxes, *num_boxes);
1454 if (rect->width == 0 || rect->height == 0 ||
1455 extents->is_bounded ||
1456 (*num_boxes == 1 && box_is_aligned (*boxes)))
1457 {
1458 *clip = NULL;
1459 }
1460 goto DONE;
1461
1462 case CAIRO_INT_STATUS_UNSUPPORTED:
1463 goto EXTENTS;
1464
1465 default:
1466 return status;
1467 }
1468
1469 EXTENTS:
1470 status = CAIRO_STATUS_SUCCESS;
1471 _cairo_box_from_rectangle (&(*boxes)[0], rect);
1472 *num_boxes = 1;
1473 DONE:
1474 return status;
1475 }
1476
1477
1478 static cairo_rectangle_list_t *
_cairo_rectangle_list_create_in_error(cairo_status_t status)1479 _cairo_rectangle_list_create_in_error (cairo_status_t status)
1480 {
1481 cairo_rectangle_list_t *list;
1482
1483 if (status == CAIRO_STATUS_NO_MEMORY)
1484 return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
1485 if (status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE)
1486 return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable;
1487
1488 list = malloc (sizeof (*list));
1489 if (unlikely (list == NULL)) {
1490 _cairo_error_throw (status);
1491 return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
1492 }
1493
1494 list->status = status;
1495 list->rectangles = NULL;
1496 list->num_rectangles = 0;
1497
1498 return list;
1499 }
1500
1501 cairo_rectangle_list_t *
_cairo_clip_copy_rectangle_list(cairo_clip_t * clip,cairo_gstate_t * gstate)1502 _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate)
1503 {
1504 #define ERROR_LIST(S) _cairo_rectangle_list_create_in_error (_cairo_error (S))
1505
1506 cairo_rectangle_list_t *list;
1507 cairo_rectangle_t *rectangles = NULL;
1508 cairo_region_t *region = NULL;
1509 cairo_int_status_t status;
1510 int n_rects = 0;
1511 int i;
1512
1513 if (clip->all_clipped)
1514 goto DONE;
1515
1516 if (!clip->path)
1517 return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
1518
1519 status = _cairo_clip_get_region (clip, ®ion);
1520 if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
1521 goto DONE;
1522 } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
1523 return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
1524 } else if (unlikely (status)) {
1525 return ERROR_LIST (status);
1526 }
1527
1528 n_rects = cairo_region_num_rectangles (region);
1529 if (n_rects) {
1530 rectangles = _cairo_malloc_ab (n_rects, sizeof (cairo_rectangle_t));
1531 if (unlikely (rectangles == NULL)) {
1532 return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
1533 }
1534
1535 for (i = 0; i < n_rects; ++i) {
1536 cairo_rectangle_int_t clip_rect;
1537
1538 cairo_region_get_rectangle (region, i, &clip_rect);
1539
1540 if (! _cairo_clip_int_rect_to_user (gstate,
1541 &clip_rect,
1542 &rectangles[i]))
1543 {
1544 free (rectangles);
1545 return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
1546 }
1547 }
1548 }
1549
1550 DONE:
1551 list = malloc (sizeof (cairo_rectangle_list_t));
1552 if (unlikely (list == NULL)) {
1553 free (rectangles);
1554 return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
1555 }
1556
1557 list->status = CAIRO_STATUS_SUCCESS;
1558 list->rectangles = rectangles;
1559 list->num_rectangles = n_rects;
1560 return list;
1561
1562 #undef ERROR_LIST
1563 }
1564
1565 /**
1566 * cairo_rectangle_list_destroy:
1567 * @rectangle_list: a rectangle list, as obtained from cairo_copy_clip_rectangles()
1568 *
1569 * Unconditionally frees @rectangle_list and all associated
1570 * references. After this call, the @rectangle_list pointer must not
1571 * be dereferenced.
1572 *
1573 * Since: 1.4
1574 **/
1575 void
cairo_rectangle_list_destroy(cairo_rectangle_list_t * rectangle_list)1576 cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list)
1577 {
1578 if (rectangle_list == NULL || rectangle_list == &_cairo_rectangles_nil ||
1579 rectangle_list == &_cairo_rectangles_not_representable)
1580 return;
1581
1582 free (rectangle_list->rectangles);
1583 free (rectangle_list);
1584 }
1585
1586 void
_cairo_clip_reset_static_data(void)1587 _cairo_clip_reset_static_data (void)
1588 {
1589 _freed_pool_reset (&clip_path_pool);
1590 }
1591