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-inline.h"
44 #include "cairo-clip-private.h"
45 #include "cairo-error-private.h"
46 #include "cairo-freed-pool-private.h"
47 #include "cairo-gstate-private.h"
48 #include "cairo-path-fixed-private.h"
49 #include "cairo-pattern-private.h"
50 #include "cairo-composite-rectangles-private.h"
51 #include "cairo-region-private.h"
52 
53 static freed_pool_t clip_path_pool;
54 static freed_pool_t clip_pool;
55 
56 const cairo_clip_t __cairo_clip_all;
57 
58 static cairo_clip_path_t *
_cairo_clip_path_create(cairo_clip_t * clip)59 _cairo_clip_path_create (cairo_clip_t *clip)
60 {
61     cairo_clip_path_t *clip_path;
62 
63     clip_path = _freed_pool_get (&clip_path_pool);
64     if (unlikely (clip_path == NULL)) {
65 	clip_path = _cairo_malloc (sizeof (cairo_clip_path_t));
66 	if (unlikely (clip_path == NULL))
67 	    return NULL;
68     }
69 
70     CAIRO_REFERENCE_COUNT_INIT (&clip_path->ref_count, 1);
71 
72     clip_path->prev = clip->path;
73     clip->path = clip_path;
74 
75     return clip_path;
76 }
77 
78 cairo_clip_path_t *
_cairo_clip_path_reference(cairo_clip_path_t * clip_path)79 _cairo_clip_path_reference (cairo_clip_path_t *clip_path)
80 {
81     assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
82 
83     _cairo_reference_count_inc (&clip_path->ref_count);
84 
85     return clip_path;
86 }
87 
88 void
_cairo_clip_path_destroy(cairo_clip_path_t * clip_path)89 _cairo_clip_path_destroy (cairo_clip_path_t *clip_path)
90 {
91     assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
92 
93     if (! _cairo_reference_count_dec_and_test (&clip_path->ref_count))
94 	return;
95 
96     _cairo_path_fixed_fini (&clip_path->path);
97 
98     if (clip_path->prev != NULL)
99 	_cairo_clip_path_destroy (clip_path->prev);
100 
101     _freed_pool_put (&clip_path_pool, clip_path);
102 }
103 
104 cairo_clip_t *
_cairo_clip_create(void)105 _cairo_clip_create (void)
106 {
107     cairo_clip_t *clip;
108 
109     clip = _freed_pool_get (&clip_pool);
110     if (unlikely (clip == NULL)) {
111 	clip = _cairo_malloc (sizeof (cairo_clip_t));
112 	if (unlikely (clip == NULL))
113 	    return NULL;
114     }
115 
116     clip->extents = _cairo_unbounded_rectangle;
117 
118     clip->path = NULL;
119     clip->boxes = NULL;
120     clip->num_boxes = 0;
121     clip->region = NULL;
122     clip->is_region = FALSE;
123 
124     return clip;
125 }
126 
127 void
_cairo_clip_destroy(cairo_clip_t * clip)128 _cairo_clip_destroy (cairo_clip_t *clip)
129 {
130     if (clip == NULL || _cairo_clip_is_all_clipped (clip))
131 	return;
132 
133     if (clip->path != NULL)
134 	_cairo_clip_path_destroy (clip->path);
135 
136     if (clip->boxes != &clip->embedded_box)
137 	free (clip->boxes);
138     cairo_region_destroy (clip->region);
139 
140     _freed_pool_put (&clip_pool, clip);
141 }
142 
143 cairo_clip_t *
_cairo_clip_copy(const cairo_clip_t * clip)144 _cairo_clip_copy (const cairo_clip_t *clip)
145 {
146     cairo_clip_t *copy;
147 
148     if (clip == NULL || _cairo_clip_is_all_clipped (clip))
149 	return (cairo_clip_t *) clip;
150 
151     copy = _cairo_clip_create ();
152 
153     if (clip->path)
154 	copy->path = _cairo_clip_path_reference (clip->path);
155 
156     if (clip->num_boxes) {
157 	if (clip->num_boxes == 1) {
158 	    copy->boxes = &copy->embedded_box;
159 	} else {
160 	    copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t));
161 	    if (unlikely (copy->boxes == NULL))
162 		return _cairo_clip_set_all_clipped (copy);
163 	}
164 
165 	memcpy (copy->boxes, clip->boxes,
166 		clip->num_boxes * sizeof (cairo_box_t));
167 	copy->num_boxes = clip->num_boxes;
168     }
169 
170     copy->extents = clip->extents;
171     copy->region = cairo_region_reference (clip->region);
172     copy->is_region = clip->is_region;
173 
174     return copy;
175 }
176 
177 cairo_clip_t *
_cairo_clip_copy_path(const cairo_clip_t * clip)178 _cairo_clip_copy_path (const cairo_clip_t *clip)
179 {
180     cairo_clip_t *copy;
181 
182     if (clip == NULL || _cairo_clip_is_all_clipped (clip))
183 	return (cairo_clip_t *) clip;
184 
185     assert (clip->num_boxes);
186 
187     copy = _cairo_clip_create ();
188     copy->extents = clip->extents;
189     if (clip->path)
190 	copy->path = _cairo_clip_path_reference (clip->path);
191 
192     return copy;
193 }
194 
195 cairo_clip_t *
_cairo_clip_copy_region(const cairo_clip_t * clip)196 _cairo_clip_copy_region (const cairo_clip_t *clip)
197 {
198     cairo_clip_t *copy;
199     int i;
200 
201     if (clip == NULL || _cairo_clip_is_all_clipped (clip))
202 	return (cairo_clip_t *) clip;
203 
204     assert (clip->num_boxes);
205 
206     copy = _cairo_clip_create ();
207     copy->extents = clip->extents;
208 
209     if (clip->num_boxes == 1) {
210 	copy->boxes = &copy->embedded_box;
211     } else {
212 	copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t));
213 	if (unlikely (copy->boxes == NULL))
214 	    return _cairo_clip_set_all_clipped (copy);
215     }
216 
217     for (i = 0; i < clip->num_boxes; i++) {
218 	copy->boxes[i].p1.x = _cairo_fixed_floor (clip->boxes[i].p1.x);
219 	copy->boxes[i].p1.y = _cairo_fixed_floor (clip->boxes[i].p1.y);
220 	copy->boxes[i].p2.x = _cairo_fixed_ceil (clip->boxes[i].p2.x);
221 	copy->boxes[i].p2.y = _cairo_fixed_ceil (clip->boxes[i].p2.y);
222     }
223     copy->num_boxes = clip->num_boxes;
224 
225     copy->region = cairo_region_reference (clip->region);
226     copy->is_region = TRUE;
227 
228     return copy;
229 }
230 
231 cairo_clip_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)232 _cairo_clip_intersect_path (cairo_clip_t       *clip,
233 			    const cairo_path_fixed_t *path,
234 			    cairo_fill_rule_t   fill_rule,
235 			    double              tolerance,
236 			    cairo_antialias_t   antialias)
237 {
238     cairo_clip_path_t *clip_path;
239     cairo_status_t status;
240     cairo_rectangle_int_t extents;
241     cairo_box_t box;
242 
243     if (_cairo_clip_is_all_clipped (clip))
244 	return clip;
245 
246     /* catch the empty clip path */
247     if (_cairo_path_fixed_fill_is_empty (path))
248 	return _cairo_clip_set_all_clipped (clip);
249 
250     if (_cairo_path_fixed_is_box (path, &box)) {
251 	if (antialias == CAIRO_ANTIALIAS_NONE) {
252 	    box.p1.x = _cairo_fixed_round_down (box.p1.x);
253 	    box.p1.y = _cairo_fixed_round_down (box.p1.y);
254 	    box.p2.x = _cairo_fixed_round_down (box.p2.x);
255 	    box.p2.y = _cairo_fixed_round_down (box.p2.y);
256 	}
257 
258 	return _cairo_clip_intersect_box (clip, &box);
259     }
260     if (_cairo_path_fixed_fill_is_rectilinear (path))
261 	return _cairo_clip_intersect_rectilinear_path (clip, path,
262 						       fill_rule, antialias);
263 
264     _cairo_path_fixed_approximate_clip_extents (path, &extents);
265     if (extents.width == 0 || extents.height == 0)
266 	return _cairo_clip_set_all_clipped (clip);
267 
268     clip = _cairo_clip_intersect_rectangle (clip, &extents);
269     if (_cairo_clip_is_all_clipped (clip))
270 	return clip;
271 
272     clip_path = _cairo_clip_path_create (clip);
273     if (unlikely (clip_path == NULL))
274 	return _cairo_clip_set_all_clipped (clip);
275 
276     status = _cairo_path_fixed_init_copy (&clip_path->path, path);
277     if (unlikely (status))
278 	return _cairo_clip_set_all_clipped (clip);
279 
280     clip_path->fill_rule = fill_rule;
281     clip_path->tolerance = tolerance;
282     clip_path->antialias = antialias;
283 
284     if (clip->region) {
285 	cairo_region_destroy (clip->region);
286 	clip->region = NULL;
287     }
288 
289     clip->is_region = FALSE;
290     return clip;
291 }
292 
293 static cairo_clip_t *
_cairo_clip_intersect_clip_path(cairo_clip_t * clip,const cairo_clip_path_t * clip_path)294 _cairo_clip_intersect_clip_path (cairo_clip_t *clip,
295 				 const cairo_clip_path_t *clip_path)
296 {
297     if (clip_path->prev)
298 	clip = _cairo_clip_intersect_clip_path (clip, clip_path->prev);
299 
300     return _cairo_clip_intersect_path (clip,
301 				       &clip_path->path,
302 				       clip_path->fill_rule,
303 				       clip_path->tolerance,
304 				       clip_path->antialias);
305 }
306 
307 cairo_clip_t *
_cairo_clip_intersect_clip(cairo_clip_t * clip,const cairo_clip_t * other)308 _cairo_clip_intersect_clip (cairo_clip_t *clip,
309 			    const cairo_clip_t *other)
310 {
311     if (_cairo_clip_is_all_clipped (clip))
312 	return clip;
313 
314     if (other == NULL)
315 	return clip;
316 
317     if (clip == NULL)
318 	return _cairo_clip_copy (other);
319 
320     if (_cairo_clip_is_all_clipped (other))
321 	return _cairo_clip_set_all_clipped (clip);
322 
323     if (! _cairo_rectangle_intersect (&clip->extents, &other->extents))
324 	return _cairo_clip_set_all_clipped (clip);
325 
326     if (other->num_boxes) {
327 	cairo_boxes_t boxes;
328 
329 	_cairo_boxes_init_for_array (&boxes, other->boxes, other->num_boxes);
330 	clip = _cairo_clip_intersect_boxes (clip, &boxes);
331     }
332 
333     if (! _cairo_clip_is_all_clipped (clip)) {
334 	if (other->path) {
335 	    if (clip->path == NULL)
336 		clip->path = _cairo_clip_path_reference (other->path);
337 	    else
338 		clip = _cairo_clip_intersect_clip_path (clip, other->path);
339 	}
340     }
341 
342     if (clip->region) {
343 	cairo_region_destroy (clip->region);
344 	clip->region = NULL;
345     }
346     clip->is_region = FALSE;
347 
348     return clip;
349 }
350 
351 cairo_bool_t
_cairo_clip_equal(const cairo_clip_t * clip_a,const cairo_clip_t * clip_b)352 _cairo_clip_equal (const cairo_clip_t *clip_a,
353 		   const cairo_clip_t *clip_b)
354 {
355     const cairo_clip_path_t *cp_a, *cp_b;
356 
357     /* are both all-clipped or no-clip? */
358     if (clip_a == clip_b)
359 	return TRUE;
360 
361     /* or just one of them? */
362     if (clip_a == NULL || clip_b == NULL ||
363 	_cairo_clip_is_all_clipped (clip_a) ||
364 	_cairo_clip_is_all_clipped (clip_b))
365     {
366 	return FALSE;
367     }
368 
369     /* We have a pair of normal clips, check their contents */
370 
371     if (clip_a->num_boxes != clip_b->num_boxes)
372 	return FALSE;
373 
374     if (memcmp (clip_a->boxes, clip_b->boxes,
375 		sizeof (cairo_box_t) * clip_a->num_boxes))
376 	return FALSE;
377 
378     cp_a = clip_a->path;
379     cp_b = clip_b->path;
380     while (cp_a && cp_b) {
381 	if (cp_a == cp_b)
382 	    return TRUE;
383 
384 	/* XXX compare reduced polygons? */
385 
386 	if (cp_a->antialias != cp_b->antialias)
387 	    return FALSE;
388 
389 	if (cp_a->tolerance != cp_b->tolerance)
390 	    return FALSE;
391 
392 	if (cp_a->fill_rule != cp_b->fill_rule)
393 	    return FALSE;
394 
395 	if (! _cairo_path_fixed_equal (&cp_a->path,
396 				       &cp_b->path))
397 	    return FALSE;
398 
399 	cp_a = cp_a->prev;
400 	cp_b = cp_b->prev;
401     }
402 
403     return cp_a == NULL && cp_b == NULL;
404 }
405 
406 static cairo_clip_t *
_cairo_clip_path_copy_with_translation(cairo_clip_t * clip,cairo_clip_path_t * other_path,int fx,int fy)407 _cairo_clip_path_copy_with_translation (cairo_clip_t      *clip,
408 					cairo_clip_path_t *other_path,
409 					int fx, int fy)
410 {
411     cairo_status_t status;
412     cairo_clip_path_t *clip_path;
413 
414     if (other_path->prev != NULL)
415 	clip = _cairo_clip_path_copy_with_translation (clip, other_path->prev,
416 						       fx, fy);
417     if (_cairo_clip_is_all_clipped (clip))
418 	return clip;
419 
420     clip_path = _cairo_clip_path_create (clip);
421     if (unlikely (clip_path == NULL))
422 	return _cairo_clip_set_all_clipped (clip);
423 
424     status = _cairo_path_fixed_init_copy (&clip_path->path,
425 					  &other_path->path);
426     if (unlikely (status))
427 	return _cairo_clip_set_all_clipped (clip);
428 
429     _cairo_path_fixed_translate (&clip_path->path, fx, fy);
430 
431     clip_path->fill_rule = other_path->fill_rule;
432     clip_path->tolerance = other_path->tolerance;
433     clip_path->antialias = other_path->antialias;
434 
435     return clip;
436 }
437 
438 cairo_clip_t *
_cairo_clip_translate(cairo_clip_t * clip,int tx,int ty)439 _cairo_clip_translate (cairo_clip_t *clip, int tx, int ty)
440 {
441     int fx, fy, i;
442     cairo_clip_path_t *clip_path;
443 
444     if (clip == NULL || _cairo_clip_is_all_clipped (clip))
445 	return clip;
446 
447     if (tx == 0 && ty == 0)
448 	return clip;
449 
450     fx = _cairo_fixed_from_int (tx);
451     fy = _cairo_fixed_from_int (ty);
452 
453     for (i = 0; i < clip->num_boxes; i++) {
454 	clip->boxes[i].p1.x += fx;
455 	clip->boxes[i].p2.x += fx;
456 	clip->boxes[i].p1.y += fy;
457 	clip->boxes[i].p2.y += fy;
458     }
459 
460     clip->extents.x += tx;
461     clip->extents.y += ty;
462 
463     if (clip->path == NULL)
464 	return clip;
465 
466     clip_path = clip->path;
467     clip->path = NULL;
468     clip = _cairo_clip_path_copy_with_translation (clip, clip_path, fx, fy);
469     _cairo_clip_path_destroy (clip_path);
470 
471     return clip;
472 }
473 
474 static cairo_status_t
_cairo_path_fixed_add_box(cairo_path_fixed_t * path,const cairo_box_t * box)475 _cairo_path_fixed_add_box (cairo_path_fixed_t *path,
476 			   const cairo_box_t *box)
477 {
478     cairo_status_t status;
479 
480     status = _cairo_path_fixed_move_to (path, box->p1.x, box->p1.y);
481     if (unlikely (status))
482 	return status;
483 
484     status = _cairo_path_fixed_line_to (path, box->p2.x, box->p1.y);
485     if (unlikely (status))
486 	return status;
487 
488     status = _cairo_path_fixed_line_to (path, box->p2.x, box->p2.y);
489     if (unlikely (status))
490 	return status;
491 
492     status = _cairo_path_fixed_line_to (path, box->p1.x, box->p2.y);
493     if (unlikely (status))
494 	return status;
495 
496     return _cairo_path_fixed_close_path (path);
497 }
498 
499 static cairo_status_t
_cairo_path_fixed_init_from_boxes(cairo_path_fixed_t * path,const cairo_boxes_t * boxes)500 _cairo_path_fixed_init_from_boxes (cairo_path_fixed_t *path,
501 				   const cairo_boxes_t *boxes)
502 {
503     cairo_status_t status;
504     const struct _cairo_boxes_chunk *chunk;
505     int i;
506 
507     _cairo_path_fixed_init (path);
508     if (boxes->num_boxes == 0)
509 	return CAIRO_STATUS_SUCCESS;
510 
511     for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
512 	for (i = 0; i < chunk->count; i++) {
513 	    status = _cairo_path_fixed_add_box (path, &chunk->base[i]);
514 	    if (unlikely (status)) {
515 		_cairo_path_fixed_fini (path);
516 		return status;
517 	    }
518 	}
519     }
520 
521     return CAIRO_STATUS_SUCCESS;
522 }
523 
524 static cairo_clip_t *
_cairo_clip_intersect_clip_path_transformed(cairo_clip_t * clip,const cairo_clip_path_t * clip_path,const cairo_matrix_t * m)525 _cairo_clip_intersect_clip_path_transformed (cairo_clip_t *clip,
526 					     const cairo_clip_path_t *clip_path,
527 					     const cairo_matrix_t *m)
528 {
529     cairo_path_fixed_t path;
530 
531     if (clip_path->prev)
532 	clip = _cairo_clip_intersect_clip_path_transformed (clip,
533 							    clip_path->prev,
534 							    m);
535 
536     if (_cairo_path_fixed_init_copy (&path, &clip_path->path))
537 	return _cairo_clip_set_all_clipped (clip);
538 
539     _cairo_path_fixed_transform (&path, m);
540 
541     clip =  _cairo_clip_intersect_path (clip,
542 				       &path,
543 				       clip_path->fill_rule,
544 				       clip_path->tolerance,
545 				       clip_path->antialias);
546     _cairo_path_fixed_fini (&path);
547 
548     return clip;
549 }
550 
551 cairo_clip_t *
_cairo_clip_transform(cairo_clip_t * clip,const cairo_matrix_t * m)552 _cairo_clip_transform (cairo_clip_t *clip, const cairo_matrix_t *m)
553 {
554     cairo_clip_t *copy;
555 
556     if (clip == NULL || _cairo_clip_is_all_clipped (clip))
557 	return clip;
558 
559     if (_cairo_matrix_is_translation (m))
560 	return _cairo_clip_translate (clip, m->x0, m->y0);
561 
562     copy = _cairo_clip_create ();
563 
564     if (clip->num_boxes) {
565 	cairo_path_fixed_t path;
566 	cairo_boxes_t boxes;
567 
568 	_cairo_boxes_init_for_array (&boxes, clip->boxes, clip->num_boxes);
569 	_cairo_path_fixed_init_from_boxes (&path, &boxes);
570 	_cairo_path_fixed_transform (&path, m);
571 
572 	copy = _cairo_clip_intersect_path (copy, &path,
573 					   CAIRO_FILL_RULE_WINDING,
574 					   0.1,
575 					   CAIRO_ANTIALIAS_DEFAULT);
576 
577 	_cairo_path_fixed_fini (&path);
578     }
579 
580     if (clip->path)
581 	copy = _cairo_clip_intersect_clip_path_transformed (copy, clip->path,m);
582 
583     _cairo_clip_destroy (clip);
584     return copy;
585 }
586 
587 cairo_clip_t *
_cairo_clip_copy_with_translation(const cairo_clip_t * clip,int tx,int ty)588 _cairo_clip_copy_with_translation (const cairo_clip_t *clip, int tx, int ty)
589 {
590     cairo_clip_t *copy;
591     int fx, fy, i;
592 
593     if (clip == NULL || _cairo_clip_is_all_clipped (clip))
594 	return (cairo_clip_t *)clip;
595 
596     if (tx == 0 && ty == 0)
597 	return _cairo_clip_copy (clip);
598 
599     copy = _cairo_clip_create ();
600     if (copy == NULL)
601 	    return _cairo_clip_set_all_clipped (copy);
602 
603     fx = _cairo_fixed_from_int (tx);
604     fy = _cairo_fixed_from_int (ty);
605 
606     if (clip->num_boxes) {
607 	if (clip->num_boxes == 1) {
608 	    copy->boxes = &copy->embedded_box;
609 	} else {
610 	    copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t));
611 	    if (unlikely (copy->boxes == NULL))
612 		return _cairo_clip_set_all_clipped (copy);
613 	}
614 
615 	for (i = 0; i < clip->num_boxes; i++) {
616 	    copy->boxes[i].p1.x = clip->boxes[i].p1.x + fx;
617 	    copy->boxes[i].p2.x = clip->boxes[i].p2.x + fx;
618 	    copy->boxes[i].p1.y = clip->boxes[i].p1.y + fy;
619 	    copy->boxes[i].p2.y = clip->boxes[i].p2.y + fy;
620 	}
621 	copy->num_boxes = clip->num_boxes;
622     }
623 
624     copy->extents = clip->extents;
625     copy->extents.x += tx;
626     copy->extents.y += ty;
627 
628     if (clip->path == NULL)
629 	return copy;
630 
631     return _cairo_clip_path_copy_with_translation (copy, clip->path, fx, fy);
632 }
633 
634 cairo_bool_t
_cairo_clip_contains_extents(const cairo_clip_t * clip,const cairo_composite_rectangles_t * extents)635 _cairo_clip_contains_extents (const cairo_clip_t *clip,
636 			      const cairo_composite_rectangles_t *extents)
637 {
638     const cairo_rectangle_int_t *rect;
639 
640     rect = extents->is_bounded ? &extents->bounded : &extents->unbounded;
641     return _cairo_clip_contains_rectangle (clip, rect);
642 }
643 
644 void
_cairo_debug_print_clip(FILE * stream,const cairo_clip_t * clip)645 _cairo_debug_print_clip (FILE *stream, const cairo_clip_t *clip)
646 {
647     int i;
648 
649     if (clip == NULL) {
650 	fprintf (stream, "no clip\n");
651 	return;
652     }
653 
654     if (_cairo_clip_is_all_clipped (clip)) {
655 	fprintf (stream, "clip: all-clipped\n");
656 	return;
657     }
658 
659     fprintf (stream, "clip:\n");
660     fprintf (stream, "  extents: (%d, %d) x (%d, %d), is-region? %d",
661 	     clip->extents.x, clip->extents.y,
662 	     clip->extents.width, clip->extents.height,
663 	     clip->is_region);
664 
665     fprintf (stream, "  num_boxes = %d\n", clip->num_boxes);
666     for (i = 0; i < clip->num_boxes; i++) {
667 	fprintf (stream, "  [%d] = (%f, %f), (%f, %f)\n", i,
668 		 _cairo_fixed_to_double (clip->boxes[i].p1.x),
669 		 _cairo_fixed_to_double (clip->boxes[i].p1.y),
670 		 _cairo_fixed_to_double (clip->boxes[i].p2.x),
671 		 _cairo_fixed_to_double (clip->boxes[i].p2.y));
672     }
673 
674     if (clip->path) {
675 	cairo_clip_path_t *clip_path = clip->path;
676 	do {
677 	    fprintf (stream, "path: aa=%d, tolerance=%f, rule=%d: ",
678 		     clip_path->antialias,
679 		     clip_path->tolerance,
680 		     clip_path->fill_rule);
681 	    _cairo_debug_print_path (stream, &clip_path->path);
682 	    fprintf (stream, "\n");
683 	} while ((clip_path = clip_path->prev) != NULL);
684     }
685 }
686 
687 const cairo_rectangle_int_t *
_cairo_clip_get_extents(const cairo_clip_t * clip)688 _cairo_clip_get_extents (const cairo_clip_t *clip)
689 {
690     if (clip == NULL)
691 	return &_cairo_unbounded_rectangle;
692 
693     if (_cairo_clip_is_all_clipped (clip))
694 	return &_cairo_empty_rectangle;
695 
696     return &clip->extents;
697 }
698 
699 const cairo_rectangle_list_t _cairo_rectangles_nil =
700   { CAIRO_STATUS_NO_MEMORY, NULL, 0 };
701 static const cairo_rectangle_list_t _cairo_rectangles_not_representable =
702   { CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, NULL, 0 };
703 
704 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)705 _cairo_clip_int_rect_to_user (cairo_gstate_t *gstate,
706 			      cairo_rectangle_int_t *clip_rect,
707 			      cairo_rectangle_t *user_rect)
708 {
709     cairo_bool_t is_tight;
710 
711     double x1 = clip_rect->x;
712     double y1 = clip_rect->y;
713     double x2 = clip_rect->x + (int) clip_rect->width;
714     double y2 = clip_rect->y + (int) clip_rect->height;
715 
716     _cairo_gstate_backend_to_user_rectangle (gstate,
717 					     &x1, &y1, &x2, &y2,
718 					     &is_tight);
719 
720     user_rect->x = x1;
721     user_rect->y = y1;
722     user_rect->width  = x2 - x1;
723     user_rect->height = y2 - y1;
724 
725     return is_tight;
726 }
727 
728 cairo_rectangle_list_t *
_cairo_rectangle_list_create_in_error(cairo_status_t status)729 _cairo_rectangle_list_create_in_error (cairo_status_t status)
730 {
731     cairo_rectangle_list_t *list;
732 
733     if (status == CAIRO_STATUS_NO_MEMORY)
734 	return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
735     if (status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE)
736 	return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable;
737 
738     list = _cairo_malloc (sizeof (*list));
739     if (unlikely (list == NULL)) {
740 	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
741 	return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
742     }
743 
744     list->status = status;
745     list->rectangles = NULL;
746     list->num_rectangles = 0;
747 
748     return list;
749 }
750 
751 cairo_rectangle_list_t *
_cairo_clip_copy_rectangle_list(cairo_clip_t * clip,cairo_gstate_t * gstate)752 _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate)
753 {
754 #define ERROR_LIST(S) _cairo_rectangle_list_create_in_error (_cairo_error (S))
755 
756     cairo_rectangle_list_t *list;
757     cairo_rectangle_t *rectangles = NULL;
758     cairo_region_t *region = NULL;
759     int n_rects = 0;
760     int i;
761 
762     if (clip == NULL)
763 	return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
764 
765     if (_cairo_clip_is_all_clipped (clip))
766 	goto DONE;
767 
768     if (! _cairo_clip_is_region (clip))
769 	return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
770 
771     region = _cairo_clip_get_region (clip);
772     if (region == NULL)
773 	return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
774 
775     n_rects = cairo_region_num_rectangles (region);
776     if (n_rects) {
777 	rectangles = _cairo_malloc_ab (n_rects, sizeof (cairo_rectangle_t));
778 	if (unlikely (rectangles == NULL)) {
779 	    return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
780 	}
781 
782 	for (i = 0; i < n_rects; ++i) {
783 	    cairo_rectangle_int_t clip_rect;
784 
785 	    cairo_region_get_rectangle (region, i, &clip_rect);
786 
787 	    if (! _cairo_clip_int_rect_to_user (gstate,
788 						&clip_rect,
789 						&rectangles[i]))
790 	    {
791 		free (rectangles);
792 		return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
793 	    }
794 	}
795     }
796 
797  DONE:
798     list = _cairo_malloc (sizeof (cairo_rectangle_list_t));
799     if (unlikely (list == NULL)) {
800         free (rectangles);
801 	return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
802     }
803 
804     list->status = CAIRO_STATUS_SUCCESS;
805     list->rectangles = rectangles;
806     list->num_rectangles = n_rects;
807     return list;
808 
809 #undef ERROR_LIST
810 }
811 
812 /**
813  * cairo_rectangle_list_destroy:
814  * @rectangle_list: a rectangle list, as obtained from cairo_copy_clip_rectangle_list()
815  *
816  * Unconditionally frees @rectangle_list and all associated
817  * references. After this call, the @rectangle_list pointer must not
818  * be dereferenced.
819  *
820  * Since: 1.4
821  **/
822 void
cairo_rectangle_list_destroy(cairo_rectangle_list_t * rectangle_list)823 cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list)
824 {
825     if (rectangle_list == NULL || rectangle_list == &_cairo_rectangles_nil ||
826         rectangle_list == &_cairo_rectangles_not_representable)
827         return;
828 
829     free (rectangle_list->rectangles);
830     free (rectangle_list);
831 }
832 
833 void
_cairo_clip_reset_static_data(void)834 _cairo_clip_reset_static_data (void)
835 {
836     _freed_pool_reset (&clip_path_pool);
837     _freed_pool_reset (&clip_pool);
838 }
839