Changes.
[cairo/gpu.git] / src / cairo-clip.c
blob79e4cbe61e16106a3323af06908e8b202a64e29d
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
4 * Copyright © 2002 University of Southern California
5 * Copyright © 2005 Red Hat, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it either under the terms of the GNU Lesser General Public
9 * License version 2.1 as published by the Free Software Foundation
10 * (the "LGPL") or, at your option, under the terms of the Mozilla
11 * Public License Version 1.1 (the "MPL"). If you do not alter this
12 * notice, a recipient may use your version of this file under either
13 * the MPL or the LGPL.
15 * You should have received a copy of the LGPL along with this library
16 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * You should have received a copy of the MPL along with this library
19 * in the file COPYING-MPL-1.1
21 * The contents of this file are subject to the Mozilla Public License
22 * Version 1.1 (the "License"); you may not use this file except in
23 * compliance with the License. You may obtain a copy of the License at
24 * http://www.mozilla.org/MPL/
26 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
27 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
28 * the specific language governing rights and limitations.
30 * The Original Code is the cairo graphics library.
32 * The Initial Developer of the Original Code is University of Southern
33 * California.
35 * Contributor(s):
36 * Carl D. Worth <cworth@cworth.org>
37 * Kristian Høgsberg <krh@redhat.com>
40 #include "cairoint.h"
41 #include "cairo-clip-private.h"
43 static cairo_clip_path_t *
44 _cairo_clip_path_reference (cairo_clip_path_t *clip_path);
46 static void
47 _cairo_clip_path_destroy (cairo_clip_path_t *clip_path);
49 void
50 _cairo_clip_init (cairo_clip_t *clip, cairo_surface_t *target)
52 if (target && target->backend)
53 clip->mode = _cairo_surface_get_clip_mode (target);
54 else
55 clip->mode = CAIRO_CLIP_MODE_MASK;
57 clip->all_clipped = FALSE;
59 clip->surface = NULL;
60 clip->surface_rect.x = 0;
61 clip->surface_rect.y = 0;
62 clip->surface_rect.width = 0;
63 clip->surface_rect.height = 0;
65 clip->serial = 0;
67 clip->region = NULL;
69 clip->path = NULL;
72 cairo_status_t
73 _cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other)
75 clip->mode = other->mode;
77 clip->all_clipped = other->all_clipped;
79 clip->surface = cairo_surface_reference (other->surface);
80 clip->surface_rect = other->surface_rect;
82 clip->serial = other->serial;
84 if (other->region) {
85 cairo_status_t status;
87 clip->region = cairo_region_copy (other->region);
89 status = cairo_region_status (clip->region);
90 if (unlikely (status)) {
91 cairo_surface_destroy (clip->surface);
92 cairo_region_destroy (clip->region);
93 clip->region = NULL;
95 return status;
97 } else {
98 clip->region = NULL;
101 clip->path = _cairo_clip_path_reference (other->path);
103 return CAIRO_STATUS_SUCCESS;
106 void
107 _cairo_clip_reset (cairo_clip_t *clip)
109 clip->all_clipped = FALSE;
111 /* destroy any existing clip-region artifacts */
112 cairo_surface_destroy (clip->surface);
113 clip->surface = NULL;
115 clip->serial = 0;
117 if (clip->region) {
118 cairo_region_destroy (clip->region);
120 clip->region = NULL;
123 _cairo_clip_path_destroy (clip->path);
124 clip->path = NULL;
127 static void
128 _cairo_clip_set_all_clipped (cairo_clip_t *clip, cairo_surface_t *target)
130 _cairo_clip_reset (clip);
132 clip->all_clipped = TRUE;
133 clip->serial = _cairo_surface_allocate_clip_serial (target);
137 static cairo_status_t
138 _cairo_clip_path_intersect_to_rectangle (cairo_clip_path_t *clip_path,
139 cairo_rectangle_int_t *rectangle)
141 while (clip_path) {
142 cairo_rectangle_int_t extents;
144 _cairo_path_fixed_approximate_clip_extents (&clip_path->path, &extents);
146 if (! _cairo_rectangle_intersect (rectangle, &extents))
147 return CAIRO_STATUS_SUCCESS;
149 clip_path = clip_path->prev;
152 return CAIRO_STATUS_SUCCESS;
155 cairo_status_t
156 _cairo_clip_intersect_to_rectangle (cairo_clip_t *clip,
157 cairo_rectangle_int_t *rectangle)
159 cairo_status_t status;
160 cairo_bool_t is_empty;
162 if (!clip)
163 return CAIRO_STATUS_SUCCESS;
165 if (clip->all_clipped) {
166 *rectangle = clip->surface_rect;
167 return CAIRO_STATUS_SUCCESS;
170 if (clip->path) {
171 status = _cairo_clip_path_intersect_to_rectangle (clip->path,
172 rectangle);
173 if (unlikely (status))
174 return status;
177 if (clip->region) {
178 cairo_rectangle_int_t extents;
180 cairo_region_get_extents (clip->region, &extents);
181 is_empty = _cairo_rectangle_intersect (rectangle, &extents);
182 if (is_empty)
183 return CAIRO_STATUS_SUCCESS;
186 if (clip->surface)
187 is_empty = _cairo_rectangle_intersect (rectangle, &clip->surface_rect);
189 return CAIRO_STATUS_SUCCESS;
192 cairo_status_t
193 _cairo_clip_intersect_to_region (cairo_clip_t *clip,
194 cairo_region_t *region)
196 cairo_status_t status;
198 if (!clip)
199 return CAIRO_STATUS_SUCCESS;
201 if (clip->all_clipped)
202 return cairo_region_intersect_rectangle (region, &clip->surface_rect);
204 if (clip->path) {
205 /* Intersect clip path into region. */
208 if (clip->region) {
209 status = cairo_region_intersect (region, clip->region);
210 if (unlikely (status))
211 return status;
214 if (clip->surface)
215 return cairo_region_intersect_rectangle (region, &clip->surface_rect);
217 return CAIRO_STATUS_SUCCESS;
220 /* Combines the region of clip->surface given by extents in
221 * device backend coordinates into the given temporary surface,
222 * which has its origin at dst_x, dst_y in backend coordinates
224 cairo_status_t
225 _cairo_clip_combine_to_surface (cairo_clip_t *clip,
226 cairo_operator_t op,
227 cairo_surface_t *dst,
228 int dst_x,
229 int dst_y,
230 const cairo_rectangle_int_t *extents)
232 cairo_surface_pattern_t pattern;
233 cairo_status_t status;
235 if (clip->all_clipped)
236 return CAIRO_STATUS_SUCCESS;
238 _cairo_pattern_init_for_surface (&pattern, clip->surface);
240 status = _cairo_surface_composite (op,
241 &pattern.base,
242 NULL,
243 dst,
244 extents->x - clip->surface_rect.x,
245 extents->y - clip->surface_rect.y,
246 0, 0,
247 extents->x - dst_x,
248 extents->y - dst_y,
249 extents->width, extents->height);
251 _cairo_pattern_fini (&pattern.base);
253 return status;
256 static cairo_status_t
257 _cairo_clip_intersect_path (cairo_clip_t *clip,
258 cairo_path_fixed_t *path,
259 cairo_fill_rule_t fill_rule,
260 double tolerance,
261 cairo_antialias_t antialias)
263 cairo_clip_path_t *clip_path;
264 cairo_status_t status;
266 if (clip->mode != CAIRO_CLIP_MODE_PATH)
267 return CAIRO_INT_STATUS_UNSUPPORTED;
269 clip_path = malloc (sizeof (cairo_clip_path_t));
270 if (unlikely (clip_path == NULL))
271 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
273 status = _cairo_path_fixed_init_copy (&clip_path->path, path);
274 if (unlikely (status)) {
275 free (clip_path);
276 return status;
279 CAIRO_REFERENCE_COUNT_INIT (&clip_path->ref_count, 1);
280 clip_path->fill_rule = fill_rule;
281 clip_path->tolerance = tolerance;
282 clip_path->antialias = antialias;
283 clip_path->prev = clip->path;
284 clip->path = clip_path;
286 return CAIRO_STATUS_SUCCESS;
289 static cairo_clip_path_t *
290 _cairo_clip_path_reference (cairo_clip_path_t *clip_path)
292 if (clip_path == NULL)
293 return NULL;
295 assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
297 _cairo_reference_count_inc (&clip_path->ref_count);
299 return clip_path;
302 static void
303 _cairo_clip_path_destroy (cairo_clip_path_t *clip_path)
305 if (clip_path == NULL)
306 return;
308 assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
310 if (! _cairo_reference_count_dec_and_test (&clip_path->ref_count))
311 return;
313 _cairo_path_fixed_fini (&clip_path->path);
314 _cairo_clip_path_destroy (clip_path->prev);
315 free (clip_path);
319 static cairo_int_status_t
320 _cairo_clip_intersect_region (cairo_clip_t *clip,
321 cairo_traps_t *traps,
322 cairo_surface_t *target)
324 cairo_region_t *region;
325 cairo_int_status_t status;
327 if (clip->all_clipped)
328 return CAIRO_STATUS_SUCCESS;
330 if (clip->mode != CAIRO_CLIP_MODE_REGION)
331 return CAIRO_INT_STATUS_UNSUPPORTED;
333 status = _cairo_traps_extract_region (traps, &region);
334 if (status)
335 return status;
337 if (clip->region) {
338 status = cairo_region_intersect (clip->region, region);
339 cairo_region_destroy (region);
340 } else {
341 clip->region = region;
344 clip->serial = _cairo_surface_allocate_clip_serial (target);
346 if (!clip->region || cairo_region_is_empty (clip->region))
347 _cairo_clip_set_all_clipped (clip, target);
349 return status;
352 static cairo_status_t
353 _cairo_clip_intersect_mask (cairo_clip_t *clip,
354 cairo_traps_t *traps,
355 cairo_antialias_t antialias,
356 cairo_surface_t *target)
358 cairo_pattern_union_t pattern;
359 cairo_box_t extents;
360 cairo_rectangle_int_t surface_rect, target_rect;
361 cairo_surface_t *surface = NULL;
362 cairo_status_t status = CAIRO_STATUS_SUCCESS;
364 if (clip->all_clipped)
365 return CAIRO_STATUS_SUCCESS;
367 /* Represent the clip as a mask surface. We create a new surface
368 * the size of the intersection of the old mask surface and the
369 * extents of the new clip path. */
371 _cairo_traps_extents (traps, &extents);
372 _cairo_box_round_to_rectangle (&extents, &surface_rect);
374 if (clip->surface != NULL) {
375 if (! _cairo_rectangle_intersect (&surface_rect, &clip->surface_rect))
376 goto DONE;
379 /* Intersect with the target surface rectangle so we don't use
380 * more memory and time than we need to. */
381 status = _cairo_surface_get_extents (target, &target_rect);
382 if (status == CAIRO_STATUS_SUCCESS) {
383 if (! _cairo_rectangle_intersect (&surface_rect, &target_rect))
384 goto DONE;
387 if (surface_rect.width == 0 || surface_rect.height == 0)
388 goto DONE;
390 _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE,
391 CAIRO_CONTENT_COLOR);
392 /* The clipping operation should ideally be something like the following to
393 * avoid having to do as many passes over the data
395 if (clip->surface != NULL) {
396 _cairo_pattern_init_for_surface (&pattern.surface, clip->surface);
397 } else {
398 _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE,
399 CAIRO_CONTENT_COLOR);
401 status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_IN,
402 &pattern.base,
403 surface,
404 antialias,
405 0, 0,
406 0, 0,
407 surface_rect.width,
408 surface_rect.height,
409 traps->traps,
410 traps->num_traps);
412 However this operation is not accelerated by pixman
414 I believe the best possible operation would probably an unbounded SRC
415 operator. Using SRC we could potentially avoid having to initialize
416 the surface which would be ideal from an efficiency point of view.
417 However, CAIRO_OPERATOR_SOURCE is bounded by the trapezoid mask and
418 _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_SOURCE) will assert
419 because it assumes CAIRO_OPERATOR_SOURCE has been converted into other
420 operations.
423 surface = _cairo_surface_create_similar_solid (target,
424 CAIRO_CONTENT_ALPHA,
425 surface_rect.width,
426 surface_rect.height,
427 CAIRO_COLOR_TRANSPARENT);
428 if (surface->status) {
429 _cairo_pattern_fini (&pattern.base);
430 return surface->status;
433 /* Render the new clipping path into the new mask surface. */
435 _cairo_traps_translate (traps, -surface_rect.x, -surface_rect.y);
437 status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_ADD,
438 &pattern.base,
439 surface,
440 antialias,
441 0, 0,
442 0, 0,
443 surface_rect.width,
444 surface_rect.height,
445 traps->traps,
446 traps->num_traps);
448 _cairo_pattern_fini (&pattern.base);
450 if (unlikely (status)) {
451 cairo_surface_destroy (surface);
452 return status;
455 /* If there was a clip surface already, combine it with the new
456 * mask surface using the IN operator, so we get the intersection
457 * of the old and new clipping paths. */
459 if (clip->surface != NULL) {
460 _cairo_pattern_init_for_surface (&pattern.surface, clip->surface);
462 status = _cairo_surface_composite (CAIRO_OPERATOR_IN,
463 &pattern.base,
464 NULL,
465 surface,
466 surface_rect.x - clip->surface_rect.x,
467 surface_rect.y - clip->surface_rect.y,
468 0, 0,
469 0, 0,
470 surface_rect.width,
471 surface_rect.height);
473 _cairo_pattern_fini (&pattern.base);
475 if (unlikely (status)) {
476 cairo_surface_destroy (surface);
477 return status;
481 DONE:
482 cairo_surface_destroy (clip->surface);
483 clip->surface = surface;
484 clip->surface_rect = surface_rect;
485 clip->serial = _cairo_surface_allocate_clip_serial (target);
487 if (surface_rect.width == 0 || surface_rect.height == 0)
488 _cairo_clip_set_all_clipped (clip, target);
490 return status;
493 static cairo_status_t
494 _cairo_clip_intersect_mask_using_spans (cairo_clip_t *clip,
495 cairo_path_fixed_t *path,
496 cairo_fill_rule_t fill_rule,
497 double tolerance,
498 cairo_antialias_t antialias,
499 cairo_surface_t *target)
501 cairo_span_renderer_t *renderer = NULL;
502 cairo_pattern_union_t pattern;
503 cairo_rectangle_int_t surface_rect;
504 cairo_surface_t *surface = NULL;
505 cairo_status_t status;
506 cairo_operator_t op;
507 cairo_composite_rectangles_t rects;
509 if (clip->all_clipped)
510 return CAIRO_STATUS_SUCCESS;
512 _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE,
513 CAIRO_CONTENT_COLOR);
515 /* If we have a clip surface we're going to use IN to combine our
516 * new clip with the old clip. The ADD is done to a transparent
517 * surface, as that's a fast way of doing it currently. We should
518 * really be using SOURCE instead, but _cairo_surface_composite()
519 * checks that it's not called with SOURCE or DEST. */
520 op = clip->surface ? CAIRO_OPERATOR_IN : CAIRO_OPERATOR_ADD;
522 /* Test if the target can composite spans. We're going to assume
523 * this is a good indicator of whether a similar surface is going
524 * to be able to composite spans too. */
525 if ( !_cairo_surface_check_span_renderer (op,
526 &pattern.base,
527 target,
528 antialias,
529 NULL))
531 status = CAIRO_INT_STATUS_UNSUPPORTED;
532 goto BAIL;
535 status = _cairo_surface_get_extents (target, &surface_rect);
536 if (status)
537 goto BAIL;
539 /* We'll create a new surface the size of the intersection of the
540 * old mask surface and the extents of the new clip path. */
542 cairo_rectangle_int_t extents;
544 _cairo_path_fixed_approximate_clip_extents (path, &extents);
545 if (! _cairo_rectangle_intersect (&surface_rect, &extents))
546 goto SUCCESS;
548 if (clip->surface != NULL &&
549 ! _cairo_rectangle_intersect (&surface_rect, &clip->surface_rect))
550 goto SUCCESS;
553 /* Make the new mask surface and optionally initialise it from the
554 * previous clip if we have one. */
555 surface = _cairo_surface_create_similar_solid (target,
556 CAIRO_CONTENT_ALPHA,
557 surface_rect.width,
558 surface_rect.height,
559 CAIRO_COLOR_TRANSPARENT);
560 if (surface->status) {
561 _cairo_pattern_fini (&pattern.base);
562 return surface->status;
565 if (clip->surface) {
566 cairo_surface_pattern_t old_clip;
567 _cairo_pattern_init_for_surface (&old_clip, clip->surface);
568 status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
569 &old_clip.base,
570 NULL,
571 surface,
572 surface_rect.x - clip->surface_rect.x,
573 surface_rect.y - clip->surface_rect.y,
574 0, 0,
575 0, 0,
576 surface_rect.width,
577 surface_rect.height);
578 _cairo_pattern_fini (&old_clip.base);
579 if (status)
580 goto BAIL;
583 _cairo_composite_rectangles_init (&rects,
584 surface_rect.x,
585 surface_rect.y,
586 surface_rect.width,
587 surface_rect.height);
588 rects.dst.x = 0;
589 rects.dst.y = 0;
591 /* Render the new clipping path into the new mask surface. We've
592 * chosen op to either combine the new clip path with the existing
593 * clip mask (if there is one) or just render it. */
594 status =_cairo_path_fixed_fill_using_spans (op, &pattern.base,
595 path, surface,
596 fill_rule, tolerance,
597 antialias, &rects);
598 if (status)
599 goto BAIL;
601 SUCCESS:
602 if (clip->surface != NULL)
603 cairo_surface_destroy (clip->surface);
604 clip->surface = surface;
605 clip->surface_rect = surface_rect;
606 clip->serial = _cairo_surface_allocate_clip_serial (target);
607 surface = NULL;
609 if (surface_rect.width == 0 || surface_rect.height == 0)
610 _cairo_clip_set_all_clipped (clip, target);
612 BAIL:
613 if (renderer)
614 renderer->destroy(renderer);
615 if (surface)
616 cairo_surface_destroy (surface);
617 _cairo_pattern_fini (&pattern.base);
618 return status;
621 cairo_status_t
622 _cairo_clip_clip (cairo_clip_t *clip,
623 cairo_path_fixed_t *path,
624 cairo_fill_rule_t fill_rule,
625 double tolerance,
626 cairo_antialias_t antialias,
627 cairo_surface_t *target)
629 cairo_status_t status;
630 cairo_rectangle_int_t limits, extents;
631 cairo_traps_t traps;
632 cairo_box_t ignored_box;
633 cairo_bool_t have_limits;
635 if (clip->all_clipped)
636 return CAIRO_STATUS_SUCCESS;
638 /* catch the empty clip path */
639 if (! path->has_current_point) {
640 _cairo_clip_set_all_clipped (clip, target);
641 return CAIRO_STATUS_SUCCESS;
644 status = _cairo_clip_intersect_path (clip,
645 path, fill_rule, tolerance,
646 antialias);
647 if (status == CAIRO_STATUS_SUCCESS)
648 clip->serial = _cairo_surface_allocate_clip_serial (target);
650 if (status != CAIRO_INT_STATUS_UNSUPPORTED)
651 return status;
653 /* TODO: allow ANTIALIAS_NONE when we have a mono scan converter
654 * again. */
655 if (antialias != CAIRO_ANTIALIAS_NONE &&
656 !_cairo_path_fixed_is_box (path, &ignored_box) &&
657 !_cairo_path_fixed_is_region (path))
659 status = _cairo_clip_intersect_mask_using_spans (
660 clip, path, fill_rule, tolerance, antialias, target);
661 if (status != CAIRO_INT_STATUS_UNSUPPORTED)
662 return status;
665 _cairo_traps_init (&traps);
667 /* Limit the traps to the target surface and current clip
668 * - so we don't add more traps than needed. */
669 have_limits = FALSE;
670 if (clip->region != NULL) {
671 cairo_region_get_extents (clip->region, &limits);
672 have_limits = TRUE;
675 if (clip->surface != NULL) {
676 if (have_limits) {
677 if (! _cairo_rectangle_intersect (&limits, &clip->surface_rect)) {
678 _cairo_clip_set_all_clipped (clip, target);
679 return CAIRO_STATUS_SUCCESS;
681 } else {
682 limits = clip->surface_rect;
683 have_limits = TRUE;
687 status = _cairo_surface_get_extents (target, &extents);
688 if (status == CAIRO_STATUS_SUCCESS) {
689 if (have_limits) {
690 if (! _cairo_rectangle_intersect (&limits, &extents)) {
691 _cairo_clip_set_all_clipped (clip, target);
692 return CAIRO_STATUS_SUCCESS;
694 } else {
695 limits = extents;
696 have_limits = TRUE;
700 if (have_limits) {
701 cairo_box_t box;
703 _cairo_box_from_rectangle (&box, &limits);
704 _cairo_traps_limit (&traps, &box);
707 status = _cairo_path_fixed_fill_to_traps (path,
708 fill_rule,
709 tolerance,
710 &traps);
711 if (unlikely (status))
712 goto bail;
714 status = _cairo_clip_intersect_region (clip, &traps, target);
715 if (status != CAIRO_INT_STATUS_UNSUPPORTED)
716 goto bail;
718 status = _cairo_clip_intersect_mask (clip, &traps, antialias, target);
720 bail:
721 _cairo_traps_fini (&traps);
723 return status;
726 void
727 _cairo_clip_translate (cairo_clip_t *clip,
728 cairo_fixed_t tx,
729 cairo_fixed_t ty)
731 if (clip->all_clipped)
732 return;
734 if (clip->region) {
735 cairo_region_translate (clip->region,
736 _cairo_fixed_integer_part (tx),
737 _cairo_fixed_integer_part (ty));
740 if (clip->surface) {
741 clip->surface_rect.x += _cairo_fixed_integer_part (tx);
742 clip->surface_rect.y += _cairo_fixed_integer_part (ty);
745 if (clip->path) {
746 cairo_clip_path_t *clip_path = clip->path;
747 cairo_matrix_t matrix;
749 cairo_matrix_init_translate (&matrix,
750 _cairo_fixed_to_double (tx),
751 _cairo_fixed_to_double (ty));
753 while (clip_path) {
754 _cairo_path_fixed_transform (&clip_path->path, &matrix);
755 clip_path = clip_path->prev;
760 static cairo_status_t
761 _cairo_clip_path_reapply_clip_path (cairo_clip_t *clip,
762 cairo_clip_path_t *clip_path)
764 cairo_status_t status;
766 if (clip_path->prev) {
767 status = _cairo_clip_path_reapply_clip_path (clip, clip_path->prev);
768 if (_cairo_status_is_error (status))
769 return status;
772 return _cairo_clip_intersect_path (clip,
773 &clip_path->path,
774 clip_path->fill_rule,
775 clip_path->tolerance,
776 clip_path->antialias);
779 cairo_status_t
780 _cairo_clip_init_deep_copy (cairo_clip_t *clip,
781 cairo_clip_t *other,
782 cairo_surface_t *target)
784 cairo_status_t status;
786 _cairo_clip_init (clip, target);
788 if (other->mode != clip->mode) {
789 /* We should reapply the original clip path in this case, and let
790 * whatever the right handling is happen */
791 } else {
792 if (other->region) {
793 clip->region = cairo_region_copy (other->region);
794 if (unlikely ((status = cairo_region_status (clip->region))))
795 goto BAIL;
798 if (other->surface) {
799 int dx, dy;
800 status = _cairo_surface_clone_similar (target, other->surface,
801 CAIRO_CONTENT_ALPHA,
804 other->surface_rect.width,
805 other->surface_rect.height,
806 &dx, &dy,
807 &clip->surface);
808 if (unlikely (status))
809 goto BAIL;
811 clip->surface_rect = other->surface_rect;
813 /* src offset was 0, so we expect an exact replica of the surface */
814 assert (dx == 0);
815 assert (dy == 0);
818 if (other->path) {
819 status = _cairo_clip_path_reapply_clip_path (clip, other->path);
820 if (_cairo_status_is_error (status))
821 goto BAIL;
825 return CAIRO_STATUS_SUCCESS;
827 BAIL:
828 if (clip->region)
829 cairo_region_destroy (clip->region);
830 if (clip->surface)
831 cairo_surface_destroy (clip->surface);
833 return status;
836 const cairo_rectangle_list_t _cairo_rectangles_nil =
837 { CAIRO_STATUS_NO_MEMORY, NULL, 0 };
838 static const cairo_rectangle_list_t _cairo_rectangles_not_representable =
839 { CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, NULL, 0 };
841 static cairo_bool_t
842 _cairo_clip_int_rect_to_user (cairo_gstate_t *gstate,
843 cairo_rectangle_int_t *clip_rect,
844 cairo_rectangle_t *user_rect)
846 cairo_bool_t is_tight;
848 double x1 = clip_rect->x;
849 double y1 = clip_rect->y;
850 double x2 = clip_rect->x + (int) clip_rect->width;
851 double y2 = clip_rect->y + (int) clip_rect->height;
853 _cairo_gstate_backend_to_user_rectangle (gstate,
854 &x1, &y1, &x2, &y2,
855 &is_tight);
857 user_rect->x = x1;
858 user_rect->y = y1;
859 user_rect->width = x2 - x1;
860 user_rect->height = y2 - y1;
862 return is_tight;
865 cairo_rectangle_list_t *
866 _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate)
868 cairo_rectangle_list_t *list;
869 cairo_rectangle_t *rectangles = NULL;
870 int n_rects = 0;
872 if (clip->all_clipped)
873 goto DONE;
875 if (clip->path || clip->surface) {
876 _cairo_error_throw (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
877 return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable;
880 if (clip->region) {
881 int i;
883 n_rects = cairo_region_num_rectangles (clip->region);
885 if (n_rects) {
886 rectangles = _cairo_malloc_ab (n_rects, sizeof (cairo_rectangle_t));
887 if (unlikely (rectangles == NULL)) {
888 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
889 return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
892 for (i = 0; i < n_rects; ++i) {
893 cairo_rectangle_int_t clip_rect;
895 cairo_region_get_rectangle (clip->region, i, &clip_rect);
897 if (!_cairo_clip_int_rect_to_user(gstate, &clip_rect, &rectangles[i])) {
898 _cairo_error_throw (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
899 free (rectangles);
900 return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable;
904 } else {
905 cairo_rectangle_int_t extents;
907 n_rects = 1;
909 rectangles = malloc(sizeof (cairo_rectangle_t));
910 if (unlikely (rectangles == NULL)) {
911 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
912 return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
915 if (_cairo_surface_get_extents (_cairo_gstate_get_target (gstate), &extents) ||
916 !_cairo_clip_int_rect_to_user(gstate, &extents, rectangles))
918 _cairo_error_throw (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
919 free (rectangles);
920 return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable;
924 DONE:
925 list = malloc (sizeof (cairo_rectangle_list_t));
926 if (unlikely (list == NULL)) {
927 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
928 free (rectangles);
929 return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
932 list->status = CAIRO_STATUS_SUCCESS;
933 list->rectangles = rectangles;
934 list->num_rectangles = n_rects;
935 return list;
939 * cairo_rectangle_list_destroy:
940 * @rectangle_list: a rectangle list, as obtained from cairo_copy_clip_rectangles()
942 * Unconditionally frees @rectangle_list and all associated
943 * references. After this call, the @rectangle_list pointer must not
944 * be dereferenced.
946 * Since: 1.4
948 void
949 cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list)
951 if (rectangle_list == NULL || rectangle_list == &_cairo_rectangles_nil ||
952 rectangle_list == &_cairo_rectangles_not_representable)
953 return;
955 free (rectangle_list->rectangles);
956 free (rectangle_list);