| /* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ |
| /* cairo - a vector graphics library with display and print output |
| * |
| * Copyright © 2002 University of Southern California |
| * Copyright © 2005 Red Hat, Inc. |
| * Copyright © 2009 Chris Wilson |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it either under the terms of the GNU Lesser General Public |
| * License version 2.1 as published by the Free Software Foundation |
| * (the "LGPL") or, at your option, under the terms of the Mozilla |
| * Public License Version 1.1 (the "MPL"). If you do not alter this |
| * notice, a recipient may use your version of this file under either |
| * the MPL or the LGPL. |
| * |
| * You should have received a copy of the LGPL along with this library |
| * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
| * You should have received a copy of the MPL along with this library |
| * in the file COPYING-MPL-1.1 |
| * |
| * The contents of this file are subject to the Mozilla Public License |
| * Version 1.1 (the "License"); you may not use this file except in |
| * compliance with the License. You may obtain a copy of the License at |
| * http://www.mozilla.org/MPL/ |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
| * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
| * the specific language governing rights and limitations. |
| * |
| * The Original Code is the cairo graphics library. |
| * |
| * The Initial Developer of the Original Code is University of Southern |
| * California. |
| * |
| * Contributor(s): |
| * Carl D. Worth <cworth@cworth.org> |
| * Kristian Høgsberg <krh@redhat.com> |
| * Chris Wilson <chris@chris-wilson.co.uk> |
| */ |
| |
| #include "cairoint.h" |
| #include "cairo-clip-inline.h" |
| #include "cairo-clip-private.h" |
| #include "cairo-error-private.h" |
| #include "cairo-freed-pool-private.h" |
| #include "cairo-gstate-private.h" |
| #include "cairo-path-fixed-private.h" |
| #include "cairo-pattern-private.h" |
| #include "cairo-composite-rectangles-private.h" |
| #include "cairo-region-private.h" |
| |
| static freed_pool_t clip_path_pool; |
| static freed_pool_t clip_pool; |
| |
| const cairo_clip_t __cairo_clip_all; |
| |
| static cairo_clip_path_t * |
| _cairo_clip_path_create (cairo_clip_t *clip) |
| { |
| cairo_clip_path_t *clip_path; |
| |
| clip_path = _freed_pool_get (&clip_path_pool); |
| if (unlikely (clip_path == NULL)) { |
| clip_path = malloc (sizeof (cairo_clip_path_t)); |
| if (unlikely (clip_path == NULL)) |
| return NULL; |
| } |
| |
| CAIRO_REFERENCE_COUNT_INIT (&clip_path->ref_count, 1); |
| |
| clip_path->prev = clip->path; |
| clip->path = clip_path; |
| |
| return clip_path; |
| } |
| |
| cairo_clip_path_t * |
| _cairo_clip_path_reference (cairo_clip_path_t *clip_path) |
| { |
| assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count)); |
| |
| _cairo_reference_count_inc (&clip_path->ref_count); |
| |
| return clip_path; |
| } |
| |
| void |
| _cairo_clip_path_destroy (cairo_clip_path_t *clip_path) |
| { |
| assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count)); |
| |
| if (! _cairo_reference_count_dec_and_test (&clip_path->ref_count)) |
| return; |
| |
| _cairo_path_fixed_fini (&clip_path->path); |
| |
| if (clip_path->prev != NULL) |
| _cairo_clip_path_destroy (clip_path->prev); |
| |
| _freed_pool_put (&clip_path_pool, clip_path); |
| } |
| |
| cairo_clip_t * |
| _cairo_clip_create (void) |
| { |
| cairo_clip_t *clip; |
| |
| clip = _freed_pool_get (&clip_pool); |
| if (unlikely (clip == NULL)) { |
| clip = malloc (sizeof (cairo_clip_t)); |
| if (unlikely (clip == NULL)) |
| return NULL; |
| } |
| |
| clip->extents = _cairo_unbounded_rectangle; |
| |
| clip->path = NULL; |
| clip->boxes = NULL; |
| clip->num_boxes = 0; |
| clip->region = NULL; |
| clip->is_region = FALSE; |
| |
| return clip; |
| } |
| |
| void |
| _cairo_clip_destroy (cairo_clip_t *clip) |
| { |
| if (clip == NULL || _cairo_clip_is_all_clipped (clip)) |
| return; |
| |
| if (clip->path != NULL) |
| _cairo_clip_path_destroy (clip->path); |
| |
| if (clip->boxes != &clip->embedded_box) |
| free (clip->boxes); |
| cairo_region_destroy (clip->region); |
| |
| _freed_pool_put (&clip_pool, clip); |
| } |
| |
| cairo_clip_t * |
| _cairo_clip_copy (const cairo_clip_t *clip) |
| { |
| cairo_clip_t *copy; |
| |
| if (clip == NULL || _cairo_clip_is_all_clipped (clip)) |
| return (cairo_clip_t *) clip; |
| |
| copy = _cairo_clip_create (); |
| |
| if (clip->path) |
| copy->path = _cairo_clip_path_reference (clip->path); |
| |
| if (clip->num_boxes) { |
| if (clip->num_boxes == 1) { |
| copy->boxes = ©->embedded_box; |
| } else { |
| copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t)); |
| if (unlikely (copy->boxes == NULL)) |
| return _cairo_clip_set_all_clipped (copy); |
| } |
| |
| memcpy (copy->boxes, clip->boxes, |
| clip->num_boxes * sizeof (cairo_box_t)); |
| copy->num_boxes = clip->num_boxes; |
| } |
| |
| copy->extents = clip->extents; |
| copy->region = cairo_region_reference (clip->region); |
| copy->is_region = clip->is_region; |
| |
| return copy; |
| } |
| |
| cairo_clip_t * |
| _cairo_clip_copy_path (const cairo_clip_t *clip) |
| { |
| cairo_clip_t *copy; |
| |
| if (clip == NULL || _cairo_clip_is_all_clipped (clip)) |
| return (cairo_clip_t *) clip; |
| |
| assert (clip->num_boxes); |
| |
| copy = _cairo_clip_create (); |
| copy->extents = clip->extents; |
| if (clip->path) |
| copy->path = _cairo_clip_path_reference (clip->path); |
| |
| return copy; |
| } |
| |
| cairo_clip_t * |
| _cairo_clip_copy_region (const cairo_clip_t *clip) |
| { |
| cairo_clip_t *copy; |
| int i; |
| |
| if (clip == NULL || _cairo_clip_is_all_clipped (clip)) |
| return (cairo_clip_t *) clip; |
| |
| assert (clip->num_boxes); |
| |
| copy = _cairo_clip_create (); |
| copy->extents = clip->extents; |
| |
| if (clip->num_boxes == 1) { |
| copy->boxes = ©->embedded_box; |
| } else { |
| copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t)); |
| if (unlikely (copy->boxes == NULL)) |
| return _cairo_clip_set_all_clipped (copy); |
| } |
| |
| for (i = 0; i < clip->num_boxes; i++) { |
| copy->boxes[i].p1.x = _cairo_fixed_floor (clip->boxes[i].p1.x); |
| copy->boxes[i].p1.y = _cairo_fixed_floor (clip->boxes[i].p1.y); |
| copy->boxes[i].p2.x = _cairo_fixed_ceil (clip->boxes[i].p2.x); |
| copy->boxes[i].p2.y = _cairo_fixed_ceil (clip->boxes[i].p2.y); |
| } |
| copy->num_boxes = clip->num_boxes; |
| |
| copy->region = cairo_region_reference (clip->region); |
| copy->is_region = TRUE; |
| |
| return copy; |
| } |
| |
| 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) |
| { |
| cairo_clip_path_t *clip_path; |
| cairo_status_t status; |
| cairo_rectangle_int_t extents; |
| cairo_box_t box; |
| |
| if (_cairo_clip_is_all_clipped (clip)) |
| return clip; |
| |
| /* catch the empty clip path */ |
| if (_cairo_path_fixed_fill_is_empty (path)) |
| return _cairo_clip_set_all_clipped (clip); |
| |
| if (_cairo_path_fixed_is_box (path, &box)) { |
| if (antialias == CAIRO_ANTIALIAS_NONE) { |
| box.p1.x = _cairo_fixed_round_down (box.p1.x); |
| box.p1.y = _cairo_fixed_round_down (box.p1.y); |
| box.p2.x = _cairo_fixed_round_down (box.p2.x); |
| box.p2.y = _cairo_fixed_round_down (box.p2.y); |
| } |
| |
| return _cairo_clip_intersect_box (clip, &box); |
| } |
| if (_cairo_path_fixed_fill_is_rectilinear (path)) |
| return _cairo_clip_intersect_rectilinear_path (clip, path, |
| fill_rule, antialias); |
| |
| _cairo_path_fixed_approximate_clip_extents (path, &extents); |
| if (extents.width == 0 || extents.height == 0) |
| return _cairo_clip_set_all_clipped (clip); |
| |
| clip = _cairo_clip_intersect_rectangle (clip, &extents); |
| if (_cairo_clip_is_all_clipped (clip)) |
| return clip; |
| |
| clip_path = _cairo_clip_path_create (clip); |
| if (unlikely (clip_path == NULL)) |
| return _cairo_clip_set_all_clipped (clip); |
| |
| status = _cairo_path_fixed_init_copy (&clip_path->path, path); |
| if (unlikely (status)) |
| return _cairo_clip_set_all_clipped (clip); |
| |
| clip_path->fill_rule = fill_rule; |
| clip_path->tolerance = tolerance; |
| clip_path->antialias = antialias; |
| |
| if (clip->region) { |
| cairo_region_destroy (clip->region); |
| clip->region = NULL; |
| } |
| |
| clip->is_region = FALSE; |
| return clip; |
| } |
| |
| static cairo_clip_t * |
| _cairo_clip_intersect_clip_path (cairo_clip_t *clip, |
| const cairo_clip_path_t *clip_path) |
| { |
| if (clip_path->prev) |
| clip = _cairo_clip_intersect_clip_path (clip, clip_path->prev); |
| |
| return _cairo_clip_intersect_path (clip, |
| &clip_path->path, |
| clip_path->fill_rule, |
| clip_path->tolerance, |
| clip_path->antialias); |
| } |
| |
| cairo_clip_t * |
| _cairo_clip_intersect_clip (cairo_clip_t *clip, |
| const cairo_clip_t *other) |
| { |
| if (_cairo_clip_is_all_clipped (clip)) |
| return clip; |
| |
| if (other == NULL) |
| return clip; |
| |
| if (clip == NULL) |
| return _cairo_clip_copy (other); |
| |
| if (_cairo_clip_is_all_clipped (other)) |
| return _cairo_clip_set_all_clipped (clip); |
| |
| if (! _cairo_rectangle_intersect (&clip->extents, &other->extents)) |
| return _cairo_clip_set_all_clipped (clip); |
| |
| if (other->num_boxes) { |
| cairo_boxes_t boxes; |
| |
| _cairo_boxes_init_for_array (&boxes, other->boxes, other->num_boxes); |
| clip = _cairo_clip_intersect_boxes (clip, &boxes); |
| } |
| |
| if (! _cairo_clip_is_all_clipped (clip)) { |
| if (other->path) { |
| if (clip->path == NULL) |
| clip->path = _cairo_clip_path_reference (other->path); |
| else |
| clip = _cairo_clip_intersect_clip_path (clip, other->path); |
| } |
| } |
| |
| if (clip->region) { |
| cairo_region_destroy (clip->region); |
| clip->region = NULL; |
| } |
| clip->is_region = FALSE; |
| |
| return clip; |
| } |
| |
| cairo_bool_t |
| _cairo_clip_equal (const cairo_clip_t *clip_a, |
| const cairo_clip_t *clip_b) |
| { |
| const cairo_clip_path_t *cp_a, *cp_b; |
| |
| /* are both all-clipped or no-clip? */ |
| if (clip_a == clip_b) |
| return TRUE; |
| |
| /* or just one of them? */ |
| if (clip_a == NULL || clip_b == NULL || |
| _cairo_clip_is_all_clipped (clip_a) || |
| _cairo_clip_is_all_clipped (clip_b)) |
| { |
| return FALSE; |
| } |
| |
| /* We have a pair of normal clips, check their contents */ |
| |
| if (clip_a->num_boxes != clip_b->num_boxes) |
| return FALSE; |
| |
| if (memcmp (clip_a->boxes, clip_b->boxes, |
| sizeof (cairo_box_t) * clip_a->num_boxes)) |
| return FALSE; |
| |
| cp_a = clip_a->path; |
| cp_b = clip_b->path; |
| while (cp_a && cp_b) { |
| if (cp_a == cp_b) |
| return TRUE; |
| |
| /* XXX compare reduced polygons? */ |
| |
| if (cp_a->antialias != cp_b->antialias) |
| return FALSE; |
| |
| if (cp_a->tolerance != cp_b->tolerance) |
| return FALSE; |
| |
| if (cp_a->fill_rule != cp_b->fill_rule) |
| return FALSE; |
| |
| if (! _cairo_path_fixed_equal (&cp_a->path, |
| &cp_b->path)) |
| return FALSE; |
| |
| cp_a = cp_a->prev; |
| cp_b = cp_b->prev; |
| } |
| |
| return cp_a == NULL && cp_b == NULL; |
| } |
| |
| static cairo_clip_t * |
| _cairo_clip_path_copy_with_translation (cairo_clip_t *clip, |
| cairo_clip_path_t *other_path, |
| int fx, int fy) |
| { |
| cairo_status_t status; |
| cairo_clip_path_t *clip_path; |
| |
| if (other_path->prev != NULL) |
| clip = _cairo_clip_path_copy_with_translation (clip, other_path->prev, |
| fx, fy); |
| if (_cairo_clip_is_all_clipped (clip)) |
| return clip; |
| |
| clip_path = _cairo_clip_path_create (clip); |
| if (unlikely (clip_path == NULL)) |
| return _cairo_clip_set_all_clipped (clip); |
| |
| status = _cairo_path_fixed_init_copy (&clip_path->path, |
| &other_path->path); |
| if (unlikely (status)) |
| return _cairo_clip_set_all_clipped (clip); |
| |
| _cairo_path_fixed_translate (&clip_path->path, fx, fy); |
| |
| clip_path->fill_rule = other_path->fill_rule; |
| clip_path->tolerance = other_path->tolerance; |
| clip_path->antialias = other_path->antialias; |
| |
| return clip; |
| } |
| |
| cairo_clip_t * |
| _cairo_clip_translate (cairo_clip_t *clip, int tx, int ty) |
| { |
| int fx, fy, i; |
| cairo_clip_path_t *clip_path; |
| |
| if (clip == NULL || _cairo_clip_is_all_clipped (clip)) |
| return clip; |
| |
| if (tx == 0 && ty == 0) |
| return clip; |
| |
| fx = _cairo_fixed_from_int (tx); |
| fy = _cairo_fixed_from_int (ty); |
| |
| for (i = 0; i < clip->num_boxes; i++) { |
| clip->boxes[i].p1.x += fx; |
| clip->boxes[i].p2.x += fx; |
| clip->boxes[i].p1.y += fy; |
| clip->boxes[i].p2.y += fy; |
| } |
| |
| clip->extents.x += tx; |
| clip->extents.y += ty; |
| |
| if (clip->path == NULL) |
| return clip; |
| |
| clip_path = clip->path; |
| clip->path = NULL; |
| clip = _cairo_clip_path_copy_with_translation (clip, clip_path, fx, fy); |
| _cairo_clip_path_destroy (clip_path); |
| |
| return clip; |
| } |
| |
| static cairo_status_t |
| _cairo_path_fixed_add_box (cairo_path_fixed_t *path, |
| const cairo_box_t *box) |
| { |
| cairo_status_t status; |
| |
| status = _cairo_path_fixed_move_to (path, box->p1.x, box->p1.y); |
| if (unlikely (status)) |
| return status; |
| |
| status = _cairo_path_fixed_line_to (path, box->p2.x, box->p1.y); |
| if (unlikely (status)) |
| return status; |
| |
| status = _cairo_path_fixed_line_to (path, box->p2.x, box->p2.y); |
| if (unlikely (status)) |
| return status; |
| |
| status = _cairo_path_fixed_line_to (path, box->p1.x, box->p2.y); |
| if (unlikely (status)) |
| return status; |
| |
| return _cairo_path_fixed_close_path (path); |
| } |
| |
| static cairo_status_t |
| _cairo_path_fixed_init_from_boxes (cairo_path_fixed_t *path, |
| const cairo_boxes_t *boxes) |
| { |
| cairo_status_t status; |
| const struct _cairo_boxes_chunk *chunk; |
| int i; |
| |
| _cairo_path_fixed_init (path); |
| if (boxes->num_boxes == 0) |
| return CAIRO_STATUS_SUCCESS; |
| |
| for (chunk = &boxes->chunks; chunk; chunk = chunk->next) { |
| for (i = 0; i < chunk->count; i++) { |
| status = _cairo_path_fixed_add_box (path, &chunk->base[i]); |
| if (unlikely (status)) { |
| _cairo_path_fixed_fini (path); |
| return status; |
| } |
| } |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| 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) |
| { |
| cairo_path_fixed_t path; |
| |
| if (clip_path->prev) |
| clip = _cairo_clip_intersect_clip_path_transformed (clip, |
| clip_path->prev, |
| m); |
| |
| if (_cairo_path_fixed_init_copy (&path, &clip_path->path)) |
| return _cairo_clip_set_all_clipped (clip); |
| |
| _cairo_path_fixed_transform (&path, m); |
| |
| clip = _cairo_clip_intersect_path (clip, |
| &path, |
| clip_path->fill_rule, |
| clip_path->tolerance, |
| clip_path->antialias); |
| _cairo_path_fixed_fini (&path); |
| |
| return clip; |
| } |
| |
| cairo_clip_t * |
| _cairo_clip_transform (cairo_clip_t *clip, const cairo_matrix_t *m) |
| { |
| cairo_clip_t *copy; |
| |
| if (clip == NULL || _cairo_clip_is_all_clipped (clip)) |
| return clip; |
| |
| if (_cairo_matrix_is_translation (m)) |
| return _cairo_clip_translate (clip, m->x0, m->y0); |
| |
| copy = _cairo_clip_create (); |
| |
| if (clip->num_boxes) { |
| cairo_path_fixed_t path; |
| cairo_boxes_t boxes; |
| |
| _cairo_boxes_init_for_array (&boxes, clip->boxes, clip->num_boxes); |
| _cairo_path_fixed_init_from_boxes (&path, &boxes); |
| _cairo_path_fixed_transform (&path, m); |
| |
| copy = _cairo_clip_intersect_path (copy, &path, |
| CAIRO_FILL_RULE_WINDING, |
| 0.1, |
| CAIRO_ANTIALIAS_DEFAULT); |
| |
| _cairo_path_fixed_fini (&path); |
| } |
| |
| if (clip->path) |
| copy = _cairo_clip_intersect_clip_path_transformed (copy, clip->path,m); |
| |
| _cairo_clip_destroy (clip); |
| return copy; |
| } |
| |
| cairo_clip_t * |
| _cairo_clip_copy_with_translation (const cairo_clip_t *clip, int tx, int ty) |
| { |
| cairo_clip_t *copy; |
| int fx, fy, i; |
| |
| if (clip == NULL || _cairo_clip_is_all_clipped (clip)) |
| return (cairo_clip_t *)clip; |
| |
| if (tx == 0 && ty == 0) |
| return _cairo_clip_copy (clip); |
| |
| copy = _cairo_clip_create (); |
| if (copy == NULL) |
| return _cairo_clip_set_all_clipped (copy); |
| |
| fx = _cairo_fixed_from_int (tx); |
| fy = _cairo_fixed_from_int (ty); |
| |
| if (clip->num_boxes) { |
| if (clip->num_boxes == 1) { |
| copy->boxes = ©->embedded_box; |
| } else { |
| copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t)); |
| if (unlikely (copy->boxes == NULL)) |
| return _cairo_clip_set_all_clipped (copy); |
| } |
| |
| for (i = 0; i < clip->num_boxes; i++) { |
| copy->boxes[i].p1.x = clip->boxes[i].p1.x + fx; |
| copy->boxes[i].p2.x = clip->boxes[i].p2.x + fx; |
| copy->boxes[i].p1.y = clip->boxes[i].p1.y + fy; |
| copy->boxes[i].p2.y = clip->boxes[i].p2.y + fy; |
| } |
| copy->num_boxes = clip->num_boxes; |
| } |
| |
| copy->extents = clip->extents; |
| copy->extents.x += tx; |
| copy->extents.y += ty; |
| |
| if (clip->path == NULL) |
| return copy; |
| |
| return _cairo_clip_path_copy_with_translation (copy, clip->path, fx, fy); |
| } |
| |
| cairo_bool_t |
| _cairo_clip_contains_extents (const cairo_clip_t *clip, |
| const cairo_composite_rectangles_t *extents) |
| { |
| const cairo_rectangle_int_t *rect; |
| |
| rect = extents->is_bounded ? &extents->bounded : &extents->unbounded; |
| return _cairo_clip_contains_rectangle (clip, rect); |
| } |
| |
| void |
| _cairo_debug_print_clip (FILE *stream, const cairo_clip_t *clip) |
| { |
| int i; |
| |
| if (clip == NULL) { |
| fprintf (stream, "no clip\n"); |
| return; |
| } |
| |
| if (_cairo_clip_is_all_clipped (clip)) { |
| fprintf (stream, "clip: all-clipped\n"); |
| return; |
| } |
| |
| fprintf (stream, "clip:\n"); |
| fprintf (stream, " extents: (%d, %d) x (%d, %d), is-region? %d", |
| clip->extents.x, clip->extents.y, |
| clip->extents.width, clip->extents.height, |
| clip->is_region); |
| |
| fprintf (stream, " num_boxes = %d\n", clip->num_boxes); |
| for (i = 0; i < clip->num_boxes; i++) { |
| fprintf (stream, " [%d] = (%f, %f), (%f, %f)\n", i, |
| _cairo_fixed_to_double (clip->boxes[i].p1.x), |
| _cairo_fixed_to_double (clip->boxes[i].p1.y), |
| _cairo_fixed_to_double (clip->boxes[i].p2.x), |
| _cairo_fixed_to_double (clip->boxes[i].p2.y)); |
| } |
| |
| if (clip->path) { |
| cairo_clip_path_t *clip_path = clip->path; |
| do { |
| fprintf (stream, "path: aa=%d, tolerance=%f, rule=%d: ", |
| clip_path->antialias, |
| clip_path->tolerance, |
| clip_path->fill_rule); |
| _cairo_debug_print_path (stream, &clip_path->path); |
| fprintf (stream, "\n"); |
| } while ((clip_path = clip_path->prev) != NULL); |
| } |
| } |
| |
| const cairo_rectangle_int_t * |
| _cairo_clip_get_extents (const cairo_clip_t *clip) |
| { |
| if (clip == NULL) |
| return &_cairo_unbounded_rectangle; |
| |
| if (_cairo_clip_is_all_clipped (clip)) |
| return &_cairo_empty_rectangle; |
| |
| return &clip->extents; |
| } |
| |
| const cairo_rectangle_list_t _cairo_rectangles_nil = |
| { CAIRO_STATUS_NO_MEMORY, NULL, 0 }; |
| static const cairo_rectangle_list_t _cairo_rectangles_not_representable = |
| { CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, NULL, 0 }; |
| |
| 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) |
| { |
| cairo_bool_t is_tight; |
| |
| double x1 = clip_rect->x; |
| double y1 = clip_rect->y; |
| double x2 = clip_rect->x + (int) clip_rect->width; |
| double y2 = clip_rect->y + (int) clip_rect->height; |
| |
| _cairo_gstate_backend_to_user_rectangle (gstate, |
| &x1, &y1, &x2, &y2, |
| &is_tight); |
| |
| user_rect->x = x1; |
| user_rect->y = y1; |
| user_rect->width = x2 - x1; |
| user_rect->height = y2 - y1; |
| |
| return is_tight; |
| } |
| |
| cairo_rectangle_list_t * |
| _cairo_rectangle_list_create_in_error (cairo_status_t status) |
| { |
| cairo_rectangle_list_t *list; |
| |
| if (status == CAIRO_STATUS_NO_MEMORY) |
| return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; |
| if (status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE) |
| return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable; |
| |
| list = malloc (sizeof (*list)); |
| if (unlikely (list == NULL)) { |
| status = _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; |
| } |
| |
| list->status = status; |
| list->rectangles = NULL; |
| list->num_rectangles = 0; |
| |
| return list; |
| } |
| |
| cairo_rectangle_list_t * |
| _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate) |
| { |
| #define ERROR_LIST(S) _cairo_rectangle_list_create_in_error (_cairo_error (S)) |
| |
| cairo_rectangle_list_t *list; |
| cairo_rectangle_t *rectangles = NULL; |
| cairo_region_t *region = NULL; |
| int n_rects = 0; |
| int i; |
| |
| if (clip == NULL) |
| return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); |
| |
| if (_cairo_clip_is_all_clipped (clip)) |
| goto DONE; |
| |
| if (! _cairo_clip_is_region (clip)) |
| return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); |
| |
| region = _cairo_clip_get_region (clip); |
| if (region == NULL) |
| return ERROR_LIST (CAIRO_STATUS_NO_MEMORY); |
| |
| n_rects = cairo_region_num_rectangles (region); |
| if (n_rects) { |
| rectangles = _cairo_malloc_ab (n_rects, sizeof (cairo_rectangle_t)); |
| if (unlikely (rectangles == NULL)) { |
| return ERROR_LIST (CAIRO_STATUS_NO_MEMORY); |
| } |
| |
| for (i = 0; i < n_rects; ++i) { |
| cairo_rectangle_int_t clip_rect; |
| |
| cairo_region_get_rectangle (region, i, &clip_rect); |
| |
| if (! _cairo_clip_int_rect_to_user (gstate, |
| &clip_rect, |
| &rectangles[i])) |
| { |
| free (rectangles); |
| return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); |
| } |
| } |
| } |
| |
| DONE: |
| list = malloc (sizeof (cairo_rectangle_list_t)); |
| if (unlikely (list == NULL)) { |
| free (rectangles); |
| return ERROR_LIST (CAIRO_STATUS_NO_MEMORY); |
| } |
| |
| list->status = CAIRO_STATUS_SUCCESS; |
| list->rectangles = rectangles; |
| list->num_rectangles = n_rects; |
| return list; |
| |
| #undef ERROR_LIST |
| } |
| |
| /** |
| * cairo_rectangle_list_destroy: |
| * @rectangle_list: a rectangle list, as obtained from cairo_copy_clip_rectangle_list() |
| * |
| * Unconditionally frees @rectangle_list and all associated |
| * references. After this call, the @rectangle_list pointer must not |
| * be dereferenced. |
| * |
| * Since: 1.4 |
| **/ |
| void |
| cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list) |
| { |
| if (rectangle_list == NULL || rectangle_list == &_cairo_rectangles_nil || |
| rectangle_list == &_cairo_rectangles_not_representable) |
| return; |
| |
| free (rectangle_list->rectangles); |
| free (rectangle_list); |
| } |
| |
| void |
| _cairo_clip_reset_static_data (void) |
| { |
| _freed_pool_reset (&clip_path_pool); |
| _freed_pool_reset (&clip_pool); |
| } |