Line data Source code
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-private.h"
44 : #include "cairo-error-private.h"
45 : #include "cairo-freed-pool-private.h"
46 : #include "cairo-gstate-private.h"
47 : #include "cairo-path-fixed-private.h"
48 : #include "cairo-composite-rectangles-private.h"
49 : #include "cairo-region-private.h"
50 :
51 : #if HAS_FREED_POOL
52 : static freed_pool_t clip_path_pool;
53 : #endif
54 :
55 : static cairo_clip_path_t *
56 0 : _cairo_clip_path_create (cairo_clip_t *clip)
57 : {
58 : cairo_clip_path_t *clip_path;
59 :
60 0 : clip_path = _freed_pool_get (&clip_path_pool);
61 0 : if (unlikely (clip_path == NULL)) {
62 0 : clip_path = malloc (sizeof (cairo_clip_path_t));
63 0 : if (unlikely (clip_path == NULL))
64 0 : return NULL;
65 : }
66 :
67 0 : CAIRO_REFERENCE_COUNT_INIT (&clip_path->ref_count, 1);
68 :
69 0 : clip_path->flags = 0;
70 0 : clip_path->region = NULL;
71 0 : clip_path->surface = NULL;
72 :
73 0 : clip_path->prev = clip->path;
74 0 : clip->path = clip_path;
75 :
76 0 : return clip_path;
77 : }
78 :
79 : static cairo_clip_path_t *
80 0 : _cairo_clip_path_reference (cairo_clip_path_t *clip_path)
81 : {
82 0 : assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
83 :
84 0 : _cairo_reference_count_inc (&clip_path->ref_count);
85 :
86 0 : return clip_path;
87 : }
88 :
89 : static void
90 0 : _cairo_clip_path_destroy (cairo_clip_path_t *clip_path)
91 : {
92 0 : assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
93 :
94 0 : if (! _cairo_reference_count_dec_and_test (&clip_path->ref_count))
95 0 : return;
96 :
97 0 : _cairo_path_fixed_fini (&clip_path->path);
98 0 : if (clip_path->region != NULL)
99 0 : cairo_region_destroy (clip_path->region);
100 0 : if (clip_path->surface != NULL)
101 0 : cairo_surface_destroy (clip_path->surface);
102 :
103 0 : if (clip_path->prev != NULL)
104 0 : _cairo_clip_path_destroy (clip_path->prev);
105 :
106 0 : _freed_pool_put (&clip_path_pool, clip_path);
107 : }
108 :
109 : void
110 2 : _cairo_clip_init (cairo_clip_t *clip)
111 : {
112 2 : clip->all_clipped = FALSE;
113 2 : clip->path = NULL;
114 2 : }
115 :
116 : static void
117 0 : _cairo_clip_set_all_clipped (cairo_clip_t *clip)
118 : {
119 0 : clip->all_clipped = TRUE;
120 0 : if (clip->path != NULL) {
121 0 : _cairo_clip_path_destroy (clip->path);
122 0 : clip->path = NULL;
123 : }
124 0 : }
125 :
126 : static cairo_status_t
127 0 : _cairo_clip_intersect_rectangle (cairo_clip_t *clip,
128 : const cairo_rectangle_int_t *rect)
129 : {
130 : cairo_clip_path_t *clip_path;
131 : cairo_status_t status;
132 :
133 0 : if (clip->path != NULL) {
134 0 : if (rect->x <= clip->path->extents.x &&
135 0 : rect->y <= clip->path->extents.y &&
136 0 : rect->x + rect->width >= clip->path->extents.x + clip->path->extents.width &&
137 0 : rect->y + rect->height >= clip->path->extents.y + clip->path->extents.height)
138 : {
139 0 : return CAIRO_STATUS_SUCCESS;
140 : }
141 : }
142 :
143 0 : clip_path = _cairo_clip_path_create (clip);
144 0 : if (unlikely (clip_path == NULL))
145 0 : return _cairo_error (CAIRO_STATUS_NO_MEMORY);
146 :
147 0 : _cairo_path_fixed_init (&clip_path->path);
148 :
149 0 : status = _cairo_path_fixed_move_to (&clip_path->path,
150 : _cairo_fixed_from_int (rect->x),
151 : _cairo_fixed_from_int (rect->y));
152 0 : assert (status == CAIRO_STATUS_SUCCESS);
153 0 : status = _cairo_path_fixed_rel_line_to (&clip_path->path,
154 : _cairo_fixed_from_int (rect->width),
155 : _cairo_fixed_from_int (0));
156 0 : assert (status == CAIRO_STATUS_SUCCESS);
157 0 : status = _cairo_path_fixed_rel_line_to (&clip_path->path,
158 : _cairo_fixed_from_int (0),
159 : _cairo_fixed_from_int (rect->height));
160 0 : assert (status == CAIRO_STATUS_SUCCESS);
161 0 : status = _cairo_path_fixed_rel_line_to (&clip_path->path,
162 0 : _cairo_fixed_from_int (-rect->width),
163 : _cairo_fixed_from_int (0));
164 0 : assert (status == CAIRO_STATUS_SUCCESS);
165 0 : status = _cairo_path_fixed_close_path (&clip_path->path);
166 0 : assert (status == CAIRO_STATUS_SUCCESS);
167 :
168 0 : clip_path->fill_rule = CAIRO_FILL_RULE_WINDING;
169 0 : clip_path->tolerance = 1;
170 0 : clip_path->antialias = CAIRO_ANTIALIAS_DEFAULT;
171 0 : clip_path->flags |= CAIRO_CLIP_PATH_IS_BOX;
172 :
173 0 : clip_path->extents = *rect;
174 0 : if (clip_path->prev != NULL) {
175 0 : if (! _cairo_rectangle_intersect (&clip_path->extents,
176 0 : &clip_path->prev->extents))
177 : {
178 0 : _cairo_clip_set_all_clipped (clip);
179 : }
180 : }
181 :
182 : /* could preallocate the region if it proves worthwhile */
183 :
184 0 : return CAIRO_STATUS_SUCCESS;
185 : }
186 :
187 : cairo_clip_t *
188 0 : _cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other)
189 : {
190 0 : if (other != NULL) {
191 0 : clip->all_clipped = other->all_clipped;
192 0 : if (other->path == NULL) {
193 0 : clip->path = NULL;
194 0 : if (! clip->all_clipped)
195 0 : clip = NULL;
196 : } else {
197 0 : clip->path = _cairo_clip_path_reference (other->path);
198 : }
199 : } else {
200 0 : _cairo_clip_init (clip);
201 0 : clip = NULL;
202 : }
203 :
204 0 : return clip;
205 : }
206 :
207 : void
208 0 : _cairo_clip_reset (cairo_clip_t *clip)
209 : {
210 0 : clip->all_clipped = FALSE;
211 0 : if (clip->path != NULL) {
212 0 : _cairo_clip_path_destroy (clip->path);
213 0 : clip->path = NULL;
214 : }
215 0 : }
216 :
217 : static cairo_status_t
218 0 : _cairo_clip_intersect_path (cairo_clip_t *clip,
219 : const cairo_path_fixed_t *path,
220 : cairo_fill_rule_t fill_rule,
221 : double tolerance,
222 : cairo_antialias_t antialias)
223 : {
224 : cairo_clip_path_t *clip_path;
225 : cairo_status_t status;
226 : cairo_rectangle_int_t extents;
227 : cairo_box_t box;
228 0 : cairo_bool_t is_box = FALSE;
229 :
230 0 : if (clip->path != NULL) {
231 0 : if (clip->path->fill_rule == fill_rule &&
232 0 : (path->is_rectilinear || tolerance == clip->path->tolerance) &&
233 0 : antialias == clip->path->antialias &&
234 0 : _cairo_path_fixed_equal (&clip->path->path, path))
235 : {
236 0 : return CAIRO_STATUS_SUCCESS;
237 : }
238 : }
239 :
240 0 : _cairo_path_fixed_approximate_clip_extents (path, &extents);
241 0 : if (extents.width == 0 || extents.height == 0) {
242 0 : _cairo_clip_set_all_clipped (clip);
243 0 : return CAIRO_STATUS_SUCCESS;
244 : }
245 :
246 0 : is_box = _cairo_path_fixed_is_box (path, &box);
247 0 : if (clip->path != NULL) {
248 0 : if (! _cairo_rectangle_intersect (&extents, &clip->path->extents)) {
249 0 : _cairo_clip_set_all_clipped (clip);
250 0 : return CAIRO_STATUS_SUCCESS;
251 : }
252 :
253 : /* does this clip wholly subsume the others? */
254 0 : if (is_box &&
255 0 : box.p1.x <= _cairo_fixed_from_int (clip->path->extents.x) &&
256 0 : box.p2.x >= _cairo_fixed_from_int (clip->path->extents.x + clip->path->extents.width) &&
257 0 : box.p1.y <= _cairo_fixed_from_int (clip->path->extents.y) &&
258 0 : box.p2.y >= _cairo_fixed_from_int (clip->path->extents.y + clip->path->extents.height))
259 : {
260 0 : return CAIRO_STATUS_SUCCESS;
261 : }
262 : }
263 :
264 0 : clip_path = _cairo_clip_path_create (clip);
265 0 : if (unlikely (clip_path == NULL))
266 0 : return _cairo_error (CAIRO_STATUS_NO_MEMORY);
267 :
268 0 : status = _cairo_path_fixed_init_copy (&clip_path->path, path);
269 0 : if (unlikely (status)) {
270 0 : clip->path = clip->path->prev;
271 0 : _cairo_clip_path_destroy (clip_path);
272 0 : return status;
273 : }
274 :
275 0 : clip_path->extents = extents;
276 0 : clip_path->fill_rule = fill_rule;
277 0 : clip_path->tolerance = tolerance;
278 0 : clip_path->antialias = antialias;
279 0 : if (is_box)
280 0 : clip_path->flags |= CAIRO_CLIP_PATH_IS_BOX;
281 :
282 0 : return CAIRO_STATUS_SUCCESS;
283 : }
284 :
285 : cairo_bool_t
286 0 : _cairo_clip_equal (const cairo_clip_t *clip_a,
287 : const cairo_clip_t *clip_b)
288 : {
289 : const cairo_clip_path_t *clip_path_a, *clip_path_b;
290 :
291 0 : clip_path_a = clip_a->path;
292 0 : clip_path_b = clip_b->path;
293 :
294 0 : while (clip_path_a && clip_path_b) {
295 0 : if (clip_path_a == clip_path_b)
296 0 : return TRUE;
297 :
298 0 : if (clip_path_a->fill_rule != clip_path_b->fill_rule)
299 0 : return FALSE;
300 :
301 0 : if (clip_path_a->tolerance != clip_path_b->tolerance)
302 0 : return FALSE;
303 :
304 0 : if (clip_path_a->antialias != clip_path_b->antialias)
305 0 : return FALSE;
306 :
307 0 : if (! _cairo_path_fixed_equal (&clip_path_a->path, &clip_path_b->path))
308 0 : return FALSE;
309 :
310 0 : clip_path_a = clip_path_a->prev;
311 0 : clip_path_b = clip_path_b->prev;
312 : }
313 :
314 0 : return clip_path_a == clip_path_b; /* ie both NULL */
315 : }
316 :
317 : cairo_status_t
318 0 : _cairo_clip_clip (cairo_clip_t *clip,
319 : const cairo_path_fixed_t *path,
320 : cairo_fill_rule_t fill_rule,
321 : double tolerance,
322 : cairo_antialias_t antialias)
323 : {
324 0 : if (clip->all_clipped)
325 0 : return CAIRO_STATUS_SUCCESS;
326 :
327 : /* catch the empty clip path */
328 0 : if (_cairo_path_fixed_fill_is_empty (path)) {
329 0 : _cairo_clip_set_all_clipped (clip);
330 0 : return CAIRO_STATUS_SUCCESS;
331 : }
332 :
333 0 : return _cairo_clip_intersect_path (clip,
334 : path, fill_rule, tolerance,
335 : antialias);
336 : }
337 :
338 : cairo_status_t
339 0 : _cairo_clip_rectangle (cairo_clip_t *clip,
340 : const cairo_rectangle_int_t *rectangle)
341 : {
342 0 : if (clip->all_clipped)
343 0 : return CAIRO_STATUS_SUCCESS;
344 :
345 0 : if (rectangle->width == 0 || rectangle->height == 0) {
346 0 : _cairo_clip_set_all_clipped (clip);
347 0 : return CAIRO_STATUS_SUCCESS;
348 : }
349 :
350 : /* if a smaller clip has already been set, ignore the new path */
351 0 : if (clip->path != NULL) {
352 0 : if (rectangle->x <= clip->path->extents.x &&
353 0 : rectangle->y <= clip->path->extents.y &&
354 0 : rectangle->x + rectangle->width >= clip->path->extents.x + clip->path->extents.width &&
355 0 : rectangle->y + rectangle->height >= clip->path->extents.y + clip->path->extents.height)
356 : {
357 0 : return CAIRO_STATUS_SUCCESS;
358 : }
359 : }
360 :
361 0 : return _cairo_clip_intersect_rectangle (clip, rectangle);
362 : }
363 :
364 : static cairo_status_t
365 0 : _cairo_clip_path_reapply_clip_path_transform (cairo_clip_t *clip,
366 : cairo_clip_path_t *other_path,
367 : const cairo_matrix_t *matrix)
368 : {
369 : cairo_status_t status;
370 : cairo_clip_path_t *clip_path;
371 : cairo_bool_t is_empty;
372 :
373 0 : if (other_path->prev != NULL) {
374 0 : status = _cairo_clip_path_reapply_clip_path_transform (clip,
375 : other_path->prev,
376 : matrix);
377 0 : if (unlikely (status))
378 0 : return status;
379 : }
380 :
381 0 : clip_path = _cairo_clip_path_create (clip);
382 0 : if (unlikely (clip_path == NULL))
383 0 : return _cairo_error (CAIRO_STATUS_NO_MEMORY);
384 :
385 0 : status = _cairo_path_fixed_init_copy (&clip_path->path,
386 0 : &other_path->path);
387 0 : if (unlikely (status)) {
388 0 : clip->path = clip->path->prev;
389 0 : _cairo_clip_path_destroy (clip_path);
390 0 : return status;
391 : }
392 :
393 0 : _cairo_path_fixed_transform (&clip_path->path, matrix);
394 0 : _cairo_path_fixed_approximate_clip_extents (&clip_path->path,
395 : &clip_path->extents);
396 0 : if (clip_path->prev != NULL) {
397 0 : is_empty = _cairo_rectangle_intersect (&clip_path->extents,
398 0 : &clip_path->prev->extents);
399 : }
400 :
401 0 : clip_path->fill_rule = other_path->fill_rule;
402 0 : clip_path->tolerance = other_path->tolerance;
403 0 : clip_path->antialias = other_path->antialias;
404 :
405 0 : return CAIRO_STATUS_SUCCESS;
406 : }
407 :
408 : static cairo_status_t
409 0 : _cairo_clip_path_reapply_clip_path_translate (cairo_clip_t *clip,
410 : cairo_clip_path_t *other_path,
411 : int tx, int ty)
412 : {
413 : cairo_status_t status;
414 : cairo_clip_path_t *clip_path;
415 :
416 0 : if (other_path->prev != NULL) {
417 0 : status = _cairo_clip_path_reapply_clip_path_translate (clip,
418 : other_path->prev,
419 : tx, ty);
420 0 : if (unlikely (status))
421 0 : return status;
422 : }
423 :
424 0 : clip_path = _cairo_clip_path_create (clip);
425 0 : if (unlikely (clip_path == NULL))
426 0 : return _cairo_error (CAIRO_STATUS_NO_MEMORY);
427 :
428 0 : status = _cairo_path_fixed_init_copy (&clip_path->path,
429 0 : &other_path->path);
430 0 : if (unlikely (status)) {
431 0 : clip->path = clip->path->prev;
432 0 : _cairo_clip_path_destroy (clip_path);
433 0 : return status;
434 : }
435 :
436 0 : _cairo_path_fixed_translate (&clip_path->path,
437 : _cairo_fixed_from_int (tx),
438 : _cairo_fixed_from_int (ty));
439 :
440 0 : clip_path->fill_rule = other_path->fill_rule;
441 0 : clip_path->tolerance = other_path->tolerance;
442 0 : clip_path->antialias = other_path->antialias;
443 :
444 0 : clip_path->flags = other_path->flags;
445 0 : if (other_path->region != NULL) {
446 0 : clip_path->region = cairo_region_copy (other_path->region);
447 0 : status = clip_path->region->status;
448 0 : if (unlikely (status)) {
449 0 : clip->path = clip->path->prev;
450 0 : _cairo_clip_path_destroy (clip_path);
451 0 : return status;
452 : }
453 :
454 0 : cairo_region_translate (clip_path->region, tx, ty);
455 : }
456 0 : clip_path->surface = cairo_surface_reference (other_path->surface);
457 :
458 0 : clip_path->extents = other_path->extents;
459 0 : clip_path->extents.x += tx;
460 0 : clip_path->extents.y += ty;
461 :
462 0 : return CAIRO_STATUS_SUCCESS;
463 : }
464 :
465 : cairo_status_t
466 0 : _cairo_clip_init_copy_transformed (cairo_clip_t *clip,
467 : cairo_clip_t *other,
468 : const cairo_matrix_t *matrix)
469 : {
470 0 : cairo_status_t status = CAIRO_STATUS_SUCCESS;
471 : int tx, ty;
472 :
473 0 : if (other == NULL) {
474 0 : _cairo_clip_init (clip);
475 0 : return CAIRO_STATUS_SUCCESS;
476 : }
477 :
478 0 : if (other->all_clipped) {
479 0 : _cairo_clip_init (clip);
480 0 : clip->all_clipped = TRUE;
481 0 : return CAIRO_STATUS_SUCCESS;
482 : }
483 :
484 0 : if (_cairo_matrix_is_identity (matrix)) {
485 0 : _cairo_clip_init_copy (clip, other);
486 0 : return CAIRO_STATUS_SUCCESS;
487 : }
488 :
489 0 : if (other->path != NULL) {
490 0 : _cairo_clip_init (clip);
491 :
492 : /* if we only need to translate, so we can reuse the caches... */
493 : /* XXX we still loose the benefit of constructs when the copy is
494 : * deleted though. Indirect clip_paths?
495 : */
496 0 : if (_cairo_matrix_is_integer_translation (matrix, &tx, &ty)) {
497 0 : status = _cairo_clip_path_reapply_clip_path_translate (clip,
498 : other->path,
499 : tx, ty);
500 : } else {
501 0 : status = _cairo_clip_path_reapply_clip_path_transform (clip,
502 : other->path,
503 : matrix);
504 0 : if (clip->path->extents.width == 0 &&
505 0 : clip->path->extents.height == 0)
506 : {
507 0 : _cairo_clip_set_all_clipped (clip);
508 : }
509 : }
510 : }
511 :
512 0 : return status;
513 : }
514 :
515 : static cairo_status_t
516 0 : _cairo_clip_apply_clip_path (cairo_clip_t *clip,
517 : const cairo_clip_path_t *path)
518 : {
519 : cairo_status_t status;
520 :
521 0 : if (path->prev != NULL)
522 0 : status = _cairo_clip_apply_clip_path (clip, path->prev);
523 :
524 0 : return _cairo_clip_intersect_path (clip,
525 : &path->path,
526 : path->fill_rule,
527 : path->tolerance,
528 : path->antialias);
529 : }
530 :
531 : cairo_status_t
532 0 : _cairo_clip_apply_clip (cairo_clip_t *clip,
533 : const cairo_clip_t *other)
534 : {
535 : cairo_status_t status;
536 :
537 0 : if (clip->all_clipped)
538 0 : return CAIRO_STATUS_SUCCESS;
539 :
540 0 : if (other->all_clipped) {
541 0 : _cairo_clip_set_all_clipped (clip);
542 0 : return CAIRO_STATUS_SUCCESS;
543 : }
544 :
545 0 : status = CAIRO_STATUS_SUCCESS;
546 0 : if (other->path != NULL)
547 0 : status = _cairo_clip_apply_clip_path (clip, other->path);
548 :
549 0 : return status;
550 : }
551 :
552 : static inline cairo_bool_t
553 0 : _clip_paths_are_rectilinear (cairo_clip_path_t *clip_path)
554 : {
555 0 : while (clip_path != NULL) {
556 0 : if (! clip_path->path.is_rectilinear)
557 0 : return FALSE;
558 :
559 0 : clip_path = clip_path->prev;
560 : }
561 :
562 0 : return TRUE;
563 : }
564 :
565 : static cairo_int_status_t
566 0 : _cairo_clip_path_to_region_geometric (cairo_clip_path_t *clip_path)
567 : {
568 : cairo_traps_t traps;
569 : cairo_box_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (cairo_box_t)];
570 0 : cairo_box_t *boxes = stack_boxes;
571 : cairo_status_t status;
572 : int n;
573 :
574 : /* If we have nothing to intersect with this path, then it cannot
575 : * magically be reduced into a region.
576 : */
577 0 : if (clip_path->prev == NULL)
578 0 : goto UNSUPPORTED;
579 :
580 : /* Start simple... Intersect some boxes with an arbitrary path. */
581 0 : if (! clip_path->path.is_rectilinear)
582 0 : goto UNSUPPORTED;
583 0 : if (clip_path->prev->prev != NULL)
584 0 : goto UNSUPPORTED;
585 :
586 0 : _cairo_traps_init (&traps);
587 0 : _cairo_box_from_rectangle (&boxes[0], &clip_path->extents);
588 0 : _cairo_traps_limit (&traps, boxes, 1);
589 :
590 0 : status = _cairo_path_fixed_fill_rectilinear_to_traps (&clip_path->path,
591 : clip_path->fill_rule,
592 : &traps);
593 0 : if (unlikely (_cairo_status_is_error (status)))
594 0 : return status;
595 0 : if (status == CAIRO_INT_STATUS_UNSUPPORTED)
596 0 : goto UNSUPPORTED;
597 :
598 0 : if (unlikely (traps.num_traps == 0)) {
599 0 : clip_path->region = cairo_region_create ();
600 0 : clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION;
601 0 : return CAIRO_STATUS_SUCCESS;
602 : }
603 :
604 0 : if (traps.num_traps > ARRAY_LENGTH (stack_boxes)) {
605 0 : boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t));
606 0 : if (unlikely (boxes == NULL))
607 0 : return _cairo_error (CAIRO_STATUS_NO_MEMORY);
608 : }
609 :
610 0 : for (n = 0; n < traps.num_traps; n++) {
611 0 : boxes[n].p1.x = traps.traps[n].left.p1.x;
612 0 : boxes[n].p1.y = traps.traps[n].top;
613 0 : boxes[n].p2.x = traps.traps[n].right.p1.x;
614 0 : boxes[n].p2.y = traps.traps[n].bottom;
615 : }
616 :
617 0 : _cairo_traps_clear (&traps);
618 0 : _cairo_traps_limit (&traps, boxes, n);
619 0 : status = _cairo_path_fixed_fill_to_traps (&clip_path->prev->path,
620 0 : clip_path->prev->fill_rule,
621 0 : clip_path->prev->tolerance,
622 : &traps);
623 0 : if (boxes != stack_boxes)
624 0 : free (boxes);
625 :
626 0 : if (unlikely (status))
627 0 : return status;
628 :
629 0 : status = _cairo_traps_extract_region (&traps, &clip_path->region);
630 0 : _cairo_traps_fini (&traps);
631 :
632 0 : if (status == CAIRO_INT_STATUS_UNSUPPORTED)
633 0 : goto UNSUPPORTED;
634 0 : if (unlikely (status))
635 0 : return status;
636 :
637 0 : clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION;
638 0 : return CAIRO_STATUS_SUCCESS;
639 :
640 : UNSUPPORTED:
641 0 : clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
642 0 : return CAIRO_INT_STATUS_UNSUPPORTED;
643 : }
644 :
645 : static cairo_int_status_t
646 0 : _cairo_clip_path_to_region (cairo_clip_path_t *clip_path)
647 : {
648 : cairo_int_status_t status;
649 0 : cairo_region_t *prev = NULL;
650 :
651 0 : if (clip_path->flags &
652 : (CAIRO_CLIP_PATH_HAS_REGION |
653 : CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED))
654 : {
655 0 : return clip_path->flags & CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED ?
656 0 : CAIRO_INT_STATUS_UNSUPPORTED :
657 : CAIRO_STATUS_SUCCESS;
658 : }
659 :
660 0 : if (! clip_path->path.maybe_fill_region)
661 0 : return _cairo_clip_path_to_region_geometric (clip_path);
662 :
663 : /* first retrieve the region for our antecedents */
664 0 : if (clip_path->prev != NULL) {
665 0 : status = _cairo_clip_path_to_region (clip_path->prev);
666 0 : if (status) {
667 0 : if (status == CAIRO_INT_STATUS_UNSUPPORTED)
668 0 : return _cairo_clip_path_to_region_geometric (clip_path);
669 :
670 0 : return status;
671 : }
672 :
673 0 : prev = clip_path->prev->region;
674 : }
675 :
676 : /* now extract the region for ourselves */
677 0 : clip_path->region =
678 0 : _cairo_path_fixed_fill_rectilinear_to_region (&clip_path->path,
679 : clip_path->fill_rule,
680 0 : &clip_path->extents);
681 0 : assert (clip_path->region != NULL);
682 :
683 0 : status = clip_path->region->status;
684 0 : if (unlikely (status))
685 0 : return status;
686 :
687 0 : if (prev != NULL) {
688 0 : status = cairo_region_intersect (clip_path->region, prev);
689 0 : if (unlikely (status))
690 0 : return status;
691 : }
692 :
693 0 : clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION;
694 0 : return CAIRO_STATUS_SUCCESS;
695 : }
696 :
697 : static inline int
698 0 : pot (int v)
699 : {
700 0 : v--;
701 0 : v |= v >> 1;
702 0 : v |= v >> 2;
703 0 : v |= v >> 4;
704 0 : v |= v >> 8;
705 0 : v |= v >> 16;
706 0 : v++;
707 0 : return v;
708 : }
709 :
710 : /* XXX there is likely a faster method! ;-) */
711 : static cairo_status_t
712 0 : _region_clip_to_boxes (const cairo_region_t *region,
713 : cairo_box_t **boxes,
714 : int *num_boxes,
715 : int *size_boxes)
716 : {
717 : cairo_traps_t traps;
718 : cairo_status_t status;
719 : int n, num_rects;
720 :
721 0 : _cairo_traps_init (&traps);
722 0 : _cairo_traps_limit (&traps, *boxes, *num_boxes);
723 0 : traps.is_rectilinear = TRUE;
724 0 : traps.is_rectangular = TRUE;
725 :
726 0 : num_rects = cairo_region_num_rectangles (region);
727 0 : for (n = 0; n < num_rects; n++) {
728 : cairo_rectangle_int_t rect;
729 : cairo_point_t p1, p2;
730 :
731 0 : cairo_region_get_rectangle (region, n, &rect);
732 :
733 0 : p1.x = _cairo_fixed_from_int (rect.x);
734 0 : p1.y = _cairo_fixed_from_int (rect.y);
735 0 : p2.x = _cairo_fixed_from_int (rect.x + rect.width);
736 0 : p2.y = _cairo_fixed_from_int (rect.y + rect.height);
737 :
738 0 : status = _cairo_traps_tessellate_rectangle (&traps, &p1, &p2);
739 0 : if (unlikely (status))
740 0 : goto CLEANUP;
741 : }
742 :
743 0 : status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps, CAIRO_FILL_RULE_WINDING);
744 0 : if (unlikely (status))
745 0 : goto CLEANUP;
746 :
747 0 : n = *size_boxes;
748 0 : if (n < 0)
749 0 : n = -n;
750 :
751 0 : if (traps.num_traps > n) {
752 : cairo_box_t *new_boxes;
753 :
754 0 : new_boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t));
755 0 : if (unlikely (new_boxes == NULL)) {
756 0 : status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
757 0 : goto CLEANUP;
758 : }
759 :
760 0 : if (*size_boxes > 0)
761 0 : free (*boxes);
762 :
763 0 : *boxes = new_boxes;
764 0 : *size_boxes = traps.num_traps;
765 : }
766 :
767 0 : for (n = 0; n < traps.num_traps; n++) {
768 0 : (*boxes)[n].p1.x = traps.traps[n].left.p1.x;
769 0 : (*boxes)[n].p1.y = traps.traps[n].top;
770 0 : (*boxes)[n].p2.x = traps.traps[n].right.p1.x;
771 0 : (*boxes)[n].p2.y = traps.traps[n].bottom;
772 : }
773 0 : *num_boxes = n;
774 :
775 : CLEANUP:
776 0 : _cairo_traps_fini (&traps);
777 :
778 0 : return status;
779 : }
780 :
781 : static cairo_status_t
782 0 : _rectilinear_clip_to_boxes (const cairo_path_fixed_t *path,
783 : cairo_fill_rule_t fill_rule,
784 : cairo_box_t **boxes,
785 : int *num_boxes,
786 : int *size_boxes)
787 : {
788 : cairo_polygon_t polygon;
789 : cairo_traps_t traps;
790 : cairo_status_t status;
791 :
792 0 : _cairo_traps_init (&traps);
793 0 : _cairo_traps_limit (&traps, *boxes, *num_boxes);
794 :
795 0 : _cairo_polygon_init (&polygon);
796 0 : _cairo_polygon_limit (&polygon, *boxes, *num_boxes);
797 :
798 0 : status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
799 : fill_rule,
800 : &traps);
801 0 : if (unlikely (_cairo_status_is_error (status)))
802 0 : goto CLEANUP;
803 0 : if (status == CAIRO_STATUS_SUCCESS)
804 0 : goto BOXES;
805 :
806 : /* tolerance will be ignored as the path is rectilinear */
807 0 : status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon);
808 0 : if (unlikely (status))
809 0 : goto CLEANUP;
810 :
811 0 : if (polygon.num_edges == 0) {
812 0 : *num_boxes = 0;
813 : } else {
814 0 : status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
815 : &polygon,
816 : fill_rule);
817 0 : if (likely (status == CAIRO_STATUS_SUCCESS)) {
818 : int i;
819 :
820 : BOXES:
821 0 : i = *size_boxes;
822 0 : if (i < 0)
823 0 : i = -i;
824 :
825 0 : if (traps.num_traps > i) {
826 : cairo_box_t *new_boxes;
827 : int new_size;
828 :
829 0 : new_size = pot (traps.num_traps);
830 0 : new_boxes = _cairo_malloc_ab (new_size, sizeof (cairo_box_t));
831 0 : if (unlikely (new_boxes == NULL)) {
832 0 : status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
833 0 : goto CLEANUP;
834 : }
835 :
836 0 : if (*size_boxes > 0)
837 0 : free (*boxes);
838 :
839 0 : *boxes = new_boxes;
840 0 : *size_boxes = new_size;
841 : }
842 :
843 0 : for (i = 0; i < traps.num_traps; i++) {
844 0 : (*boxes)[i].p1.x = traps.traps[i].left.p1.x;
845 0 : (*boxes)[i].p1.y = traps.traps[i].top;
846 0 : (*boxes)[i].p2.x = traps.traps[i].right.p1.x;
847 0 : (*boxes)[i].p2.y = traps.traps[i].bottom;
848 : }
849 0 : *num_boxes = i;
850 : }
851 : }
852 :
853 : CLEANUP:
854 0 : _cairo_polygon_fini (&polygon);
855 0 : _cairo_traps_fini (&traps);
856 :
857 0 : return status;
858 : }
859 :
860 : static cairo_int_status_t
861 0 : _cairo_clip_path_to_boxes (cairo_clip_path_t *clip_path,
862 : cairo_box_t **boxes,
863 : int *count)
864 : {
865 0 : int size = -*count;
866 0 : int num_boxes = 0;
867 : cairo_status_t status;
868 :
869 0 : if (clip_path->region != NULL) {
870 : int num_rects, n;
871 :
872 0 : num_rects = cairo_region_num_rectangles (clip_path->region);
873 0 : if (num_rects > -size) {
874 : cairo_box_t *new_boxes;
875 :
876 0 : new_boxes = _cairo_malloc_ab (num_rects, sizeof (cairo_box_t));
877 0 : if (unlikely (new_boxes == NULL))
878 0 : return _cairo_error (CAIRO_STATUS_NO_MEMORY);
879 :
880 0 : *boxes = new_boxes;
881 : }
882 :
883 0 : for (n = 0; n < num_rects; n++) {
884 : cairo_rectangle_int_t rect;
885 :
886 0 : cairo_region_get_rectangle (clip_path->region, n, &rect);
887 0 : (*boxes)[n].p1.x = _cairo_fixed_from_int (rect.x);
888 0 : (*boxes)[n].p1.y = _cairo_fixed_from_int (rect.y);
889 0 : (*boxes)[n].p2.x = _cairo_fixed_from_int (rect.x + rect.width);
890 0 : (*boxes)[n].p2.y = _cairo_fixed_from_int (rect.y + rect.height);
891 : }
892 :
893 0 : *count = num_rects;
894 0 : return CAIRO_STATUS_SUCCESS;
895 : }
896 :
897 : /* keep it simple at first */
898 0 : if (! _clip_paths_are_rectilinear (clip_path))
899 0 : return CAIRO_INT_STATUS_UNSUPPORTED;
900 :
901 0 : assert (-size >= 1);
902 0 : if (_cairo_path_fixed_is_box (&clip_path->path, *boxes)) {
903 0 : num_boxes = 1;
904 : } else {
905 0 : status = _rectilinear_clip_to_boxes (&clip_path->path,
906 : clip_path->fill_rule,
907 : boxes, &num_boxes, &size);
908 0 : if (unlikely (status))
909 0 : return status;
910 : }
911 :
912 0 : while (num_boxes > 0 && (clip_path = clip_path->prev) != NULL) {
913 : cairo_box_t box;
914 :
915 0 : if (clip_path->region != NULL) {
916 0 : status = _region_clip_to_boxes (clip_path->region,
917 : boxes, &num_boxes, &size);
918 0 : if (unlikely (status))
919 0 : return status;
920 :
921 0 : break;
922 0 : } else if (_cairo_path_fixed_is_box (&clip_path->path, &box)) {
923 : int i, j;
924 :
925 0 : for (i = j = 0; i < num_boxes; i++) {
926 0 : if (j != i)
927 0 : (*boxes)[j] = (*boxes)[i];
928 :
929 0 : if (box.p1.x > (*boxes)[j].p1.x)
930 0 : (*boxes)[j].p1.x = box.p1.x;
931 0 : if (box.p2.x < (*boxes)[j].p2.x)
932 0 : (*boxes)[j].p2.x = box.p2.x;
933 :
934 0 : if (box.p1.y > (*boxes)[j].p1.y)
935 0 : (*boxes)[j].p1.y = box.p1.y;
936 0 : if (box.p2.y < (*boxes)[j].p2.y)
937 0 : (*boxes)[j].p2.y = box.p2.y;
938 :
939 0 : j += (*boxes)[j].p2.x > (*boxes)[j].p1.x &&
940 0 : (*boxes)[j].p2.y > (*boxes)[j].p1.y;
941 : }
942 :
943 0 : num_boxes = j;
944 : } else {
945 0 : status = _rectilinear_clip_to_boxes (&clip_path->path,
946 : clip_path->fill_rule,
947 : boxes, &num_boxes, &size);
948 0 : if (unlikely (status))
949 0 : return status;
950 : }
951 : }
952 :
953 0 : *count = num_boxes;
954 0 : return CAIRO_STATUS_SUCCESS;
955 : }
956 :
957 : static cairo_surface_t *
958 0 : _cairo_clip_path_get_surface (cairo_clip_path_t *clip_path,
959 : cairo_surface_t *target,
960 : int *tx, int *ty)
961 : {
962 0 : const cairo_rectangle_int_t *clip_extents = &clip_path->extents;
963 : cairo_bool_t need_translate;
964 : cairo_surface_t *surface;
965 : cairo_clip_path_t *prev;
966 : cairo_status_t status;
967 :
968 0 : while (clip_path->prev != NULL &&
969 0 : clip_path->flags & CAIRO_CLIP_PATH_IS_BOX &&
970 : clip_path->path.maybe_fill_region)
971 : {
972 0 : clip_path = clip_path->prev;
973 : }
974 :
975 0 : clip_extents = &clip_path->extents;
976 0 : if (clip_path->surface != NULL &&
977 0 : clip_path->surface->backend == target->backend)
978 : {
979 0 : *tx = clip_extents->x;
980 0 : *ty = clip_extents->y;
981 0 : return clip_path->surface;
982 : }
983 :
984 0 : surface = _cairo_surface_create_similar_scratch (target,
985 : CAIRO_CONTENT_ALPHA,
986 : clip_extents->width,
987 : clip_extents->height);
988 0 : if (surface == NULL) {
989 0 : surface = cairo_image_surface_create (CAIRO_FORMAT_A8,
990 : clip_extents->width,
991 : clip_extents->height);
992 : }
993 0 : if (unlikely (surface->status))
994 0 : return surface;
995 :
996 0 : need_translate = clip_extents->x | clip_extents->y;
997 0 : if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX &&
998 : clip_path->path.maybe_fill_region)
999 : {
1000 0 : status = _cairo_surface_paint (surface,
1001 : CAIRO_OPERATOR_SOURCE,
1002 : &_cairo_pattern_white.base,
1003 : NULL);
1004 0 : if (unlikely (status))
1005 0 : goto BAIL;
1006 : }
1007 : else
1008 : {
1009 0 : status = _cairo_surface_paint (surface,
1010 : CAIRO_OPERATOR_CLEAR,
1011 : &_cairo_pattern_clear.base,
1012 : NULL);
1013 0 : if (unlikely (status))
1014 0 : goto BAIL;
1015 :
1016 0 : if (need_translate) {
1017 0 : _cairo_path_fixed_translate (&clip_path->path,
1018 0 : _cairo_fixed_from_int (-clip_extents->x),
1019 0 : _cairo_fixed_from_int (-clip_extents->y));
1020 : }
1021 0 : status = _cairo_surface_fill (surface,
1022 : CAIRO_OPERATOR_ADD,
1023 : &_cairo_pattern_white.base,
1024 : &clip_path->path,
1025 : clip_path->fill_rule,
1026 : clip_path->tolerance,
1027 : clip_path->antialias,
1028 : NULL);
1029 0 : if (need_translate) {
1030 0 : _cairo_path_fixed_translate (&clip_path->path,
1031 : _cairo_fixed_from_int (clip_extents->x),
1032 : _cairo_fixed_from_int (clip_extents->y));
1033 : }
1034 :
1035 0 : if (unlikely (status))
1036 0 : goto BAIL;
1037 : }
1038 :
1039 0 : prev = clip_path->prev;
1040 0 : while (prev != NULL) {
1041 0 : if (prev->flags & CAIRO_CLIP_PATH_IS_BOX &&
1042 : prev->path.maybe_fill_region)
1043 : {
1044 : /* a simple box only affects the extents */
1045 : }
1046 0 : else if (prev->path.is_rectilinear ||
1047 0 : prev->surface == NULL ||
1048 0 : prev->surface->backend != target->backend)
1049 : {
1050 0 : if (need_translate) {
1051 0 : _cairo_path_fixed_translate (&prev->path,
1052 0 : _cairo_fixed_from_int (-clip_extents->x),
1053 0 : _cairo_fixed_from_int (-clip_extents->y));
1054 : }
1055 0 : status = _cairo_surface_fill (surface,
1056 : CAIRO_OPERATOR_IN,
1057 : &_cairo_pattern_white.base,
1058 : &prev->path,
1059 : prev->fill_rule,
1060 : prev->tolerance,
1061 : prev->antialias,
1062 : NULL);
1063 0 : if (need_translate) {
1064 0 : _cairo_path_fixed_translate (&prev->path,
1065 : _cairo_fixed_from_int (clip_extents->x),
1066 : _cairo_fixed_from_int (clip_extents->y));
1067 : }
1068 :
1069 0 : if (unlikely (status))
1070 0 : goto BAIL;
1071 : }
1072 : else
1073 : {
1074 : cairo_surface_pattern_t pattern;
1075 : cairo_surface_t *prev_surface;
1076 : int prev_tx, prev_ty;
1077 :
1078 0 : prev_surface = _cairo_clip_path_get_surface (prev, target, &prev_tx, &prev_ty);
1079 0 : status = prev_surface->status;
1080 0 : if (unlikely (status))
1081 0 : goto BAIL;
1082 :
1083 0 : _cairo_pattern_init_for_surface (&pattern, prev_surface);
1084 0 : pattern.base.filter = CAIRO_FILTER_NEAREST;
1085 0 : cairo_matrix_init_translate (&pattern.base.matrix,
1086 0 : clip_extents->x - prev_tx,
1087 0 : clip_extents->y - prev_ty);
1088 0 : status = _cairo_surface_paint (surface,
1089 : CAIRO_OPERATOR_IN,
1090 : &pattern.base,
1091 : NULL);
1092 0 : _cairo_pattern_fini (&pattern.base);
1093 :
1094 0 : if (unlikely (status))
1095 0 : goto BAIL;
1096 :
1097 0 : break;
1098 : }
1099 :
1100 0 : prev = prev->prev;
1101 : }
1102 :
1103 0 : *tx = clip_extents->x;
1104 0 : *ty = clip_extents->y;
1105 0 : cairo_surface_destroy (clip_path->surface);
1106 0 : return clip_path->surface = surface;
1107 :
1108 : BAIL:
1109 0 : cairo_surface_destroy (surface);
1110 0 : return _cairo_surface_create_in_error (status);
1111 : }
1112 :
1113 : cairo_bool_t
1114 0 : _cairo_clip_contains_rectangle (cairo_clip_t *clip,
1115 : const cairo_rectangle_int_t *rect)
1116 : {
1117 : cairo_clip_path_t *clip_path;
1118 :
1119 0 : if (clip == NULL)
1120 0 : return FALSE;
1121 :
1122 0 : clip_path = clip->path;
1123 0 : if (clip_path->extents.x > rect->x ||
1124 0 : clip_path->extents.y > rect->y ||
1125 0 : clip_path->extents.x + clip_path->extents.width < rect->x + rect->width ||
1126 0 : clip_path->extents.y + clip_path->extents.height < rect->y + rect->height)
1127 : {
1128 0 : return FALSE;
1129 : }
1130 :
1131 : do {
1132 : cairo_box_t box;
1133 :
1134 0 : if ((clip_path->flags & CAIRO_CLIP_PATH_IS_BOX) == 0)
1135 0 : return FALSE;
1136 :
1137 0 : if (! _cairo_path_fixed_is_box (&clip_path->path, &box))
1138 0 : return FALSE;
1139 :
1140 0 : if (box.p1.x > _cairo_fixed_from_int (rect->x) ||
1141 0 : box.p1.y > _cairo_fixed_from_int (rect->y) ||
1142 0 : box.p2.x < _cairo_fixed_from_int (rect->x + rect->width) ||
1143 0 : box.p2.y < _cairo_fixed_from_int (rect->y + rect->height))
1144 : {
1145 0 : return FALSE;
1146 : }
1147 0 : } while ((clip_path = clip_path->prev) != NULL);
1148 :
1149 0 : return TRUE;
1150 : }
1151 :
1152 : cairo_bool_t
1153 0 : _cairo_clip_contains_extents (cairo_clip_t *clip,
1154 : const cairo_composite_rectangles_t *extents)
1155 : {
1156 : const cairo_rectangle_int_t *rect;
1157 :
1158 0 : if (clip == NULL)
1159 0 : return FALSE;
1160 :
1161 0 : rect = extents->is_bounded ? &extents->bounded : &extents->unbounded;
1162 0 : return _cairo_clip_contains_rectangle (clip, rect);
1163 : }
1164 :
1165 : void
1166 0 : _cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip)
1167 : {
1168 : cairo_clip_path_t *clip_path;
1169 :
1170 0 : if (clip == NULL) {
1171 0 : fprintf (stream, "no clip\n");
1172 0 : return;
1173 : }
1174 :
1175 0 : if (clip->all_clipped) {
1176 0 : fprintf (stream, "clip: all-clipped\n");
1177 0 : return;
1178 : }
1179 :
1180 0 : if (clip->path == NULL) {
1181 0 : fprintf (stream, "clip: empty\n");
1182 0 : return;
1183 : }
1184 :
1185 0 : fprintf (stream, "clip:\n");
1186 :
1187 0 : clip_path = clip->path;
1188 : do {
1189 0 : fprintf (stream, "path: has region? %s, has surface? %s, aa=%d, tolerance=%f, rule=%d: ",
1190 0 : clip_path->region == NULL ? "no" : "yes",
1191 0 : clip_path->surface == NULL ? "no" : "yes",
1192 0 : clip_path->antialias,
1193 : clip_path->tolerance,
1194 0 : clip_path->fill_rule);
1195 0 : _cairo_debug_print_path (stream, &clip_path->path);
1196 0 : fprintf (stream, "\n");
1197 0 : } while ((clip_path = clip_path->prev) != NULL);
1198 : }
1199 :
1200 : cairo_surface_t *
1201 0 : _cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target, int *tx, int *ty)
1202 : {
1203 : /* XXX is_clear -> all_clipped */
1204 0 : assert (clip->path != NULL);
1205 0 : return _cairo_clip_path_get_surface (clip->path, target, tx, ty);
1206 : }
1207 :
1208 : cairo_status_t
1209 0 : _cairo_clip_combine_with_surface (cairo_clip_t *clip,
1210 : cairo_surface_t *dst,
1211 : int dst_x, int dst_y)
1212 : {
1213 0 : cairo_clip_path_t *clip_path = clip->path;
1214 : cairo_bool_t need_translate;
1215 : cairo_status_t status;
1216 :
1217 0 : assert (clip_path != NULL);
1218 :
1219 0 : need_translate = dst_x | dst_y;
1220 : do {
1221 0 : if (clip_path->surface != NULL &&
1222 0 : clip_path->surface->backend == dst->backend)
1223 : {
1224 : cairo_surface_pattern_t pattern;
1225 :
1226 0 : _cairo_pattern_init_for_surface (&pattern, clip_path->surface);
1227 0 : cairo_matrix_init_translate (&pattern.base.matrix,
1228 0 : dst_x - clip_path->extents.x,
1229 0 : dst_y - clip_path->extents.y);
1230 0 : pattern.base.filter = CAIRO_FILTER_NEAREST;
1231 0 : status = _cairo_surface_paint (dst,
1232 : CAIRO_OPERATOR_IN,
1233 : &pattern.base,
1234 : NULL);
1235 :
1236 0 : _cairo_pattern_fini (&pattern.base);
1237 :
1238 0 : return status;
1239 : }
1240 :
1241 0 : if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX &&
1242 : clip_path->path.maybe_fill_region)
1243 : {
1244 0 : continue;
1245 : }
1246 :
1247 0 : if (need_translate) {
1248 0 : _cairo_path_fixed_translate (&clip_path->path,
1249 : _cairo_fixed_from_int (-dst_x),
1250 : _cairo_fixed_from_int (-dst_y));
1251 : }
1252 0 : status = _cairo_surface_fill (dst,
1253 : CAIRO_OPERATOR_IN,
1254 : &_cairo_pattern_white.base,
1255 : &clip_path->path,
1256 : clip_path->fill_rule,
1257 : clip_path->tolerance,
1258 : clip_path->antialias,
1259 : NULL);
1260 0 : if (need_translate) {
1261 0 : _cairo_path_fixed_translate (&clip_path->path,
1262 : _cairo_fixed_from_int (dst_x),
1263 : _cairo_fixed_from_int (dst_y));
1264 : }
1265 :
1266 0 : if (unlikely (status))
1267 0 : return status;
1268 0 : } while ((clip_path = clip_path->prev) != NULL);
1269 :
1270 0 : return CAIRO_STATUS_SUCCESS;
1271 : }
1272 :
1273 : static const cairo_rectangle_int_t _cairo_empty_rectangle_int = { 0, 0, 0, 0 };
1274 :
1275 : const cairo_rectangle_int_t *
1276 0 : _cairo_clip_get_extents (const cairo_clip_t *clip)
1277 : {
1278 0 : if (clip->all_clipped)
1279 0 : return &_cairo_empty_rectangle_int;
1280 :
1281 0 : if (clip->path == NULL)
1282 0 : return NULL;
1283 :
1284 0 : return &clip->path->extents;
1285 : }
1286 :
1287 : void
1288 0 : _cairo_clip_drop_cache (cairo_clip_t *clip)
1289 : {
1290 : cairo_clip_path_t *clip_path;
1291 :
1292 0 : if (clip->path == NULL)
1293 0 : return;
1294 :
1295 0 : clip_path = clip->path;
1296 : do {
1297 0 : if (clip_path->region != NULL) {
1298 0 : cairo_region_destroy (clip_path->region);
1299 0 : clip_path->region = NULL;
1300 : }
1301 :
1302 0 : if (clip_path->surface != NULL) {
1303 0 : cairo_surface_destroy (clip_path->surface);
1304 0 : clip_path->surface = NULL;
1305 : }
1306 :
1307 0 : clip_path->flags &= ~CAIRO_CLIP_PATH_HAS_REGION;
1308 0 : } while ((clip_path = clip_path->prev) != NULL);
1309 : }
1310 :
1311 : const cairo_rectangle_list_t _cairo_rectangles_nil =
1312 : { CAIRO_STATUS_NO_MEMORY, NULL, 0 };
1313 : static const cairo_rectangle_list_t _cairo_rectangles_not_representable =
1314 : { CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, NULL, 0 };
1315 :
1316 : static cairo_bool_t
1317 0 : _cairo_clip_int_rect_to_user (cairo_gstate_t *gstate,
1318 : cairo_rectangle_int_t *clip_rect,
1319 : cairo_rectangle_t *user_rect)
1320 : {
1321 : cairo_bool_t is_tight;
1322 :
1323 0 : double x1 = clip_rect->x;
1324 0 : double y1 = clip_rect->y;
1325 0 : double x2 = clip_rect->x + (int) clip_rect->width;
1326 0 : double y2 = clip_rect->y + (int) clip_rect->height;
1327 :
1328 0 : _cairo_gstate_backend_to_user_rectangle (gstate,
1329 : &x1, &y1, &x2, &y2,
1330 : &is_tight);
1331 :
1332 0 : user_rect->x = x1;
1333 0 : user_rect->y = y1;
1334 0 : user_rect->width = x2 - x1;
1335 0 : user_rect->height = y2 - y1;
1336 :
1337 0 : return is_tight;
1338 : }
1339 :
1340 : cairo_int_status_t
1341 0 : _cairo_clip_get_region (cairo_clip_t *clip,
1342 : cairo_region_t **region)
1343 : {
1344 : cairo_int_status_t status;
1345 :
1346 0 : if (clip->all_clipped)
1347 0 : goto CLIPPED;
1348 :
1349 0 : assert (clip->path != NULL);
1350 :
1351 0 : status = _cairo_clip_path_to_region (clip->path);
1352 0 : if (status)
1353 0 : return status;
1354 :
1355 0 : if (cairo_region_is_empty (clip->path->region)) {
1356 0 : _cairo_clip_set_all_clipped (clip);
1357 0 : goto CLIPPED;
1358 : }
1359 :
1360 0 : if (region)
1361 0 : *region = clip->path->region;
1362 0 : return CAIRO_STATUS_SUCCESS;
1363 :
1364 : CLIPPED:
1365 0 : if (region)
1366 0 : *region = NULL;
1367 0 : return CAIRO_INT_STATUS_NOTHING_TO_DO;
1368 : }
1369 :
1370 : cairo_int_status_t
1371 0 : _cairo_clip_get_boxes (cairo_clip_t *clip,
1372 : cairo_box_t **boxes,
1373 : int *count)
1374 : {
1375 : cairo_int_status_t status;
1376 :
1377 0 : if (clip->all_clipped)
1378 0 : return CAIRO_INT_STATUS_NOTHING_TO_DO;
1379 :
1380 0 : assert (clip->path != NULL);
1381 :
1382 0 : status = _cairo_clip_path_to_boxes (clip->path, boxes, count);
1383 0 : if (status)
1384 0 : return status;
1385 :
1386 0 : if (*count == 0) {
1387 0 : _cairo_clip_set_all_clipped (clip);
1388 0 : return CAIRO_INT_STATUS_NOTHING_TO_DO;
1389 : }
1390 :
1391 0 : return CAIRO_STATUS_SUCCESS;
1392 : }
1393 :
1394 : static cairo_bool_t
1395 0 : box_is_aligned (const cairo_box_t *box)
1396 : {
1397 : return
1398 0 : _cairo_fixed_is_integer (box->p1.x) &&
1399 0 : _cairo_fixed_is_integer (box->p1.y) &&
1400 0 : _cairo_fixed_is_integer (box->p2.x) &&
1401 0 : _cairo_fixed_is_integer (box->p2.y);
1402 : }
1403 :
1404 : static void
1405 0 : intersect_with_boxes (cairo_composite_rectangles_t *extents,
1406 : cairo_box_t *boxes,
1407 : int num_boxes)
1408 : {
1409 : cairo_rectangle_int_t rect;
1410 : cairo_box_t box;
1411 : cairo_bool_t is_empty;
1412 :
1413 0 : box.p1.x = box.p1.y = INT_MIN;
1414 0 : box.p2.x = box.p2.y = INT_MAX;
1415 0 : while (num_boxes--) {
1416 0 : if (boxes->p1.x < box.p1.x)
1417 0 : box.p1.x = boxes->p1.x;
1418 0 : if (boxes->p1.y < box.p1.y)
1419 0 : box.p1.y = boxes->p1.y;
1420 :
1421 0 : if (boxes->p2.x > box.p2.x)
1422 0 : box.p2.x = boxes->p2.x;
1423 0 : if (boxes->p2.y > box.p2.y)
1424 0 : box.p2.y = boxes->p2.y;
1425 : }
1426 :
1427 0 : _cairo_box_round_to_rectangle (&box, &rect);
1428 0 : is_empty = _cairo_rectangle_intersect (&extents->bounded, &rect);
1429 0 : is_empty = _cairo_rectangle_intersect (&extents->unbounded, &rect);
1430 0 : }
1431 :
1432 : cairo_status_t
1433 0 : _cairo_clip_to_boxes (cairo_clip_t **clip,
1434 : cairo_composite_rectangles_t *extents,
1435 : cairo_box_t **boxes,
1436 : int *num_boxes)
1437 : {
1438 : cairo_status_t status;
1439 : const cairo_rectangle_int_t *rect;
1440 :
1441 0 : rect = extents->is_bounded ? &extents->bounded : &extents->unbounded;
1442 :
1443 0 : if (*clip == NULL)
1444 0 : goto EXTENTS;
1445 :
1446 0 : status = _cairo_clip_rectangle (*clip, rect);
1447 0 : if (unlikely (status))
1448 0 : return status;
1449 :
1450 0 : status = _cairo_clip_get_boxes (*clip, boxes, num_boxes);
1451 0 : switch ((int) status) {
1452 : case CAIRO_STATUS_SUCCESS:
1453 0 : intersect_with_boxes (extents, *boxes, *num_boxes);
1454 0 : if (rect->width == 0 || rect->height == 0 ||
1455 0 : extents->is_bounded ||
1456 0 : (*num_boxes == 1 && box_is_aligned (*boxes)))
1457 : {
1458 0 : *clip = NULL;
1459 : }
1460 0 : goto DONE;
1461 :
1462 : case CAIRO_INT_STATUS_UNSUPPORTED:
1463 0 : goto EXTENTS;
1464 :
1465 : default:
1466 0 : return status;
1467 : }
1468 :
1469 : EXTENTS:
1470 0 : status = CAIRO_STATUS_SUCCESS;
1471 0 : _cairo_box_from_rectangle (&(*boxes)[0], rect);
1472 0 : *num_boxes = 1;
1473 : DONE:
1474 0 : return status;
1475 : }
1476 :
1477 :
1478 : static cairo_rectangle_list_t *
1479 0 : _cairo_rectangle_list_create_in_error (cairo_status_t status)
1480 : {
1481 : cairo_rectangle_list_t *list;
1482 :
1483 0 : if (status == CAIRO_STATUS_NO_MEMORY)
1484 0 : return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
1485 0 : if (status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE)
1486 0 : return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable;
1487 :
1488 0 : list = malloc (sizeof (*list));
1489 0 : if (unlikely (list == NULL)) {
1490 0 : _cairo_error_throw (status);
1491 0 : return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
1492 : }
1493 :
1494 0 : list->status = status;
1495 0 : list->rectangles = NULL;
1496 0 : list->num_rectangles = 0;
1497 :
1498 0 : return list;
1499 : }
1500 :
1501 : cairo_rectangle_list_t *
1502 0 : _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate)
1503 : {
1504 : #define ERROR_LIST(S) _cairo_rectangle_list_create_in_error (_cairo_error (S))
1505 :
1506 : cairo_rectangle_list_t *list;
1507 0 : cairo_rectangle_t *rectangles = NULL;
1508 0 : cairo_region_t *region = NULL;
1509 : cairo_int_status_t status;
1510 0 : int n_rects = 0;
1511 : int i;
1512 :
1513 0 : if (clip->all_clipped)
1514 0 : goto DONE;
1515 :
1516 0 : if (!clip->path)
1517 0 : return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
1518 :
1519 0 : status = _cairo_clip_get_region (clip, ®ion);
1520 0 : if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
1521 0 : goto DONE;
1522 0 : } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
1523 0 : return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
1524 0 : } else if (unlikely (status)) {
1525 0 : return ERROR_LIST (status);
1526 : }
1527 :
1528 0 : n_rects = cairo_region_num_rectangles (region);
1529 0 : if (n_rects) {
1530 0 : rectangles = _cairo_malloc_ab (n_rects, sizeof (cairo_rectangle_t));
1531 0 : if (unlikely (rectangles == NULL)) {
1532 0 : return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
1533 : }
1534 :
1535 0 : for (i = 0; i < n_rects; ++i) {
1536 : cairo_rectangle_int_t clip_rect;
1537 :
1538 0 : cairo_region_get_rectangle (region, i, &clip_rect);
1539 :
1540 0 : if (! _cairo_clip_int_rect_to_user (gstate,
1541 : &clip_rect,
1542 0 : &rectangles[i]))
1543 : {
1544 0 : free (rectangles);
1545 0 : return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
1546 : }
1547 : }
1548 : }
1549 :
1550 : DONE:
1551 0 : list = malloc (sizeof (cairo_rectangle_list_t));
1552 0 : if (unlikely (list == NULL)) {
1553 0 : free (rectangles);
1554 0 : return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
1555 : }
1556 :
1557 0 : list->status = CAIRO_STATUS_SUCCESS;
1558 0 : list->rectangles = rectangles;
1559 0 : list->num_rectangles = n_rects;
1560 0 : return list;
1561 :
1562 : #undef ERROR_LIST
1563 : }
1564 :
1565 : /**
1566 : * cairo_rectangle_list_destroy:
1567 : * @rectangle_list: a rectangle list, as obtained from cairo_copy_clip_rectangles()
1568 : *
1569 : * Unconditionally frees @rectangle_list and all associated
1570 : * references. After this call, the @rectangle_list pointer must not
1571 : * be dereferenced.
1572 : *
1573 : * Since: 1.4
1574 : **/
1575 : void
1576 0 : cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list)
1577 : {
1578 0 : if (rectangle_list == NULL || rectangle_list == &_cairo_rectangles_nil ||
1579 : rectangle_list == &_cairo_rectangles_not_representable)
1580 0 : return;
1581 :
1582 0 : free (rectangle_list->rectangles);
1583 0 : free (rectangle_list);
1584 : }
1585 :
1586 : void
1587 0 : _cairo_clip_reset_static_data (void)
1588 : {
1589 0 : _freed_pool_reset (&clip_path_pool);
1590 0 : }
|