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