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
36 * Carl D. Worth <cworth@cworth.org>
37 * Kristian Høgsberg <krh@redhat.com>
41 #include "cairo-clip-private.h"
43 static cairo_clip_path_t
*
44 _cairo_clip_path_reference (cairo_clip_path_t
*clip_path
);
47 _cairo_clip_path_destroy (cairo_clip_path_t
*clip_path
);
50 _cairo_clip_init (cairo_clip_t
*clip
, cairo_surface_t
*target
)
53 clip
->mode
= _cairo_surface_get_clip_mode (target
);
55 clip
->mode
= CAIRO_CLIP_MODE_MASK
;
57 clip
->all_clipped
= FALSE
;
60 clip
->surface_rect
.x
= 0;
61 clip
->surface_rect
.y
= 0;
62 clip
->surface_rect
.width
= 0;
63 clip
->surface_rect
.height
= 0;
67 _cairo_region_init (&clip
->region
);
68 clip
->has_region
= FALSE
;
74 _cairo_clip_init_copy (cairo_clip_t
*clip
, cairo_clip_t
*other
)
76 clip
->mode
= other
->mode
;
78 clip
->all_clipped
= other
->all_clipped
;
80 clip
->surface
= cairo_surface_reference (other
->surface
);
81 clip
->surface_rect
= other
->surface_rect
;
83 clip
->serial
= other
->serial
;
85 _cairo_region_init (&clip
->region
);
87 if (other
->has_region
) {
88 cairo_status_t status
;
90 status
= _cairo_region_copy (&clip
->region
, &other
->region
);
92 _cairo_region_fini (&clip
->region
);
93 cairo_surface_destroy (clip
->surface
);
96 clip
->has_region
= TRUE
;
98 clip
->has_region
= FALSE
;
101 clip
->path
= _cairo_clip_path_reference (other
->path
);
103 return CAIRO_STATUS_SUCCESS
;
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
;
117 if (clip
->has_region
) {
118 /* _cairo_region_fini just releases the resources used but
119 * doesn't bother with leaving the region in a valid state.
120 * So _cairo_region_init has to be called afterwards. */
121 _cairo_region_fini (&clip
->region
);
122 _cairo_region_init (&clip
->region
);
124 clip
->has_region
= FALSE
;
127 _cairo_clip_path_destroy (clip
->path
);
132 _cairo_clip_set_all_clipped (cairo_clip_t
*clip
, cairo_surface_t
*target
)
134 _cairo_clip_reset (clip
);
136 clip
->all_clipped
= TRUE
;
137 clip
->serial
= _cairo_surface_allocate_clip_serial (target
);
141 static cairo_status_t
142 _cairo_clip_path_intersect_to_rectangle (cairo_clip_path_t
*clip_path
,
143 cairo_rectangle_int_t
*rectangle
)
146 cairo_status_t status
;
149 cairo_rectangle_int_t extents_rect
;
151 _cairo_box_from_rectangle (&extents
, rectangle
);
153 _cairo_traps_init (&traps
);
154 _cairo_traps_limit (&traps
, &extents
);
156 status
= _cairo_path_fixed_fill_to_traps (&clip_path
->path
,
157 clip_path
->fill_rule
,
158 clip_path
->tolerance
,
161 _cairo_traps_fini (&traps
);
165 _cairo_traps_extents (&traps
, &extents
);
166 _cairo_traps_fini (&traps
);
168 _cairo_box_round_to_rectangle (&extents
, &extents_rect
);
169 if (! _cairo_rectangle_intersect (rectangle
, &extents_rect
))
170 return CAIRO_STATUS_SUCCESS
;
172 clip_path
= clip_path
->prev
;
175 return CAIRO_STATUS_SUCCESS
;
179 _cairo_clip_intersect_to_rectangle (cairo_clip_t
*clip
,
180 cairo_rectangle_int_t
*rectangle
)
182 cairo_status_t status
;
183 cairo_bool_t is_empty
;
186 return CAIRO_STATUS_SUCCESS
;
188 if (clip
->all_clipped
) {
189 *rectangle
= clip
->surface_rect
;
190 return CAIRO_STATUS_SUCCESS
;
194 status
= _cairo_clip_path_intersect_to_rectangle (clip
->path
,
200 if (clip
->has_region
) {
201 cairo_region_t intersection
;
203 _cairo_region_init_rect (&intersection
, rectangle
);
205 status
= _cairo_region_intersect (&intersection
, &clip
->region
,
209 _cairo_region_get_extents (&intersection
, rectangle
);
211 _cairo_region_fini (&intersection
);
218 is_empty
= _cairo_rectangle_intersect (rectangle
, &clip
->surface_rect
);
220 return CAIRO_STATUS_SUCCESS
;
224 _cairo_clip_intersect_to_region (cairo_clip_t
*clip
,
225 cairo_region_t
*region
)
227 cairo_status_t status
;
230 return CAIRO_STATUS_SUCCESS
;
232 if (clip
->all_clipped
) {
233 cairo_region_t clip_rect
;
235 _cairo_region_init_rect (&clip_rect
, &clip
->surface_rect
);
237 status
= _cairo_region_intersect (region
, &clip_rect
, region
);
239 _cairo_region_fini (&clip_rect
);
245 /* Intersect clip path into region. */
248 if (clip
->has_region
) {
249 status
= _cairo_region_intersect (region
, &clip
->region
, region
);
255 cairo_region_t clip_rect
;
257 _cairo_region_init_rect (&clip_rect
, &clip
->surface_rect
);
259 status
= _cairo_region_intersect (region
, &clip_rect
, region
);
261 _cairo_region_fini (&clip_rect
);
267 return CAIRO_STATUS_SUCCESS
;
270 /* Combines the region of clip->surface given by extents in
271 * device backend coordinates into the given temporary surface,
272 * which has its origin at dst_x, dst_y in backend coordinates
275 _cairo_clip_combine_to_surface (cairo_clip_t
*clip
,
277 cairo_surface_t
*dst
,
280 const cairo_rectangle_int_t
*extents
)
282 cairo_surface_pattern_t pattern
;
283 cairo_status_t status
;
285 if (clip
->all_clipped
)
286 return CAIRO_STATUS_SUCCESS
;
288 _cairo_pattern_init_for_surface (&pattern
, clip
->surface
);
290 status
= _cairo_surface_composite (op
,
294 extents
->x
- clip
->surface_rect
.x
,
295 extents
->y
- clip
->surface_rect
.y
,
299 extents
->width
, extents
->height
);
301 _cairo_pattern_fini (&pattern
.base
);
306 static cairo_status_t
307 _cairo_clip_intersect_path (cairo_clip_t
*clip
,
308 cairo_path_fixed_t
*path
,
309 cairo_fill_rule_t fill_rule
,
311 cairo_antialias_t antialias
)
313 cairo_clip_path_t
*clip_path
;
314 cairo_status_t status
;
316 if (clip
->mode
!= CAIRO_CLIP_MODE_PATH
)
317 return CAIRO_INT_STATUS_UNSUPPORTED
;
319 clip_path
= malloc (sizeof (cairo_clip_path_t
));
320 if (clip_path
== NULL
)
321 return _cairo_error (CAIRO_STATUS_NO_MEMORY
);
323 status
= _cairo_path_fixed_init_copy (&clip_path
->path
, path
);
329 CAIRO_REFERENCE_COUNT_INIT (&clip_path
->ref_count
, 1);
330 clip_path
->fill_rule
= fill_rule
;
331 clip_path
->tolerance
= tolerance
;
332 clip_path
->antialias
= antialias
;
333 clip_path
->prev
= clip
->path
;
334 clip
->path
= clip_path
;
336 return CAIRO_STATUS_SUCCESS
;
339 static cairo_clip_path_t
*
340 _cairo_clip_path_reference (cairo_clip_path_t
*clip_path
)
342 if (clip_path
== NULL
)
345 assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path
->ref_count
));
347 _cairo_reference_count_inc (&clip_path
->ref_count
);
353 _cairo_clip_path_destroy (cairo_clip_path_t
*clip_path
)
355 if (clip_path
== NULL
)
358 assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path
->ref_count
));
360 if (! _cairo_reference_count_dec_and_test (&clip_path
->ref_count
))
363 _cairo_path_fixed_fini (&clip_path
->path
);
364 _cairo_clip_path_destroy (clip_path
->prev
);
369 static cairo_int_status_t
370 _cairo_clip_intersect_region (cairo_clip_t
*clip
,
371 cairo_traps_t
*traps
,
372 cairo_surface_t
*target
)
374 cairo_region_t region
;
375 cairo_int_status_t status
;
377 if (clip
->all_clipped
)
378 return CAIRO_STATUS_SUCCESS
;
380 if (clip
->mode
!= CAIRO_CLIP_MODE_REGION
)
381 return CAIRO_INT_STATUS_UNSUPPORTED
;
383 status
= _cairo_traps_extract_region (traps
, ®ion
);
388 if (!clip
->has_region
) {
389 status
= _cairo_region_copy (&clip
->region
, ®ion
);
390 if (status
== CAIRO_STATUS_SUCCESS
)
391 clip
->has_region
= TRUE
;
393 cairo_region_t intersection
;
395 _cairo_region_init (&intersection
);
397 status
= _cairo_region_intersect (&intersection
,
401 if (status
== CAIRO_STATUS_SUCCESS
)
402 status
= _cairo_region_copy (&clip
->region
, &intersection
);
404 _cairo_region_fini (&intersection
);
407 clip
->serial
= _cairo_surface_allocate_clip_serial (target
);
408 _cairo_region_fini (®ion
);
410 if (! _cairo_region_not_empty (&clip
->region
))
411 _cairo_clip_set_all_clipped (clip
, target
);
416 static cairo_status_t
417 _cairo_clip_intersect_mask (cairo_clip_t
*clip
,
418 cairo_traps_t
*traps
,
419 cairo_antialias_t antialias
,
420 cairo_surface_t
*target
)
422 cairo_pattern_union_t pattern
;
424 cairo_rectangle_int_t surface_rect
, target_rect
;
425 cairo_surface_t
*surface
= NULL
;
426 cairo_status_t status
;
428 if (clip
->all_clipped
)
429 return CAIRO_STATUS_SUCCESS
;
431 /* Represent the clip as a mask surface. We create a new surface
432 * the size of the intersection of the old mask surface and the
433 * extents of the new clip path. */
435 _cairo_traps_extents (traps
, &extents
);
436 _cairo_box_round_to_rectangle (&extents
, &surface_rect
);
438 if (clip
->surface
!= NULL
) {
439 if (! _cairo_rectangle_intersect (&surface_rect
, &clip
->surface_rect
))
443 /* Intersect with the target surface rectangle so we don't use
444 * more memory and time than we need to. */
445 status
= _cairo_surface_get_extents (target
, &target_rect
);
446 if (status
== CAIRO_STATUS_SUCCESS
) {
447 if (! _cairo_rectangle_intersect (&surface_rect
, &target_rect
))
451 if (surface_rect
.width
== 0 || surface_rect
.height
== 0)
454 _cairo_pattern_init_solid (&pattern
.solid
, CAIRO_COLOR_WHITE
,
455 CAIRO_CONTENT_COLOR
);
456 /* The clipping operation should ideally be something like the following to
457 * avoid having to do as many passes over the data
459 if (clip->surface != NULL) {
460 _cairo_pattern_init_for_surface (&pattern.surface, clip->surface);
462 _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE,
463 CAIRO_CONTENT_COLOR);
465 status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_IN,
476 However this operation is not accelerated by pixman
478 I believe the best possible operation would probably an unbounded SRC
479 operator. Using SRC we could potentially avoid having to initialize
480 the surface which would be ideal from an efficiency point of view.
481 However, _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_SOURCE) is
486 surface
= _cairo_surface_create_similar_solid (target
,
490 CAIRO_COLOR_TRANSPARENT
);
491 if (surface
->status
) {
492 _cairo_pattern_fini (&pattern
.base
);
493 return surface
->status
;
496 /* Render the new clipping path into the new mask surface. */
498 _cairo_traps_translate (traps
, -surface_rect
.x
, -surface_rect
.y
);
500 status
= _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_ADD
,
511 _cairo_pattern_fini (&pattern
.base
);
514 cairo_surface_destroy (surface
);
518 /* If there was a clip surface already, combine it with the new
519 * mask surface using the IN operator, so we get the intersection
520 * of the old and new clipping paths. */
522 if (clip
->surface
!= NULL
) {
523 _cairo_pattern_init_for_surface (&pattern
.surface
, clip
->surface
);
525 status
= _cairo_surface_composite (CAIRO_OPERATOR_IN
,
529 surface_rect
.x
- clip
->surface_rect
.x
,
530 surface_rect
.y
- clip
->surface_rect
.y
,
534 surface_rect
.height
);
536 _cairo_pattern_fini (&pattern
.base
);
539 cairo_surface_destroy (surface
);
545 cairo_surface_destroy (clip
->surface
);
546 clip
->surface
= surface
;
547 clip
->surface_rect
= surface_rect
;
548 clip
->serial
= _cairo_surface_allocate_clip_serial (target
);
550 if (surface_rect
.width
== 0 || surface_rect
.height
== 0)
551 _cairo_clip_set_all_clipped (clip
, target
);
557 _cairo_clip_clip (cairo_clip_t
*clip
,
558 cairo_path_fixed_t
*path
,
559 cairo_fill_rule_t fill_rule
,
561 cairo_antialias_t antialias
,
562 cairo_surface_t
*target
)
564 cairo_status_t status
;
565 cairo_rectangle_int_t rectangle
;
568 if (clip
->all_clipped
)
569 return CAIRO_STATUS_SUCCESS
;
571 /* catch the empty clip path */
572 if (! path
->has_current_point
) {
573 _cairo_clip_set_all_clipped (clip
, target
);
574 return CAIRO_STATUS_SUCCESS
;
577 status
= _cairo_clip_intersect_path (clip
,
578 path
, fill_rule
, tolerance
,
580 if (status
== CAIRO_STATUS_SUCCESS
)
581 clip
->serial
= _cairo_surface_allocate_clip_serial (target
);
583 if (status
!= CAIRO_INT_STATUS_UNSUPPORTED
)
586 _cairo_traps_init (&traps
);
588 /* Limit the traps to the target surface
589 * - so we don't add more traps than needed. */
590 status
= _cairo_surface_get_extents (target
, &rectangle
);
591 if (status
== CAIRO_STATUS_SUCCESS
) {
594 _cairo_box_from_rectangle (&box
, &rectangle
);
595 _cairo_traps_limit (&traps
, &box
);
598 status
= _cairo_path_fixed_fill_to_traps (path
,
605 status
= _cairo_clip_intersect_region (clip
, &traps
, target
);
606 if (status
!= CAIRO_INT_STATUS_UNSUPPORTED
)
609 status
= _cairo_clip_intersect_mask (clip
, &traps
, antialias
, target
);
612 _cairo_traps_fini (&traps
);
618 _cairo_clip_translate (cairo_clip_t
*clip
,
622 if (clip
->all_clipped
)
625 if (clip
->has_region
) {
626 _cairo_region_translate (&clip
->region
,
627 _cairo_fixed_integer_part (tx
),
628 _cairo_fixed_integer_part (ty
));
632 clip
->surface_rect
.x
+= _cairo_fixed_integer_part (tx
);
633 clip
->surface_rect
.y
+= _cairo_fixed_integer_part (ty
);
637 cairo_clip_path_t
*clip_path
= clip
->path
;
638 cairo_matrix_t matrix
;
640 cairo_matrix_init_translate (&matrix
,
641 _cairo_fixed_to_double (tx
),
642 _cairo_fixed_to_double (ty
));
645 _cairo_path_fixed_transform (&clip_path
->path
, &matrix
);
646 clip_path
= clip_path
->prev
;
651 static cairo_status_t
652 _cairo_clip_path_reapply_clip_path (cairo_clip_t
*clip
,
653 cairo_clip_path_t
*clip_path
)
655 cairo_status_t status
;
657 if (clip_path
->prev
) {
658 status
= _cairo_clip_path_reapply_clip_path (clip
, clip_path
->prev
);
659 if (status
&& status
!= CAIRO_INT_STATUS_UNSUPPORTED
)
663 return _cairo_clip_intersect_path (clip
,
665 clip_path
->fill_rule
,
666 clip_path
->tolerance
,
667 clip_path
->antialias
);
671 _cairo_clip_init_deep_copy (cairo_clip_t
*clip
,
673 cairo_surface_t
*target
)
675 cairo_status_t status
;
677 _cairo_clip_init (clip
, target
);
679 if (other
->mode
!= clip
->mode
) {
680 /* We should reapply the original clip path in this case, and let
681 * whatever the right handling is happen */
683 if (other
->has_region
) {
684 status
= _cairo_region_copy (&clip
->region
, &other
->region
);
688 clip
->has_region
= TRUE
;
691 if (other
->surface
) {
693 status
= _cairo_surface_clone_similar (target
, other
->surface
,
696 other
->surface_rect
.width
,
697 other
->surface_rect
.height
,
703 clip
->surface_rect
= other
->surface_rect
;
705 /* src offset was 0, so we expect an exact replica of the surface */
711 status
= _cairo_clip_path_reapply_clip_path (clip
, other
->path
);
712 if (status
&& status
!= CAIRO_INT_STATUS_UNSUPPORTED
)
717 return CAIRO_STATUS_SUCCESS
;
720 if (clip
->has_region
)
721 _cairo_region_fini (&clip
->region
);
723 cairo_surface_destroy (clip
->surface
);
728 const cairo_rectangle_list_t _cairo_rectangles_nil
=
729 { CAIRO_STATUS_NO_MEMORY
, NULL
, 0 };
730 static const cairo_rectangle_list_t _cairo_rectangles_not_representable
=
731 { CAIRO_STATUS_CLIP_NOT_REPRESENTABLE
, NULL
, 0 };
734 _cairo_clip_int_rect_to_user (cairo_gstate_t
*gstate
,
735 cairo_rectangle_int_t
*clip_rect
,
736 cairo_rectangle_t
*user_rect
)
738 cairo_bool_t is_tight
;
740 double x1
= clip_rect
->x
;
741 double y1
= clip_rect
->y
;
742 double x2
= clip_rect
->x
+ (int) clip_rect
->width
;
743 double y2
= clip_rect
->y
+ (int) clip_rect
->height
;
745 _cairo_gstate_backend_to_user_rectangle (gstate
,
751 user_rect
->width
= x2
- x1
;
752 user_rect
->height
= y2
- y1
;
757 cairo_rectangle_list_t
*
758 _cairo_clip_copy_rectangle_list (cairo_clip_t
*clip
, cairo_gstate_t
*gstate
)
760 cairo_rectangle_list_t
*list
;
761 cairo_rectangle_t
*rectangles
= NULL
;
764 if (clip
->all_clipped
)
767 if (clip
->path
|| clip
->surface
) {
768 _cairo_error_throw (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE
);
769 return (cairo_rectangle_list_t
*) &_cairo_rectangles_not_representable
;
772 if (clip
->has_region
) {
773 cairo_box_int_t
*boxes
;
776 if (_cairo_region_get_boxes (&clip
->region
, &n_boxes
, &boxes
))
777 return (cairo_rectangle_list_t
*) &_cairo_rectangles_nil
;
780 rectangles
= _cairo_malloc_ab (n_boxes
, sizeof (cairo_rectangle_t
));
781 if (rectangles
== NULL
) {
782 _cairo_region_boxes_fini (&clip
->region
, boxes
);
783 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY
);
784 return (cairo_rectangle_list_t
*) &_cairo_rectangles_nil
;
787 for (i
= 0; i
< n_boxes
; ++i
) {
788 cairo_rectangle_int_t clip_rect
;
790 clip_rect
.x
= boxes
[i
].p1
.x
;
791 clip_rect
.y
= boxes
[i
].p1
.y
;
792 clip_rect
.width
= boxes
[i
].p2
.x
- boxes
[i
].p1
.x
;
793 clip_rect
.height
= boxes
[i
].p2
.y
- boxes
[i
].p1
.y
;
795 if (!_cairo_clip_int_rect_to_user(gstate
, &clip_rect
, &rectangles
[i
])) {
796 _cairo_error_throw (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE
);
797 _cairo_region_boxes_fini (&clip
->region
, boxes
);
799 return (cairo_rectangle_list_t
*) &_cairo_rectangles_not_representable
;
804 _cairo_region_boxes_fini (&clip
->region
, boxes
);
806 cairo_rectangle_int_t extents
;
810 rectangles
= malloc(sizeof (cairo_rectangle_t
));
811 if (rectangles
== NULL
) {
812 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY
);
813 return (cairo_rectangle_list_t
*) &_cairo_rectangles_nil
;
816 if (_cairo_surface_get_extents (_cairo_gstate_get_target (gstate
), &extents
) ||
817 !_cairo_clip_int_rect_to_user(gstate
, &extents
, rectangles
))
819 _cairo_error_throw (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE
);
821 return (cairo_rectangle_list_t
*) &_cairo_rectangles_not_representable
;
826 list
= malloc (sizeof (cairo_rectangle_list_t
));
828 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY
);
830 return (cairo_rectangle_list_t
*) &_cairo_rectangles_nil
;
833 list
->status
= CAIRO_STATUS_SUCCESS
;
834 list
->rectangles
= rectangles
;
835 list
->num_rectangles
= n_boxes
;
840 * cairo_rectangle_list_destroy:
841 * @rectangle_list: a rectangle list, as obtained from cairo_copy_clip_rectangles()
843 * Unconditionally frees @rectangle_list and all associated
844 * references. After this call, the @rectangle_list pointer must not
850 cairo_rectangle_list_destroy (cairo_rectangle_list_t
*rectangle_list
)
852 if (rectangle_list
== NULL
|| rectangle_list
== &_cairo_rectangles_nil
||
853 rectangle_list
== &_cairo_rectangles_not_representable
)
856 free (rectangle_list
->rectangles
);
857 free (rectangle_list
);