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 64 : _cairo_clip_init (cairo_clip_t *clip)
111 : {
112 64 : clip->all_clipped = FALSE;
113 64 : clip->path = NULL;
114 64 : }
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 64 : _cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other)
189 : {
190 64 : if (other != NULL) {
191 64 : clip->all_clipped = other->all_clipped;
192 64 : if (other->path == NULL) {
193 64 : clip->path = NULL;
194 64 : if (! clip->all_clipped)
195 64 : 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 64 : return clip;
205 : }
206 :
207 : void
208 128 : _cairo_clip_reset (cairo_clip_t *clip)
209 : {
210 128 : clip->all_clipped = FALSE;
211 128 : if (clip->path != NULL) {
212 0 : _cairo_clip_path_destroy (clip->path);
213 0 : clip->path = NULL;
214 : }
215 128 : }
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 (traps.num_traps > ARRAY_LENGTH (stack_boxes)) {
599 0 : boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t));
600 0 : if (unlikely (boxes == NULL))
601 0 : return _cairo_error (CAIRO_STATUS_NO_MEMORY);
602 : }
603 :
604 0 : for (n = 0; n < traps.num_traps; n++) {
605 0 : boxes[n].p1.x = traps.traps[n].left.p1.x;
606 0 : boxes[n].p1.y = traps.traps[n].top;
607 0 : boxes[n].p2.x = traps.traps[n].right.p1.x;
608 0 : boxes[n].p2.y = traps.traps[n].bottom;
609 : }
610 :
611 0 : _cairo_traps_clear (&traps);
612 0 : _cairo_traps_limit (&traps, boxes, n);
613 0 : status = _cairo_path_fixed_fill_to_traps (&clip_path->prev->path,
614 0 : clip_path->prev->fill_rule,
615 0 : clip_path->prev->tolerance,
616 : &traps);
617 0 : if (boxes != stack_boxes)
618 0 : free (boxes);
619 :
620 0 : if (unlikely (status))
621 0 : return status;
622 :
623 0 : status = _cairo_traps_extract_region (&traps, &clip_path->region);
624 0 : _cairo_traps_fini (&traps);
625 :
626 0 : if (status == CAIRO_INT_STATUS_UNSUPPORTED)
627 0 : goto UNSUPPORTED;
628 0 : if (unlikely (status))
629 0 : return status;
630 :
631 0 : clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION;
632 0 : return CAIRO_STATUS_SUCCESS;
633 :
634 : UNSUPPORTED:
635 0 : clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
636 0 : return CAIRO_INT_STATUS_UNSUPPORTED;
637 : }
638 :
639 : static cairo_int_status_t
640 0 : _cairo_clip_path_to_region (cairo_clip_path_t *clip_path)
641 : {
642 : cairo_int_status_t status;
643 0 : cairo_region_t *prev = NULL;
644 :
645 0 : if (clip_path->flags &
646 : (CAIRO_CLIP_PATH_HAS_REGION |
647 : CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED))
648 : {
649 0 : return clip_path->flags & CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED ?
650 : CAIRO_INT_STATUS_UNSUPPORTED :
651 : CAIRO_STATUS_SUCCESS;
652 : }
653 :
654 0 : if (! clip_path->path.maybe_fill_region)
655 0 : return _cairo_clip_path_to_region_geometric (clip_path);
656 :
657 : /* first retrieve the region for our antecedents */
658 0 : if (clip_path->prev != NULL) {
659 0 : status = _cairo_clip_path_to_region (clip_path->prev);
660 0 : if (status) {
661 0 : if (status == CAIRO_INT_STATUS_UNSUPPORTED)
662 0 : return _cairo_clip_path_to_region_geometric (clip_path);
663 :
664 0 : return status;
665 : }
666 :
667 0 : prev = clip_path->prev->region;
668 : }
669 :
670 : /* now extract the region for ourselves */
671 0 : clip_path->region =
672 0 : _cairo_path_fixed_fill_rectilinear_to_region (&clip_path->path,
673 : clip_path->fill_rule,
674 0 : &clip_path->extents);
675 0 : assert (clip_path->region != NULL);
676 :
677 0 : status = clip_path->region->status;
678 0 : if (unlikely (status))
679 0 : return status;
680 :
681 0 : if (prev != NULL) {
682 0 : status = cairo_region_intersect (clip_path->region, prev);
683 0 : if (unlikely (status))
684 0 : return status;
685 : }
686 :
687 0 : clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION;
688 0 : return CAIRO_STATUS_SUCCESS;
689 : }
690 :
691 : static inline int
692 0 : pot (int v)
693 : {
694 0 : v--;
695 0 : v |= v >> 1;
696 0 : v |= v >> 2;
697 0 : v |= v >> 4;
698 0 : v |= v >> 8;
699 0 : v |= v >> 16;
700 0 : v++;
701 0 : return v;
702 : }
703 :
704 : /* XXX there is likely a faster method! ;-) */
705 : static cairo_status_t
706 0 : _region_clip_to_boxes (const cairo_region_t *region,
707 : cairo_box_t **boxes,
708 : int *num_boxes,
709 : int *size_boxes)
710 : {
711 : cairo_traps_t traps;
712 : cairo_status_t status;
713 : int n, num_rects;
714 :
715 0 : _cairo_traps_init (&traps);
716 0 : _cairo_traps_limit (&traps, *boxes, *num_boxes);
717 0 : traps.is_rectilinear = TRUE;
718 0 : traps.is_rectangular = TRUE;
719 :
720 0 : num_rects = cairo_region_num_rectangles (region);
721 0 : for (n = 0; n < num_rects; n++) {
722 : cairo_rectangle_int_t rect;
723 : cairo_point_t p1, p2;
724 :
725 0 : cairo_region_get_rectangle (region, n, &rect);
726 :
727 0 : p1.x = _cairo_fixed_from_int (rect.x);
728 0 : p1.y = _cairo_fixed_from_int (rect.y);
729 0 : p2.x = _cairo_fixed_from_int (rect.x + rect.width);
730 0 : p2.y = _cairo_fixed_from_int (rect.y + rect.height);
731 :
732 0 : status = _cairo_traps_tessellate_rectangle (&traps, &p1, &p2);
733 0 : if (unlikely (status))
734 0 : goto CLEANUP;
735 : }
736 :
737 0 : status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps, CAIRO_FILL_RULE_WINDING);
738 0 : if (unlikely (status))
739 0 : goto CLEANUP;
740 :
741 0 : n = *size_boxes;
742 0 : if (n < 0)
743 0 : n = -n;
744 :
745 0 : if (traps.num_traps > n) {
746 : cairo_box_t *new_boxes;
747 :
748 0 : new_boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t));
749 0 : if (unlikely (new_boxes == NULL)) {
750 0 : status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
751 0 : goto CLEANUP;
752 : }
753 :
754 0 : if (*size_boxes > 0)
755 0 : free (*boxes);
756 :
757 0 : *boxes = new_boxes;
758 0 : *size_boxes = traps.num_traps;
759 : }
760 :
761 0 : for (n = 0; n < traps.num_traps; n++) {
762 0 : (*boxes)[n].p1.x = traps.traps[n].left.p1.x;
763 0 : (*boxes)[n].p1.y = traps.traps[n].top;
764 0 : (*boxes)[n].p2.x = traps.traps[n].right.p1.x;
765 0 : (*boxes)[n].p2.y = traps.traps[n].bottom;
766 : }
767 0 : *num_boxes = n;
768 :
769 : CLEANUP:
770 0 : _cairo_traps_fini (&traps);
771 :
772 0 : return status;
773 : }
774 :
775 : static cairo_status_t
776 0 : _rectilinear_clip_to_boxes (const cairo_path_fixed_t *path,
777 : cairo_fill_rule_t fill_rule,
778 : cairo_box_t **boxes,
779 : int *num_boxes,
780 : int *size_boxes)
781 : {
782 : cairo_polygon_t polygon;
783 : cairo_traps_t traps;
784 : cairo_status_t status;
785 :
786 0 : _cairo_traps_init (&traps);
787 0 : _cairo_traps_limit (&traps, *boxes, *num_boxes);
788 :
789 0 : _cairo_polygon_init (&polygon);
790 0 : _cairo_polygon_limit (&polygon, *boxes, *num_boxes);
791 :
792 0 : status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
793 : fill_rule,
794 : &traps);
795 0 : if (unlikely (_cairo_status_is_error (status)))
796 0 : goto CLEANUP;
797 0 : if (status == CAIRO_STATUS_SUCCESS)
798 0 : goto BOXES;
799 :
800 : /* tolerance will be ignored as the path is rectilinear */
801 0 : status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon);
802 0 : if (unlikely (status))
803 0 : goto CLEANUP;
804 :
805 0 : if (polygon.num_edges == 0) {
806 0 : *num_boxes = 0;
807 : } else {
808 0 : status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
809 : &polygon,
810 : fill_rule);
811 0 : if (likely (status == CAIRO_STATUS_SUCCESS)) {
812 : int i;
813 :
814 : BOXES:
815 0 : i = *size_boxes;
816 0 : if (i < 0)
817 0 : i = -i;
818 :
819 0 : if (traps.num_traps > i) {
820 : cairo_box_t *new_boxes;
821 : int new_size;
822 :
823 0 : new_size = pot (traps.num_traps);
824 0 : new_boxes = _cairo_malloc_ab (new_size, sizeof (cairo_box_t));
825 0 : if (unlikely (new_boxes == NULL)) {
826 0 : status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
827 0 : goto CLEANUP;
828 : }
829 :
830 0 : if (*size_boxes > 0)
831 0 : free (*boxes);
832 :
833 0 : *boxes = new_boxes;
834 0 : *size_boxes = new_size;
835 : }
836 :
837 0 : for (i = 0; i < traps.num_traps; i++) {
838 0 : (*boxes)[i].p1.x = traps.traps[i].left.p1.x;
839 0 : (*boxes)[i].p1.y = traps.traps[i].top;
840 0 : (*boxes)[i].p2.x = traps.traps[i].right.p1.x;
841 0 : (*boxes)[i].p2.y = traps.traps[i].bottom;
842 : }
843 0 : *num_boxes = i;
844 : }
845 : }
846 :
847 : CLEANUP:
848 0 : _cairo_polygon_fini (&polygon);
849 0 : _cairo_traps_fini (&traps);
850 :
851 0 : return status;
852 : }
853 :
854 : static cairo_int_status_t
855 0 : _cairo_clip_path_to_boxes (cairo_clip_path_t *clip_path,
856 : cairo_box_t **boxes,
857 : int *count)
858 : {
859 0 : int size = -*count;
860 0 : int num_boxes = 0;
861 : cairo_status_t status;
862 :
863 0 : if (clip_path->region != NULL) {
864 : int num_rects, n;
865 :
866 0 : num_rects = cairo_region_num_rectangles (clip_path->region);
867 0 : if (num_rects > -size) {
868 : cairo_box_t *new_boxes;
869 :
870 0 : new_boxes = _cairo_malloc_ab (num_rects, sizeof (cairo_box_t));
871 0 : if (unlikely (new_boxes == NULL))
872 0 : return _cairo_error (CAIRO_STATUS_NO_MEMORY);
873 :
874 0 : *boxes = new_boxes;
875 : }
876 :
877 0 : for (n = 0; n < num_rects; n++) {
878 : cairo_rectangle_int_t rect;
879 :
880 0 : cairo_region_get_rectangle (clip_path->region, n, &rect);
881 0 : (*boxes)[n].p1.x = _cairo_fixed_from_int (rect.x);
882 0 : (*boxes)[n].p1.y = _cairo_fixed_from_int (rect.y);
883 0 : (*boxes)[n].p2.x = _cairo_fixed_from_int (rect.x + rect.width);
884 0 : (*boxes)[n].p2.y = _cairo_fixed_from_int (rect.y + rect.height);
885 : }
886 :
887 0 : *count = num_rects;
888 0 : return CAIRO_STATUS_SUCCESS;
889 : }
890 :
891 : /* keep it simple at first */
892 0 : if (! _clip_paths_are_rectilinear (clip_path))
893 0 : return CAIRO_INT_STATUS_UNSUPPORTED;
894 :
895 0 : assert (-size >= 1);
896 0 : if (_cairo_path_fixed_is_box (&clip_path->path, *boxes)) {
897 0 : num_boxes = 1;
898 : } else {
899 0 : status = _rectilinear_clip_to_boxes (&clip_path->path,
900 : clip_path->fill_rule,
901 : boxes, &num_boxes, &size);
902 0 : if (unlikely (status))
903 0 : return status;
904 : }
905 :
906 0 : while (num_boxes > 0 && (clip_path = clip_path->prev) != NULL) {
907 : cairo_box_t box;
908 :
909 0 : if (clip_path->region != NULL) {
910 0 : status = _region_clip_to_boxes (clip_path->region,
911 : boxes, &num_boxes, &size);
912 0 : if (unlikely (status))
913 0 : return status;
914 :
915 0 : break;
916 0 : } else if (_cairo_path_fixed_is_box (&clip_path->path, &box)) {
917 : int i, j;
918 :
919 0 : for (i = j = 0; i < num_boxes; i++) {
920 0 : if (j != i)
921 0 : (*boxes)[j] = (*boxes)[i];
922 :
923 0 : if (box.p1.x > (*boxes)[j].p1.x)
924 0 : (*boxes)[j].p1.x = box.p1.x;
925 0 : if (box.p2.x < (*boxes)[j].p2.x)
926 0 : (*boxes)[j].p2.x = box.p2.x;
927 :
928 0 : if (box.p1.y > (*boxes)[j].p1.y)
929 0 : (*boxes)[j].p1.y = box.p1.y;
930 0 : if (box.p2.y < (*boxes)[j].p2.y)
931 0 : (*boxes)[j].p2.y = box.p2.y;
932 :
933 0 : j += (*boxes)[j].p2.x > (*boxes)[j].p1.x &&
934 0 : (*boxes)[j].p2.y > (*boxes)[j].p1.y;
935 : }
936 :
937 0 : num_boxes = j;
938 : } else {
939 0 : status = _rectilinear_clip_to_boxes (&clip_path->path,
940 : clip_path->fill_rule,
941 : boxes, &num_boxes, &size);
942 0 : if (unlikely (status))
943 0 : return status;
944 : }
945 : }
946 :
947 0 : *count = num_boxes;
948 0 : return CAIRO_STATUS_SUCCESS;
949 : }
950 :
951 : static cairo_surface_t *
952 0 : _cairo_clip_path_get_surface (cairo_clip_path_t *clip_path,
953 : cairo_surface_t *target,
954 : int *tx, int *ty)
955 : {
956 0 : const cairo_rectangle_int_t *clip_extents = &clip_path->extents;
957 : cairo_bool_t need_translate;
958 : cairo_surface_t *surface;
959 : cairo_clip_path_t *prev;
960 : cairo_status_t status;
961 :
962 0 : while (clip_path->prev != NULL &&
963 0 : clip_path->flags & CAIRO_CLIP_PATH_IS_BOX &&
964 : clip_path->path.maybe_fill_region)
965 : {
966 0 : clip_path = clip_path->prev;
967 : }
968 :
969 0 : clip_extents = &clip_path->extents;
970 0 : if (clip_path->surface != NULL &&
971 0 : clip_path->surface->backend == target->backend)
972 : {
973 0 : *tx = clip_extents->x;
974 0 : *ty = clip_extents->y;
975 0 : return clip_path->surface;
976 : }
977 :
978 0 : surface = _cairo_surface_create_similar_scratch (target,
979 : CAIRO_CONTENT_ALPHA,
980 : clip_extents->width,
981 : clip_extents->height);
982 0 : if (surface == NULL) {
983 0 : surface = cairo_image_surface_create (CAIRO_FORMAT_A8,
984 : clip_extents->width,
985 : clip_extents->height);
986 : }
987 0 : if (unlikely (surface->status))
988 0 : return surface;
989 :
990 0 : need_translate = clip_extents->x | clip_extents->y;
991 0 : if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX &&
992 : clip_path->path.maybe_fill_region)
993 : {
994 0 : status = _cairo_surface_paint (surface,
995 : CAIRO_OPERATOR_SOURCE,
996 : &_cairo_pattern_white.base,
997 : NULL);
998 0 : if (unlikely (status))
999 0 : goto BAIL;
1000 : }
1001 : else
1002 : {
1003 0 : status = _cairo_surface_paint (surface,
1004 : CAIRO_OPERATOR_CLEAR,
1005 : &_cairo_pattern_clear.base,
1006 : NULL);
1007 0 : if (unlikely (status))
1008 0 : goto BAIL;
1009 :
1010 0 : if (need_translate) {
1011 0 : _cairo_path_fixed_translate (&clip_path->path,
1012 0 : _cairo_fixed_from_int (-clip_extents->x),
1013 0 : _cairo_fixed_from_int (-clip_extents->y));
1014 : }
1015 0 : status = _cairo_surface_fill (surface,
1016 : CAIRO_OPERATOR_ADD,
1017 : &_cairo_pattern_white.base,
1018 : &clip_path->path,
1019 : clip_path->fill_rule,
1020 : clip_path->tolerance,
1021 : clip_path->antialias,
1022 : NULL);
1023 0 : if (need_translate) {
1024 0 : _cairo_path_fixed_translate (&clip_path->path,
1025 : _cairo_fixed_from_int (clip_extents->x),
1026 : _cairo_fixed_from_int (clip_extents->y));
1027 : }
1028 :
1029 0 : if (unlikely (status))
1030 0 : goto BAIL;
1031 : }
1032 :
1033 0 : prev = clip_path->prev;
1034 0 : while (prev != NULL) {
1035 0 : if (prev->flags & CAIRO_CLIP_PATH_IS_BOX &&
1036 : prev->path.maybe_fill_region)
1037 : {
1038 : /* a simple box only affects the extents */
1039 : }
1040 0 : else if (prev->path.is_rectilinear ||
1041 0 : prev->surface == NULL ||
1042 0 : prev->surface->backend != target->backend)
1043 : {
1044 0 : if (need_translate) {
1045 0 : _cairo_path_fixed_translate (&prev->path,
1046 0 : _cairo_fixed_from_int (-clip_extents->x),
1047 0 : _cairo_fixed_from_int (-clip_extents->y));
1048 : }
1049 0 : status = _cairo_surface_fill (surface,
1050 : CAIRO_OPERATOR_IN,
1051 : &_cairo_pattern_white.base,
1052 : &prev->path,
1053 : prev->fill_rule,
1054 : prev->tolerance,
1055 : prev->antialias,
1056 : NULL);
1057 0 : if (need_translate) {
1058 0 : _cairo_path_fixed_translate (&prev->path,
1059 : _cairo_fixed_from_int (clip_extents->x),
1060 : _cairo_fixed_from_int (clip_extents->y));
1061 : }
1062 :
1063 0 : if (unlikely (status))
1064 0 : goto BAIL;
1065 : }
1066 : else
1067 : {
1068 : cairo_surface_pattern_t pattern;
1069 : cairo_surface_t *prev_surface;
1070 : int prev_tx, prev_ty;
1071 :
1072 0 : prev_surface = _cairo_clip_path_get_surface (prev, target, &prev_tx, &prev_ty);
1073 0 : status = prev_surface->status;
1074 0 : if (unlikely (status))
1075 0 : goto BAIL;
1076 :
1077 0 : _cairo_pattern_init_for_surface (&pattern, prev_surface);
1078 0 : pattern.base.filter = CAIRO_FILTER_NEAREST;
1079 0 : cairo_matrix_init_translate (&pattern.base.matrix,
1080 0 : clip_extents->x - prev_tx,
1081 0 : clip_extents->y - prev_ty);
1082 0 : status = _cairo_surface_paint (surface,
1083 : CAIRO_OPERATOR_IN,
1084 : &pattern.base,
1085 : NULL);
1086 0 : _cairo_pattern_fini (&pattern.base);
1087 :
1088 0 : if (unlikely (status))
1089 0 : goto BAIL;
1090 :
1091 0 : break;
1092 : }
1093 :
1094 0 : prev = prev->prev;
1095 : }
1096 :
1097 0 : *tx = clip_extents->x;
1098 0 : *ty = clip_extents->y;
1099 0 : cairo_surface_destroy (clip_path->surface);
1100 0 : return clip_path->surface = surface;
1101 :
1102 : BAIL:
1103 0 : cairo_surface_destroy (surface);
1104 0 : return _cairo_surface_create_in_error (status);
1105 : }
1106 :
1107 : cairo_bool_t
1108 0 : _cairo_clip_contains_rectangle (cairo_clip_t *clip,
1109 : const cairo_rectangle_int_t *rect)
1110 : {
1111 : cairo_clip_path_t *clip_path;
1112 :
1113 0 : if (clip == NULL)
1114 0 : return FALSE;
1115 :
1116 0 : clip_path = clip->path;
1117 0 : if (clip_path->extents.x > rect->x ||
1118 0 : clip_path->extents.y > rect->y ||
1119 0 : clip_path->extents.x + clip_path->extents.width < rect->x + rect->width ||
1120 0 : clip_path->extents.y + clip_path->extents.height < rect->y + rect->height)
1121 : {
1122 0 : return FALSE;
1123 : }
1124 :
1125 : do {
1126 : cairo_box_t box;
1127 :
1128 0 : if ((clip_path->flags & CAIRO_CLIP_PATH_IS_BOX) == 0)
1129 0 : return FALSE;
1130 :
1131 0 : if (! _cairo_path_fixed_is_box (&clip_path->path, &box))
1132 0 : return FALSE;
1133 :
1134 0 : if (box.p1.x > _cairo_fixed_from_int (rect->x) ||
1135 0 : box.p1.y > _cairo_fixed_from_int (rect->y) ||
1136 0 : box.p2.x < _cairo_fixed_from_int (rect->x + rect->width) ||
1137 0 : box.p2.y < _cairo_fixed_from_int (rect->y + rect->height))
1138 : {
1139 0 : return FALSE;
1140 : }
1141 0 : } while ((clip_path = clip_path->prev) != NULL);
1142 :
1143 0 : return TRUE;
1144 : }
1145 :
1146 : cairo_bool_t
1147 64 : _cairo_clip_contains_extents (cairo_clip_t *clip,
1148 : const cairo_composite_rectangles_t *extents)
1149 : {
1150 : const cairo_rectangle_int_t *rect;
1151 :
1152 64 : if (clip == NULL)
1153 64 : return FALSE;
1154 :
1155 0 : rect = extents->is_bounded ? &extents->bounded : &extents->unbounded;
1156 0 : return _cairo_clip_contains_rectangle (clip, rect);
1157 : }
1158 :
1159 : void
1160 0 : _cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip)
1161 : {
1162 : cairo_clip_path_t *clip_path;
1163 :
1164 0 : if (clip == NULL) {
1165 0 : fprintf (stream, "no clip\n");
1166 0 : return;
1167 : }
1168 :
1169 0 : if (clip->all_clipped) {
1170 0 : fprintf (stream, "clip: all-clipped\n");
1171 0 : return;
1172 : }
1173 :
1174 0 : if (clip->path == NULL) {
1175 0 : fprintf (stream, "clip: empty\n");
1176 0 : return;
1177 : }
1178 :
1179 0 : fprintf (stream, "clip:\n");
1180 :
1181 0 : clip_path = clip->path;
1182 : do {
1183 0 : fprintf (stream, "path: has region? %s, has surface? %s, aa=%d, tolerance=%f, rule=%d: ",
1184 0 : clip_path->region == NULL ? "no" : "yes",
1185 0 : clip_path->surface == NULL ? "no" : "yes",
1186 0 : clip_path->antialias,
1187 : clip_path->tolerance,
1188 0 : clip_path->fill_rule);
1189 0 : _cairo_debug_print_path (stream, &clip_path->path);
1190 0 : fprintf (stream, "\n");
1191 0 : } while ((clip_path = clip_path->prev) != NULL);
1192 : }
1193 :
1194 : cairo_surface_t *
1195 0 : _cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target, int *tx, int *ty)
1196 : {
1197 : /* XXX is_clear -> all_clipped */
1198 0 : assert (clip->path != NULL);
1199 0 : return _cairo_clip_path_get_surface (clip->path, target, tx, ty);
1200 : }
1201 :
1202 : cairo_status_t
1203 0 : _cairo_clip_combine_with_surface (cairo_clip_t *clip,
1204 : cairo_surface_t *dst,
1205 : int dst_x, int dst_y)
1206 : {
1207 0 : cairo_clip_path_t *clip_path = clip->path;
1208 : cairo_bool_t need_translate;
1209 : cairo_status_t status;
1210 :
1211 0 : assert (clip_path != NULL);
1212 :
1213 0 : need_translate = dst_x | dst_y;
1214 : do {
1215 0 : if (clip_path->surface != NULL &&
1216 0 : clip_path->surface->backend == dst->backend)
1217 : {
1218 : cairo_surface_pattern_t pattern;
1219 :
1220 0 : _cairo_pattern_init_for_surface (&pattern, clip_path->surface);
1221 0 : cairo_matrix_init_translate (&pattern.base.matrix,
1222 0 : dst_x - clip_path->extents.x,
1223 0 : dst_y - clip_path->extents.y);
1224 0 : pattern.base.filter = CAIRO_FILTER_NEAREST;
1225 0 : status = _cairo_surface_paint (dst,
1226 : CAIRO_OPERATOR_IN,
1227 : &pattern.base,
1228 : NULL);
1229 :
1230 0 : _cairo_pattern_fini (&pattern.base);
1231 :
1232 0 : return status;
1233 : }
1234 :
1235 0 : if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX &&
1236 : clip_path->path.maybe_fill_region)
1237 : {
1238 0 : continue;
1239 : }
1240 :
1241 0 : if (need_translate) {
1242 0 : _cairo_path_fixed_translate (&clip_path->path,
1243 : _cairo_fixed_from_int (-dst_x),
1244 : _cairo_fixed_from_int (-dst_y));
1245 : }
1246 0 : status = _cairo_surface_fill (dst,
1247 : CAIRO_OPERATOR_IN,
1248 : &_cairo_pattern_white.base,
1249 : &clip_path->path,
1250 : clip_path->fill_rule,
1251 : clip_path->tolerance,
1252 : clip_path->antialias,
1253 : NULL);
1254 0 : if (need_translate) {
1255 0 : _cairo_path_fixed_translate (&clip_path->path,
1256 : _cairo_fixed_from_int (dst_x),
1257 : _cairo_fixed_from_int (dst_y));
1258 : }
1259 :
1260 0 : if (unlikely (status))
1261 0 : return status;
1262 0 : } while ((clip_path = clip_path->prev) != NULL);
1263 :
1264 0 : return CAIRO_STATUS_SUCCESS;
1265 : }
1266 :
1267 : static const cairo_rectangle_int_t _cairo_empty_rectangle_int = { 0, 0, 0, 0 };
1268 :
1269 : const cairo_rectangle_int_t *
1270 0 : _cairo_clip_get_extents (const cairo_clip_t *clip)
1271 : {
1272 0 : if (clip->all_clipped)
1273 0 : return &_cairo_empty_rectangle_int;
1274 :
1275 0 : if (clip->path == NULL)
1276 0 : return NULL;
1277 :
1278 0 : return &clip->path->extents;
1279 : }
1280 :
1281 : void
1282 0 : _cairo_clip_drop_cache (cairo_clip_t *clip)
1283 : {
1284 : cairo_clip_path_t *clip_path;
1285 :
1286 0 : if (clip->path == NULL)
1287 0 : return;
1288 :
1289 0 : clip_path = clip->path;
1290 : do {
1291 0 : if (clip_path->region != NULL) {
1292 0 : cairo_region_destroy (clip_path->region);
1293 0 : clip_path->region = NULL;
1294 : }
1295 :
1296 0 : if (clip_path->surface != NULL) {
1297 0 : cairo_surface_destroy (clip_path->surface);
1298 0 : clip_path->surface = NULL;
1299 : }
1300 :
1301 0 : clip_path->flags &= ~CAIRO_CLIP_PATH_HAS_REGION;
1302 0 : } while ((clip_path = clip_path->prev) != NULL);
1303 : }
1304 :
1305 : const cairo_rectangle_list_t _cairo_rectangles_nil =
1306 : { CAIRO_STATUS_NO_MEMORY, NULL, 0 };
1307 : static const cairo_rectangle_list_t _cairo_rectangles_not_representable =
1308 : { CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, NULL, 0 };
1309 :
1310 : static cairo_bool_t
1311 0 : _cairo_clip_int_rect_to_user (cairo_gstate_t *gstate,
1312 : cairo_rectangle_int_t *clip_rect,
1313 : cairo_rectangle_t *user_rect)
1314 : {
1315 : cairo_bool_t is_tight;
1316 :
1317 0 : double x1 = clip_rect->x;
1318 0 : double y1 = clip_rect->y;
1319 0 : double x2 = clip_rect->x + (int) clip_rect->width;
1320 0 : double y2 = clip_rect->y + (int) clip_rect->height;
1321 :
1322 0 : _cairo_gstate_backend_to_user_rectangle (gstate,
1323 : &x1, &y1, &x2, &y2,
1324 : &is_tight);
1325 :
1326 0 : user_rect->x = x1;
1327 0 : user_rect->y = y1;
1328 0 : user_rect->width = x2 - x1;
1329 0 : user_rect->height = y2 - y1;
1330 :
1331 0 : return is_tight;
1332 : }
1333 :
1334 : cairo_int_status_t
1335 0 : _cairo_clip_get_region (cairo_clip_t *clip,
1336 : cairo_region_t **region)
1337 : {
1338 : cairo_int_status_t status;
1339 :
1340 0 : if (clip->all_clipped)
1341 0 : goto CLIPPED;
1342 :
1343 0 : assert (clip->path != NULL);
1344 :
1345 0 : status = _cairo_clip_path_to_region (clip->path);
1346 0 : if (status)
1347 0 : return status;
1348 :
1349 0 : if (cairo_region_is_empty (clip->path->region)) {
1350 0 : _cairo_clip_set_all_clipped (clip);
1351 0 : goto CLIPPED;
1352 : }
1353 :
1354 0 : if (region)
1355 0 : *region = clip->path->region;
1356 0 : return CAIRO_STATUS_SUCCESS;
1357 :
1358 : CLIPPED:
1359 0 : if (region)
1360 0 : *region = NULL;
1361 0 : return CAIRO_INT_STATUS_NOTHING_TO_DO;
1362 : }
1363 :
1364 : cairo_int_status_t
1365 0 : _cairo_clip_get_boxes (cairo_clip_t *clip,
1366 : cairo_box_t **boxes,
1367 : int *count)
1368 : {
1369 : cairo_int_status_t status;
1370 :
1371 0 : if (clip->all_clipped)
1372 0 : return CAIRO_INT_STATUS_NOTHING_TO_DO;
1373 :
1374 0 : assert (clip->path != NULL);
1375 :
1376 0 : status = _cairo_clip_path_to_boxes (clip->path, boxes, count);
1377 0 : if (status)
1378 0 : return status;
1379 :
1380 0 : if (*count == 0) {
1381 0 : _cairo_clip_set_all_clipped (clip);
1382 0 : return CAIRO_INT_STATUS_NOTHING_TO_DO;
1383 : }
1384 :
1385 0 : return CAIRO_STATUS_SUCCESS;
1386 : }
1387 :
1388 : static cairo_bool_t
1389 0 : box_is_aligned (const cairo_box_t *box)
1390 : {
1391 0 : return
1392 0 : _cairo_fixed_is_integer (box->p1.x) &&
1393 0 : _cairo_fixed_is_integer (box->p1.y) &&
1394 0 : _cairo_fixed_is_integer (box->p2.x) &&
1395 0 : _cairo_fixed_is_integer (box->p2.y);
1396 : }
1397 :
1398 : static void
1399 0 : intersect_with_boxes (cairo_composite_rectangles_t *extents,
1400 : cairo_box_t *boxes,
1401 : int num_boxes)
1402 : {
1403 : cairo_rectangle_int_t rect;
1404 : cairo_box_t box;
1405 : cairo_bool_t is_empty;
1406 :
1407 0 : box.p1.x = box.p1.y = INT_MIN;
1408 0 : box.p2.x = box.p2.y = INT_MAX;
1409 0 : while (num_boxes--) {
1410 0 : if (boxes->p1.x < box.p1.x)
1411 0 : box.p1.x = boxes->p1.x;
1412 0 : if (boxes->p1.y < box.p1.y)
1413 0 : box.p1.y = boxes->p1.y;
1414 :
1415 0 : if (boxes->p2.x > box.p2.x)
1416 0 : box.p2.x = boxes->p2.x;
1417 0 : if (boxes->p2.y > box.p2.y)
1418 0 : box.p2.y = boxes->p2.y;
1419 : }
1420 :
1421 0 : _cairo_box_round_to_rectangle (&box, &rect);
1422 0 : is_empty = _cairo_rectangle_intersect (&extents->bounded, &rect);
1423 0 : is_empty = _cairo_rectangle_intersect (&extents->unbounded, &rect);
1424 0 : }
1425 :
1426 : cairo_status_t
1427 64 : _cairo_clip_to_boxes (cairo_clip_t **clip,
1428 : cairo_composite_rectangles_t *extents,
1429 : cairo_box_t **boxes,
1430 : int *num_boxes)
1431 : {
1432 : cairo_status_t status;
1433 : const cairo_rectangle_int_t *rect;
1434 :
1435 64 : rect = extents->is_bounded ? &extents->bounded : &extents->unbounded;
1436 :
1437 64 : if (*clip == NULL)
1438 64 : goto EXTENTS;
1439 :
1440 0 : status = _cairo_clip_rectangle (*clip, rect);
1441 0 : if (unlikely (status))
1442 0 : return status;
1443 :
1444 0 : status = _cairo_clip_get_boxes (*clip, boxes, num_boxes);
1445 0 : switch ((int) status) {
1446 : case CAIRO_STATUS_SUCCESS:
1447 0 : intersect_with_boxes (extents, *boxes, *num_boxes);
1448 0 : if (rect->width == 0 || rect->height == 0 ||
1449 0 : extents->is_bounded ||
1450 0 : (*num_boxes == 1 && box_is_aligned (*boxes)))
1451 : {
1452 0 : *clip = NULL;
1453 : }
1454 0 : goto DONE;
1455 :
1456 : case CAIRO_INT_STATUS_UNSUPPORTED:
1457 0 : goto EXTENTS;
1458 :
1459 : default:
1460 0 : return status;
1461 : }
1462 :
1463 : EXTENTS:
1464 64 : status = CAIRO_STATUS_SUCCESS;
1465 64 : _cairo_box_from_rectangle (&(*boxes)[0], rect);
1466 64 : *num_boxes = 1;
1467 : DONE:
1468 64 : return status;
1469 : }
1470 :
1471 :
1472 : static cairo_rectangle_list_t *
1473 0 : _cairo_rectangle_list_create_in_error (cairo_status_t status)
1474 : {
1475 : cairo_rectangle_list_t *list;
1476 :
1477 0 : if (status == CAIRO_STATUS_NO_MEMORY)
1478 0 : return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
1479 0 : if (status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE)
1480 0 : return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable;
1481 :
1482 0 : list = malloc (sizeof (*list));
1483 0 : if (unlikely (list == NULL)) {
1484 0 : _cairo_error_throw (status);
1485 0 : return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
1486 : }
1487 :
1488 0 : list->status = status;
1489 0 : list->rectangles = NULL;
1490 0 : list->num_rectangles = 0;
1491 :
1492 0 : return list;
1493 : }
1494 :
1495 : cairo_rectangle_list_t *
1496 0 : _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate)
1497 : {
1498 : #define ERROR_LIST(S) _cairo_rectangle_list_create_in_error (_cairo_error (S))
1499 :
1500 : cairo_rectangle_list_t *list;
1501 0 : cairo_rectangle_t *rectangles = NULL;
1502 0 : cairo_region_t *region = NULL;
1503 : cairo_int_status_t status;
1504 0 : int n_rects = 0;
1505 : int i;
1506 :
1507 0 : if (clip->all_clipped)
1508 0 : goto DONE;
1509 :
1510 0 : if (!clip->path)
1511 0 : return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
1512 :
1513 0 : status = _cairo_clip_get_region (clip, ®ion);
1514 0 : if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
1515 0 : goto DONE;
1516 0 : } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
1517 0 : return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
1518 0 : } else if (unlikely (status)) {
1519 0 : return ERROR_LIST (status);
1520 : }
1521 :
1522 0 : n_rects = cairo_region_num_rectangles (region);
1523 0 : if (n_rects) {
1524 0 : rectangles = _cairo_malloc_ab (n_rects, sizeof (cairo_rectangle_t));
1525 0 : if (unlikely (rectangles == NULL)) {
1526 0 : return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
1527 : }
1528 :
1529 0 : for (i = 0; i < n_rects; ++i) {
1530 : cairo_rectangle_int_t clip_rect;
1531 :
1532 0 : cairo_region_get_rectangle (region, i, &clip_rect);
1533 :
1534 0 : if (! _cairo_clip_int_rect_to_user (gstate,
1535 : &clip_rect,
1536 0 : &rectangles[i]))
1537 : {
1538 0 : free (rectangles);
1539 0 : return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
1540 : }
1541 : }
1542 : }
1543 :
1544 : DONE:
1545 0 : list = malloc (sizeof (cairo_rectangle_list_t));
1546 0 : if (unlikely (list == NULL)) {
1547 0 : free (rectangles);
1548 0 : return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
1549 : }
1550 :
1551 0 : list->status = CAIRO_STATUS_SUCCESS;
1552 0 : list->rectangles = rectangles;
1553 0 : list->num_rectangles = n_rects;
1554 0 : return list;
1555 :
1556 : #undef ERROR_LIST
1557 : }
1558 :
1559 : /**
1560 : * cairo_rectangle_list_destroy:
1561 : * @rectangle_list: a rectangle list, as obtained from cairo_copy_clip_rectangles()
1562 : *
1563 : * Unconditionally frees @rectangle_list and all associated
1564 : * references. After this call, the @rectangle_list pointer must not
1565 : * be dereferenced.
1566 : *
1567 : * Since: 1.4
1568 : **/
1569 : void
1570 0 : cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list)
1571 : {
1572 0 : if (rectangle_list == NULL || rectangle_list == &_cairo_rectangles_nil ||
1573 : rectangle_list == &_cairo_rectangles_not_representable)
1574 0 : return;
1575 :
1576 0 : free (rectangle_list->rectangles);
1577 0 : free (rectangle_list);
1578 : }
1579 :
1580 : void
1581 3 : _cairo_clip_reset_static_data (void)
1582 : {
1583 : _freed_pool_reset (&clip_path_pool);
1584 3 : }
|